Gathering detailed insights and metrics for canvas-capture
Gathering detailed insights and metrics for canvas-capture
Gathering detailed insights and metrics for canvas-capture
Gathering detailed insights and metrics for canvas-capture
html-to-image
Generates an image from a DOM node using HTML5 canvas and SVG.
gif-capture-canvas
Capture a canvas with an animated gif
@freder/canvas-capture
super basic https://github.com/spite/ccapture.js alternative.
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.
Record the canvas as an image, mp4 video, or gif from the browser
npm install canvas-capture
Typescript
Module System
Node Version
NPM Version
58
Supply Chain
94.5
Quality
74.4
Maintenance
100
Vulnerability
99.3
License
JavaScript (74.94%)
TypeScript (23.79%)
HTML (1.19%)
CSS (0.09%)
Total Downloads
26,311
Last Day
18
Last Week
154
Last Month
749
Last Year
12,153
206 Stars
287 Commits
15 Forks
8 Watching
3 Branches
2 Contributors
Latest Version
2.1.1
Package Id
canvas-capture@2.1.1
Unpacked Size
1.37 MB
Size
389.11 kB
File Count
13
NPM Version
8.19.2
Node Version
16.18.0
Publised On
23 Apr 2023
Cumulative downloads
Total Downloads
Last day
-47.1%
18
Compared to previous day
Last week
-17.6%
154
Compared to previous week
Last month
-33.4%
749
Compared to previous month
Last year
42.4%
12,153
Compared to previous year
A small wrapper around CCapture.js and ffmpeg.wasm to record the canvas as an image (png/jpeg), video (mp4/webm), or gif – all from the browser!
Demo at: apps.amandaghassaei.com/canvas-capture/demo/
All media formats are currently supported by both Chrome and Firefox (see Caveats for more details about browser support and server header requirements).
This project doesn't expose all the features of either CCapture.js or ffmpeg.wasm, but it packages some of the most useful functionality into a few simple methods. This package can be installed via npm and run in the browser (I'm mostly using this in projects built with webpack). Some key features:
1npm install canvas-capture
Then import via:
1import { CanvasCapture } from 'canvas-capture';
OR in the browser you can add canvas-capture.js or canvas-capture.min.js to your html:
1<html> 2 <head> 3 .... 4 <script src="canvas-capture.js"></script> 5 </head> 6 <body> 7 </body> 8</html>
Then in your js files, you can access the global variable CanvasCaptureLib
:
1const { CanvasCapture } = CanvasCaptureLib;
See a demo importing canvas-capture via html at apps.amandaghassaei.com/canvas-capture/demo-simple/
There are a few ways to call canvas-capture. You can bind hotkeys to start/stop recording:
1import { CanvasCapture } from 'canvas-capture';
2
3// Initialize and pass in canvas.
4CanvasCapture.init(
5 document.getElementById('my-canvas'),
6 { showRecDot: true }, // Options are optional, more info below.
7);
8
9// Bind key presses to begin/end recordings.
10CanvasCapture.bindKeyToVideoRecord('v', {
11 format: 'mp4', // Options are optional, more info below.
12 name: 'myVideo',
13 quality: 0.6,
14});
15CanvasCapture.bindKeyToGIFRecord('g');
16// Download a series of frames as a zip.
17CanvasCapture.bindKeyToPNGFramesRecord('f', {
18 onProgress: (progress) => { // Options are optional, more info below.
19 console.log(`Zipping... ${Math.round(progress * 100)}% complete.`);
20 },
21}); // Also try bindKeyToJPEGFramesRecord().
22
23// These methods immediately save a single snapshot on keydown.
24CanvasCapture.bindKeyToPNGSnapshot('p');
25CanvasCapture.bindKeyToJPEGSnapshot('j', {
26 name: 'myJpeg', // Options are optional, more info below.
27 quality: 0.8,
28});
29
30function loop() {
31 requestAnimationFrame(loop);
32
33 // Render something...
34
35 // It is recommended to use checkHotkeys() right after rendering
36 // frame in render loop to ensure that PNG and JPEG
37 // snapshots are triggered at the right time.
38 // Otherwise, blank images may be generated due to the browser
39 // clearing the render buffer before onKeyDown is triggered.
40 CanvasCapture.checkHotkeys();
41
42 // You need to call recordFrame() only if you are recording
43 // a video, gif, or frames.
44 if (CanvasCapture.isRecording()) CanvasCapture.recordFrame();
45}
46
47loop(); // Start loop.
Alternatively, you can call beginXXXRecord
and takeXXXSnapshot
directly:
1import { CanvasCapture } from 'canvas-capture';
2
3// Initialize and pass in canvas.
4CanvasCapture.init(
5 document.getElementById('my-canvas'),
6 { showRecDot: true }, // Options are optional, more info below.
7);
8
9CanvasCapture.beginGIFRecord({ name: 'myGif', fps: 10 });
10.... // Draw something.
11CanvasCapture.recordFrame();
12.... // Draw something.
13CanvasCapture.recordFrame();
14CanvasCapture.stopRecord();
15
16// Now you may start another recording.
17CanvasCapture.beginVideoRecord({ format: CanvasCapture.MP4 });
18CanvasCapture.recordFrame();
19....
20CanvasCapture.stopRecord();
21
22// Also try beginJPEGFramesRecord(jpegOptions)
23// and beginPNGFramesRecord(pngOptions)
24
25// Or you can call `takeXXXSnapshot` to take a single snapshot.
26// No need to call `recordFrame` or `stopRecord` for these methods.
27CanvasCapture.takePNGSnapshot();
28CanvasCapture.takeJPEGSnapshot({ dpi: 600, onExport: (blob, filename) => {
29 // Instead of automatically downloading the file, you can pass an
30 // optional onExport callback to handle blob manually.
31}});
32
Available options for each capture type - passed in as an optional argument to bindKeyToXXX
, beginXXXRecord
, or takeXXXSnapshot
:
1videoOptions = { 2 format: CanvasCapture.MP4 | CanvasCapture.WEBM, // Defaults to 'CanvasCapture.MP4'. 3 name: string, // Defaults to 'Video_Capture'. 4 fps: number, // Frames per second of the output video, defaults to 60. 5 quality: number, // A number between 0 and 1, defaults to 1. 6 motionBlurFrames: number, // Number of intermediary frames used to calculate motion blur. 7 onExportProgress: (progress: number) => void, // progress: range [0-1]. 8 onExport: (blob: Blob, filename: string) => void, // Handle blob manually. 9 onExportFinish: () => void, // Callback after successful export. 10 onError: (error: Error | any) => void, // Callback on error. 11 // Options below for ffmpeg conversion to mp4, not used for webm export. 12 ffmpegOptions?: { [key: string]: string }, // FFMPEG option flags 13 // Defaults to 14 // { '-c:v': 'libx264', 15 // '-preset': 'slow', 16 // '-crf': '22', 17 // '-pix_fmt': 'yuv420p' } 18 // Internally the ffmpeg conversion runs with additional flags to crop 19 // to an even number of px dimensions (required for mp4): 20 // '-vf crop=trunc(iw/2)*2:trunc(ih/2)*2' 21 // and export no audio channel: '-an' 22} 23gifOptions = { 24 name: string, // Defaults to 'GIF_Capture'. 25 fps: number, // The frames per second of the output gif, defaults to 60. 26 quality: number, // A number between 0 and 1, defaults to 1. 27 motionBlurFrames: number, // Number of intermediary frames used to calculate motion blur. 28 onExportProgress: (progress: number) => void, // progress: range [0-1]. 29 onExport: (blob: Blob, filename: string) => void, // Handle blob manually. 30 onExportFinish: () => void, // Callback after successful export. 31 onError: (error: Error | any) => void, // Callback on error. 32} 33pngOptions = { 34 name: string, // Defaults to 'PNG_Capture'. 35 dpi: number, // Defaults to screen resolution (72 dpi). 36 onExport: (blob: Blob, filename: string) => void, // Handle blob manually. 37 // onExportProgress and onExportFinish gives zipping updates for 38 // recording PNG frames (only used by bindKeyToPNGFramesRecord() 39 // and beginPNGFramesRecord()): 40 onExportProgress: (progress: number) => void, // progress: range [0-1]. 41 onExportFinish: () => void, // Callback after successful export. 42 onError: (error: Error | any) => void, // Callback on error. 43} 44jpegOptions = { 45 name: string, // Defaults to 'JPEG_Capture'. 46 quality: number, // A number between 0 and 1, defaults to 1. 47 dpi: number, // Defaults to screen resolution (72 dpi). 48 onExport: (blob: Blob, filename: string) => void, // Handle blob manually. 49 // onExportProgress and onExportFinish gives zipping updates for 50 // recording JPEG frames (only used by bindKeyToJPEGFramesRecord() 51 // and beginJPEGFramesRecord()): 52 onExportProgress: (progress: number) => void, // progress: range [0-1]. 53 onExportFinish: () => void, // Callback after successful export. 54 onError: (error: Error | any) => void, // Callback on error. 55}
Note that changing the dpi of png/jpeg does not change the amount of pixels captured, just the dimensions of the resulting image.
You can initialize CanvasCapture
with the following options:
1import { CanvasCapture } from 'canvas-capture'; 2 3CanvasCapture.init(document.getElementById('my-canvas'), { 4 // Verbosity of console output. 5 verbose: true, // Default is false. 6 // Show a red dot on the screen during records. 7 showRecDot: true, // Default is false. 8 // CSS overrides for record dot. 9 recDotCSS: { right: '0', top: '0', margin: '10px' }, // Default is {}. 10 // Show alert dialogs during export in case of errors. 11 showAlerts: true, // Default is false. 12 // Show informational dialogs during export. 13 showDialogs: true, // Default is false. 14 // Path to a copy of ffmpeg-core to be loaded asynchronously. 15 // ffmpeg-core has not been included in this library by default because 16 // it is very large (~25MB) and is only needed for mp4 export. 17 ffmpegCorePath: './node_modules/@ffmpeg/core/dist/ffmpeg-core.js', 18 // By default, ffmpegCorePath is set to load remotely from 19 // https://unpkg.com/@ffmpeg/core@0.10.0/dist/ffmpeg-core.js 20 // If you would like to load locally, you can set ffmpegCorePath to 21 // load from node_modules: 22 // './node_modules/@ffmpeg/core/dist/ffmpeg-core.js' 23 // using a copy of @ffmpeg/core installed via npm, or copy the files 24 // (ffmpeg-core.js, ffmpeg-core.wasm, and ffmpeg-core.worker.js), save 25 // them in your project, and set ffmpegCorePath to point to 26 // ffmpeg-core.js 27});
The baseline CSS for the record dot places it in the upper right corner of the screen, any of these params can be overwritten via options.recDotCSS:
1background: "red", 2width: "20px", 3height: "20px", 4"border-radius": "50%", // Make circle. 5position: "absolute", 6top: "0", 7right: "0", 8"z-index": "10", 9margin: "20px",
Additionally, you can set the verbosity of the console output at any time by:
1CanvasCapture.setVerbose(false);
I've also included a helper function to show a simple modal dialog with a title and message:
1const options = {
2 // Set the amount of time to wait before auto-closing dialog,
3 // or -1 to disable auto-close.
4 autoCloseDelay: 7000, // Default is -1.
5};
6// title and message are strings, options are optional.
7CanvasCapture.showDialog(title, message, options);
Additionally, if you want to unbind all events from CanvasCapture:
1CanvasCapture.dispose();
mp4 export currently works best in Chrome, but it does work in the latest release of Firefox (96.0.1), now that this Firefox bug seems to have been addressed. I have noticed that ffmpeg can get stuck in Firefox, but only when the console/devtools are open, see this issue.
This repo depends on ffmpeg.wasm to export mp4 video, not all browsers are supported:
Only browsers with SharedArrayBuffer support can use ffmpeg.wasm, you can check HERE for the complete list.
In order for mp4 export to work, you need to configure your (local or remote) server correctly:
SharedArrayBuffer is only available to pages that are cross-origin isolated. So you need to host your own server with
Cross-Origin-Embedder-Policy: require-corp
andCross-Origin-Opener-Policy: same-origin
headers to use ffmpeg.wasm.
I've included a script for initializing a local server with the correct Cross-Origin policy at canvas-capture/server.js. If you need to start up your own server for testing, try running the code below to boot up a server at localhost:8080
:
1node node_modules/canvas-capture/server.js
If you're building an application with webpack-dev-server, you can add the appropriate headers to your webpack.config.js
:
1module.exports = { 2 ... 3 devServer: { 4 ... 5 headers: { 6 "Cross-Origin-Embedder-Policy": "require-corp", 7 "Cross-Origin-Opener-Policy": "same-origin", 8 } 9 } 10}
If you're hosting an application on Github Pages, I recommend checking out coi-serviceworker to get the correct headers on your page. I was able to get this to work for my demo page.
Additionally, you can test for browser support with the following methods:
1// Returns true if the browser supports webm recording.
2CanvasCapture.browserSupportsWEBM();
3
4// Returns true if the browser supports mp4 recording.
5CanvasCapture.browserSupportsMP4();
6
7// Returns true if the browser supports gif recording.
8CanvasCapture.browserSupportsGIF();
I'm not aware of any browser limitations for the image export options (obviously, the browser must support canvas as a bare minimum).
Another thing to be aware of: this library defaults to pulling a copy of ffmpeg.wasm remotely from unpkg.com/@ffmpeg/core@0.10.0/dist/, so it requires an internet connection to export mp4. If you want to host your own copy of ffmpeg-core, you'll need to provide a path to ffmpeg-core.js
with the ffmpegCorePath
option in CanvasCapture.init()
. Be sure to also include ffmpeg-core.wasm
and ffmpeg-core.worker.js
in the same folder.
beginXXXRecord
methods return a capture
object that can be passed to CanvasCapture.recordFrame(capture)
or CanvasCapture.stopRecord(capture)
to target a specific recording. If recordFrame
or stopRecord
are called with no arguments, all active captures are affected.takeXXXSnapshot()
if you have a lot of files to save.Not all browsers support mp4 export, and even if they do, you may decide to export webm anyway for performance reasons (I tend to do this, they are much faster to export). Webm is a bit annoying as a format though – I've found that I can play webm videos with VLC player, but the framerate tends to be choppy, and very few websites/programs support them. If you want to convert your webms to mp4 (or any other format) after you've already downloaded them, I recommend using ffmpeg from the terminal:
ffmpeg -i PATH/FILENAME.webm -vf "crop=trunc(iw/2)*2:trunc(ih/2)*2" -c:v libx264 -preset slow -crf 22 -pix_fmt yuv420p -an PATH/FILENAME.mp4
-vf "crop=trunc(iw/2)*2:trunc(ih/2)*2"
crops the video so that its dimensions are even numbers (required for mp4)
-c:v libx264 -preset slow -crf 22
encodes as h.264 with better compression settings
-pix_fmt yuv420p
makes it compatible with the web browser
-an
creates a video with no audio
For Mac users: I recommend checking out MacOS Automator and creating a Quick Action for these types of conversions. I have some instructions for that here. I have a Quick Action for "Convert to MP4" that invokes the above ffmpeg command on whatever file I've selected in the Finder – it's a big time saver!
The code in this repo is licensed under an MIT license, but it depends on other codebases and proprietary video codecs with different licenses. Please be aware of this and check this project's dependencies for more info, specifically:
@ffmpeg/core contains WebAssembly code which is transpiled from original FFmpeg C code with minor modifications, but overall it still following the same licenses as FFmpeg and its external libraries (as each external libraries might have its own license).
Pull requests welcome!
Install development dependencies by running:
npm install
To build src
to dist
run and recompile demo
:
npm run build
Please note that this repo depends on CCapture.js, but there is currently some weirdness around importing CCapture with npm. I'm using a copy of CCapture from the npm-fix
branch at github.com/amandaghassaei/ccapture.js/tree/npm-fix. I'm not proud of the changes I had to make to get this to work (see diff here), but it's fine for now. In order to package this (canvas-capture) repo up nicely for npm and remove all github-hosted dependencies, I had to make a copy of my CCapture npm-fix
branch in the src/CCapture.js/
directory of this repo. It's ugly, but hopefully this can all be cleared up at some point in the future.
Also, in order to get the CCapture constructor to work correctly, I had to call window.CCapture()
rather than using the module import directly. You'll also see I had to assign the default export from CCapture to an unused temp variable to make sure it was included in the build:
1// Importing my local copy of CCapture.js.
2import CCapture from './CCapture.js/CCapture';
3// This is an unused variable, but critically necessary.
4const temp = CCapture;
5
6....
7
8const capturer = new window.CCapture({
9 format: 'webm',
10 name: 'WEBM_Capture',
11 framerate: 60,
12 quality: 63,
13 verbose: false,
14});
15// This didn't work:
16// const capturer = new CCapture({
17// format: 'webm',
18// name: 'WEBM_Capture',
19// framerate: 60,
20// quality: 63,
21// verbose: false,
22// });
Hopefully this will all be fixed in the future, see notes here:
https://github.com/spite/ccapture.js/issues/78
This repo also includes a demo for testing, currently hosted at apps.amandaghassaei.com/canvas-capture/demo/. You can find the demo source code at demo/src/index.ts. An even simpler demo (no webpack, no compilation, import canvas-capture directly in HTML) can be found at apps.amandaghassaei.com/canvas-capture/demo-simple/.
To build the demo
folder, run:
npm run build-demo
To run the demo locally, run:
npm run start
This will boot up a local server with the correct Cross-Origin policies to support ffmpeg.wasm (a dependency for exporting mp4 video). Navigate to the following address in your browser:
localhost:8080/demo/
No vulnerabilities found.
No security vulnerabilities found.