DEV Community

Cover image for Budget waterfall mist with particle lifetime before adding density
Hiroshi TK
Hiroshi TK

Posted on

Budget waterfall mist with particle lifetime before adding density

Waterfall mist can consume a surprising amount of a scene's visual budget because it is usually continuous. A burst ends; an ambient emitter remains active while the player explores. The useful first question is how many particles the effect keeps alive, not how dense it looks in the editor.

This NixieFX tutorial uses a small Three.js fixture: a rectangular waterfall prop, a soft emitter at its base, and a visible runtime readout. The prop is ordinary host geometry. The mist is a NixieFX 0.1.20 billboard effect rendered with Three.js r185.

Set the population before tuning the appearance

The emitter creates 24 particles per second, with a lifetime of three seconds and a capacity of 96. Ignoring boundary timing, the expected steady population is approximately 24 × 3 = 72 particles. Capacity is a hard ceiling, not a promise that all 96 particles are always present.

emitter.maxParticles = 96;
emitter.spawn.rate = 24;
emitter.spawn.rateValue = constant(24);
emitter.initializeParticle.lifetime = constant(3);
emitter.spawn.shape = 'box';
emitter.spawn.box = [1.8, 0.2, 0.4];
Enter fullscreen mode Exit fullscreen mode

The code uses createConstantParticleScalar as constant. The complete source initializes the remaining fields from createDefaultParticleEffect, enables a fade over lifetime, and gives each particle a small upward velocity. Depth testing stays enabled and depth writing stays disabled for the translucent billboards.

Place the effect relative to a readable prop

Place the effect at [0, 0.2, 0]. The demonstration waterfall sits behind it and supplies a stable scale reference. The scene is deliberately plain: mist density should be readable without a complex material, lighting rig, or postprocessing chain.

A dark background makes soft particle overlap visible. It can also make an additive effect seem more impressive than it will appear in a bright level, so this fixture uses alpha blending. Test against the actual level colors before treating its appearance as final.

The emitter does not collide with the waterfall or surrounding terrain. It does not simulate water volume, spray breakup, or moisture. It is a visual layer placed near a prop.

Keep simulation accounting separate from rendering accounting

Advance the runtime once per fixed simulation step, then render the scene. Do not advance an instance separately when its renderer already owns the update.

while (accumulator >= 1 / 60) {
  vfx.update(1 / 60);
  accumulator -= 1 / 60;
}
renderer.render(scene, camera);
Enter fullscreen mode Exit fullscreen mode

The fixture caps the elapsed time added after a long frame. That prevents a large catch-up burst when a tab resumes, but it also means the demo is not a wall-clock simulation under stalls. Its fixed-step sequence is useful for comparisons.

The readout reports active particles, visible particles, capacity, instanced draw calls, and legacy particle draw calls. These are different quantities. A particle whose alpha is zero at birth may exist in the simulation while contributing nothing visible.

Read the observed result carefully

At a recorded simulation time of 60 seconds, the fixture showed 73 active particles, 72 visible particles, capacity 96, and one instanced draw call. The peak active count was 73. The extra particle around the simple 72-particle estimate reflects spawn and lifetime boundaries in the stepped simulation; it is not evidence that the capacity limit failed.

The runtime's support report was supported, and its unsupported-feature list was empty for this definition. A separate repeatability check compared two 90-step runs with seed 2444 and found identical particle debug transforms.

This is not a performance benchmark. A single emitter does not reproduce a full level's overdraw, resolution, effects, or device limits. The particle counter is a useful diagnostic, but it cannot replace a frame capture on the target hardware.

Change one budget variable at a time

First hold lifetime at three seconds and adjust the emission rate. Then restore the rate and adjust lifetime. Finally, test billboard size, because larger translucent quads can increase overdraw even when the particle count stays constant.

Keep the same camera and seed during each comparison. If you add a flipbook, material graph, or other feature, inspect the rendering path again instead of assuming the original draw-call result still applies.

The NixieFX Three.js runtime guide documents the counters and integration controls used here. A useful ambient effect is one whose visual contribution and resource cost can both be explained.

Top comments (0)