Phaser Scene Management: How We Structure 9 Scenes
Phaser Scene Management: How We Structure 9 Scenes
Direct answer: Keep one scene per screen, load assets only in a dedicated BootScene, and share game state through the registry or a module — not through scene-to-scene property passing. A 9-scene game stays readable if each scene only knows its own UI and the transitions it owns.
Merge Fish 2048 is built with 9 scenes. Here is exactly how they are laid out and why.
1. Our scene map
| Scene | Job | Loads assets? |
|---|---|---|
BootScene | Preloads all assets, shows progress bar | Yes — everything |
MenuScene | Main menu, mode pick | No |
GameScene | Gameplay: board, input, HUD, animations | No |
GameOverScene | Game over / victory overlay | No |
LevelSelectScene | Chapter & level grid, stars, progress | No |
AquariumScene | Fish collection / aquarium | No |
RankScene | Rank / achievement board | No |
HelpScene | How-to-play overlay | No |
SettingsScene | Music / SFX toggles | No |
The rule we follow: assets load exactly once, in BootScene. Every other scene is instant because textures, audio, and fonts are already in the cache. A scene that needs an image it didn’t load is a bug waiting to happen.
2. The boot → menu → game flow
// main.js — Phaser config
scenes: [BootScene, MenuScene, GameScene, GameOverScene,
LevelSelectScene, AquariumScene, RankScene, HelpScene, SettingsScene]
- BootScene shows a loading bar (we animate it with a tween) and, when finished, calls
this.scene.start('Menu'). - MenuScene never touches assets; it only builds buttons.
- GameScene is started fresh each run (
this.scene.start('Game')) so board state never leaks between plays.
One decision that saved us hours: each scene owns its transitions. GameScene knows it can go to GameOverScene or SettingsScene, but it does not know about AquariumScene. When we added the aquarium later, we only touched MenuScene and one button — no ripple through the rest.
3. Cross-scene state without spaghetti
Player data (best score, music on/off, per-mode stats) lives in a Storage module, not in scene properties:
// anywhere in any scene
Storage.get('best_score'); // -> number
Storage.set('music_on', false);
Scenes read and write through this single module. The Phaser registry works too, but a module has one advantage: it can persist to localStorage in the same call, so you never lose state when a scene restarts. For a merge game where the player’s best score must survive scene switches and page reloads, that is the difference between “it works” and “it works on Tuesday but not after refresh”.
4. Overlay scenes instead of stacking UI
Help and Settings are full scenes layered on top of the current screen, not modal containers inside GameScene. Each opens with this.scene.launch('Settings') and closes with this.scene.stop(). Launch/stop keeps the gameplay scene alive underneath — pause the game on launch, resume on stop, and you get a pause menu for free without a single line of modal code.
Common mistakes
- Loading assets in every scene: your second scene blocks on re-downloads or, worse, missing keys. Load once in BootScene.
- Restarting the whole game instead of one scene:
scene.restart()resets that scene’s data;scene.start()from a fresh context clears references. Know which one you call. - Scene count as a feature: 9 scenes is fine when each has a job. The moment scenes duplicate each other’s UI (two different pause menus, two settings screens), merge them.
Related reading
- Phaser 3 Game Tutorial: Your First Playable in a Day
- Phaser Swipe Input: Touch Controls for Mobile Games
- LocalStorage Game Save in Phaser: A Working Example
Written by ruofan, independent HTML5 game developer. Structure and numbers come from Merge Fish 2048 (Phaser 3.90 + Vite), a game we’re building. API details: Phaser scene docs.