Command Palette

Search for a command to run...

Hectal
Course outline
Topic 15.5Advanced ~20 min · step 5 of 6

Particles and Instanced Effects

In one line

Sparks, dust and light beams are many small soft shapes. Draw them as one Points object or one InstancedMesh (one draw call), blend them additively, and stop them from writing depth, or they hide each other.

0/6 · 0%

Think of it like this

A sparkler. Hundreds of tiny glowing specks, each almost see-through, and where two overlap the light adds up. Nobody draws each speck with a pencil: you stamp the same speck hundreds of times. Particles are that stamp.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

particle
One small stamp of a repeated effect: a spark, a speck of dust.
Points
A three.js object that draws one screen-facing square per vertex.
InstancedMesh
One mesh drawn many times in one call, each copy with its own matrix and optionally colour.
additive blending
Adding the new colour to the one already on the screen, so overlaps brighten.
depthWrite
Whether drawing a surface records its distance, so later surfaces behind it are rejected.
overdraw
Drawing the same pixel many times in one frame, from overlapping shapes.

Step by step

01Make a soft spot

A 64 × 64 canvas gets a radial gradient: opaque white in the centre, fully transparent at the edge. CanvasTexture turns it into the particle's alpha picture.

02Two particles, one draw call

A single Points object holds a near blue particle (index 0) and a far orange one (index 1). The near one is drawn first. Each is 90 pixels across, and they overlap.

03Change only the depth writing and the blending

Normal blending with depthWrite: true: the orange spark is hidden under the blue square. depthWrite: false: the orange spark shows, drawn on top of the blue because it comes later in the list, even though it is farther away. AdditiveBlending: both lights add, the order stops mattering, the overlap is almost white.

04Count the draw calls

The second example renders the same 200 sparks three ways. The status bar and renderer.info.render show 200, 1 and 1 calls. To move an instanced spark, call setMatrixAt and set instanceMatrix.needsUpdate = true.

Try it yourself

  1. 1

    Move the particles apart

    In the first example change size: 90 to size: 40. The squares no longer overlap at the sampled pixel, and all three lines print 235, 160, 105: the depth problem only appears where particles overlap.

  2. 2

    A thousand more sparks

    In the second example change length: 200 to length: 2000. The first line prints draw calls: 2000 | triangles: 4000 | points: 0. The Points line prints draw calls: 1 | triangles: 0 | points: 2000, and the InstancedMesh line draw calls: 1 | triangles: 4000 | points: 0.

Code & diagrams

Particles that hide each other New tab

Pixel values are rounded to steps of 5. The sampled pixel is under the middle of the far (orange) particle, inside the near (blue) particle's square.

idle

Console

Run the scene to see its console.

Expected console output

normal blending, depthWrite true     pixel under the far particle: 55, 105, 125
normal blending, depthWrite false    pixel under the far particle: 245, 170, 115
additive blending, depthWrite false  pixel under the far particle: 255, 255, 235
200 sparks, three ways New tab

Positions come from a seeded generator, so the numbers never change. Triangles for the instanced version: 200 quads × 2.

idle

Console

Run the scene to see its console.

Expected console output

200 separate meshes            draw calls: 200 | triangles: 400 | points: 0
one Points object              draw calls:   1 | triangles:   0 | points: 200
one InstancedMesh              draw calls:   1 | triangles: 400 | points: 0

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Particles with depthWrite on

In the first example read the first line.

terminal
$ Run
── what you'll see ──
normal blending, depthWrite true pixel under the far particle: 55, 105, 125

Break #2

One mesh per spark

In the second example read the first line.

terminal
$ Run
── what you'll see ──
200 separate meshes draw calls: 200 | triangles: 400 | points: 0

Myth vs fact

Myth

Transparent objects never write depth.

Fact

They do by default. transparent: true only changes the draw order and blending; depthWrite is separate.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Transparent objects are sorted far to near by their object centre, once per frame. Inside one Points or InstancedMesh three.js does not sort anything. That is why additive blending (order does not matter) is the safe choice for dense effects, and why smoke with normal blending needs sorting on the CPU or a few big layers.

  • ▸

    A point's size is limited by the GPU and driver: gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE) gives the range for the machine you are on, and a request above the maximum is drawn at the maximum. The first example asks for 90-pixel points, so on a GPU whose limit is lower the spots come out smaller. For big soft spots use InstancedMesh quads that face the camera.

  • ▸

    Moving, flickering sparks are motion. Respect the showroom's pause button and the reduced-motion setting (Phase 9): freeze the particles when the visitor asks for less movement. Positions changed each frame are re-sent to the GPU (needsUpdate = true): thousands of animated particles cost bandwidth even in one draw call.

Remember this

  1. 1

    The problem this solves: a hundred sparks drawn as a hundred meshes are a hundred draw calls, and the CPU, not the GPU, becomes the limit (Phase 17). Particles must be one object, and they must blend softly instead of showing hard square edges that cut each other.

  2. 2

    Three ways to draw many small things. Points: one vertex per particle, drawn as a screen-facing square whose size is set by PointsMaterial.size. InstancedMesh: one small mesh drawn N times, each with its own matrix and colour, so each particle can have its own size and turn. Sprite: one object per particle, simple but one draw call each. The second example draws 200 sparks as 200 meshes (200 draw calls), one Points (1) and one InstancedMesh (1). With 2,000 sparks the meshes cost 2,000 calls, the other two still 1.

  3. 3

    Soft spots need an alpha picture. A point is a square. To make it a round glow, give PointsMaterial a map that is white in the middle and fades to transparent at the edge, and set transparent: true. The first example draws it with a canvas gradient: no image file.

  4. 4

    Additive blending adds light. With AdditiveBlending each particle's colour is added to what is already there, so overlaps get brighter and the result does not depend on the drawing order. It suits fire, sparks, glow and light beams. It cannot make anything darker, so smoke needs normal blending.

  5. 5

    Depth writing is the usual trap. A transparent particle still writes its depth by default. Particles in one Points are drawn in index order, not far to near. So a near particle drawn first writes depth over its whole square, and a far particle drawn later is rejected wherever it overlaps that square, even where the near one is almost transparent. In the first example the far orange spark vanishes under the near blue one: the pixel reads 55, 105, 125, a blue-grey. With depthWrite: false it reads 245, 170, 115; with additive blending, 255, 255, 235.

Explain it without notes

01

Why can a near particle hide a far one even though the near one is almost transparent?

02

When do you pick Points, InstancedMesh and Sprite?

Practice

01

Make 500 seeded dust specks as one Points object that hide nothing.

02

Say why additive blending is a good fit for the headlight beam but not for fog.

Done when you can

  • Draw hundreds of particles in one draw call

  • Make a soft round spot from a gradient texture

  • Choose between normal and additive blending

  • Turn off depth writing so transparent particles do not hide each other