// Short synthesized blips, so there are no audio files to ship. Silent until the // first move, since browsers only allow audio after a user gesture. let ctx: AudioContext | null = null; function tone(freq: number, ms: number, gain: number) { if (!ctx) ctx = new (window.AudioContext || (window as any).webkitAudioContext)(); const osc = ctx.createOscillator(); const vol = ctx.createGain(); osc.type = "triangle"; osc.frequency.value = freq; vol.gain.value = gain; osc.connect(vol).connect(ctx.destination); const t = ctx.currentTime; vol.gain.setValueAtTime(gain, t); vol.gain.exponentialRampToValueAtTime(0.0001, t + ms / 1000); osc.start(t); osc.stop(t + ms / 1000); } export const sound = { move: () => tone(220, 70, 0.05), capture: () => tone(150, 110, 0.07), check: () => tone(440, 130, 0.06), castle: () => { tone(196, 70, 0.05); setTimeout(() => tone(294, 90, 0.05), 70); }, promote: () => { tone(523, 80, 0.06); setTimeout(() => tone(659, 80, 0.06), 80); setTimeout(() => tone(784, 150, 0.06), 160); }, start: () => { tone(330, 80, 0.06); setTimeout(() => tone(440, 80, 0.06), 90); setTimeout(() => tone(554, 140, 0.06), 180); }, tick: () => tone(880, 30, 0.03), end: () => { tone(330, 160, 0.06); setTimeout(() => tone(247, 220, 0.06), 120); }, };