Phaser 4 Core
Set up the foundation of a Phaser game: the Game config, the Scene
lifecycle, asset loading, cameras, and passing data between scenes. Targets
Phaser 4.2 for new projects; keep an existing Phaser 3.90 project on its
pinned major unless the user explicitly asks for a migration.
When to use
- Use when starting a Phaser game, wiring the
Phaser.Game config, structuring
Scenes, loading assets in preload, or fixing scene transitions and shared
state.
- Use when the project has
phaser in package.json or import Phaser from 'phaser',
and code uses preload()/create()/update().
When not to use: movement, velocity, colliders, gravity, or overlap → use
phaser-arcade-physics. Complex rigid-body simulation uses Matter physics (a
separate concern). For cross-engine save/load patterns use save-systems.
Core workflow
- Detect the installed major first. Read
package.json and the lockfile. Use
Phaser 4.2 for new work; do not silently rewrite a Phaser 3 project as Phaser 4.
- Create the game from a config.
new Phaser.Game(config) with type: Phaser.AUTO (WebGL with Canvas fallback), a width/height, and a scene
array. The first scene (and any with active: true) starts automatically.
- Model each screen as a
Scene. Subclass Phaser.Scene, pass a unique
key to super, and implement the lifecycle: init(data) → preload() →
create(data) → update(time, delta).
- Load assets in
preload, use them in create. Queued assets are not
available until create. The loader is per-scene; the cache it fills is global.
- Reset per-run state in
init(), not the constructor. A scene instance is
reused across restarts, so constructor-set fields keep stale values.
- Move between screens with
this.scene.start/launch/switch/sleep/wake.
Share data through this.registry (global) or a sibling scene's event emitter.
- Run and observe. Serve the page, open it, and confirm assets load (watch the
Network tab and console) and scenes switch as expected before assuming success.
Patterns
1. Game config + boot (ES module)
// main.js — one Game owns the renderer, loop, cache, and Scene Manager.
import Phaser from 'phaser';
import BootScene from './scenes/BootScene.js';
import PlayScene from './scenes/PlayScene.js';
const config = {
type: Phaser.AUTO, // WebGL if available, else Canvas
width: 800,
height: 600,
backgroundColor: '#1d1d28',
scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH },
scene: [BootScene, PlayScene] // BootScene starts first
};
new Phaser.Game(config);
2. A Scene with the full lifecycle
// scenes/PlayScene.js
import Phaser from 'phaser';
export default class PlayScene extends Phaser.Scene {
constructor() {
super('play'); // unique scene key
}
init(data) {
// Reset run-specific state HERE so restarts start clean.
this.score = 0;
this.level = data.level ?? 1;
}
preload() {
// Queue downloads. Not usable until create().
this.load.image('player', 'assets/player.png');
this.load.spritesheet('coin', 'assets/coin.png', { frameWidth: 16, frameHeight: 16 });
}
create() {
this.player = this.add.sprite(400, 300, 'player');
this.scoreText = this.add.text(10, 10, 'Score: 0', { fontSize: '20px', color: '#fff' });
this.cursors = this.input.keyboard.createCursorKeys();
}
update(time, delta) {
// delta is milliseconds since last frame; divide by 1000 for seconds.
const speed = 200 * (delta / 1000);
if (this.cursors.left.isDown) this.player.x -= speed;
if (this.cursors.right.isDown) this.player.x += speed;
}
}
3. Cross-scene data + events
// The registry is a global DataManager shared by every scene.
this.registry.set('coins', 0); // in any scene
const coins = this.registry.get('coins'); // read anywhere
// React to registry changes (e.g. a HUD scene listening to gameplay):
this.registry.events.on('changedata-coins', (parent, value) => {
this.coinText.setText(`Coins: ${value}`);
});
// Talk directly to another running scene via its event emitter:
const ui = this.scene.get('hud');
ui.events.emit('show-message', 'Level cleared!');
4. Scene transitions (pick the right verb)
this.scene.start('gameover', { score: this.score }); // stop this scene, start target
this.scene.launch('hud'); // run a second scene in parallel (overlay HUD)
this.scene.switch('menu'); // sleep this scene, start/wake target
this.scene.pause(); // freeze updates but keep rendering (modal)
this.scene.sleep(); // stop updating AND rendering, keep state for wake
5. A camera that follows the player
this.cameras.main.setBounds(0, 0, 1600, 1200); // world size
this.cameras.main.startFollow(this.player, true, 0.1, 0.1); // smooth lerp follow
this.cameras.main.setZoom(1.5);
Pitfalls
- Assets are
undefined in create/update → you forgot to queue them in
preload, or used the wrong key. The loader runs between preload and create.
- State leaks across a restart → you set fields in the constructor. The Scene
instance is reused; reset run state in
init() and clear arrays on shutdown.
this.scene.start vs this.scene.launch → start stops the calling scene;
launch runs the target alongside it. Using start for a HUD hides the game.
this is wrong in a callback → arrow functions keep the Scene's this; plain
function callbacks need a context argument or .bind(this).
- Phaser 2 tutorials don't work → "States" were renamed to "Scenes" in Phaser 3,
and each Scene owns its own systems (input, cameras, tweens) rather than a global
Game World.
- Phaser 3 custom pipelines fail in Phaser 4 → Phaser 4 rebuilt the renderer and
replaced the old FX/pipeline extension points. Migrate custom shaders and renderer
plugins against the Phaser 4 guide; do not mechanically copy internal renderer code.
- Nothing renders / black screen → confirm the canvas mounted,
width/height
are set, and a scene actually started (check game.scene.dump() output).
References
- For the full scene state machine (pause/resume vs sleep/wake vs stop/start, the
restart-state bug, and removing/replacing scenes), read
references/scene-flow.md.
Related skills
phaser-arcade-physics — velocity, gravity, colliders, overlap, and groups.
input-systems — rebindable, multi-device input architecture (engine-agnostic).
pixijs-rendering / threejs-scene-setup — other browser rendering stacks.
platformer / puzzle — genre templates that compose Phaser skills.
1---
2name: phaser-core
3description: >
4 Set up and debug a Phaser 4 game: the Game config, the Scene lifecycle
5 (init/preload/create/update), the asset loader, cameras, and cross-scene
6 communication. Use when building or debugging a Phaser game — when the user
7 mentions Phaser, Phaser.Game, Phaser.Scene, preload/create/update, this.load,
8 this.add, or scene transitions. For Arcade Physics movement/collisions use
9 phaser-arcade-physics.
10---
11
12# Phaser 4 Core
13
14Set up the foundation of a Phaser game: the `Game` config, the `Scene`
15lifecycle, asset loading, cameras, and passing data between scenes. Targets
16**Phaser 4.2** for new projects; keep an existing Phaser 3.90 project on its
17pinned major unless the user explicitly asks for a migration.
18
19## When to use
20
21- Use when starting a Phaser game, wiring the `Phaser.Game` config, structuring
22 `Scene`s, loading assets in `preload`, or fixing scene transitions and shared
23 state.
24- Use when the project has `phaser` in `package.json` or `import Phaser from 'phaser'`,
25 and code uses `preload()`/`create()`/`update()`.
26
27**When *not* to use:** movement, velocity, colliders, gravity, or overlap → use
28`phaser-arcade-physics`. Complex rigid-body simulation uses Matter physics (a
29separate concern). For cross-engine save/load patterns use `save-systems`.
30
31## Core workflow
32
331. **Detect the installed major first.** Read `package.json` and the lockfile. Use
34 Phaser 4.2 for new work; do not silently rewrite a Phaser 3 project as Phaser 4.
352. **Create the game from a config.** `new Phaser.Game(config)` with `type:
36 Phaser.AUTO` (WebGL with Canvas fallback), a `width`/`height`, and a `scene`
37 array. The first scene (and any with `active: true`) starts automatically.
383. **Model each screen as a `Scene`.** Subclass `Phaser.Scene`, pass a unique
39 `key` to `super`, and implement the lifecycle: `init(data)` → `preload()` →
40 `create(data)` → `update(time, delta)`.
414. **Load assets in `preload`, use them in `create`.** Queued assets are not
42 available until `create`. The loader is per-scene; the cache it fills is global.
435. **Reset per-run state in `init()`, not the constructor.** A scene instance is
44 reused across restarts, so constructor-set fields keep stale values.
456. **Move between screens** with `this.scene.start/launch/switch/sleep/wake`.
46 Share data through `this.registry` (global) or a sibling scene's event emitter.
477. **Run and observe.** Serve the page, open it, and confirm assets load (watch the
48 Network tab and console) and scenes switch as expected before assuming success.
49
50## Patterns
51
52### 1. Game config + boot (ES module)
53
54```js
55// main.js — one Game owns the renderer, loop, cache, and Scene Manager.
56import Phaser from 'phaser';
57import BootScene from './scenes/BootScene.js';
58import PlayScene from './scenes/PlayScene.js';
59
60const config = {
61 type: Phaser.AUTO, // WebGL if available, else Canvas
62 width: 800,
63 height: 600,
64 backgroundColor: '#1d1d28',
65 scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH },
66 scene: [BootScene, PlayScene] // BootScene starts first
67};
68
69new Phaser.Game(config);
70```
71
72### 2. A Scene with the full lifecycle
73
74```js
75// scenes/PlayScene.js
76import Phaser from 'phaser';
77
78export default class PlayScene extends Phaser.Scene {
79 constructor() {
80 super('play'); // unique scene key
81 }
82
83 init(data) {
84 // Reset run-specific state HERE so restarts start clean.
85 this.score = 0;
86 this.level = data.level ?? 1;
87 }
88
89 preload() {
90 // Queue downloads. Not usable until create().
91 this.load.image('player', 'assets/player.png');
92 this.load.spritesheet('coin', 'assets/coin.png', { frameWidth: 16, frameHeight: 16 });
93 }
94
95 create() {
96 this.player = this.add.sprite(400, 300, 'player');
97 this.scoreText = this.add.text(10, 10, 'Score: 0', { fontSize: '20px', color: '#fff' });
98 this.cursors = this.input.keyboard.createCursorKeys();
99 }
100
101 update(time, delta) {
102 // delta is milliseconds since last frame; divide by 1000 for seconds.
103 const speed = 200 * (delta / 1000);
104 if (this.cursors.left.isDown) this.player.x -= speed;
105 if (this.cursors.right.isDown) this.player.x += speed;
106 }
107}
108```
109
110### 3. Cross-scene data + events
111
112```js
113// The registry is a global DataManager shared by every scene.
114this.registry.set('coins', 0); // in any scene
115const coins = this.registry.get('coins'); // read anywhere
116
117// React to registry changes (e.g. a HUD scene listening to gameplay):
118this.registry.events.on('changedata-coins', (parent, value) => {
119 this.coinText.setText(`Coins: ${value}`);
120});
121
122// Talk directly to another running scene via its event emitter:
123const ui = this.scene.get('hud');
124ui.events.emit('show-message', 'Level cleared!');
125```
126
127### 4. Scene transitions (pick the right verb)
128
129```js
130this.scene.start('gameover', { score: this.score }); // stop this scene, start target
131this.scene.launch('hud'); // run a second scene in parallel (overlay HUD)
132this.scene.switch('menu'); // sleep this scene, start/wake target
133this.scene.pause(); // freeze updates but keep rendering (modal)
134this.scene.sleep(); // stop updating AND rendering, keep state for wake
135```
136
137### 5. A camera that follows the player
138
139```js
140this.cameras.main.setBounds(0, 0, 1600, 1200); // world size
141this.cameras.main.startFollow(this.player, true, 0.1, 0.1); // smooth lerp follow
142this.cameras.main.setZoom(1.5);
143```
144
145## Pitfalls
146
147- **Assets are `undefined` in `create`/`update`** → you forgot to queue them in
148 `preload`, or used the wrong key. The loader runs between `preload` and `create`.
149- **State leaks across a restart** → you set fields in the constructor. The Scene
150 instance is reused; reset run state in `init()` and clear arrays on `shutdown`.
151- **`this.scene.start` vs `this.scene.launch`** → `start` stops the calling scene;
152 `launch` runs the target alongside it. Using `start` for a HUD hides the game.
153- **`this` is wrong in a callback** → arrow functions keep the Scene's `this`; plain
154 `function` callbacks need a context argument or `.bind(this)`.
155- **Phaser 2 tutorials don't work** → "States" were renamed to "Scenes" in Phaser 3,
156 and each Scene owns its own systems (input, cameras, tweens) rather than a global
157 Game World.
158- **Phaser 3 custom pipelines fail in Phaser 4** → Phaser 4 rebuilt the renderer and
159 replaced the old FX/pipeline extension points. Migrate custom shaders and renderer
160 plugins against the Phaser 4 guide; do not mechanically copy internal renderer code.
161- **Nothing renders / black screen** → confirm the canvas mounted, `width`/`height`
162 are set, and a scene actually started (check `game.scene.dump()` output).
163
164## References
165
166- For the full scene state machine (pause/resume vs sleep/wake vs stop/start, the
167 restart-state bug, and removing/replacing scenes), read
168 `references/scene-flow.md`.
169
170## Related skills
171
172- `phaser-arcade-physics` — velocity, gravity, colliders, overlap, and groups.
173- `input-systems` — rebindable, multi-device input architecture (engine-agnostic).
174- `pixijs-rendering` / `threejs-scene-setup` — other browser rendering stacks.
175- `platformer` / `puzzle` — genre templates that compose Phaser skills.