phaser 3 + vite HMR — canvas getting nuked on every module reload, here's what fixed it

259 views 0 replies

Working on a browser game with Phaser 3 and Vite and kept running into the same issue: edit a file, Vite hot-reloads the module, and the canvas either goes blank or the game enters some half-baked state where new code is running but old objects are still alive. Occasionally I'd get two canvas elements stacked in the DOM. Not a great iteration loop.

The root problem is that Phaser creates and owns a canvas element, and when modules hot-reload, the old Phaser instance doesn't automatically clean up. Orphaned instances, event listeners pointing at dead objects, the works.

Fix: explicit teardown in the HMR accept handler using import.meta.hot:

// main.js
import Phaser from 'phaser';
import { GameConfig } from './config';

let game = new Phaser.Game(GameConfig);

if (import.meta.hot) {
  import.meta.hot.accept('./config', (newModule) => {
    game.destroy(true);
    game = new Phaser.Game(newModule.GameConfig);
  });

  import.meta.hot.dispose(() => {
    game.destroy(true);
  });
}

This works well for config changes and anything that flows through your GameConfig. For changes deeper in scene logic you still need a full reload. There's no clean way to hot-swap a running Phaser Scene mid-state that I've found.

One gotcha: game.destroy(true) removes the canvas from the DOM entirely. If you're mounting into a specific container element, the new instance needs to re-append to it. Passing the container ID in your GameConfig handles this automatically, but if you're doing anything custom with your mount point, watch for a second canvas appearing outside the container on reload.

Anyone found a way to preserve actual scene state across reloads? Full teardown/reinit is fine for structural changes, but it'd be nice to not lose player position and score every time I tweak a balance constant.

Moonjump
Forum Search Shader Sandbox
Sign In Register