From 75936d33244f7e28f2f45ac00e9f77e538cbe189 Mon Sep 17 00:00:00 2001 From: Oliver Rieger Date: Sun, 2 Aug 2026 15:57:36 +0200 Subject: [PATCH] Fall the light off across the run-off, so it reads as ground and not margin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Riding the court up left a wide band below the near baseline, and `pal.out` is one flat colour — so the perspective visibly stopped partway down the screen and the rest read as dead margin rather than as the back of a court. A gradient from the baseline to the bottom edge fixes it: the band gets depth, the rack gets a frame, and what darkens is exactly the area the thumb covers anyway. Starts at alpha 0 on the baseline, so there is no seam. A procedural "trodden ground" variant (scuffs and drag marks) was built and cut: random ellipses read as unexplained puddles, not as wear. That idea wants authored detail and gore that accumulates over a run, not noise. Co-Authored-By: Claude Opus 5 --- render.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/render.js b/render.js index 42f2dca..bd36cb8 100644 --- a/render.js +++ b/render.js @@ -161,6 +161,24 @@ export function proj(x, y, z) { return { x: W / 2 + x * s, y: TUNE.horizon + (TUNE.camY + aimCam * TUNE.aimCamY - y) * s, s }; } +// The run-off apron below the near baseline. Raising the court to make room for the pull (see +// fitViewport) left a wide band down there, and `pal.out` is a single flat colour — so the +// perspective visibly stopped partway down the screen and the rest read as dead margin. This falls +// the light off toward you, which gives the band depth, frames the rack, and darkens exactly the +// area the thumb covers anyway. Starts at alpha 0 on the baseline, so there is no seam to see. +// NOT a near backstop: at camZ 48 with the baseline at z 23.77 a real one would sit between the +// camera and the player and draw enormous. You are looking OVER it, which is why a broadcast frame +// never shows one either. Wear and props at the margins are the next layer, not this. +function drawRunOff() { + const top = proj(0, 0, COURT.L).y; + if (top >= H) return; // landscape has no apron at all — the baseline projects off the bottom + const g = ctx.createLinearGradient(0, top, 0, H); + g.addColorStop(0, 'rgba(6,10,6,0)'); + g.addColorStop(1, 'rgba(6,10,6,0.72)'); + ctx.fillStyle = g; + ctx.fillRect(0, top, W, H - top); +} + // ---------------------------------------------------------------- sprites ---- // assets/.png; game falls back to code-drawn figures until sprites load. const SPRV = 78; // asset cache-buster — bump with main.js?v=N (see the img.src note below) @@ -831,6 +849,7 @@ export function render() { line(-COURT.W / 2, COURT.svcNear, COURT.W / 2, COURT.svcNear, 1); line(0, COURT.svcFar, 0, COURT.svcNear, 1); line(0, 0, 0, 0.35, 1); line(0, COURT.L - 0.35, 0, COURT.L, 1); // center marks + drawRunOff(); // light falls off across the apron below the baseline // gore decals: what remains of the visiting team (flat ground layer, then far-side mounds) for (const d of gore) {