Paint the stands once

- The opaque menu no longer has the whole court rendered behind it at full
  resolution — minutes of battery burn nobody could see.
- Stands, ads, rot tint, court, lines and the run-off are one cached blit,
  rebuilt only when the palette actually moves; the clap ripple stays live.
- RES derives from devicePixelRatio (capped at 4), so a 2x phone stops
  painting double the pixels it can show.
- One shared noise buffer for every synthesized sound — applause alone was
  allocating ~200 fresh buffers per cheer.
- The touch-steer path stops re-measuring a fixed canvas rect per move event.

Skipped as not-quick: the z-sort closure churn and swap-remove splices —
real but small, and each is a behavior-preserving refactor for another day.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Fable 5 committed 2026-08-06 09:36:17 +02:00
1 parent f1f0d05a61
commit e07196f247
4 files changed
+173 -129

No files matched your search

+4
View File
@@ -21,6 +21,10 @@ let paused = false;
function frame(now) {
requestAnimationFrame(frame); // schedule FIRST: one bad frame must never kill the loop (a thrown error froze the game for good)
if (TUNE.fps && now - last < 1000 / TUNE.fps - 2) return; // frame cap (battery): skip RAF ticks until it's our beat
// THE MENU IS OPAQUE (#intro: fixed, inset 0, solid background). Painting the whole court at RES
// density behind it — for the minutes a phone idles on the splash and stats screens — was pure
// battery burn nobody could see. The world holds still too; nothing on it predates the menu.
if (menuUp()) { last = now; return; }
if (paused) { last = now; render(); return; }
let dt = Math.min(0.04, (now - last) / 1000); last = now;
// A chapter boss dies in slow motion. The clock that ENDS it runs at real speed, so the effect is