← All decisions

Wire the platformer's rendering/input (glemy/game_platformer), verified via a new browser-check using captured-GPU-uniform position checks

architecturethird-gameplatformerrenderingbrowser-check

Context

RM-034 Phase 2: wiring the platformer's already-verified pure logic (decision 0059) to real rendering and input, mirroring glemy/game_breakout's own Phase 2 (decision 0053) structurally. Only the player is a real physics.Entity, rendered via the existing WebGPU pipeline unchanged; platforms and the goal are CSS-div overlays, same category of technique as Breakout's paddle/bricks. Genuinely simpler than Breakout's own equivalent phase in one real way: platforms and the goal are static and never destroyed (unlike Breakout's destructible bricks), so there is no Brick.id/event-driven-hide problem to solve at all -- every overlay element is created once at startup and never touched again. This phase also needed its own real-browser verification script, and since the player renders through WebGPU (not a CSS div, unlike Breakout's paddle), confirming real keyboard-driven movement needed a different technique than browser_check_breakout.ts's own DOM-position reads.

Options considered

Render the player as a CSS div overlay too, matching how Breakout's paddle is positioned, so its position could be read directly from the DOM in the browser check — rejected
The player is this game's one real physics.Entity -- rendering it through the existing WebGPU circle pipeline (render.render_entities_to_canvas, completely unchanged) is both the simpler implementation (no per-frame CSS position write needed at all, unlike Breakout's paddle) and the more meaningful test of the Core rendering pipeline actually working end to end for a third game. Verifying its real-browser position instead reuses tools/browser_check.ts's own already-proven captured-GPU-uniform-buffer technique (the same one that already confirms Tiers' real click-to-spawn world-x placement), rather than inventing a new rendering path just to make browser verification easier.
Skip the automated terminal-status (Won/Lost) check in the browser-check script, since navigating a real headless browser through a full platforming sequence to reach the goal is fragile to automate reliably — rejected
Correct that reaching the real goal (multiple precisely-timed jumps) would be fragile to script reliably -- but falling off the starting platform doesn't require any precision at all: holding a single direction key from spawn, with no jump, deterministically walks the player off the platform's own edge and free-falls past bounds.min.y within under a second, computed directly from the real move_speed/gravity constants. This exercises the real Lost path end to end (the same category of real terminal-status confirmation Breakout's own browser check performs) without needing fragile multi-jump choreography.

Decision

Built glemy/game_platformer.gleam (tick_and_render/start/loop/main, no score-equivalent needed since this game has none) and glemy/game_platformer_ffi.mjs (setStatusMessage/createLevelElements -- platforms and goal created once, no hide/destroy counterpart needed -- playJumpSound/playLandSound, its own AudioContext instance) plus platformer.html. Extended tools/browser_check_shared.ts's bootstrap to a third caller (already extracted for exactly this at Breakout's own Phase 2, decision 0053 -- now confirmed correctly scoped by a real third use, not over-engineered for two). Added tools/browser_check_platformer.ts, reusing the exact captured-GPU-uniform-buffer technique tools/browser_check.ts already established for Tiers' click-to-spawn confirmation (adapted here to confirm real ArrowRight movement and a real space-triggered jump moved the player in world space), plus a deterministic real fall-death check (hold one direction key from spawn, no jump, reaching a real "You fell!" status within ~2s).

Verification

gleam test and gleam test --target javascript both pass -- 281 Erlang / 308 JavaScript, exactly the prior baseline (281/306 as of decision 0059) plus 2 new @target(javascript)-only tick_and_render integration tests using a real OffscreenCanvas (decision 0015's pattern). deno task check-warnings passes after updating the baseline (104 warnings, every new entry confirmed as the already-documented @target(javascript) target-gating artifact under the new game_platformer.gleam/game_platformer_test.gleam files -- including an 'Empty module' entry on the Erlang target, already precedented by glemy/io's own equivalent). deno task browser-check-platformer passes for real on its first run with no iteration needed (39 real requestAnimationFrame frames, a real held ArrowRight moved the captured player position past world-x 28, a real held-space jump moved it past world-y 20, 2 sounds played, and holding ArrowLeft from a fresh page load reached a real "You fell!" status). deno task browser-check and browser-check-breakout were also re-run for regression safety (CLAUDE.md's standing rule for anything touching glemy/game-shaped code) -- both still pass unchanged.

Consequences

glemy now has three complete, independently-launchable reference games (index.html/Tiers, breakout.html/Breakout, platformer.html/Platformer), each with its own real-browser verification script sharing only the genuinely game-agnostic launch/dev-server bootstrap. The captured-GPU-uniform-buffer world-position-verification technique tools/browser_check.ts originally built for one specific check (Tiers' click-to-spawn) is now confirmed reusable as a general pattern for 'did a real WebGPU-rendered entity move to roughly where real input should have put it' -- valuable precedent for a future game whose own real physics entity also has no DOM position to read directly. Phase 3 (feel-constant tuning, glemy-website catalog integration) is the only remaining item on this game's roadmap.

References