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.
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
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.
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
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
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.
setTransform() silently wiping out outer scaling assumptionsobject-fit / viewport scaling/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.AbortControllers plus monotonic sequence IDs for list/detail fetches, and ignore stale responses before mutating selected state, captions, or canvas layout.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.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.