Disclosure: AI created this tutorial, source and synthetic scene for a NixieFX marketing assignment. The numeric checks and browser captures described here ran locally with nixie-fx 0.1.17 and Three.js 0.185.1. The review record consists of AI source inspection and actual runtime checks; no human code-review signoff is claimed.
A campfire ember test becomes hard to compare when the camera, seed and elapsed time all change between captures. This example holds those inputs steady and changes the initial X velocity. The result is a modest stream of particles that rises and drifts to one side of a synthetic log marker.
The demo has Right and Left wind options, a replay button, a fixed-time inspection button and scene disposal. It uses a real exported NixieFX effect with a Three.js adapter. Wind here means an authored initial velocity bias. It does not mean a fluid simulation or a changing force field acting on every live particle.
The effect has a small, explicit budget
The emitter uses a 0.35 by 0.05 by 0.35 spawn box, emits at 24 particles per second, and gives particles a two-second lifetime. Its maximum capacity is 56. Each ember is a small unlit procedural billboard. Depth testing is enabled and depth writing is disabled so the transparent particles do not write an opaque depth surface.
The upward velocity ranges from 0.7 to 1.1 units per second. Z ranges from -0.16 to 0.16. For the Right variant, X ranges from 0.22 to 0.62. For Left, X ranges from -0.62 to -0.22. These are demonstration units. Their physical interpretation depends on the host scene's scale.
The original export validates before the host creates a derived preview definition. Keep the package versions and the exported source in the handoff. Replacing the effect file without replacing its manifest can invalidate the bundle's identity checks.
Load the bundle before constructing the instance
Fetch the manifest and its referenced effect files, then give those parsed values to loadVfxExportBundle. Pass requiredBackend: "three3d". The loader verifies the export structure and support information; fetching arbitrary JSON and hoping the renderer accepts it loses that check.
The complete source includes the loading path and the authored project. This excerpt focuses on the wind variant after a verified effect has been selected:
import { normalizeThreeVfxEffect } from "nixie-fx/three";
const definition = normalizeThreeVfxEffect(exportedEmbers);
const velocity = definition.emitters[0].initializeParticle.velocity;
velocity.min[0] = rightWind ? 0.22 : -0.62;
velocity.max[0] = rightWind ? 0.62 : -0.22;
const embers = vfx.createEffect(definition, {
position: [0, 0.16, 0],
seed: 42,
});
// Preview inspection only. Do not seek on every animation frame.
embers.seek(1);
embers.pause();
During normal animation, the host advances vfx.update(deltaSeconds) once per frame and renders its scene. The inspection action seeks to one second and pauses. It makes the comparison reproducible without capturing the stream at an arbitrary wall-clock moment.
Compare the output at the same simulation time
The local numeric check created both variants with seed 42 and sampled at one second. Each contained 23 live particles at that sample. The mean X position was approximately +0.207 for Right and -0.209 for Left. Those values describe this particular fixture and update schedule. They are not a performance measurement or a claim about an arbitrary game's wind model.
The camera stays at the same oblique angle for both captures. A fixed camera prevents a view change from making the wind reversal appear larger or smaller. A front view remains available for inspection, but it should be treated as a separate check.
Stop and release the effect with the scene
The replay action removes and destroys the previous instance before creating another. Scene disposal cancels the animation callback, disconnects the resize observer, destroys the VFX renderer and disposes host geometry and materials. The demo also releases its texture store, although this particular ember effect uses no texture file.
That ownership split matters when a scene contains imported assets. An effect instance does not automatically own every resource supplied by the host. Release the resources your host created when that scene ends, and avoid destroying shared assets still used elsewhere.
What this example establishes
The export has no validation errors and the active Three.js ember instance reports no unsupported features in the local check. The example does not exercise collision, authored lights, trails, complex material graphs or PixiJS visual equivalence. Turning those settings on would require another support and runtime check.
If you use a three js particle editor for the authored effect, the NixieFX Three.js runtime reference explains the adapter and instance controls. Keep the wind values, seed, sample time and camera position in the review record. That small amount of context lets another developer reproduce a visual disagreement before changing the effect.


Top comments (0)