canvas-record
v5.1.0
Published
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.
Downloads
2,482
Maintainers
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-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 |
MediaCaptureEncoder
Kind: global class
new MediaCaptureEncoder([options])
| Param | Type | | --------- | ---------------------------------------------------------------------- | | [options] | MediaCaptureEncoderOptions |
MP4WasmEncoder
Kind: global class
new MP4WasmEncoder([options])
| Param | Type | | --------- | ------------------------------------------------------------ | | [options] | MP4WasmEncoderOptions |
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
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 : MuxerOptions
Kind: global typedef See
License
All MIT:
MIT. See license file.