A bed that finishes loading wakes the context it was started for

Oliver's hunch was right: the tap unlocks audio and starts a 768 KB fetch, but
the source starts seconds later inside a promise with no gesture near it. iOS
suspends an idle context in that gap, and a source started into a suspended
context is silent for good — nothing on the splash ever asks for audio again.
Resume once more when the buffer lands, which the first tap has already
authorised, and again when the app comes back to the foreground.

Intermittent because it only bites when the decode outlasts the browser's
patience: a phone, a cold cache, a slow radio.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-15 15:16:41 +02:00
1 parent 3b639e743b
commit 1daa3e9d6a
4 files changed
+35 -4

No files matched your search

+13
View File
@@ -452,6 +452,19 @@ followed you onto the silent menu.
it, and ducks both court beds. `ambient()` and `zombieMusic()` un-duck themselves for the same
reason — the SECOND run would otherwise have been played in the silence the menu left them in.
**A BED MUST RESUME THE CONTEXT WHEN IT FINISHES LOADING, NOT ONLY WHEN IT IS ASKED FOR.** The
second half of the report — *"sometimes, when initially entering the game and tapping somewhere,
the background music does not start; I suspect the sound is not yet loaded"* — and he had it
exactly right. The tap unlocks the context and starts a 768 KB fetch; the `src.start()` that
follows happens seconds later inside a promise, with no gesture anywhere near it. iOS suspends an
idle context in that gap, and **a source started into a suspended context is silent forever** —
nothing on the splash calls `audio()` again, so the theme simply never arrived. `loopBed` now calls
`audio()` a second time in its `.then`, which is allowed because the first tap already unlocked the
context. That is why it was intermittent: it only bites when the decode outlasts the browser's
patience, i.e. a phone on a cold cache. A `visibilitychange` resume covers the same trap from the
other end (a phone that locks ON THE MENU came back silent; during play every pock resumes it for
free, but a title screen makes no sound of its own).
**...and each of those un-ducks ITS OWN BED ONLY.** The first cut had `ambient()` un-duck both
court beds together, which put the splatter music back on in classic tennis for the rest of the
session — Oliver caught it on the retest: *"after playing the splatter gore, the zombie background