| import { Howl, Howler } from 'howler' | |
| let unlocked = false | |
| export function isAudioUnlocked(): boolean { | |
| return unlocked | |
| } | |
| /** | |
| * Call within a real user interaction (click / keydown). | |
| * Unlocks the Howler global AudioContext so all subsequent | |
| * playback works without further interaction. | |
| */ | |
| export async function unlockAudio(): Promise<void> { | |
| if (unlocked) return | |
| // 1. Resume Howler's global AudioContext | |
| const ctx = Howler.ctx | |
| if (ctx && ctx.state === 'suspended') { | |
| await ctx.resume() | |
| } | |
| // 2. Play a silent WAV to force-activate (iOS Safari compat) | |
| const silentHowl = new Howl({ | |
| src: ['data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAIA+AAACABAAZGF0YQAAAAA='], | |
| volume: 0, | |
| html5: true, | |
| }) | |
| silentHowl.play() | |
| silentHowl.once('end', () => silentHowl.unload()) | |
| unlocked = true | |
| } | |