Package Exports
- canvas-record
Readme
canvas-record
Record a video in the browser or directly on the File System from a canvas region (2D/WebGL/WebGPU) as MP4, WebM, MKV, MOV, GIF, PNG/JPG Sequence using WebCodecs and Wasm when available.

Installation
npm install canvas-recordUsage
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 / mov |
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:
WebCodecsencoderOptions 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.WebCodecs5-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 withFFmpegCLI (ffmpeg -framerate 30 -i "%05d.jpg" -b:v 60M -r 30 -profile:v baseline -pix_fmt yuv420p -movflags +faststart output.mp4)MP4Wasmis embedded from mp4-wasm for ease of use (FFmpegwill requireencoderOptions.corePath)
Roadmap:
- add debug logging
- use WebWorkers for gifenc
Modules
- canvas-record
Re-export Recorder, RecorderStatus, all Encoders and utils.
Classes
- Recorder
- Encoder
- FFmpegEncoder
- FrameEncoder
- GIFEncoder
- H264MP4Encoder
- MediaCaptureEncoder
- MP4WasmEncoder
- WebCodecsEncoder
Constants
- isWebCodecsSupported :
boolean Check for WebCodecs support on the current platform.
Functions
- 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.
Typedefs
- onStatusChangeCb :
function A callback to notify on the status change. To compare with RecorderStatus enum values.
- RecorderOptions :
object Options for recording. All optional.
- RecorderStartOptions :
object Options for recording initialisation. All optional.
- EncoderExtensions :
"mp4"|"webm"|"png"|"jpg"|"gif"|"mkv"|"mov" - EncoderTarget :
"in-browser"|"file-system" - FFmpegEncoderOptions :
object - FFmpegEncoderEncoderOptions :
module:@ffmpeg/ffmpeg/dist/esm/types.js~FFMessageLoadConfig - GIFEncoderOptions :
object - GIFEncoderQuantizeOptions :
object - GIFEncoderEncoderOptions :
object - H264MP4EncoderOptions :
object - H264MP4EncoderEncoderOptions :
module:h264-mp4-encoder~H264MP4Encoder - MediaCaptureEncoderOptions :
object - MediaCaptureEncoderEncoderOptions :
MediaRecorderOptions - MP4WasmEncoderOptions :
object - MP4WasmEncoderEncoderOptions :
VideoEncoderConfig - WebCodecsEncoderOptions :
object - WebCodecsEncoderEncoderOptions :
VideoEncoderConfig - WebCodecsMuxerOptions :
module:mediabunny~OutputOptions
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 methods 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
new FFmpegEncoder([options])
| Param | Type |
|---|---|
| [options] | FFmpegEncoderOptions |
FrameEncoder
GIFEncoder
new GIFEncoder([options])
| Param | Type |
|---|---|
| [options] | GIFEncoderOptions |
H264MP4Encoder
new H264MP4Encoder([options])
| Param | Type |
|---|---|
| [options] | H264MP4EncoderOptions |
MediaCaptureEncoder
new MediaCaptureEncoder([options])
| Param | Type |
|---|---|
| [options] | MediaCaptureEncoderOptions |
MP4WasmEncoder
new MP4WasmEncoder([options])
| Param | Type |
|---|---|
| [options] | MP4WasmEncoderOptions |
WebCodecsEncoder
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.
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 MbpsonStatusChangeCb : 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. |
| [rect] | Array |
[] |
Sub-region [x, y, width, height] of the canvas to encode from bottom left. Default to 0, 0 and context.drawingBufferWidth/drawingBufferHeight or canvas.width/height. |
| [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 "mediabunny" for a list of options. | |
| [frameOptions] | object |
Options for createImageBitmap(), VideoFrame, getImageData() or canvas-screenshot. | |
| [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" | "mov"
EncoderTarget : "in-browser" | "file-system"
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
MediaCaptureEncoderOptions : object
Kind: global typedef Properties
| Name | Type | Default |
|---|---|---|
| [flushFrequency] | number |
10 |
| [encoderOptions] | MediaCaptureEncoderEncoderOptions |
{} |
MediaCaptureEncoderEncoderOptions : MediaRecorderOptions
Kind: global typedef See: MediaRecorder#options
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
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 : module:mediabunny~OutputOptions
Kind: global typedef See: mediabunny#output-formats
License
All MIT:
MIT. See license file.