CCapture · canvas capture, rebuilt for 2026

Record canvas animations
at a fixed framerate.

Your sketch renders as fast — or as slowly — as it needs to. CCapture lies to it about what time it is, so the exported video is perfectly smooth at exactly the framerate you asked for. 30, 60, 240 — even a 4K frame that takes two seconds to draw.

Browse examples ↓ GitHub Read the docs

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 fixed dt — 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 timeLimit or frameLimit and the file downloads itself.
sketch.js
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:

TimeWarp

Owns time

Hooks performance.now, rAF, timers, Date and media currentTime, advancing a virtual clock on demand. Knows nothing about canvases.

FrameWrap

Owns capture

Takes drawn frames, applies motion blur, enforces limits, and feeds a pluggable encoder. Knows nothing about time.

CCapture

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.

mp4H.264 / AV1 · WebCodecs webmVP9 / VP8 / AV1 · WebCodecs gif256-color · gifenc gif-hqgifski (opt-in) pngTAR sequence · alpha jpgTAR sequence webpTAR sequence · alpha

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

Raw graphics

Media & audio

Techniques & combos