Phaser Particle Effects: A Practical Intro
Phaser Particle Effects: A Practical Intro
Honesty note: the three recipes below are the effects my game (Merge Fish 2048) actually uses — merge flash, level-up burst, and a click ripple — written as a tutorial with the configs that worked (game context).
TL;DR
- One emitter, many effects:
this.add.particles(x, y, texture, config)creates aParticleEmitter; the config fields (speed, scale, alpha, tint, frequency, lifespan) are the whole language. Same emitter, different configs = explosion, flash, burst (tween feel). - The fields that matter:
speed(spread),scale(size + growth via start/end or a scale object),alpha(fade),tint(color per-emitter),lifespan(particle lifetime),frequency(emit interval —frequency: -1emits once),emitZone(shape to emit from). - Performance is a cap, not a hope: set
maxParticles, keep frequencies sane, and use one texture per emitter family. A merge flash needs ~10 particles, not 500 (the 60fps budget). - Where the juice is: particles sell the moment — a merge flash with
scalegrowing +alphafading + a short tween sells “merge feels good” more than any polish elsewhere (juice guide).
The three recipes
1. Merge flash (small, fast, fades)
this.add.particles(x, y, 'spark', {
speed: { min: 30, max: 120 },
scale: { start: 1.2, end: 0 },
alpha: { start: 1, end: 0 },
lifespan: 400,
frequency: -1, // emit once
quantity: 8,
tint: 0xffd27f
});
2. Level-up burst (bigger, longer, tinted)
this.add.particles(x, y, 'spark', {
speed: { min: 80, max: 220 },
scale: { start: 1.6, end: 0 },
alpha: { start: 1, end: 0 },
lifespan: 900,
frequency: -1,
quantity: 24,
tint: [0xffd27f, 0xff9f43, 0xffffff]
});
3. Click ripple (emits over time from a zone)
this.add.particles(0, 0, 'ring', {
emitZone: { type: 'random', source: new Phaser.Geom.Rectangle(0, 0, 800, 600) },
speed: 0,
scale: { start: 0.5, end: 2.5 },
alpha: { start: 0.6, end: 0 },
lifespan: 700,
frequency: 30, // emit every 30ms
maxParticles: 60
});
The config language (fields you’ll actually touch)
frequency— emit interval in ms;-1= single burst.quantity— particles per emission.speed— {min,max} spread; 0 = static.scale/alpha— {start,end} (or object arrays) for growth/fade.tint— color; an array cycles per particle.lifespan— ms each particle lives.emitZone— where emission happens (point/rectangle/circle).maxParticles— the cap; set it.
Performance rules (non-negotiable)
- Cap everything:
maxParticles+ sane frequency; a flash is 8–24 particles, a ripple 60. - Share textures: one 8×8 white spark tinted per emitter beats five different textures (batch-friendly, atlas reality).
- Don’t emit per-frame for long — time-limited bursts beat continuous streams for “moment” effects.
- Destroy emitters you’re done with —
emitter.destroy()on scene shutdown (memory discipline).
Pitfalls
- No
maxParticles— a burst emitter becomes a leak; cap always. - Per-particle textures — breaks batching, tanks FPS (fps tips).
- Continuous streams for moments — a “merge flash” isn’t a firehose; use
frequency: -1. emitZonewith wrong geometry — off-screen emission; test placement.- Forgetting destroy — emitters are game objects; clean them up (leak guide).
Bottom line
Phaser particles are one emitter with a config language: speed/scale/alpha/tint/lifespan/frequency/emitZone/maxParticles — and the three recipes (merge flash, level-up burst, click ripple) cover most casual-game moments. Cap everything, share textures, destroy when done, and let particles sell the moment (the juice guide).