PURPOSE

Quick Web Audio synth helpers for reward-cinematic SFX. Cinematic modules fire short tones without registering a RECIPE in micro-sfx.ts. Intentionally simple (square/triangle wave, short envelope); sound design that locks in should graduate to micro-sfx.ts.

OWNS

  • CinematicToneKind union type: slot-tick, slot-chunk, sparkle, cheer, glitch.
  • _TONE_PRESETS table mapping each kind to { freq, dur, type, gain }.
  • LOWPASS_HZ constant (4000 Hz shared cutoff).
  • playCinematicTone(kind, freq?, dur?) function that builds the oscillator-envelope-lowpass graph per call.

READS FROM

  • AudioBus.getCtx() for the shared AudioContext.
  • AudioBus.getUiSfxBus() for the UI SFX destination node.
  • MicroSfx.enabled for the global mute check.

PUSHES TO

  • The UI SFX bus node returned by AudioBus.getUiSfxBus(). Per call routing: oscillator to gain envelope to lowpass biquad to UI bus.
  • AudioBus.resume() is called to nudge the context out of suspended state.

DOES NOT

  • Does not register RECIPEs with micro-sfx.ts.
  • Does not own or create the AudioContext or buses.
  • Does not maintain persistent nodes; every call builds a fresh graph and stops the oscillator after dur + 0.02s.
  • Does not honor anything other than MicroSfx.enabled for mute; no per-cinematic volume.
  • Does not play if Web Audio is unavailable or the user has muted sound — silent no-op.

Signals

  • Returns void. No events emitted, no observable state.
  • Schedules oscillator stop at now + d + 0.02 so nodes tear down on their own.

Entry points

  • playCinematicTone(kind: CinematicToneKind, freq?: number, dur?: number): void — only public entry; called by cinematic modules under reward-cinematics/.

Pattern notes

  • Gain envelope: setValueAtTime(0, now) to linearRampToValueAtTime(preset.gain, now + 0.004) attack, then exponentialRampToValueAtTime(0.0001, now + d) decay.
  • Shared 4 kHz lowpass biquad with default Q (~1) applied to every tone so reward screens never feel harsh; rolls the ear-piercing top off square ticks while keeping crispness.
  • Preset gains are linear and intentionally low — these layer on top of game music.
  • Tone kinds and _TONE_PRESETS entries must be added together; missing preset short-circuits with a silent return.
  • Optional freq / dur arguments override preset defaults per call.