Phaser 3 Game Tutorial: Your First Playable in a Day
Phaser 3 Game Tutorial: Your First Playable in a Day
Honesty note: this is the tutorial I wish I’d had when I started my game (Merge Fish 2048) — it’s the smallest playable slice: click a fish, it merges, score goes up. No physics, no scene juggling yet — those are the next steps (physics, scenes).
TL;DR
- Phaser is a scene → game-object → tween loop: a
Phaser.Gamewith config, scenes containing game objects (sprites/text/images), input on those objects, tweens for movement. You build the smallest version of each and grow (my structure). - One playable in a day is realistic — the loop: create config → one scene → draw something → make it clickable → add a score → add a tween. Each step is ~10 lines.
- The three beginner gotchas: (1)
thisin callbacks — bind or use arrow functions or the emitter’s context; (2) assets won’t load if paths/format are wrong — test with one tiny sprite first (asset pipeline); (3) scene lifecycle — put setup increate(), per-frame logic inupdate()(scene structure). - Copy-paste the working version below, then break it on purpose — changing values and watching it break is how you learn the API (tween play).
The working example (a click-to-merge slice)
// main.js — the smallest playable
const config = {
type: Phaser.AUTO,
width: 800, height: 600,
scene: { preload, create, update },
scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH }
};
function preload() {
this.load.image('fish', 'assets/fish.png'); // any sprite
}
function create() {
this.score = 0;
const fish = this.add.image(400, 300, 'fish').setInteractive();
fish.on('pointerdown', () => {
this.score += 10;
this.children.getByName('scoreText').setText('Score: ' + this.score);
this.tweens.add({ targets: fish, scale: 1.3, duration: 120, yoyo: true });
});
this.add.text(16, 16, 'Score: 0', { fontSize: '24px' })
.setName('scoreText');
}
function update() { /* per-frame logic goes here */ }
new Phaser.Game(config);
Run it with a tiny setup: npm create vite → install phaser → replace main.js — the full starter.
The build order (one day, in order)
| Hour | Step | What you learn |
|---|---|---|
| 1 | Config + empty scene | Phaser.Game, config object |
| 2 | Load + display a sprite | preload/create, images |
| 3 | Click it (setInteractive) | Input, callbacks, this gotcha |
| 4 | Score counter | State, text objects |
| 5 | Tween on click | Tweens, yoyo |
| 6 | Grid + merge rule | Arrays, game logic (2048 merge) |
| 7 | Save high score | localStorage (save pattern) |
Stop there for day one: a playable, scoreable, savable slice is a game you can test — the honest definition of a first milestone.
The three gotchas (read before you type)
thisin event callbacks — insidefish.on('pointerdown', function(){...}),thisisn’t the scene. Use arrow functions (above) orthis-bound handlers. The classic first-day bug.- Asset loading — wrong path or a missing file makes
preloadfail silently and the scene never starts; console-check and use one small test sprite (pipeline). - create vs update — setup belongs in
create()(once), logic inupdate()(per frame); putting setup inupdateduplicates objects every frame (scene guide).
Pitfalls
- Big scene design on day one — start with one scene and one object; structure grows later.
- Physics from hour one — not needed for click-and-merge; add only when movement demands it (physics decision).
- Fancy assets first — use a colored square to learn; art later (asset sizes).
- Skipping the console — Phaser logs errors clearly; read them, they name the fix.
- Copy-paste without breaking — tweak a value, watch it fail, understand why (tween guide).
Bottom line
Your first Phaser 3 game in a day is config → scene → sprite → click → score → tween: the smallest playable slice, in that order, with the three gotchas (this-binding, asset paths, create/update) in mind. Mine started exactly this way and grew one scene at a time (structure) — day one is about a game you can actually test, not a plan you can’t.