From SPECTRUM_START_S the nodes stop following the lyric and behave as a
spectrum analyser: every sung node is given one log-spaced frequency band
(40 Hz–12 kHz), dealt out in a seeded shuffle, and sizes up and down with that
band's amplitude.
- spectrum.ts: band maths and every tunable. Levels come from
@remotion/media-utils visualizeAudio on audio loaded by
useWindowedAudioData (10 s windows, so the 227 MB WAV is never decoded
whole; before the outro the window stays parked at its start). Bands average
the FFT bins they span, or interpolate for bands narrower than a bin, so
neighbouring bass nodes never move in lockstep. Level =
(raw × GAIN × (f/40 Hz)^TREBLE_TILT)^CURVE with a release that looks back
RELEASE_FRAMES frames at RELEASE_DECAY per frame. Defaults were calibrated
on the release2 outro (nodes pinned at full size 0.6% of the time). The
node→band mapping uses Remotion's seeded random() (BAND_SEED), not
Math.random(), so render tabs and the Studio agree.
- nodeAnimation.ts: SPECTRUM_PEAK and spectrumLook(level), alongside lightUp
and switchOff, plus a levelExponential shaping curve.
- Transect.tsx: hands each node over from its lyric look to its spectrum look
over SPECTRUM_FADE_S.
- package.json: declare @remotion/media-utils 4.0.522 (already installed as a
dependency of the CLI; now pinned alongside the other Remotion packages).
Verified: eslint + tsc clean on this commit's tree; outro stills show nodes
sized by band level, so the windowed audio loads during renders.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PLt25nCVMsoroKALYUBu6u