Motion graphics drawn entirely in code — one HTML file per piece, every frame deterministic, sound synthesized in the same file.
No timeline editor, no stock footage, no samples, no fonts to download. Each piece is a single index.html: open it and it plays in the browser; hand it to the renderer and it becomes an MP4, frame for frame and sample for sample the same every time.
Click a preview to open the piece live in your browser (click once more there for sound).
| piece | vertical |
|---|---|
01 · Motion Showreel 18 s Eighteen seconds of kinetic type, shape morphs, a dive through a letter and a tunnel warp, cut to a synthesized 120 BPM beat. open in browser · still · source |
9:16 · 17 s open · still |
02 · BytePatterns Launch Film 24 s A 24-second launch film: a paragraph turns into sorted bars, product cards play their own lessons, and 300 tiles light up. open in browser · still · source |
9:16 · 18 s open · still |
03 · A Short History of Sorting 30 s Merge sort, Shellsort, quicksort, heapsort and Timsort, 1945 to 2002, each one running for real on a row of bars. open in browser · still · source |
9:16 · 20 s open · still |
04 · Fourier Epicycles 20 s Spinning circles add up to a square wave as the series grows from 1 to 31 terms, and you hear the same sum. open in browser · still · source |
9:16 · 15 s open · still |
05 · Same Start, Different Fate 24 s Three double pendulums released 0.001 rad apart move as one, then split apart around the twelve-second mark. open in browser · still · source |
9:16 · 18 s open · still |
06 · Murmuration 24 s 2,400 starlings follow three local rules at dusk, dodge a hawk and draw a letter in the sky. open in browser · still · source |
9:16 · 17 s open · still |
07 · Raymarch City 20 s A camera flight through a neon city that exists only as a distance function in one fragment shader. open in browser · still · source |
9:16 · 15 s open · still |
08 · Neon Breakout 20 s A neon Breakout level that plays itself: deterministic physics, multi-ball, a fire-ball and every brick cleared. open in browser · still · source |
9:16 · 18 s open · still |
09 · Sorting Race 28 s Eight sorting algorithms sort the same 64 numbers side by side, paced by their real comparison counts. open in browser · still · source |
9:16 · 18 s open · still |
10 · Signal 20 s A documentary title sequence: one trace, a pulse that keeps quickening, then silence and the title. open in browser · still · source |
9:16 · 15 s open · still |
git clone https://github.com/bytepatterns/motion-lab.git
cd motion-lab
npm i # Playwright + its headless Chromium
node tools/render.mjs 01-showreel # -> out/01-showreel.mp4 (1920x1080, 30 fps, AAC)
Or skip the install and just open projects/01-showreel/index.html in a browser.
Requirements: Node 20+ and ffmpeg + ffprobe on your PATH.
| Command | What it does |
|---|---|
node tools/render.mjs NN-slug |
draws every frame headless, pipes the PNGs into ffmpeg (h264, yuv420p, crf 18), muxes the synthesized soundtrack → out/NN-slug.mp4 |
node tools/still.mjs NN-slug [frame ...] |
poster frame → out/NN-slug.png; with frame numbers → out/stills/ |
node tools/gif.mjs NN-slug |
a 6-8 s, 960 px, ≤ 3 MB loop with a two-pass palette → out/NN-slug.gif |
node tools/verify.mjs NN-slug |
checks the piece and its MP4 against the contract, exits 1 on failure |
node tools/all.mjs [NN-slug ...] |
render + still + gif + verify for every piece, then rebuilds this gallery |
node tools/build-readme.mjs |
rebuilds the gallery above from projects/*/index.html |
render.mjs takes --workers N (parallel pages), --range a:b (a quick preview of a few frames) and --keep-frames (also write the PNGs to out/frames/). Every tool takes --variant vertical for a piece’s 9:16 cut.
Every piece also has a 1080×1920, 12–20 s cut, recomposed from the same scene code with all type inside the area no platform covers, a hook on the first frame and a closing card — it is the right-hand column of the gallery, and ?variant=vertical plays it in the browser. node tools/all.mjs --variant vertical renders them to out/NN-slug.vertical.mp4, and verify checks the safe area on top of the usual contract (see CONTRACT.md).
Every piece exposes one object, and the tools use nothing else:
window.__motion = {
title, description, // shown in the gallery
fps: 30, width: 1920, height: 1080,
frames: 540, // 15-30 s
seek(i) {}, // draws frame i: synchronous, a pure function of i
async audio() {}, // the whole soundtrack as one AudioBuffer (OfflineAudioContext, 48 kHz stereo)
};
seek(i) is pure. No clock, no Math.random — randomness comes from a seeded PRNG (mulberry32). Frame 400 looks the same whether it is drawn first, last or twice, so the renderer draws frames in parallel and out of order, and verify re-draws random frames to prove it.at = 45 / 30 seconds in the OfflineAudioContext, so picture and sound cannot drift.seek from requestAnimationFrame and starts the sound on your first click; in the renderer it stays out of the way.The full rules — canvas, fonts, determinism, audio format, what verify checks — are in CONTRACT.md.
verify checks| Group | Checks |
|---|---|
| file | one self-contained file: no external src / href, no CSS imports, has a <title> |
| page | loads without errors and without a single network request; __motion has 30 fps, 1920×1080, 450-900 frames |
| frames | random frames are not empty, are pixel-identical when re-drawn out of order, and seek never calls Math.random, Date.now or performance.now |
| audio | 2 channels, 48 kHz, exactly frames / fps seconds, not silent, no clipping |
| mp4 | h264 1920×1080 yuv420p, duration within 0.1 s, audio stream present and audible |
| luma | every frame of the MP4 decoded: none near-black, none a flat colour |
| safe | vertical cuts only: on 7 frames nothing brighter than luma 70 outside x 60..900, y 230..1400 |
cp -r template projects/11-my-piece (the folder name is NN-slug, and the file must stay index.html).draw(f) and renderAudio() with your own; set title, description, frames.?frame=240 freezes on a frame, Space pauses.node tools/all.mjs 11-my-piece — then look at a few frames with node tools/still.mjs 11-my-piece 30 240 480.If verify fails, the piece is fixed — never the tool.
projects/NN-slug/index.html one piece = one file
template/index.html starter piece with the player block and helpers
tools/ render, still, gif, verify, all, build-readme (+ lib.mjs)
out/ GIFs and posters (also *.vertical.*) are committed; MP4s and frames are not
CONTRACT.md the rules every piece follows
Animated algorithm lessons, free and without an account: bytepatterns.com · the reel engine: code-motion
MIT. The soundtracks are synthesized by the code in each file, so they carry no third-party licence.