A generative, code-rendered music video with word-synced karaoke typography. Every frame is a deterministic function of song time, so the live preview in the browser and the offline 1080p60 (or 4K60) export are identical.
Watch it in 4K on YouTube: https://www.youtube.com/watch?v=5EoO5413dBY
The video was made with Claude (Opus 5.5) in Claude Code: the concept and treatment, the lyric alignment and audio analysis, the renderer, every scene and the renders were all worked out in conversation with Claude.
The concept, style bible and plate-by-plate treatment are in docs/TREATMENT.md. The engine and scene API are documented in docs/ENGINE.md.
audio/pdoom.mp3— the song.lyrics/lyrics.src.js— the original line-level lyrics (approximate timings).analysis/— Python (uv) tools that produced the timing data: Demucs stem separation, CTC forced alignment cross-checked with Whisper, beat/downbeat/onset analysis. Seeanalysis/align.pyandanalysis/analyze.py.data/lyrics.json— word-level (and some syllable-level) lyric timings.data/audio.json— tempo (132.007 BPM), beats, downbeats, sections, drum/vocal onsets and loudness envelopes.app/— the renderer: TypeScript + three.js, bun + Vite.src/engine/— renderer core: timeline playback, post-processing (bloom, halation, grain), typography (Archivo, IBM Plex Mono, Cormorant Garamond, single-stroke plotter fonts), GPU line batches, HUD.src/scenes/— one module per plate (open,loss,prompt,hook,room,shoggoth,spacetime,ascent,bureau,leftturn,paperclips,fuse,stack,dense,loom,ilya,outro) plus shared motifs.src/timeline.ts— the edit: scene windows anchored to lyric lines and snapped to the beat grid.scripts/render.ts— offline renderer (headless Chrome → raw frames over WebSocket → ffmpeg).
out/— renders (not in the repo).
bun, Google Chrome (the offline renderer drives it headless through playwright-core) and ffmpeg with libx264. The analysis tools need uv; the renderer doesn't.
cd app
bun install
bunx viteOpen http://localhost:5173 and use the keys below. ?t=23 starts at a given time.
| Key | Action |
|---|---|
| space | play / pause |
| ← / → | seek ±1 s (±5 s with shift) |
, / . |
step one frame |
[ / ] |
previous / next scene |
l |
loop the current scene |
h |
hide the UI |
The preview renders in real time on a recent Mac. The export is not real time and is heavier.
cd app
bun scripts/render.ts video --samples 4 --shutter 0.2 --out ../out/pdoom.mp4- Output: 1920×1080 at 60 fps, x264 CRF 16, AAC audio.
--samples 4: averages four sub-frames per frame over a short shutter, which gives temporal anti-aliasing of the fine engraved lines.- Other modes:
stills,sheet(contact sheets,--cutsfor every scene boundary),perf, andplates(regeneratespublic/plates/, the stills used by the outro's rewind montage; rerun it after changing a scene).
cd app
bun scripts/render.ts video --scale 2 --samples 4 --shutter 0.2 --out ../out/pdoom-4k.mp4- Output: a true 3840×2160 render (not an upscale): every layer, line and shader is rendered at the physical resolution. Scenes are laid out in 1920×1080 logical pixels, so the 4K frame looks like the 1080p one, only sharper.
- Cost: about 4× the 1080p render time (roughly 35–40 minutes for the whole song at
--samples 4on an M5 Pro). Headless Chrome uses about 4.5 GB. The film grain is rendered per 4K pixel, which is expensive to encode: at the default CRF 16 the file runs at about 660–700 Mbit/s (about 13 GB for the song, 8× the 1080p file),--crf 18gives about 420 Mbit/s and--crf 20about 230 Mbit/s. --scale 2works with every mode.stillsthen saves full-resolution PNGs, andperfmeasures 4K frame times. In the browser preview, add&scale=2to the URL.
The committed data/*.json files are all the renderer needs. Regenerating them needs the stems and intermediates, which are not in the repo:
- Stems: Demucs
htdemucs_ftintoanalysis/stems/htdemucs_ft/pdoom/(uv run python -m demucs -n htdemucs_ft -o stems ../audio/pdoom.mp3), plus the lead vocal from a mel-band-roformer karaoke model (audio-separator) inanalysis/stems/karaoke/lead.wav. - Intermediates:
ctc_emissions.py,whisper_run.pyandvocal_feats.pywrite them toanalysis/work/. The pipeline is described at the top ofanalysis/align.py.
cd analysis
uv run python align.py # data/lyrics.json
uv run python analyze.py # data/audio.jsonThe models download about 4 GB of weights into analysis/.cache/; delete that folder afterwards.
- Fonts: Archivo, IBM Plex Mono and Cormorant Garamond (SIL Open Font License). Single-stroke EMS and Hershey fonts via the
hersheytextpackage (OFL / public domain).
The code is released under the MIT License. The fonts in app/public/fonts/ keep their own licenses (see Credits), and the song and lyrics (audio/, lyrics/, data/lyrics.json) are not covered by it.
0 comments
log in to comment.