DEVOLOGIST</>

Mastering Scene Switching in Phaser 4: The Ultimate Guide

Phaser
15/08/20269 min read

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).

// Switching from Menu to Game
this.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).

// Launching UI without stopping the game
this.scene.launch('UIScene');

The Difference: start vs launch vs restart

MethodStops Current?Runs Alongside?Use Case
start()YesNoSwitching screens (e.g., Menu -> Level)
launch()NoYesOverlays, HUDs, Pause menus
restart()YesNoResetting current level
stop()YesNoRemoving 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 MenuScene
this.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.

  1. Launch the UI when the Game starts.
  2. 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 MainScene
const 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 scene
this.scene.pause('GameScene');
// Launch the pause menu
this.scene.launch('PauseMenuScene');

And to return:

// Stop the menu and resume the game
this.scene.stop('PauseMenuScene');
this.scene.resume('GameScene');

Best Practices

  1. Keep Logic Isolated:Don't put "Game Over" logic inside the GameScene. Let GameScene finish, then this.scene.start('GameOverScene').
  2. 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. Use this.events.emit('eventName') instead.
  3. Clean up: If you use events to communicate, make sure to remove listeners when a scene is stopped to prevent memory leaks.
  4. Scene Keys: Use constant strings or keys to avoid typos.
// Better than strings everywhere
const SCENE_KEYS = {
MENU: 'MenuScene',
GAME: 'GameScene'
};
this.scene.start(SCENE_KEYS.GAME);

Quick Cheat Sheet

// Basic switch
this.scene.start('GameScene');
// Switch with data
this.scene.start('GameScene', { score: 100 });
// Overlay/UI
this.scene.launch('UIScene');
// Manage state
this.scene.pause('GameScene');
this.scene.resume('GameScene');
this.scene.stop('UIScene');
// Get access to another scene
const 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.