Music ↔ Warp-Puddle Coupling
While the ship is inside a warp puddle, the in-game music is modulated in real time to reinforce the altered-state feel: the music bus is muffled by a low-pass sweep and the active track slows down. Both transforms are driven from a single normalized input — ship.warpT — that ramps 0→1 on entry and 1→0 on exit.
Wiring
Each engine frame, bridge.ts (warp-puddle tick block, ~L3205-3231) pushes the current ship.warpT into the music subsystem before running the puddle’s enter/exit hooks:
MusicPlayer.setWarpIntensity(ship.warpT);This is the only call site. The bridge owns the gameplay state; the music player owns the audio-graph transform.
What setWarpIntensity(t) does
MusicPlayer.setWarpIntensity(t) (in engine/audio/music-player.ts) clamps t to [0, 1] then applies two interpolations:
-
Music-bus low-pass cutoff — exponential. The shared music low-pass filter (
AudioBus.getMusicLowpass()) is swept in log-frequency space between two named constants:WARP_LPF_NORMAL_HZ = 1000Hz att = 0.WARP_LPF_MAX_HZ = 400Hz att = 1.- Interpolation is
exp(logNormal + (logWarped - logNormal) * t), so the sweep feels natural to the ear (filter sweeps perceptually live in log frequency, not linear). - The new cutoff is scheduled with
lpf.frequency.setTargetAtTime(hz, ctx.currentTime, 0.05)— a 0.05 s time constant. This avoids zipper-noise / clicks while still tracking the upstream warpT tween (~0.25 s) closely enough to feel snappy.
-
Track playback rate — linear. The active track’s
HTMLAudioElement.playbackRateis set directly (no smoothing) to:1.0att = 0.WARP_PLAYBACK_RATE_MIN = 0.8att = 1(i.e. 20% slowdown at full warp depth).- Formula:
rate = 1 + (0.8 - 1) * t. - Only applied if a track is currently loaded (
_currentnon-null). No-op otherwise.
If the AudioContext or music low-pass node isn’t ready yet (audio not unlocked, mission not started), the LPF branch silently no-ops; the playback-rate branch likewise only fires when a track is active. The function is safe to call every frame regardless of audio state.
Why these specific values
- 400 Hz floor — gives the aggressive “muffled through a wall” character desired for the altered-state. From the 1000 Hz baseline, that’s roughly a 1.3-octave drop.
- 0.8× playback floor — couples a pitch + tempo droop to the muffling. The combined effect reads as the music itself being affected by the warp field rather than as a generic audio filter.
- 0.05 s LPF smoothing constant — chosen against the upstream
warpTtween duration (~0.25 s) so the audio leads/lags the visual feel by a perceptually inaudible margin.
Related
engine/world/warp-puddle-system.ts— owns theship.warpTtween and warp-group membership.engine/audio/audio-context.ts— declares the music-bus low-pass and its 1000 Hz default cutoff.engine/rendering/warp-puddle-render.ts— paired visual layer (black mask + raymarched interior) keyed off the sameship.warpT.