canvas-record v5.0.0
canvas-record
Record a video in the browser or directly on the File System from a canvas (2D/WebGL/WebGPU) as MP4, WebM, MKV, GIF, PNG/JPG Sequence using WebCodecs and Wasm when available.
Installation
npm install canvas-record
Usage
import { Recorder, RecorderStatus, Encoders } from "canvas-record";
import createCanvasContext from "canvas-context";
import { AVC } from "media-codecs";
// Setup
const pixelRatio = devicePixelRatio;
const width = 512;
const height = 512;
const { context, canvas } = createCanvasContext("2d", {
width: width * pixelRatio,
height: height * pixelRatio,
contextAttributes: { willReadFrequently: true },
});
Object.assign(canvas.style, { width: `${width}px`, height: `${height}px` });
const mainElement = document.querySelector("main");
mainElement.appendChild(canvas);
// Animation
let canvasRecorder;
function render() {
const width = canvas.width;
const height = canvas.height;
const t = canvasRecorder.frame / canvasRecorder.frameTotal || Number.EPSILON;
context.clearRect(0, 0, width, height);
context.fillStyle = "red";
context.fillRect(0, 0, t * width, height);
}
const tick = async () => {
render();
if (canvasRecorder.status !== RecorderStatus.Recording) return;
await canvasRecorder.step();
if (canvasRecorder.status !== RecorderStatus.Stopped) {
requestAnimationFrame(() => tick());
}
};
canvasRecorder = new Recorder(context, {
name: "canvas-record-example",
encoderOptions: {
codec: AVC.getCodec({ profile: "Main", level: "5.2" }),
},
});
// Start and encode frame 0
await canvasRecorder.start();
// Animate to encode the rest
tick(canvasRecorder);
API
Encoder comparison:
Encoder | Extension | Required Web API | WASM | Speed |
---|---|---|---|---|
WebCodecs | mp4 / webm / mkv | WebCodecs | ❌ | Fast |
MP4Wasm | mp4 | WebCodecs | ✅ (embed) | Fast |
H264MP4 | mp4 | ✅ (embed) | Medium | |
FFmpeg | mp4 / webm | SharedArrayBuffer | ✅ (need binary path) | Slow |
GIF | gif | WebWorkers (wip) | ❌ | Fast |
Frame | png / jpg | File System Access | ❌ | Fast |
MediaCapture | mkv / webm | MediaStream | ❌ | Realtime |
Note:
WebCodecs
encoderOptions allow different codecs to be used: VP8/VP9/AV1/HEVC. See media-codecs to get a codec string from human readable options and check which ones are supported in your browser with github.io/media-codecs.WebCodecs
5-10x faster than H264MP4Encoder and 20x faster thanFFmpeg
(it needs to mux files after writing png to virtual FS)FFmpeg
(mp4 and webm) andWebCodecs
(mp4) have a AVC maximum frame size of 9437184 pixels. That's fine until a bit more than 4K 16:9 @ 30fps. So if you need 4K Square or 8K exports, be patient withH264MP4Encoder
(which probably also has the 4GB memory limit) or use Frame encoder and mux them manually withFFmpeg
CLI (ffmpeg -framerate 30 -i "%05d.jpg" -b:v 60M -r 30 -profile:v baseline -pix_fmt yuv420p -movflags +faststart output.mp4
)MP4Wasm
is embedded from mp4-wasm for ease of use (FFmpeg
will requireencoderOptions.corePath
)
Roadmap:
- add debug logging
- use WebWorkers for gifenc
Modules
Classes
Constants
Functions
Typedefs
canvas-record
Re-export Recorder, RecorderStatus, all Encoders and utils.
Recorder
Kind: global class
- Recorder
- new Recorder(context, [options])
- .defaultOptions : RecorderOptions
- .mimeTypes : object
- .start([startOptions])
- .step()
- .stop() ⇒ ArrayBuffer | Uint8Array | Array.<Blob> | undefined
- .dispose()
new Recorder(context, options)
Create a Recorder instance
Param | Type | Default |
---|---|---|
context | RenderingContext | |
options | RecorderOptions | {} |
recorder.defaultOptions : RecorderOptions
Sensible defaults for recording so that the recorder "just works".
Kind: instance property of Recorder
recorder.mimeTypes : object
A mapping of extension to their mime types
Kind: instance property of Recorder
recorder.start(startOptions)
Start the recording by initializing and optionally calling the initial step.
Kind: instance method of Recorder
Param | Type | Default |
---|---|---|
startOptions | RecorderStartOptions | {} |
recorder.step()
Encode a frame and increment the time and the playhead.
Calls await canvasRecorder.stop()
when duration is reached.
Kind: instance method of Recorder
recorder.stop() ⇒ ArrayBuffer | Uint8Array | Array.<Blob> | undefined
Stop the recording and return the recorded buffer. If options.download is set, automatically start downloading the resulting file. Is called when duration is reached or manually.
Kind: instance method of Recorder
recorder.dispose()
Clean up the recorder and encoder
Kind: instance method of Recorder
Encoder
Kind: global class Properties
Name | Type |
---|---|
target | EncoderTarget |
extension | EncoderExtensions |
encoderOptions | object |
muxerOptions | object |
- Encoder
- new Encoder(options)
- .supportedExtensions : Array.<Extensions>
- .supportedTargets : Array.<EncoderTarget>
- .init(options)
- .encode(frame, [frameNumber])
- .stop() ⇒ ArrayBuffer | Uint8Array | Array.<Blob> | undefined
- .dispose()
new Encoder(options)
Base Encoder class. All Encoders extend it and its method are called by the Recorder.
Param | Type |
---|---|
options | object |
encoder.supportedExtensions : Array.<Extensions>
The extension the encoder supports
Kind: instance property of Encoder
encoder.supportedTargets : Array.<EncoderTarget>
The target to download the file to.
Kind: instance property of Encoder
encoder.init(options)
Setup the encoder: load binary, instantiate muxers, setup file system target...
Kind: instance method of Encoder
Param | Type |
---|---|
options | object |
encoder.encode(frame, frameNumber)
Encode a single frame. The frameNumber is usually used for GOP (Group Of Pictures).
Kind: instance method of Encoder
Param | Type |
---|---|
frame | number |
frameNumber | number |
encoder.stop() ⇒ ArrayBuffer | Uint8Array | Array.<Blob> | undefined
Stop the encoding process and cleanup the temporary data.
Kind: instance method of Encoder
encoder.dispose()
Clean up the encoder
Kind: instance method of Encoder
FFmpegEncoder
Kind: global class
new FFmpegEncoder(options)
Param | Type |
---|---|
options | FFmpegEncoderOptions |
FrameEncoder
Kind: global class
GIFEncoder
Kind: global class
new GIFEncoder(options)
Param | Type |
---|---|
options | GIFEncoderOptions |
H264MP4Encoder
Kind: global class
new H264MP4Encoder(options)
Param | Type |
---|---|
options | H264MP4EncoderOptions |
MP4WasmEncoder
Kind: global class
new MP4WasmEncoder(options)
Param | Type |
---|---|
options | MP4WasmEncoderOptions |
MediaCaptureEncoder
Kind: global class
new MediaCaptureEncoder(options)
Param | Type |
---|---|
options | MediaCaptureEncoderOptions |
WebCodecsEncoder
Kind: global class
new WebCodecsEncoder(options)
Param | Type |
---|---|
options | WebCodecsEncoderOptions |
RecorderStatus : enum
Enum for recorder status
Kind: global enum Read only: true Example
// Check recorder status before continuing
if (canvasRecorder.status !== RecorderStatus.Stopped) {
rAFId = requestAnimationFrame(() => tick());
}
isWebCodecsSupported : boolean
Check for WebCodecs support on the current platform.
Kind: global constant
estimateBitRate(width, height, frameRate, motionRank, bitrateMode) ⇒ number
Estimate the bit rate of a video rounded to nearest megabit. Based on "H.264 for the rest of us" by Kush Amerasinghe.
Kind: global function Returns: number - A bitrate value in bits per second
Param | Type | Default | Description |
---|---|---|---|
width | number | ||
height | number | ||
frameRate | number | 30 | |
motionRank | number | 4 | A factor of 1, 2 or 4 |
bitrateMode | "variable" | "constant" | variable |
Example
// Full HD (1080p)
const bitRate = estimateBitRate(1920, 1080, 30, "variable");
const bitRateMbps = bitRate * 1_000_000; // => 13 Mbps
onStatusChangeCb : function
A callback to notify on the status change. To compare with RecorderStatus enum values.
Kind: global typedef
Param | Type | Description |
---|---|---|
RecorderStatus | number | the status |
RecorderOptions : object
Options for recording. All optional.
Kind: global typedef Properties
Name | Type | Default | Description |
---|---|---|---|
name | string | "\"\"" | A name for the recorder, used as prefix for the default file name. |
duration | number | 10 | The recording duration in seconds. If set to Infinity, await canvasRecorder.stop() needs to be called manually. |
frameRate | number | 30 | The frame rate in frame per seconds. Use await canvasRecorder.step(); to go to the next frame. |
download | boolean | true | Automatically download the recording when duration is reached or when await canvasRecorder.stop() is manually called. |
extension | string | "\"mp4\"" | Default file extension: infers which Encoder is selected. |
target | string | "\"in-browser\"" | Default writing target: in-browser or file-system when available. |
encoder | object | A specific encoder. Default encoder based on options.extension: GIF > WebCodecs > H264MP4. | |
encoderOptions | object | See src/encoders or individual packages for a list of options. | |
muxerOptions | object | See "mp4-muxer" and "webm-muxer" for a list of options. | |
onStatusChange | onStatusChangeCb |
RecorderStartOptions : object
Options for recording initialisation. All optional.
Kind: global typedef Properties
Name | Type | Description |
---|---|---|
filename | string | Overwrite the file name completely. |
initOnly | boolean | Only initialised the recorder and don't call the first await recorder.step(). |
EncoderExtensions : "mp4" | "webm" | "png" | "jpg" | "gif" | "mkv"
Kind: global typedef
EncoderTarget : "in-browser" | "file-system"
Kind: global typedef
FFmpegEncoderOptions : object
Kind: global typedef Properties
Name | Type | Default |
---|---|---|
encoderOptions | FFmpegEncoderEncoderOptions | {} |
FFmpegEncoderEncoderOptions : module:@ffmpeg/ffmpeg/dist/esm/types.js~FFMessageLoadConfig
Kind: global typedef See: FFmpeg#load
GIFEncoderOptions : object
Kind: global typedef Properties
Name | Type | Default |
---|---|---|
maxColors | number | 256 |
quantizeOptions | GIFEncoderQuantizeOptions | |
encoderOptions | GIFEncoderEncoderOptions | {} |
GIFEncoderQuantizeOptions : object
Kind: global typedef See: QuantizeOptions Properties
Name | Type | Default |
---|---|---|
format | "rgb565" | "rgb444" | "rgba4444" | "rgb565" |
oneBitAlpha | boolean | number | false |
clearAlpha | boolean | true |
clearAlphaThreshold | number | 0 |
clearAlphaColor | number | 0x00 |
GIFEncoderEncoderOptions : object
Kind: global typedef See: WriteFrameOpts Properties
Name | Type | Default |
---|---|---|
palette | Array.<Array.<number>> | |
first | boolean | false |
transparent | boolean | 0 |
transparentIndex | number | 0 |
delay | number | 0 |
repeat | number | 0 |
dispose | number | -1 |
H264MP4EncoderOptions : object
Kind: global typedef Properties
Name | Type | Default |
---|---|---|
debug | boolean | |
encoderOptions | H264MP4EncoderEncoderOptions | {} |
H264MP4EncoderEncoderOptions : module:h264-mp4-encoder~H264MP4Encoder
Kind: global typedef See: h264-mp4-encoder#api
MP4WasmEncoderOptions : object
Kind: global typedef Properties
Name | Type | Default |
---|---|---|
groupOfPictures | number | 20 |
flushFrequency | number | 10 |
encoderOptions | MP4WasmEncoderEncoderOptions | {} |
MP4WasmEncoderEncoderOptions : VideoEncoderConfig
Kind: global typedef See: VideoEncoder.configure
MediaCaptureEncoderOptions : object
Kind: global typedef Properties
Name | Type | Default |
---|---|---|
flushFrequency | number | 10 |
encoderOptions | MediaCaptureEncoderEncoderOptions | {} |
MediaCaptureEncoderEncoderOptions : MediaRecorderOptions
Kind: global typedef See: MediaRecorder#options
WebCodecsEncoderOptions : object
Kind: global typedef Properties
Name | Type | Default |
---|---|---|
groupOfPictures | number | 20 |
flushFrequency | number | 10 |
encoderOptions | WebCodecsEncoderEncoderOptions | {} |
WebCodecsEncoderEncoderOptions : VideoEncoderConfig
Kind: global typedef See: VideoEncoder.configure
WebCodecsMuxerOptions : MuxerOptions
Kind: global typedef See
License
All MIT:
MIT. See license file.
4 months ago
6 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
3 years ago
3 years ago
4 years ago
4 years ago
5 years ago