Phaser 3 performance and implementation best-practice reference. Use when writing, reviewing, or optimising a Phaser 3 browser game — covers scene architecture, scene management, physics selection (Arcade vs Matter.js), asset pipeline (texture atlas, audio sprites), input handling, sprite animation, tilemap, object pooling, camera system, ScaleManager, and mobile optimisation.
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Phaser 3 performance and implementation best-practice reference. Use when writing, reviewing, or optimising a Phaser 3 browser game — covers scene architecture, scene management, physics selection (Arcade vs Matter.js), asset pipeline (texture atlas, audio sprites), input handling, sprite animation, tilemap, object pooling, camera system, ScaleManager, and mobile optimisation.
license
MIT
compatibility
Portable reference skill for agents that support markdown skills or prompt files. Works best alongside project Phaser 3 source files and Chrome DevTools Performance captures.
Reference guide for Phaser 3.60+. Rules are grouped by topic. Critical rules are marked CRITICAL.
1. Scene Architecture
The five lifecycle methods
classGameSceneextendsPhaser.Scene {
constructor() {
super({ key: 'GameScene' });
}
init(data) {
// Called first. Receive data passed from previous scene.// Initialise instance variables here — NOT in constructor.this.level = data.level ?? 1;
}
preload() {
// Load assets. Runs once per scene start.this.load.image('player', 'assets/player.png');
this.load.atlas('sprites', 'assets/sprites.png', 'assets/sprites.json');
}
create(data) {
// Assets are loaded. Build the scene here.this.player = this.physics.add.sprite(100, 200, 'player');
}
update(time, delta) {
..(... ? - : );
}
() {
...();
}
}
// Called every frame. Keep this lean — no object creation.
this
player
setVelocityX
this
cursors
left
isDown
160
0
shutdown
// Called when a scene stops. Clean up event listeners.
this
input
keyboard
removeAllListeners
CRITICAL — Do not create objects in update(). Creating new objects every frame causes GC pauses. Allocate once in create() and reuse.
CRITICAL — Put instance variable initialisation in init(), not in the constructor. The constructor runs once; init() runs every time the scene restarts, so state resets correctly.
Boot → Preload → Game scene pattern
Always use a dedicated preload scene for asset loading. Never load game assets inside the Boot scene.
Boot (scene key: 'Boot')
→ create: launch 'Preload'
Preload (scene key: 'Preload')
→ preload: load all assets
→ create: show loading bar, then start 'MainMenu' on complete
MainMenu → Game → HUD (overlay) → GameOver
Pause rendering and updates but keep state in memory
scene.wake('Key')
Resume a sleeping scene without re-running create
scene.stop('Key')
Tear down scene completely, free memory
scene.pause('Key')
Stop updates but keep rendering
scene.resume('Key')
Resume updates on a paused scene
Prefer sleep/wake over stop/start for frequently toggled scenes (e.g. pause menus). Avoids re-running preload and rebuilding the scene graph.
// Open pause menu without destroying game scenethis.scene.launch('PauseMenu');
this.scene.sleep('GameScene');
// Close pause menuthis.scene.stop('PauseMenu');
this.scene.wake('GameScene');
Arcade is significantly faster. Default to Arcade unless the design genuinely requires physics features it cannot provide.
Arcade Physics setup
// In Phaser.Game configphysics: {
default: 'arcade',
arcade: {
gravity: { y: 600 },
debug: false, // set true during dev to see hitboxes
}
}
// In create()this.player = this.physics.add.sprite(100, 450, 'player');
this.player.setBounce(0.1);
this.player.setCollideWorldBounds(true);
// Shrink hitbox to match character sprite (critical for feel)this.player.body.setSize(20, 32); // width, heightthis.player.body.setOffset(6, 16); // offset from sprite origin
// Load atlasthis.load.atlas('game', 'assets/sprites.png', 'assets/sprites.json');
// Use frame from atlasthis.add.image(100, 100, 'game', 'player_idle_01');
this.physics.add.sprite(200, 200, 'game', 'enemy_walk_01');
Audio sprites for SFX
Pack multiple short sound effects into a single audio sprite to reduce HTTP requests and audio engine overhead.
this.load.audioSprite('sfx', 'assets/sfx.json', [
'assets/sfx.ogg',
'assets/sfx.mp3', // fallback
]);
// Play a named spritethis.sound.playAudioSprite('sfx', 'coin_collect');
this.sound.playAudioSprite('sfx', 'player_jump');
LoadingManager pattern
preload() {
// Progress barconst bar = this.add.graphics();
this.load.on('progress', v => {
bar.clear().fillStyle(0xffffff).fillRect(100, 280, 600 * v, 20);
});
this.load.on('complete', () => bar.destroy());
// Load everything needed for the first playable scenethis.load.atlas('game', 'assets/game.png', 'assets/game.json');
this.load.tilemapTiledJSON('level1', 'assets/level1.json');
this.load.audioSprite('sfx', 'assets/sfx.json', ['assets/sfx.ogg', 'assets/sfx.mp3']);
}
5. Input Handling
Keyboard
// In create()this.cursors = this.input.keyboard.createCursorKeys();
this.wasd = this.input.keyboard.addKeys({
up: Phaser.Input.Keyboard.KeyCodes.W,
down: Phaser.Input.Keyboard.KeyCodes.S,
left: Phaser.Input.Keyboard.KeyCodes.A,
right: Phaser.Input.Keyboard.KeyCodes.D,
});
// One-shot key events (preferred over polling for actions)this.input.keyboard.on('keydown-SPACE', () => {
this.player.jump();
});
// In update() — polling for held keysif (this.cursors.left.isDown) {
this.player.setVelocityX(-160);
}
CRITICAL — Remove keyboard listeners in shutdown(). Listeners on this.input.keyboard persist if not removed, causing duplicate callbacks when a scene restarts.
// Unified pointer (mouse and touch)this.input.on('pointerdown', pointer => {
this.shoot(pointer.worldX, pointer.worldY);
});
// Multi-touchthis.input.addPointer(2); // support up to 3 touch points total// Interactive game objectsthis.button.setInteractive();
this.button.on('pointerdown', () =>this.scene.start('Game'));
this.button.on('pointerover', () =>this.button.setTint(0xaaaaaa));
this.button.on('pointerout', () =>this.button.clearTint());
Gamepad
// Enable in game configinput: { gamepad: true }
// In update()const pad = this.input.gamepad.getPad(0);
if (pad) {
const { x, y } = pad.leftStick;
this.player.setVelocity(x * 200, y * 200);
if (pad.A) this.player.jump();
}
6. Sprite Animation
Defining animations
CRITICAL — Define animations once in a shared scene or registry, not in every scene that uses them. Duplicate definitions cause silent overwrites.
// Play (restarts from frame 0 if already playing)this.player.play('player_walk');
// Play only if not already playing this animationthis.player.anims.play('player_walk', true);
// Chain: play jump, then return to idlethis.player.play('player_jump');
this.player.once('animationcomplete', () => {
this.player.play('player_idle');
});
Detail split out of this file. Each is self-contained; read one only when its trigger applies.
references/tilemaps-and-camera.md — Tilemaps and the camera system. Read when building tile-based levels, or configuring a following, bounded or multi-viewport camera.
references/performance-and-display.md — Performance and display targets. Read when setting up responsive layout, choosing between WebGL and Canvas, tuning for mobile, or pooling frequently spawned objects.
Quick-reference checklist
Instance variables initialised in init(), not constructor
No object creation in update()
All sprites packed into a texture atlas
Audio SFX packed into an audio sprite
Keyboard and pointer listeners removed in shutdown()
Animations defined once (not in every scene)
Hitboxes adjusted to match visual bounds (setSize / setOffset)
Frequently spawned objects use a pooled Group
Camera bounds set to map bounds for scrolling levels
HUD elements use setScrollFactor(0) or a separate camera
ScaleManager mode set; positions use this.scale.width / this.scale.height
Audio context unlocked on first user gesture (mobile)
Physics choice justified: Arcade for performance, Matter.js only when needed