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.