| name | canvascut |
| description | Capture canvas, WebGL, and animation-heavy websites as smooth high-resolution constant-frame-rate video using browser tab capture instead of screen recording. Use when the user wants to record, capture, or make a video of a website, landing page, canvas/WebGL/shader animation, or a scripted UI demo — or when their screen recording of an animated site looks choppy, low-fps, laggy, or juddery despite the site itself being smooth. Triggers on "record my site", "capture this animation", "make a demo video", "my screen recording looks choppy", "record the canvas". |
CanvasCut — smooth web page video capture
Records the browser's own composited output (canvas + DOM + CSS transitions) rather
than screen-recording the display. Nothing competes with the page for the GPU, so
WebGL and shader-heavy sites record cleanly at a locked frame rate.
When to use this
- Recording a site with canvas/WebGL/shader animation
- Producing a repeatable scripted product demo (click here, wait, click there)
- Diagnosing "my site is smooth but my recording is choppy"
Reach for a normal screen recorder instead if the user needs to capture outside
the browser (desktop apps, multiple windows) or needs audio.
Diagnosing choppy recordings
Before reaching for the tool, it's worth naming the cause — there are three, and
they stack:
- GPU contention. The recorder composites and encodes in real time on the same
GPU the shader is using. Tools that apply live zoom/cursor effects during capture
(e.g. Screen Studio's auto-zoom) make this materially worse — recording raw and
adding effects in post is a real fix.
- No inter-frame redundancy. A full-viewport noise/shader background changes
every pixel every frame, so H.264 either drops frames or starves the bitrate.
- Variable frame rate. macOS's built-in recorder writes VFR. Unevenly spaced
frames read as stutter even when no frames were lost. Check with
ffprobe file.mov — an odd fps like 47.3 means VFR.
Usage
npx canvascut https://example.com -d 15 -o demo.mp4
npx canvascut --config demo.json
Config file shape:
{
"url": "https://example.com",
"width": 1600,
"height": 1000,
"fps": 60,
"out": "demo.mp4",
"steps": [
{ "wait": 1500 },
{ "click": "get started" },
{ "wait": 3000 },
{ "clickSelector": "#theme-toggle" },
{ "scroll": 100, "times": 5 },
{
click matches visible text, aria-label, or title (case-insensitive regex).
Clicks are real CDP input events, so they work where element.click() doesn't.
Building a good choreography
- Load the page first and read its interactive elements to learn the real
structure — don't guess button names. Buttons often don't do what their label
implies (a "theme toggle" may reopen a picker rather than toggling).
- Leave 3–4s after each interaction so transitions play out fully.
- Avoid scrolling if you can. Wheel events force layout/compositing work
mid-capture and are the most common source of dropped frames. In one measured
case, removing a scroll took a capture from 57.9fps with 5 dropped frames to a
clean 60.0fps with zero.
- Start the capture before the first interaction so the opening transition is
included.
Verifying the result
CanvasCut prints a frame-timing report after every run:
frames 1298 over 21.61s
effective fps 60.1 (target 60)
frame spacing median 17.0ms, worst 25.0ms (ideal 16.7ms)
verdict smooth — no dropped frames
Trust this over file size or a claimed fps. If the verdict reports dropped frames,
the usual culprit is scrolling in the choreography, or another GPU-heavy app running.
Troubleshooting
- "Failed to start tab capture" — Chrome ignored
--auto-accept-this-tab-capture.
That flag auto-approves the screen-capture permission dialog; if a Chrome update
renamed it, capture can't run unattended.
- Blank or frozen output — usually means something forced headless/software
rendering. CanvasCut runs non-headless deliberately; SwiftShader does not render
most shaders correctly.
- Missing DOM text, only the canvas — that's canvas-only capture
(
canvas.captureStream()), not tab capture. Tab capture includes the DOM.
Verify before reporting success
Always confirm the capture actually contains what was intended — extract a frame
and look at it:
ffmpeg -i demo.mp4 -vf "select=eq(n\,300)" -vframes 1 /tmp/check.png
File size and frame count do not prove the content is right. A page can render a
loading state, a cookie banner, or a first-visit interstitial for the entire capture.