--- name: html5-canvas-game-development description: Class-level patterns for extending HTML5 Canvas games without rewriting core logic, especially mobile/touch adaptation and browser-based online multiplayer. version: 1.0.0 author: Hermes Agent license: MIT metadata: hermes: tags: [html5, canvas, games, mobile, touch, multiplayer, websocket] related_skills: [systematic-debugging] --- # HTML5 Canvas Game Development ## Overview Use this for the common class of work where an existing browser Canvas game must gain major new capabilities without a full engine rewrite. The reliable strategy is adaptation-by-injection: preserve the game loop and state model, then bridge new inputs, layouts, and networking into it. ## When to Use - Porting a desktop Canvas game to mobile portrait play - Adding touch controls to a keyboard-driven game - Adding online multiplayer to a local-only/deterministic game - Fixing Canvas/HTML overlay interaction issues on responsive layouts - Building smooth interactive radial/rotary visualizations such as astrology wheels, dials, timelines, or other gesture-driven charts ## 1. Mobile/touch adaptation Key pattern: inject touch state into the same `heldKeys` / `pressedKeys` structures the game already reads. Important lessons: - adapt camera width/viewport first; do not rewrite game logic - keep SCENE_HEIGHT unchanged when narrowing mobile view, or portrait letterboxing gets ugly - use HTML controls for touch input instead of Canvas hit-testing - prevent browser scroll/zoom aggressively on the game/control surfaces HUD/layout lesson: - many games call `setTransform(...)` internally, which clobbers parent scale wrappers - scale HUD coordinates directly instead of relying on one outer `context.scale()` wrapper ## 2. Online multiplayer Preferred architecture for existing browser games: - P1 host runs the full simulation - P2 sends inputs via WebSocket relay - P1 injects remote inputs, simulates, and broadcasts state back - server stays a dumb relay, not a full game server This avoids a huge rewrite while still giving usable real-time multiplayer. ## 3. HTML overlays beat Canvas UI For lobbies, room codes, buttons, and actionable text: - use HTML overlays, not canvas-drawn controls - Canvas text becomes fragile under CSS scaling and mobile layouts - HTML is easier for copying links, accessibility, touch handling, and responsive styling ## 4. Performance and sync For multiplayer sync: - send state every second frame when possible - shorten JSON keys / use delta compression - send remote inputs only on change - interpolate positions client-side while snapping discrete animation/state fields ## 5. Smooth radial/dial interactions For interactive wheels (astrology charts, time dials, circular selectors), split **immediate visual feedback** from **authoritative recalculation**: - during drag/slider/nudge, transform the already-rendered layer groups instantly in the browser - debounce the expensive/API-backed recalculation until the gesture settles - keep the old rendering visible while recalculating; show a small status badge instead of replacing the chart with a loading blank - if using SVG, prefer semantic layer groups/classes (`zodiac`, `planets`, `aspects`, labels) and apply `transform="rotate(angle cx cy)"` directly when CSS transforms are unreliable inside generated SVG - expose element toggles by layer (zodiac, planets, aspects, labels/houses) before attempting a full renderer rewrite - only move to Canvas/WebGL after the data model and gesture semantics are proven; the same chart JSON can later feed a canvas renderer When rotating an SVG wheel, beware: glyphs and labels will rotate with their groups unless each glyph is separately transformed. Decide the glyph orientation intentionally: upright labels require counter-rotation; radial astrological glyphs may instead rotate so each glyph sits on the wheel with its base pointing toward the center. If glyph orientation, dynamic aspects, and timeline synchronization are core requirements, prefer a retained Canvas 2D renderer fed by semantic chart JSON instead of continuing to patch generated SVG. For astrology/time-wave work, do not invent a generic Canvas look before inspecting the existing reference UI/assets. Use the same AstroFont/glyph codes and transpose the existing Time Wave geometry when the user asks for parity. See `references/astro-engine-smooth-svg-wheel.md` for a concrete Astro Engine SVG-hybrid example and `references/astro-engine-canvas-timewave.md` for the Canvas + horizontal time-wave migration pattern, including the Astral Hermes Time Wave parity notes. For artistic generated astrology topology diagrams, prefer a renderer-independent scene model plus **raw Canvas 2D with a tiny local retained scene model** as the live renderer. Use local geometry helpers for radial circuits, trees, brackets, gateways, and hit testing; reserve Remotion for later deterministic video/export. Alex rejected Konva/react-konva and D3 as too heavy-handed for the first rebuild. If prior DOM/SVG diagram attempts were rejected, delete/isolate them before creating the canvas spike; do not keep patching JSX/CSS mockups. For mutual reception examples, include both circular top-to-top/bottom-to-bottom exchange and optional figure-eight variants. For 3/4/5/6+ body circuits, draw the arrows as **true circular arc segments around one shared circle** (`ctx.arc` with angular trimming), not quadratic card-to-card chords that read as rounded triangles/squares/polygons; trim angle gaps so arrowheads remain visibly outside planet cards. When moving from approved static examples into a story explainer, keep the same raw Canvas scene grammar and add slide controls where Auto animates but numbered slides jump to completed states for review. When Alex asks for actual chart data, fetch through Astro Engine and build the scene from `normalizeChartForRulership(...)` + `computeRulershipChainsFast(...)`; do not leave demo bodies/topology in place. For rulership semantics, arrows point **from the ruler to the ruled body** (e.g. Mercury → Venus when Venus is in Virgo), not from the ruled body toward its ruler. For chart-driven rulership diagrams, card typography should be rulership-first: body/point glyph + body label + sign glyph/name. Do **not** put house labels directly on the rulership cards; houses are condition/context metadata for readings, but the rulership edge is determined by the sign's traditional ruler. When he asks for random chart examples, use the Astro Engine corpus endpoints through same-origin read-only proxies and render each sampled chart through the same story renderer. Do **not** force random charts into a mutual-reception or generic “main reception loop” template: classify each result as terminal/final dispositor, mutual reception, 3+ body circuit, or open/mixed chain, and label/render those differently. See `references/artistic-astrology-canvas-diagrams.md`; for a concrete route/test/export pattern and visual QA checklist for a raw Canvas lab gallery, see `references/raw-canvas-diagram-lab-examples.md`; for animated story patterns, slide controls, and final-state QA pitfalls, see `references/animated-rulership-canvas-story.md`; for chart-driven story wiring and Alex's verified topology, see `references/actual-chart-rulership-canvas-story.md`; for random corpus chart animations, see `references/random-astro-engine-chart-canvas-stories.md`. ## Common Pitfalls 1. `setTransform()` silently wiping out outer scaling assumptions 2. Canvas-drawn UI becoming unusable under `object-fit` / viewport scaling 3. Using fixed-position HTML controls that overlap mobile touch controls 4. Attempting a headless authoritative server rewrite when P1-host relay would suffice 5. Forgetting to handle Blob/ArrayBuffer WebSocket payloads in browsers 6. Viewer published pages (`/p/:slug`) cannot rely on public browser calls to `/api/*` because nginx may protect `/api/*` with Viewer admin auth. For public canvas/data demos, add read-only page-scoped proxy endpoints under `/p/...` (for example `/p/astro/charts`) and have page JavaScript fetch those paths instead. 7. Permanent bottom search/list panels can make mobile canvas animations feel messy by obscuring the actual animation. For Viewer canvas demos with data browsers, keep the canvas full-viewport by default and put the browser behind a small top-right icon; open it as a temporary drawer/modal and close it after selection. 8. Searchable chart/data browsers can double-load or display stale charts unless list and detail requests are guarded. Use separate `AbortController`s plus monotonic sequence IDs for list/detail fetches, and ignore stale responses before mutating selected state, captions, or canvas layout. 9. Awaiting `document.fonts.load(...)` before the first animation frame can leave a blank parchment canvas in mobile/in-app browsers. Draw colored primitives immediately, then redraw glyphs once fonts resolve. ## Viewer canvas chart browser overlay pattern For published Viewer canvas pages that include a searchable data/chart browser, keep the canvas full-viewport by default and put the browser behind a small top-right icon. Do not leave a permanent bottom list panel over the animation. Use an overlay/drawer that closes after selection, with request aborts/sequence guards so stale chart/list responses cannot overwrite the current chart. Also draw colored canvas primitives immediately instead of blocking on `document.fonts.load(...)`; redraw glyphs when the font resolves. See `references/viewer-canvas-chart-browser-overlay.md` for the detailed pattern and verification checklist. ## Verification Checklist - [ ] Touch input maps into existing keyboard-style input state correctly - [ ] Mobile portrait layout leaves room for controls and avoids severe letterboxing/cropping - [ ] HUD elements remain positioned/scaled correctly on mobile - [ ] Multiplayer host/remote flows exchange inputs and render synchronized state - [ ] Lobby and buttons are HTML overlays, not fragile Canvas controls