Three verdicts from the phone

The PERFECT spark ring is out — the hitstop, the click and the praise word
already carry the moment. The net-cord wobble drops to ~1 px. And the
floodlight stutter is gone entirely: on the iPhone a random dark dip over
the stands reads as the crowd flickering black — a rendering bug, not
ambience.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Fable 5 committed 2026-08-06 09:58:11 +02:00
1 parent 8ab7ac0c25
commit b504cb3565
3 files changed
+9 -36

No files matched your search

+6 -28
View File
@@ -1301,7 +1301,6 @@ function drawModal(m) {
// ------------------------------------------------------------------ frame ----
const ballHist = []; // hot-ball ghost trail positions (render-owned, see the streak block in render)
let flickAt = 14; // idleT of the next floodlight stutter (splatter ambience, see render)
const AD_H = 14; // px tall the courtside ad strip is drawn — also the ceiling for centred HUD text
const FLASH_Y = 68; // the ONE slot every free-floating message uses. Clear of the scoreboard block
// (6 + 42 + 15 = 63 at its tallest), which the old 60 was not in portrait —
@@ -1331,18 +1330,9 @@ export function render() {
// stands, ads, rot tint, court, lines and the run-off, as one cached blit (see paintBackdrop);
// the clap ripple is overlaid live inside. The ad strip sits flush on the court's back edge (the
// out-area quad reaches z=-6), which keeps the boards clear of the lawn in BOTH orientations.
const standsBottom = drawBackdrop(pal, pt);
// floodlight stutter (splatter only): every 8-15 s the stadium lights dip for a tenth of a second
// — "the lights went wrong", made ambient. An overlay over the stands, so it never invalidates
// the backdrop cache; skipped during the duel, where the lights come back to tennis.
if (G.mode === 'splatter' && !G.interlude) {
if (flickAt > G.idleT + 16) flickAt = G.idleT + 8 + Math.random() * 7; // a fresh run pulled it back in
if (G.idleT > flickAt + 0.12) flickAt = G.idleT + 8 + Math.random() * 7;
else if (G.idleT >= flickAt) {
ctx.fillStyle = `rgba(4,2,10,${(0.3 * Math.abs(Math.sin((G.idleT - flickAt) / 0.12 * Math.PI * 2))).toFixed(3)})`;
ctx.fillRect(0, 0, W, standsBottom);
}
}
drawBackdrop(pal, pt);
// (a random floodlight stutter darkened the stands here for one build — on the phone it read as
// the crowd area flickering black, i.e. as a rendering bug, not as ambience. Gone.)
// gore decals: what remains of the visiting team (flat ground layer, then far-side mounds)
for (const d of gore) {
@@ -1413,7 +1403,7 @@ export function render() {
for (let i = 0; i <= 24; i++) {
const x = nlt.x + (nrt.x - nlt.x) * i / 24;
const y = nlt.y + (nrt.y - nlt.y) * i / 24
+ Math.sin((x - px) * 0.3 + rip.t * 34) * 3 * (rip.t / 0.4) * Math.exp(-Math.abs(x - px) * 0.05);
+ Math.sin((x - px) * 0.3 + rip.t * 34) * 1.1 * (rip.t / 0.4) * Math.exp(-Math.abs(x - px) * 0.05); // ~1 px at the clip — a wobble, not a wave
i ? ctx.lineTo(x, y) : ctx.moveTo(x, y);
}
} else { ctx.moveTo(nlt.x, nlt.y); ctx.lineTo(nrt.x, nrt.y); }
@@ -1558,20 +1548,8 @@ export function render() {
ctx.beginPath(); ctx.ellipse(p.x, p.y, p.s * 0.62 * k, p.s * 0.22 * k, 0, 0, 7); ctx.stroke();
}
if (G.mode !== 'eaten') drawChar(P); // ...the dying man is drawn with the crowd instead (drawDying)
if (G.spark) { // PERFECT contact: a one-shot expanding ring + four spark pixels at the strike.
// Light, which the procedural rule explicitly exempts — same clause as the barrel's glow.
const s = G.spark, k = 1 - s.t / 0.3, p = proj(s.x, s.y, s.z);
const r = Math.max(2, p.s * (0.06 + 0.4 * k));
ctx.strokeStyle = `rgba(255,255,255,${(0.9 * (1 - k)).toFixed(3)})`;
ctx.lineWidth = 1.5;
ctx.beginPath(); ctx.ellipse(p.x, p.y, r, r * 0.75, 0, 0, 7); ctx.stroke();
ctx.fillStyle = `rgba(255,250,220,${(1 - k).toFixed(3)})`;
for (let i = 0; i < 4; i++) { // sparks fly the diagonals, riding the ring's edge outward
const a = i * 1.5708 + 0.7854;
ctx.fillRect(Math.round(p.x + Math.cos(a) * r * 1.35), Math.round(p.y + Math.sin(a) * r), 1.5, 1.5);
}
ctx.lineWidth = 1;
}
// (a PERFECT-contact spark ring lived here for one build — Oliver's verdict: too much. The
// hitstop, the click and the praise word already carry the moment.)
// The braced "rambo racket" is NOT drawn any more. It was a procedural racket laid over a sprite
// that already holds one: straight up-court it projected onto his back as a floating oval, and
// offset to the side it just stuck out. The pellets streaming up-court are the feedback; the man