Sound was dead in the app because status 0 is not a failure; and back is a swipe to the RIGHT

Oliver on the first shell build: *"I don't hear any of the games sounds.. daniel the umpire is
synthesized which is horrible and no music is playing on start"* — two independent causes.

- **Opaque fetch.** Capacitor serves the bundle through a custom WKURLSchemeHandler, and `fetch`
  reports those responses as `status: 0, ok: false` with the whole body present and decoding
  perfectly. All three loads in `audio.js` guarded with `if (!r.ok) throw`, so inside the app every
  recorded sound failed at the door: the umpire fell through to the synthesized `say()`, no bed
  started, no sample played. The synthesized sfx kept working, which made it read as a mixing bug
  rather than a loader one. Fixed once, in the shared `bytes()` helper every audio fetch now routes
  through — a status of 0 is the ABSENCE of an HTTP status, so trust it only when there is one and
  a real 404 still falls back. Any future `!r.ok` on a bundle-relative fetch is this same bug.
- **The ring switch.** WKWebView's WebAudio runs in the SoloAmbient session, which the hardware
  silent switch mutes outright — a silent game on a phone that lives on silent, with nothing on
  screen to explain it. AppDelegate now sets AVAudioSession `.playback`, the games category.

Confirmed in the shell: the bed reaches PLAYING, the context is `running` with no gesture at all
(autoplay really is allowed in WKWebView, as LATER predicted), and `say()` never fires. On the web,
a genuinely missing file still falls back — checked both.

And the swipe flips: **right goes back**, the way iOS's own pop gesture goes (*"that's the iOS
way"*). Left now does nothing. Noted in PROJECT.md that on a platform gesture the platform's
convention beats the literal wording of the brief, and that is worth saying up front.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-20 17:18:47 +02:00
1 parent d71a26831a
commit 76ea1d0d79
6 files changed
+67 -20

No files matched your search

+4 -3
View File
@@ -537,7 +537,8 @@ document.getElementById('sBack').addEventListener('click', () => {
});
document.getElementById('bsBack').addEventListener('click', () => go('secret'));
// SWIPE TO GO BACK. Every screen but the splash wears exactly one chevron in its header (.hdr in
// SWIPE TO GO BACK — RIGHTWARD, the way iOS's own pop gesture goes (Oliver, 2026-08-20: *"that's
// the iOS way"*). Every screen but the splash wears exactly one chevron in its header (.hdr in
// index.html) and that button ALREADY knows where back is for its own screen — the wizard's shape
// lives in the handlers above and nowhere else. So the gesture finds the one visible chevron and
// clicks it, rather than keeping a second map that would drift the first time a stage is added.
@@ -553,8 +554,8 @@ intro.addEventListener('touchend', e => {
if (!from || e.touches.length) return;
const t = e.changedTouches[0];
const dx = t.clientX - from.x, dy = t.clientY - from.y;
// leftward, and clearly horizontal: the stats and board screens scroll vertically under the finger
if (dx > -SWIPE_MIN || Math.abs(dy) > Math.abs(dx) * 0.7) return;
// rightward, and clearly horizontal: the stats and board screens scroll vertically under the finger
if (dx < SWIPE_MIN || Math.abs(dy) > Math.abs(dx) * 0.7) return;
const back = [...intro.querySelectorAll('.chev')].find(b => b.offsetParent); // the one on screen
if (!back) return;
// NOT passive, and this is why: a swipe that starts and ends inside one of the big poster cards