Ten steps, one engine, one MP4. Click "Mark done" on each step as you
go. The left rail tracks your progress. The "Live engine" panel below
reads state from the engine if you have it open in another tab.
Live engine state
Open the engine in a new tab. This panel polls window.SWR every second. Useful for verifying that the engine actually loaded your song, the library, and the layers.
Engine
not loaded
Library
—
Audio
no song
Layers
0
The poll checks window.opener.SWR first (in case you opened
the engine from this page). If that's empty, it tries
localStorage['swr-debug'] — write a snapshot from the engine
console with localStorage.setItem('swr-debug', JSON.stringify({lib: Lib.items.length, audio: !!Audio.audioEl, layers: Layers.list.length})).
Start the dev server
Open a terminal and start the Vite dev server on port 5174. It serves the engine, the 5 versions, and the library.
# from the project root$ cd /Users/kajicadjuric/Documents/autodashboard/products/sainted-word-records
$ nohup ./node_modules/.bin/vite --port 5174 --host 0.0.0.0 > /tmp/swr.log 2>&1 &✓ Vite ready · http://localhost:5174/
Open the page in a browser
Chrome 126+ or Safari 14.5+ for native MP4 export. Width ≥ 1080px so the 3-column layout doesn't squeeze.
# URL→ http://localhost:5174/
Other tabs? Close them. The engine captures audio from this tab only. If you have YouTube open in another tab, that's fine, but make sure the engine's tab is the focused one when you hit PLAY.
Load a song
Click LOAD SONG in the top bar. Pick any audio file — .mp3, .wav, .m4a, .flac. The engine unlocks the Web Audio context the moment you pick a file (browsers require a user gesture).
The 27-item library (12 videos + 15 images) auto-loads from the library/ folder in the background. You don't need to do anything for it.
Wait for the status pill to say READY
The pill in the top bar goes from IDLE (gray) to READY (green) once the engine has analyzed your library. Each video gets a motion score by sampling frames over a few seconds. If you've never used the engine before, this takes 5–10 seconds.
Status pill
awaiting engine
Library
—
Hit ▶ PLAY
Audio starts, the meter in the top bar jumps, the canvas begins rendering. The engine reads the FFT 60 times per second, extracts 5 bands (sub / bass / mid / treble / air), and pushes them to the layer reactors. Each clip in the canvas scales, shifts hue, slides position — all driven by the spectrum.
If the canvas looks static, click RE-MAP in the top-right. The auto-mapper picks the best clips for each role and shuffles them — different mapping = different visual story, all driven by the same audio.
Pick a duration from the dropdown
Next to the REC button. Default is 30s — what this how-to is for. Other options:
Option
What it does
manual
Old behavior — REC keeps going until you click again
10s
Short reel / TikTok intro
15s
Instagram Reel
30s
Default. Instagram Reel, YouTube Short, 30s ad
60s
One-minute loop
song
Records the remaining length of the loaded track
Hit ● REC
The button turns red and reads ■ STOP. The status pill in the top bar shows recording · MP4 · 30s and counts down. The engine captures the canvas at 30 fps and the audio at 48 kHz, muxes them into one MP4 stream, and streams it to a Blob.
Don't close the tab while recording. The recorder is streaming to a Blob in memory. Closing the tab aborts the recording and you get no file.
Wait for auto-stop
At 30s, the engine fires its setTimeout, calls recorder.stop(), and the status flips to exported. The countdown in the status pill is your clock — when it hits 0s you're done.
// the auto-stop, line by line
this.autoStopAt = durMs > 0 ? Date.now() + durMs : 0;
// ... MediaRecorder.start(500) ...
this.autoStopTimer = setTimeout(() => {
if (this.recording) this.stop();
}, this.autoStopAt - Date.now());
You can also click ■ STOP early to record less than 30s.
Grab the MP4 from your downloads
The browser auto-clicks an invisible <a download> element with a Blob URL. The file lands in your default downloads folder as sainted-word-{timestamp}.mp4. ~4 MB for a 30s recording at 8 Mbps.
// the save, line by line
_save() {
const blob = new Blob(this.chunks, { type: this.mime });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = `sainted-word-${ts}.${ext}`; // .mp4 or .webm
a.click();
}
Done. Open the MP4 in any editor.
Real H.264 + AAC, 48 kHz stereo, 30 fps. Plays in QuickTime, Premiere, DaVinci, Final Cut, VLC. Uploads to YouTube, TikTok, X, Instagram. The codec string is avc1.42E01E + mp4a.40.2 — universal compatibility.
You just made a music video. 10 steps, 0 API keys, 0 servers, 0 installs for the user. Drag the MP4 into Premiere and you've got an editable timeline.
How the recording actually works
The pipeline is short and all in the browser. Four boxes, three arrows, one MP4.
🎨
Canvas
6 layers, 30 fps
→
🎵
Audio
48 kHz from analyser
→
📼
MediaRecorder
H.264 + AAC, 8 Mbps
→
💾
.mp4 file
auto-downloads
The mime preference, in order
The recorder asks for the best MP4 first, then falls back to WebM if the browser can't do MP4. Chrome 126+ and Safari 14.5+ all give you MP4 natively.
Top-left badge on the engine: 5 visual versions → opens the directory. Each version is the same engine with a different blend / blur / grain treatment.
If you want a scene-based, hand-keyframed video (full creative control, no audio reactivity), the hyperframes skill is the right tool. SWR drives the visuals from the audio; hyperframes lets you plan every second with multi-phase scene blueprints, GSAP / CSS / Anime.js keyframes, and offline rendering via Remotion.