Phaser Cheat Sheet
Overview
Phaser is a popular open-source HTML5 game framework for creating 2D games that run in web browsers. It provides WebGL and Canvas rendering with automatic fallback, multiple physics engines (Arcade, Matter.js), comprehensive input handling, audio management, sprite animation, tilemap support, and a camera system. Phaser games run on any device with a modern web browser, including desktop, mobile, and tablets, making it one of the most accessible game development frameworks available.
Phaser 3 features a modular architecture where you only include what you need, scene-based game flow management, a powerful data manager, tween animation system, particle effects, and support for texture atlases and sprite sheets. The framework has a vibrant community with extensive documentation, tutorials, and hundreds of examples. It integrates well with modern JavaScript tooling including TypeScript, webpack, and Vite, and can be wrapped in Electron, Cordova, or Capacitor for native distribution.
Installation
# Via npm
npm install phaser
# Create project with Vite template
npm create @phaserjs/game@latest my-game
cd my-game
npm install
npm run dev
# Or manually with Vite
mkdir my-phaser-game && cd my-phaser-game
npm init -y
npm install phaser
npm install -D vite
# CDN (for quick prototyping)
# Add to HTML: <script src="https://cdn.jsdelivr.net/npm/phaser@3/dist/phaser.min.js"></script>
# TypeScript setup
npm install -D typescript @types/phaser
npx tsc --init
Basic Game Setup
import Phaser from 'phaser';
const config = {
type: Phaser.AUTO, // WebGL with Canvas fallback
width: 800,
height: 600,
parent: 'game-container',
backgroundColor: '#2d2d2d',
physics: {
default: 'arcade',
arcade: {
gravity: { y: 300 },
debug: false
}
},
scene: {
preload: preload,
create: create,
update: update
}
};
const game = new Phaser.Game(config);
function preload() {
this.load.image('sky', 'assets/sky.png');
this.load.image('ground', 'assets/platform.png');
this.load.spritesheet('player', 'assets/player.png', {
frameWidth: 32,
frameHeight: 48
});
this.load.audio('jump', 'assets/jump.wav');
}
let player, cursors, platforms, score = 0, scoreText;
function create() {
this.add.image(400, 300, 'sky');
platforms = this.physics.add.staticGroup();
platforms.create(400, 568, 'ground').setScale(2).refreshBody();
player = this.physics.add.sprite(100, 450, 'player');
player.setBounce(0.2);
player.setCollideWorldBounds(true);
this.physics.add.collider(player, platforms);
cursors = this.input.keyboard.createCursorKeys();
scoreText = this.add.text(16, 16, 'Score: 0', {
fontSize: '32px',
fill: '#fff'
});
}
function update() {
if (cursors.left.isDown) {
player.setVelocityX(-160);
player.anims.play('left', true);
} else if (cursors.right.isDown) {
player.setVelocityX(160);
player.anims.play('right', true);
} else {
player.setVelocityX(0);
player.anims.play('idle', true);
}
if (cursors.up.isDown && player.body.touching.down) {
player.setVelocityY(-330);
}
}
Scene-Based Architecture
class BootScene extends Phaser.Scene {
constructor() {
super({ key: 'BootScene' });
}
preload() {
// Load assets
this.load.image('logo', 'assets/logo.png');
// Progress bar
const bar = this.add.graphics();
this.load.on('progress', (value) => {
bar.clear();
bar.fillStyle(0xffffff, 1);
bar.fillRect(200, 290, 400 * value, 20);
});
}
create() {
this.scene.start('MenuScene');
}
}
class MenuScene extends Phaser.Scene {
constructor() {
super({ key: 'MenuScene' });
}
create() {
const title = this.add.text(400, 200, 'MY GAME', {
fontSize: '64px',
fontFamily: 'Arial',
color: '#ffffff'
}).setOrigin(0.5);
const playButton = this.add.text(400, 400, 'PLAY', {
fontSize: '32px',
color: '#00ff00'
}).setOrigin(0.5).setInteractive();
playButton.on('pointerdown', () => {
this.scene.start('GameScene');
});
playButton.on('pointerover', () => {
playButton.setStyle({ color: '#ff0' });
});
playButton.on('pointerout', () => {
playButton.setStyle({ color: '#0f0' });
});
}
}
class GameScene extends Phaser.Scene {
constructor() {
super({ key: 'GameScene' });
}
init(data) {
this.level = data.level || 1;
}
create() {
// Game setup
}
update(time, delta) {
// Game loop (delta in ms)
}
}
// Config with multiple scenes
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
scene: [BootScene, MenuScene, GameScene]
};
Sprites and Animation
// Create sprite
const player = this.add.sprite(400, 300, 'player');
player.setScale(2);
player.setOrigin(0.5, 1); // Bottom center
player.setAlpha(0.8);
player.setTint(0xff0000);
player.setFlipX(true);
// Spritesheet animation
this.anims.create({
key: 'walk',
frames: this.anims.generateFrameNumbers('player', { start: 0, end: 7 }),
frameRate: 10,
repeat: -1 // Loop forever
});
this.anims.create({
key: 'idle',
frames: this.anims.generateFrameNumbers('player', { start: 0, end: 3 }),
frameRate: 5,
repeat: -1
});
this.anims.create({
key: 'jump',
frames: this.anims.generateFrameNumbers('player', { frames: [8, 9, 10] }),
frameRate: 10,
repeat: 0
});
player.play('walk');
player.on('animationcomplete', (anim) => {
if (anim.key === 'jump') player.play('idle');
});
Physics (Arcade)
// Physics sprite
const player = this.physics.add.sprite(100, 400, 'player');
player.setBounce(0.2);
player.setCollideWorldBounds(true);
player.setGravityY(300);
player.setDragX(200);
player.setMaxVelocity(300, 500);
// Static group (platforms)
const platforms = this.physics.add.staticGroup();
platforms.create(400, 568, 'ground');
// Dynamic group (enemies)
const enemies = this.physics.add.group({
key: 'enemy',
repeat: 5,
setXY: { x: 100, y: 0, stepX: 120 }
});
enemies.children.iterate((enemy) => {
enemy.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));
});
// Colliders and overlaps
this.physics.add.collider(player, platforms);
this.physics.add.collider(enemies, platforms);
this.physics.add.overlap(player, enemies, hitEnemy, null, this);
function hitEnemy(player, enemy) {
if (player.body.velocity.y > 0) {
enemy.destroy();
score += 10;
} else {
player.setTint(0xff0000);
this.physics.pause();
}
}
Input Handling
// Keyboard
const cursors = this.input.keyboard.createCursorKeys();
const wasd = this.input.keyboard.addKeys('W,A,S,D');
const spaceKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
// In update
if (cursors.left.isDown || wasd.A.isDown) { /* move left */ }
if (Phaser.Input.Keyboard.JustDown(spaceKey)) { /* jump once */ }
// Mouse / Touch
this.input.on('pointerdown', (pointer) => {
console.log(pointer.x, pointer.y);
if (pointer.leftButtonDown()) { /* shoot */ }
});
// Drag
this.input.setDraggable(sprite);
sprite.on('drag', (pointer, dragX, dragY) => {
sprite.x = dragX;
sprite.y = dragY;
});
// Gamepad
this.input.gamepad.once('connected', (pad) => {
pad.on('down', (index, value, button) => {
if (index === 0) player.jump();
});
});
Camera
// Follow player
this.cameras.main.startFollow(player, true, 0.05, 0.05);
this.cameras.main.setZoom(1.5);
this.cameras.main.setBounds(0, 0, 3200, 600);
// Camera effects
this.cameras.main.shake(200, 0.01);
this.cameras.main.flash(500, 255, 0, 0);
this.cameras.main.fade(1000, 0, 0, 0);
// Deadzone
this.cameras.main.setDeadzone(200, 100);
Tweens
// Basic tween
this.tweens.add({
targets: sprite,
x: 600,
y: 300,
duration: 2000,
ease: 'Power2',
yoyo: true,
repeat: -1
});
// Scale bounce
this.tweens.add({
targets: button,
scaleX: 1.2,
scaleY: 1.2,
duration: 100,
yoyo: true,
ease: 'Quad.easeInOut'
});
// Timeline
const timeline = this.tweens.createTimeline();
timeline.add({ targets: sprite, x: 600, duration: 1000 });
timeline.add({ targets: sprite, y: 400, duration: 500 });
timeline.add({ targets: sprite, alpha: 0, duration: 300 });
timeline.play();
Tilemaps
// Load in preload
this.load.tilemapTiledJSON('level1', 'assets/maps/level1.json');
this.load.image('tiles', 'assets/tilesets/tileset.png');
// Create in create
const map = this.make.tilemap({ key: 'level1' });
const tileset = map.addTilesetImage('tileset-name', 'tiles');
const backgroundLayer = map.createLayer('Background', tileset, 0, 0);
const groundLayer = map.createLayer('Ground', tileset, 0, 0);
// Set collision
groundLayer.setCollisionByProperty({ collides: true });
// Or by tile index
groundLayer.setCollisionBetween(1, 50);
this.physics.add.collider(player, groundLayer);
// World bounds from map
this.physics.world.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
this.cameras.main.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
Configuration
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
parent: 'game-container'
},
physics: {
default: 'arcade',
arcade: {
gravity: { y: 300 },
debug: process.env.NODE_ENV === 'development'
}
},
render: {
pixelArt: true, // No anti-aliasing for pixel art
antialias: false,
roundPixels: true
},
audio: {
disableWebAudio: false
},
scene: [BootScene, MenuScene, GameScene],
fps: {
target: 60,
forceSetTimeOut: false
}
};
Advanced Usage
// Particle emitter
const particles = this.add.particles(400, 300, 'particle', {
speed: { min: 100, max: 200 },
angle: { min: 0, max: 360 },
scale: { start: 1, end: 0 },
lifespan: 1000,
gravityY: 200,
quantity: 2,
frequency: 50
});
// Follow a sprite
particles.startFollow(player);
// Groups with recycling
const bulletGroup = this.physics.add.group({
classType: Bullet,
maxSize: 30,
runChildUpdate: true
});
function fireBullet() {
const bullet = bulletGroup.get(player.x, player.y);
if (bullet) {
bullet.fire(player.x, player.y, target.x, target.y);
}
}
// Data manager for persistence
this.registry.set('score', 0);
this.registry.set('lives', 3);
const score = this.registry.get('score');
this.registry.events.on('changedata-score', (parent, value) => {
scoreText.setText(`Score: ${value}`);
});
// Timer events
this.time.addEvent({
delay: 1000,
callback: spawnEnemy,
callbackScope: this,
loop: true
});
Troubleshooting
| Issue | Solution |
|---|---|
| Black screen | Check asset paths are correct; look for console errors |
| Blurry pixel art | Set pixelArt: true and roundPixels: true in config |
| Physics bodies misaligned | Use setOrigin() consistently; enable debug to visualize bodies |
| Game runs slow on mobile | Reduce particle count; use texture atlases; limit physics bodies |
| Audio not playing on mobile | Play audio on first user interaction (tap); use this.sound.unlock() |
| Assets not loading | Check file paths relative to index.html; use devtools network tab |
| Scene not switching | Verify scene key matches; check this.scene.start() is called |
| Tilemap not rendering | Ensure tileset name in JSON matches addTilesetImage first parameter |
| Input not working | Check scene is active; verify input processor is not blocked |
| Memory leaks | Destroy sprites/groups in scene shutdown; use object pooling |