Same line of code in both video-scrubbing demos, opposite consequences, and both invisible because the error is swallowed:
.catch(() => { s.loading = false; });
docs/demos/soglia/app.js:114
docs/demos/stacco/app.js:218
soglia — unbounded refetch loop
read() runs on every scroll frame and calls loadClip, whose only guard is:
if (reduce || s.loading || s.ready) return;
Resetting loading = false on failure clears the guard, so the next scroll frame refetches. A clip that 404s or fails to decode becomes an unbounded refetch loop — several MB per attempt, up to once per animation frame, for as long as the visitor keeps scrolling near that segment. On a metered connection that is somebody's data plan.
stacco — permanent silent abandonment
want() guards on one extra flag:
if (reduce || s.loading || s.ready || s.queued) return;
s.queued = true;
s.queued is set to true and never cleared anywhere in the file. So after a single failure the shot is abandoned for good: no retry, no log, and the scene stays on its poster with nothing to indicate anything went wrong.
Why it matters
The two files are the same pattern — stacco evolved from soglia — and they disagree on what failure means. Neither behaviour looks deliberate. The swallowed error is what let them drift apart without anyone noticing.
Suggested fix
A bounded retry in both, sharing one shape:
- 2–3 attempts with backoff, then stop
- clear
s.queued alongside s.loading in stacco so a retry is actually possible
console.warn on the final give-up, naming the clip URL and the status — the poster fallback is fine as an outcome, but it should not be a secret
- keep the visual behaviour identical: the still stays, the page keeps working
Related, smaller
docs/demos/descent/app.js:316 — boot().catch() removes the HUD but skips the honest-copy fallback that the !inst branch applies. If data/quakes-2025.bin fails to fetch, the page keeps claiming the scene above is running:
}).catch(() => {
document.querySelector(".hud")?.remove();
});
The !inst path rewrites .beat--table .beat__body to say the scene is not running. The catch path should do the same.
Found while auditing swallowed errors for #45. Not fixed there — the fix is a behaviour change to two live demos and wants a decision first.
Same line of code in both video-scrubbing demos, opposite consequences, and both invisible because the error is swallowed:
docs/demos/soglia/app.js:114docs/demos/stacco/app.js:218soglia — unbounded refetch loop
read()runs on every scroll frame and callsloadClip, whose only guard is:Resetting
loading = falseon failure clears the guard, so the next scroll frame refetches. A clip that 404s or fails to decode becomes an unbounded refetch loop — several MB per attempt, up to once per animation frame, for as long as the visitor keeps scrolling near that segment. On a metered connection that is somebody's data plan.stacco — permanent silent abandonment
want()guards on one extra flag:s.queuedis set totrueand never cleared anywhere in the file. So after a single failure the shot is abandoned for good: no retry, no log, and the scene stays on its poster with nothing to indicate anything went wrong.Why it matters
The two files are the same pattern — stacco evolved from soglia — and they disagree on what failure means. Neither behaviour looks deliberate. The swallowed error is what let them drift apart without anyone noticing.
Suggested fix
A bounded retry in both, sharing one shape:
s.queuedalongsides.loadingin stacco so a retry is actually possibleconsole.warnon the final give-up, naming the clip URL and the status — the poster fallback is fine as an outcome, but it should not be a secretRelated, smaller
docs/demos/descent/app.js:316—boot().catch()removes the HUD but skips the honest-copy fallback that the!instbranch applies. Ifdata/quakes-2025.binfails to fetch, the page keeps claiming the scene above is running:The
!instpath rewrites.beat--table .beat__bodyto say the scene is not running. Thecatchpath should do the same.Found while auditing swallowed errors for #45. Not fixed there — the fix is a behaviour change to two live demos and wants a decision first.