Mastering Scene Switching in Phaser 4: The Ultimate Guide
In Phaser 4, your game is rarely just one big file. Instead, you break it into Scenes—Menu, Gameplay, UI, Game Over, Settings. Managing how the player moves between these segments is the backbone of your game architecture.
This guide covers the core methods for switching, launching, and managing scenes, along with the patterns for passing data between them.
The Core Methods
Phaser provides a few primary methods on this.scene to control your game flow.
this.scene.start('Key')
Use this for: Changing major game states (e.g., Menu to Game, Game to Level 2).
- Behavior: Stops the current scene and starts the target scene.
- Result: The current scene is destroyed and cleaned up.
// Switching from Menu to Gamethis.scene.start('GameScene');
this.scene.launch('Key')
Use this for: Running scenes in parallel (e.g., running a permanent HUD or Pause UI on top of the game).
- Behavior: Runs the target scene alongside the current scene.
- Result: Both scenes run at the same time.
// Launching UI without stopping the gamethis.scene.launch('UIScene');
The Difference: start vs launch vs restart
| Method | Stops Current? | Runs Alongside? | Use Case |
|---|---|---|---|
start() | Yes | No | Switching screens (e.g., Menu -> Level) |
launch() | No | Yes | Overlays, HUDs, Pause menus |
restart() | Yes | No | Resetting current level |
stop() | Yes | No | Removing an active scene |
For more detail on restart(), read our guide on Restarting a Scene in Phaser 4.
Passing Data Between Scenes
Passing data is essential. You need to tell the GameScene which level to load, or tell the GameOverScenewhat the player's final score was.
Sending Data
Pass an object as the second argument:
// In MenuScenethis.scene.start('GameScene', { level: 2, difficulty: 'hard' });
Receiving Data
Use the init() method in your target scene to catch that data before preload() or create() runs.
export default class GameScene extends Phaser.Scene {init(data) {this.level = data.level || 1;this.difficulty = data.difficulty || 'normal';}create() {console.log(`Starting level ${this.level} on ${this.difficulty} mode`);}}
Practical Architecture: The UI Overlay Pattern
One of the most common patterns is having a persistent UI scene that runs on top of the game.
- Launch the UI when the Game starts.
- Communicate between them.
MainScene.js
create() {this.scene.launch('UIScene');}
UIScene.js
export default class UIScene extends Phaser.Scene {constructor() { super({ key: 'UIScene' }); }create() {this.scoreText = this.add.text(10, 10, 'Score: 0');// Listen for events from MainSceneconst mainScene = this.scene.get('MainScene');mainScene.events.on('updateScore', (newScore) => {this.scoreText.setText(`Score: ${newScore}`);});}}
Common Pitfall: Forgetting Config Registration
The #1 error beginners make is calling this.scene.start('MyScene') without registering it.
You must include every scene in your game config object:
const config = {type: Phaser.AUTO,width: 800,height: 600,scene: [MenuScene, GameScene, UIScene, GameOverScene] // <--- Required!};const game = new Phaser.Game(config);
Managing Scene States: Pause and Resume
If you have a pause menu, you don't necessarily want to destroy the game scene. Instead, pause the game logic and launch an overlay.
// Pause the game scenethis.scene.pause('GameScene');// Launch the pause menuthis.scene.launch('PauseMenuScene');
And to return:
// Stop the menu and resume the gamethis.scene.stop('PauseMenuScene');this.scene.resume('GameScene');
Best Practices
- Keep Logic Isolated:Don't put "Game Over" logic inside the
GameScene. LetGameScenefinish, thenthis.scene.start('GameOverScene'). - Use Events for Communication:Don't try to directly manipulate objects from another scene (e.g.,
this.scene.get('UIScene').scoreText.text = 100). It's messy. Usethis.events.emit('eventName')instead. - Clean up: If you use
eventsto communicate, make sure to remove listeners when a scene is stopped to prevent memory leaks. - Scene Keys: Use constant strings or keys to avoid typos.
// Better than strings everywhereconst SCENE_KEYS = {MENU: 'MenuScene',GAME: 'GameScene'};this.scene.start(SCENE_KEYS.GAME);
Quick Cheat Sheet
// Basic switchthis.scene.start('GameScene');// Switch with datathis.scene.start('GameScene', { score: 100 });// Overlay/UIthis.scene.launch('UIScene');// Manage statethis.scene.pause('GameScene');this.scene.resume('GameScene');this.scene.stop('UIScene');// Get access to another sceneconst gameScene = this.scene.get('GameScene');
FAQ
What is the difference between scene.start() and scene.launch() in Phaser 4?
start() stops the current scene and switches to a new one, while launch() runs the target scene alongside the current one. This makes launch() ideal for HUDs, pause menus, and persistent UI overlays.
How do I pass data between scenes in Phaser 4?
Pass an object as the second argument to scene.start or scene.launch: this.scene.start('GameScene', { level: 2 }). Then receive it in the target scene's init(data) method before preload or create runs.
Why is my scene.start() not working?
The most common reason is that the target scene was not registered in the game config's scene array. Every scene must be listed in the config object: scene: [MenuScene, GameScene].
What is the best way to build a persistent HUD in Phaser 4?
Use scene.launch() to run a dedicated UI scene on top of your main game scene. Then communicate between them using the emitter pattern with this.events.emit and this.events.on, instead of directly manipulating other scenes' objects.
Conclusion
Mastering scene switching is the foundation of a well-architected Phaser game. By combining start(), launch(), pause(), and resume() with clean event communication, you can build games that are modular, maintainable, and ready to scale to complex multi-scene structures.