Scene transitions
The `scene.transition()` API — animated handoff between scenes with both scenes running during the overlap.
scene.start(key) swaps scenes hard — the outgoing scene’s shutdown fires before the incoming scene’s create. scene.transition({...}) gives you an overlap window where both scenes run together, perfect for crossfades, slide-ins, and “wipe one scene off the screen while the next slides on” effects.
The shape
this.scene.transition({
target: 'Game', // key of the scene to bring in
duration: 600, // ms — both scenes run for this long
sleep: false, // if true, this scene sleeps (resumable); else shutdown
remove: false, // if true, this scene is removed entirely
moveAbove: true, // render the target above this scene
allowInput: false, // disable input on this scene during transition
onUpdate: (progress) => { /* called each frame, 0..1 */ },
});
Reads as: “start Game running alongside me; for the next 600ms call onUpdate with progress; when it ends, do X to me (sleep / shutdown / remove).”
The target scene’s init and create fire immediately. Its update runs every frame from frame zero of the transition. The outgoing scene keeps updating too (unless you stop it from the onUpdate).
Crossfade
The most common pattern. Tween the outgoing scene’s alpha down while the incoming scene’s alpha rises.
// In the outgoing scene:
this.scene.transition({
target: 'Game',
duration: 500,
moveAbove: true,
allowInput: false,
onUpdate: (progress) => {
this.cameras.main.setAlpha(1 - progress);
},
});
// The incoming scene's create():
create() {
this.cameras.main.setAlpha(0);
this.scene.get('Title').events.once('transitionout', () => {
// Outgoing scene is done — clean up if needed.
});
// The transition's progress is on `this.scene.systems.settings.transitionProgress`
// during the overlap window; this scene's camera alpha can mirror it.
}
If both scenes mirror the progress, you get a true crossfade — the incoming scene rises from 0 alpha while the outgoing scene falls.
Slide-in
this.scene.transition({
target: 'Pause',
duration: 400,
sleep: true, // pause this scene; we'll resume after Pause closes
moveAbove: true,
onUpdate: (progress) => {
// Slide the pause scene in from the right.
const pause = this.scene.get('Pause');
pause.cameras.main.scrollX = (1 - progress) * this.scale.width;
},
});
sleep: true is the trick — when the pause scene closes (this.scene.stop() from inside it), Phaser wakes this scene rather than recreating it. Saves the whole “preserve state across scene swap” problem.
Events fired during transition
| Event | Where | When |
|---|---|---|
transitionout | outgoing scene’s events | When the transition starts. |
transitioninit | target scene’s events | After target’s init, before create. |
transitionstart | target scene’s events | After target’s create. |
transitioncomplete | target scene’s events | When the transition window ends. |
this.events.once('transitioncomplete', () => {
this.cameras.main.setAlpha(1); // ensure we're fully visible
});
When to use transition vs. start vs. launch
| Want | Use |
|---|---|
| Hard cut, replace current scene | scene.start(key) |
| Run another scene alongside, indefinitely (HUD, ambient music) | scene.launch(key) |
| Animate a swap with overlap | scene.transition({...}) |
| Animate a swap with no overlap (cut + animate in destination) | scene.start + camera.fadeIn in the new scene |
The fade-based “swap” demonstrated in examples/scene-transition is the non-overlapping version — fade out, swap, fade in. That’s simpler and good for most cases. Use scene.transition() when you need both scenes drawing simultaneously.
Common pitfalls
Forgetting allowInput: false. During the overlap, both scenes’ input handlers are live. A click on a button in the outgoing scene fires its handler — usually not what you want. Setting allowInput: false on the outgoing scene during transition prevents this.
Stale state on resume. If you use sleep: true, the scene’s create does not re-run on resume. Reset transient state in a 'wake' event handler instead:
this.events.on('wake', () => {
this.timer = 0;
this.player.setVelocity(0);
});
Cleanup on remove: true. Setting remove: true destroys the outgoing scene’s display list immediately. If you had a tween writing to that display list during the transition, it’ll error when the targets vanish. Use sleep or shutdown semantics for animated handoffs.
Related
- Scenes — lifecycle, parallel scenes, data passing.
- Cameras —
fadeIn/fadeOutfor simpler non-overlapping transitions. - examples/scene-transition — runnable fade-based swap.