Fix SFX: unlock AudioContext from the click, louder/clearer blips

sfx() bailed whenever the context wasn't already 'running', so the autoplay-unlock
race dropped the blips. It's invoked from a click, so resume the context there and
always schedule the tone; bump the envelope and use a triangle wave (with a small
up-chirp on choices) for a more audible cue.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 20:13:51 +02:00
co-authored by Claude Opus 4.8
parent dafc70b047
commit 6d4026476e
+11 -7
View File
@@ -55,16 +55,20 @@ export const audio = {
sfx(kind: Sfx) { sfx(kind: Sfx) {
if (muted) return if (muted) return
const context = audioContext() const context = audioContext()
if (!context || context.state !== 'running') return if (!context) return
// Called from a click, so we can unlock the context right here if it's suspended.
if (context.state === 'suspended') void context.resume()
const osc = context.createOscillator(), gain = context.createGain() const osc = context.createOscillator(), gain = context.createGain()
osc.type = 'sine' osc.type = 'triangle'
osc.frequency.value = kind === 'choice' ? 540 : kind === 'sting' ? 300 : 400 const now = context.currentTime + 0.01
const now = context.currentTime const base = kind === 'choice' ? 500 : kind === 'sting' ? 260 : 420
osc.frequency.setValueAtTime(base, now)
if (kind === 'choice') osc.frequency.exponentialRampToValueAtTime(base * 1.5, now + 0.09) // a little up-chirp
gain.gain.setValueAtTime(0.0001, now) gain.gain.setValueAtTime(0.0001, now)
gain.gain.exponentialRampToValueAtTime(0.07, now + 0.008) gain.gain.exponentialRampToValueAtTime(0.16, now + 0.012)
gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.12) gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.16)
osc.connect(gain).connect(context.destination) osc.connect(gain).connect(context.destination)
osc.start(now); osc.stop(now + 0.13) osc.start(now); osc.stop(now + 0.18)
}, },
// Resume the context and retry pending music on a user gesture. // Resume the context and retry pending music on a user gesture.
resume() { resume() {