vidar.games

Dense-looking particles

2026-11-06 · Discuss on X

Same 400 particles on both halves, same emitters, same motion.
Left: plain round sprites. Right: six cheap tricks stacked, the kind PS2-era effects leaned on.

The problem

A summon or spell burst wants to look like thousands of sparks. Simulating thousands means updating, uploading and often sorting thousands of particles every frame. On older hardware that budget was not there, so the trick is to make each particle look like more than one.

How it works

Six tricks, each cheap on its own:

  1. Multi-dot texture. Each sprite carries a bright core, 7 smaller dots around it and a thin cross flare. One quad reads as a small cluster.
  2. Additive blending. Overlaps add up instead of covering each other, so dense spots glow and no sorting is needed.
  3. Stretch along velocity. The quad is lengthened along its direction of motion on screen and thinned across it. Fast particles turn into streaks.
  4. Trail ribbons. A ribbon through each particle's last 6 positions, 0.035 s apart, fading to nothing.
  5. Flicker over life. Size and brightness jump randomly 18 times a second, and size shrinks as the particle ages.
  6. Rotated copies. Each particle is drawn 4 times, turned 90° around the centre. The effect is round anyway, so copies don't look like copies.

The simulation still runs 400 particles. Only the drawing changes: 1,600 sprites and 1,600 ribbons.

What I hit

My first additive pass turned the whole frame white. Additive light stacks fast: with 4 copies, trails and bloom on top, per-sprite alpha had to drop to about an eighth of the plain version (0.12 against 0.9). Each trick added means turning the others down.

Stretching made it worse at first. A long streak covers many more pixels than a dot, so I also thinned the quad across its direction while stretching it.

My build

three.js (r187): one InstancedBufferGeometry quad with a custom shader for the sprites, one ribbon mesh, UnrealBloomPass. Particle motion is closed-form (drag plus constant lift), so any frame can be rendered from its time and trails are sampled from the same formula.

At the end, the left half runs 10,000 plain additive particles. three.js reports 1 draw call and 20,000 triangles for it; the 400-particle version is 2 draw calls and 22,400 triangles.

Trade-offs

When to use it

Spells, summons, pickups, sparks and hit effects, anywhere glow and speed matter more than individual particles. Skip it for smoke, dust and anything lit.

New breakdowns go out on X first: @vidar_0208. If this one helped, you can buy me a coffee.

Source and credits

Topic credit: @TheMirzaBeig (breakdown of how a PS2-era summon effect builds density from few particles).

Engine: three.js by @mrdoob and contributors, https://github.com/mrdoob/three.js, MIT license, branch dev at commit fa01dd6ad164c48554f1bb9456f6500db052ec4f. Used unchanged: InstancedBufferGeometry, ShaderMaterial, EffectComposer, UnrealBloomPass, OutputPass.

My own code: the particle motion, both emitters, the sprite shader with velocity stretch, the trail ribbons, flicker, rotated copies, the procedural sprite textures and magic circle, the stage and camera. No effect shapes, colours, textures or footage from any game or from the original post. No external assets. No IA model.