1
0 Comments

Which parts of a particle effect actually have to be renderer-specific?

Most web games hand-roll their particle effects, then hand-roll them again when the renderer changes. I've been picking apart which parts of an effect genuinely have to be renderer-specific, and it turns out to be less than I expected.

What actually is renderer-specific: how sprites get batched, what a draw call costs, blend-mode naming, texture upload. Roughly that.

What usually gets written as if it were, but isn't:

  1. Lifetime curves. Size, alpha and colour are nearly always a function of t = age / life. Store them as keyframe data instead of as branching inside the draw call and the same definition replays anywhere. Small demo: https://codepen.io/SonaArajyan03/details/xbqGxVm

  2. Motion fields. If direction is sampled from a shared field each frame rather than held as per-particle velocity state, you can retune the entire look by editing the field, and per-particle memory drops. Demo: https://codepen.io/SonaArajyan03/details/jEBPOML

  3. Emission shape and rate. Pure data.

  4. Blending intent. Additive vs alpha is portable even though the API name for it isn't.

The practical upshot: keep those four as a serialisable description, write a thin per-renderer player, and porting an effect stops being a rewrite and becomes a config change.

Disclosure: I work on NixieFX (https://nixiefx.com/threejs-runtime/), which is built on exactly this split, so I'm not a neutral party here. The split holds even if you roll your own though. What I'd actually like to know is where it breaks: has anyone hit an effect the curve-plus-field model genuinely can't express?

on August 28, 2026