The least-intrusive API
One line in your render loop.
Make a recorder, start() it, and call
capture(canvas) once per frame. Drive motion from
performance.now() / the rAF timestamp like you already do —
CCapture warps that clock during capture so every frame lands on an exact
grid.
- No restructuring. No
await, no fixeddt— drop it into the loop you have. - As fast as your machine. Capture is decoupled from wall-clock; it runs flat-out with bounded memory.
- Self-finishing. Set a
timeLimitorframeLimitand the file downloads itself.
import CCapture from "ccapture.js"; const capturer = new CCapture({ format: "mp4", framerate: 60, timeLimit: 5 }); capturer.start(); function render() { requestAnimationFrame(render); // your normal loop drawMyScene(); // uses performance.now() — now warped capturer.capture(canvas); // ← the only line you add } render();
How it works
A virtual clock, and two halves you can use alone.
CCapture replaces the browser's notion of time with a clock that advances one fixed step per captured frame — so anything driven by elapsed time steps deterministically. It's three composable pieces:
Owns time
Hooks performance.now, rAF, timers, Date and media
currentTime, advancing a virtual clock on demand. Knows nothing about canvases.
Owns capture
Takes drawn frames, applies motion blur, enforces limits, and feeds a pluggable encoder. Knows nothing about time.
Plug & play
Composes the two and drives them — the unified recorder for the common "record a live animation" case.
What's in the box
Modern encoders, real motion blur, sync.
WebCodecs, hardware-fast
MP4 (H.264/AV1) & WebM (VP9/VP8/AV1) via the browser's own encoder, back-pressured so you run flat-out with bounded memory.
Motion blur, GPU or CPU
Averages N sub-frames per output frame — real shutter blur. Stays on the GPU (WebGL2) when it can, no readback.
Any framerate, smooth
The virtual clock means a slow render still exports a flawless 30, 60 or 240 fps file — no dropped or doubled frames.
Frame-accurate audio
Sample a track offline at each frame's exact time for visuals that line up — and mux the audio right into the file.
Video, frame-stepped
Register a <video> and it seeks frame-by-frame in
lockstep — accurate, not a best-effort realtime grab.
Bring your own encoder
A tiny interface (frame-by-frame or batch) wraps anything — WASM codecs, sprite sheets, an upload. Register it as a format.
ESM, UMD & TypeScript
Import as native modules with no build, drop in the <script>
global, or lean on the bundled .d.ts types.
Drop-in successor
Same start / capture / stop / save flow as CCapture 1.x,
rebuilt for today's browsers.
Formats
Pick an output; CCapture negotiates the codec.
Codec strings are probed at runtime and odd/resized frames are fitted to a valid coded size. WebCodecs is the default path; WebM falls back to a legacy writer where it's missing.
See it work
Examples
Every example uses the same drop-in panel — pick a format,
framerate and motion blur, then record. Serve over HTTP
(npm run serve) and open this page; ES modules need it.
Frameworks
Three.js
A WebGL scene captured to a smooth fixed-rate MP4.
p5.js
An ordinary p5 sketch — captured with one extra line.
GSAP timeline
A GSAP timeline scrubbed by the warped clock.
Raw graphics
Basic 2D
A plain 2D-canvas sketch — the simplest starting point.
Raw WebGL2
A no-framework fragment shader (GPU motion blur).
WebGPU
A WGSL shader, captured the very same way.
Media & audio
Music video
Audio-reactive shader, motion blur, audio muxed in.
Audio-reactive
Frame-accurate spectrum bars, with the audio muxed in.
Video sync
Frame-stepping a <video> in lockstep with capture.
Video + overlay
Upload a video; record an audio-reactive overlay on it.
Techniques & combos
Heavy scene
A slow raymarcher captured smoothly — render cost doesn't matter.
Slow motion
Capture a fast motion at 240 fps, retime for crisp slow-mo.
Sprite sheet
A bring-your-own encoder that tiles frames into one PNG grid.
Transparent
PNG/WebP sequence with a transparent background for compositing.
Lockstep split
Two canvases, one shared TimeWarp → two frame-synced files.
Minimal demo
The smallest possible capture loop, no UI.