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

  1. One emitter, many effects: this.add.particles(x, y, texture, config) creates a ParticleEmitter; the config fields (speed, scale, alpha, tint, frequency, lifespan) are the whole language. Same emitter, different configs = explosion, flash, burst (tween feel).
  2. 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: -1 emits once), emitZone (shape to emit from).
  3. 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).
  4. Where the juice is: particles sell the moment — a merge flash with scale growing + alpha fading + 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)

Performance rules (non-negotiable)

  1. Cap everything: maxParticles + sane frequency; a flash is 8–24 particles, a ripple 60.
  2. Share textures: one 8×8 white spark tinted per emitter beats five different textures (batch-friendly, atlas reality).
  3. Don’t emit per-frame for long — time-limited bursts beat continuous streams for “moment” effects.
  4. Destroy emitters you’re done with — emitter.destroy() on scene shutdown (memory discipline).

Pitfalls

  1. No maxParticles — a burst emitter becomes a leak; cap always.
  2. Per-particle textures — breaks batching, tanks FPS (fps tips).
  3. Continuous streams for moments — a “merge flash” isn’t a firehose; use frequency: -1.
  4. emitZone with wrong geometry — off-screen emission; test placement.
  5. 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).