Package Exports
- canvas-record
Readme
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-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 |
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
- index
Re-export Recorder, RecorderStatus, all Encoders and utils.
Classes
- Recorder
Base Recorder class.
Functions
- onStatusChangeCb(RecorderStatus)
A callback to notify on the status change. To compare with RecorderStatus enum values.
Typedefs
- RecorderOptions :
object Options for recording. All optional.
- RecorderStartOptions :
object Options for recording. All optional.
index
Re-export Recorder, RecorderStatus, all Encoders and utils.
Recorder
Base Recorder class.
Kind: global class Properties
| Name | Type | Default | Description |
|---|---|---|---|
| [enabled] | boolean |
true |
Enable/disable pointer interaction and drawing. |
- Recorder
- new Recorder(context, options)
- .start(startOptions)
- .step()
- .stop() ⇒
ArrayBuffer|Uint8Array|Array.<Blob>|undefined - .dispose()
new Recorder(context, options)
| Param | Type |
|---|---|
| context | RenderingContext |
| options | RecorderOptions |
recorder.start(startOptions)
Start the recording by initializing and optionally calling the initial step.
Kind: instance method of Recorder
| Param | Type |
|---|---|
| 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
Kind: instance method of Recorder
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());
}onStatusChangeCb(RecorderStatus)
A callback to notify on the status change. To compare with RecorderStatus enum values.
Kind: global function
| 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] | boolean |
"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. 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(). |
License
All MIT:
MIT. See license file.