PixiJS renderer cutover: replace the DOM map renderer (fixes far-zoom breakage) #2

Closed
opened 2026-07-05 09:13:30 +00:00 by beasty · 1 comment
Owner

Context

The DOM map renderer breaks at far zoom-out: ~90k absolutely-positioned nodes stall the frame, and unfetched chunks render as gray voids (user-reported; reproducible today by zooming all the way out on /game). The engine analysis (docs/ENGINE_FRONTEND.md) chose PixiJS v8 + pixi-viewport, and the spike at /game/pixi (40ab476) validated it: same live world via the shared projection/sprite table/SSE/chunks API, two LOD bands (per-tile sprites close, one dominant-biome diamond per chunk past scale 0.2), measured 60fps at whole-map overview with all 625 chunks, ~54fps mid, 60fps close, zero console errors.

Task

Promote the spike to the real renderer at /game, replacing the DOM tile/building/cat layers and the hand-rolled MapViewport.

Prerequisite: #1 — hoist the pure render helpers out of TileLayer.tsx into a shared module both renderers import. Do not start the port before that lands.

Port in the step order from docs/ENGINE_FRONTEND.md, each step shippable behind the /game/pixi route until the final swap:

  1. Tiles — close the spike's known gaps: road autotiling (reuse roadSpriteFor + the neighbour pass), exact biome tints via pooled ColorMatrixFilter per distinct CSS-filter string (spike only reproduces brightness), and the organic village fence/clearing/fog from the claimed set (25571c4) instead of the spike's hardcoded ring radius 4.
  2. Buildings — construction scaffolds/progress, all building types.
  3. Cats — carry icons, name labels, activity badges, selection highlight (spike already has pooled sprites + 8-facing walk animation).
  4. Zones & raiders — zone overlays with countdowns, raid warband rendering.
  5. Interaction & swap — click-to-select via eventMode:'static' + hitArea (isoToTile is the inverse), zone/road drag-drawing, then swap /game to the Pixi screen. React DOM keeps the HUD/panels/modals above the canvas. Keep the DOM renderer reachable (e.g. /game/dom) for one release as a fallback, then delete.

Acceptance criteria

  • /game holds ~60fps at whole-map zoom-out with zero gray voids (the ChunkStore's TTL cache + bounded fetch queue from the spike fixes the request storm).
  • Feature parity checklist vs today's /game: fog, fence+gate, roads, stumps, water, buildings w/ progress, cats w/ labels/carry/facing, zones, raiders, selection, boost clicks, zone painting, road building.
  • Mobile pinch-zoom works (pixi-viewport).
  • In-browser verification with screenshots at 3+ zooms, side-by-side against the pre-cutover renderer; document FPS numbers in the PR.
  • Full suite + typecheck green; new pure logic (LOD thresholds, chunk store eviction) unit-tested.

Non-goals

  • No gameplay/simulation changes. No art changes. WebGPU/other engines are out of scope (the verdict doc covers why).
## Context The DOM map renderer breaks at far zoom-out: ~90k absolutely-positioned nodes stall the frame, and unfetched chunks render as gray voids (user-reported; reproducible today by zooming all the way out on `/game`). The engine analysis (`docs/ENGINE_FRONTEND.md`) chose **PixiJS v8 + pixi-viewport**, and the spike at `/game/pixi` (`40ab476`) validated it: same live world via the shared projection/sprite table/SSE/chunks API, two LOD bands (per-tile sprites close, one dominant-biome diamond per chunk past scale 0.2), measured 60fps at whole-map overview with all 625 chunks, ~54fps mid, 60fps close, zero console errors. ## Task Promote the spike to the real renderer at `/game`, replacing the DOM tile/building/cat layers and the hand-rolled `MapViewport`. **Prerequisite: #1** — hoist the pure render helpers out of `TileLayer.tsx` into a shared module both renderers import. Do not start the port before that lands. Port in the step order from `docs/ENGINE_FRONTEND.md`, each step shippable behind the `/game/pixi` route until the final swap: 1. **Tiles** — close the spike's known gaps: road autotiling (reuse `roadSpriteFor` + the neighbour pass), exact biome tints via pooled `ColorMatrixFilter` per distinct CSS-filter string (spike only reproduces `brightness`), and the organic village fence/clearing/fog from the claimed set (`25571c4`) instead of the spike's hardcoded ring radius 4. 2. **Buildings** — construction scaffolds/progress, all building types. 3. **Cats** — carry icons, name labels, activity badges, selection highlight (spike already has pooled sprites + 8-facing walk animation). 4. **Zones & raiders** — zone overlays with countdowns, raid warband rendering. 5. **Interaction & swap** — click-to-select via `eventMode:'static'` + `hitArea` (isoToTile is the inverse), zone/road drag-drawing, then swap `/game` to the Pixi screen. React DOM keeps the HUD/panels/modals above the canvas. Keep the DOM renderer reachable (e.g. `/game/dom`) for one release as a fallback, then delete. ## Acceptance criteria - [ ] `/game` holds ~60fps at whole-map zoom-out with zero gray voids (the ChunkStore's TTL cache + bounded fetch queue from the spike fixes the request storm). - [ ] Feature parity checklist vs today's `/game`: fog, fence+gate, roads, stumps, water, buildings w/ progress, cats w/ labels/carry/facing, zones, raiders, selection, boost clicks, zone painting, road building. - [ ] Mobile pinch-zoom works (pixi-viewport). - [ ] In-browser verification with screenshots at 3+ zooms, side-by-side against the pre-cutover renderer; document FPS numbers in the PR. - [ ] Full suite + typecheck green; new pure logic (LOD thresholds, chunk store eviction) unit-tested. ## Non-goals - No gameplay/simulation changes. No art changes. WebGPU/other engines are out of scope (the verdict doc covers why).
beasty referenced this issue from a commit 2026-07-06 12:59:00 +00:00
Author
Owner

Closed after the Rust/Bevy cutover. The PixiJS renderer migration is superseded by the native Bevy client.

Closed after the Rust/Bevy cutover. The PixiJS renderer migration is superseded by the native Bevy client.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
beasty/cat_idler#2
No description provided.