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
setTransform() silently wiping out outer scaling assumptionsobject-fit / viewport scaling