the room tone tries to start on load (plays where the browser allows, otherwise on the first tap) — the seed page is never silent for want of a gesture
shots / shots (push) Successful in 2m22s

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6897PWcUEwHiqZMcVnNF6
This commit is contained in:
teahubxyzandClaude Fable 5.1 committed 2026-10-21 05:17:12 +00:00
1 parent 131f3b255b
commit 8fc613ed20
1 file changed
+5 -2
+5 -2
View File
@@ -647,13 +647,14 @@ function makeCrackle() {
/* the bed: crackle whenever no music is playing. Decided from state, called whenever state changes. */
function bed() {
clearTimeout(crackle.timer);
crackle.timer = setTimeout(() => { const want = state.primed && !state.playing && !state.holding && state.audio.paused; if (want) crackleOn(); else crackleOff(); }, 300);
crackle.timer = setTimeout(() => { const want = !state.playing && !state.holding && state.audio.paused; if (want) crackleOn(); else crackleOff(); }, 300);
}
function crackleOn() {
if (crackle.on || state.holding) return;
crackle.el ||= makeCrackle(); crackle.on = true;
const base = crackle.base ?? crackle.el.volume, t0 = performance.now();
crackle.el.volume = 0; crackle.el.currentTime = 0; crackle.el.play().catch(() => {});
crackle.el.volume = 0; crackle.el.currentTime = 0;
crackle.el.play().catch(() => { crackle.on = false; clearInterval(crackle.ramp); }); // refused without a gesture: the first tap calls again
clearInterval(crackle.ramp);
crackle.ramp = setInterval(() => { const k = Math.min(1, (performance.now() - t0) / 600); if (!crackle.on) { clearInterval(crackle.ramp); return; } crackle.el.volume = base * k; if (k >= 1) clearInterval(crackle.ramp); }, 40);
}
@@ -675,11 +676,13 @@ function prime() {
state.audio.play().then(() => { state.playing = true; state.primed = true; crackleOff(); setPlayIcon(); vizStart(); }).catch(() => { state.needTap = true; $("#cover").classList.add("tap"); });
return;
}
if (!crackle.on) bed();
if (state.primed || state.current || (state.audio.src && !state.audio.paused)) return;
try { state.audio.src = SILENCE; const p = state.audio.play(); if (p && p.then) p.then(() => { state.primed = true; bed(); }).catch(() => {}); } catch {}
try { crackle.el ||= makeCrackle(); if (!crackle.on) crackleOn(); } catch {}
}
for (const ev of ["pointerdown", "touchend", "keydown"]) document.addEventListener(ev, prime, { capture: true, passive: true });
setTimeout(bed, 400); // the room tone, from the start where the browser lets it
/* iOS routes sound through Web Audio the moment the element is wired to an analyser, and suspends Web Audio when
the screen locks (and obeys the mute switch). So on iOS the cover pulses to the beat from the sheet instead,
and the music stays a plain <audio> element that keeps playing in the background. */