From 6d4026476e3941227e5f0e50b6793d2c36b0a055 Mon Sep 17 00:00:00 2001 From: jenstandstad Date: Tue, 18 Aug 2026 20:13:51 +0200 Subject: [PATCH] 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 --- src/audio.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/audio.ts b/src/audio.ts index b89c6e1..1ac5a36 100644 --- a/src/audio.ts +++ b/src/audio.ts @@ -55,16 +55,20 @@ export const audio = { sfx(kind: Sfx) { if (muted) return 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() - osc.type = 'sine' - osc.frequency.value = kind === 'choice' ? 540 : kind === 'sting' ? 300 : 400 - const now = context.currentTime + osc.type = 'triangle' + const now = context.currentTime + 0.01 + 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.exponentialRampToValueAtTime(0.07, now + 0.008) - gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.12) + gain.gain.exponentialRampToValueAtTime(0.16, now + 0.012) + gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.16) 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() {