let audioCtx: AudioContext | null = null; function getAudioContext(): AudioContext { if (!audioCtx) { audioCtx = new AudioContext(); } if (audioCtx.state === 'suspended') { audioCtx.resume(); } return audioCtx; } function playTone( ctx: AudioContext, frequency: number, startTime: number, duration: number, type: OscillatorType = 'sine', volume = 0.3, ) { const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.type = type; osc.frequency.value = frequency; gain.gain.setValueAtTime(volume, startTime); gain.gain.exponentialRampToValueAtTime(0.001, startTime + duration); osc.connect(gain); gain.connect(ctx.destination); osc.start(startTime); osc.stop(startTime + duration); } export function playCorrectSound() { try { const ctx = getAudioContext(); const now = ctx.currentTime; // Cheerful ascending two-note ding playTone(ctx, 523.25, now, 0.15, 'sine', 0.25); // C5 playTone(ctx, 659.25, now + 0.12, 0.2, 'sine', 0.25); // E5 } catch { /* audio unavailable */ } } export function playIncorrectSound() { try { const ctx = getAudioContext(); const now = ctx.currentTime; // Gentle descending two-note playTone(ctx, 330, now, 0.15, 'triangle', 0.2); // E4 playTone(ctx, 262, now + 0.12, 0.25, 'triangle', 0.2); // C4 } catch { /* audio unavailable */ } } export function playCompletionSound() { try { const ctx = getAudioContext(); const now = ctx.currentTime; // Ascending fanfare playTone(ctx, 523.25, now, 0.15, 'sine', 0.2); // C5 playTone(ctx, 659.25, now + 0.1, 0.15, 'sine', 0.2); // E5 playTone(ctx, 783.99, now + 0.2, 0.15, 'sine', 0.2); // G5 playTone(ctx, 1046.5, now + 0.3, 0.35, 'sine', 0.25); // C6 } catch { /* audio unavailable */ } }