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:

  1. 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 = 1000 Hz at t = 0.
    • WARP_LPF_MAX_HZ = 400 Hz at t = 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.
  2. Track playback rate — linear. The active track’s HTMLAudioElement.playbackRate is set directly (no smoothing) to:

    • 1.0 at t = 0.
    • WARP_PLAYBACK_RATE_MIN = 0.8 at t = 1 (i.e. 20% slowdown at full warp depth).
    • Formula: rate = 1 + (0.8 - 1) * t.
    • Only applied if a track is currently loaded (_current non-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 warpT tween duration (~0.25 s) so the audio leads/lags the visual feel by a perceptually inaudible margin.
  • engine/world/warp-puddle-system.ts — owns the ship.warpT tween 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 same ship.warpT.