Disclosure: This draft was generated primarily with AI for NixieFX's marketing work. The source, screenshots and measurements come from the supplied local demo. Human technical and platform-policy review are required before publication.
A speaking portrait can have a little motion around it while the dialogue stays still. The effect should identify the speaker without becoming another bright object behind the words.
This example uses NixieFX with PixiJS 8.22.0 and nixie-fx 0.1.20. It keeps the aura behind an opaque portrait, clips it to the portrait area, and puts the dialogue in a separate opaque layer. The speaking state is a local control. There is no audio detection or lip sync.
Build the layer boundary before tuning the particles
The portrait occupies a 128-pixel circle. Its effect layer has a 96-pixel-radius mask around the same center. A separate plate holds the speaker name, a Speaking label and two lines of dialogue.
The relevant setup is:
const aura = new Container();
aura.position.set(40, 130);
dialogue.addChild(aura);
const clip = new Graphics()
.circle(110, 110, 96)
.fill('#ffffff');
clip.position.set(40, 130);
dialogue.addChild(clip);
aura.mask = clip;
// Add the opaque portrait and dialogue plate after the aura.
dialogue.addChild(portrait, plate);
const renderer = new PixiVfxRenderer({
parent: aura,
projection: createPixiVfx2dProjection({
originX: 110,
originY: 110,
pixelsPerUnit: 80,
yAxis: 'up'
})
});
The mask belongs to Pixi's host scene. It is not a NixieFX emitter setting. Its position matches the aura container, while the projection maps the effect's origin to the portrait's local center. The text plate starts beyond the mask's right edge.
This gives the effect a limited region to draw into. Lowering opacity alone would still allow it to alter pixels behind the text if the layer order or geometry changed.
Use two small emitters
The first emitter is a soft blue circle that grows and fades behind the portrait. It emits two particles per second, gives each a 1.6-second lifetime, and has a capacity of four. The opaque portrait covers the bright center, leaving a visible halo around it.
The second emitter adds slow motes around a flat circle. It emits four particles per second with a 1.1-second lifetime and a capacity of six. The motes are small billboards, not light sources. Both emitters loop; together their configured capacity is ten particles.
The effect uses unlit additive compositing, color and size over lifetime, and small initial velocities. It needs no file textures or material provider. Its normalized authoring JSON and compiled export are both in the source kit.
If you use a pixi js particle editor such as NixieFX, keep the speaking-state controller and UI layering in the game. The NixieFX PixiJS particle-effects guide explains the export-bundle and renderer integration used here.
Let the speaking state own playback
The demo creates one reusable instance with autoStart disabled. Selecting a speaker resets that instance with a fixed seed, updates the portrait and name, and reveals the aura. The normal ticker advances the renderer once per frame:
const fx = renderer.createEffect(definition, {
autoStart: false,
seed: 398
});
app.ticker.add(ticker => {
if (aura.visible) {
renderer.update(ticker.deltaMS / 1000);
}
});
Silence stops the ticker and hides the aura. Turning VFX off preserves the explicit Speaking label. The effect therefore supplements a readable state indicator instead of being the only way to identify the speaker.
The stop operation does not destroy the instance. In the captured silence check, four particles remained allocated in the paused instance. They were hidden and no longer advancing. Resuming resets the instance, and final page teardown destroys the renderer and Pixi application.
Compare the composited text pixels
Caption: Actual Chrome viewport, paused at 1.00 seconds with a fixed simulation clock. The geometric portrait and aura are rendered by the supplied PixiJS demo.
The verification control stops ordinary playback, captures the full scene's body-text region with the aura hidden, and compares that reference with 120 fixed 1/60-second updates of the aura. It extracts the full scene composition, not the text container alone:
const textRegion = new Rectangle(292, 222, 540, 100);
const pixels = () => app.renderer.extract.pixels({
target: app.stage,
frame: textRegion,
resolution: 1,
antialias: true
}).pixels;
The recorded crop contains 54,000 pixels, including 2,798 bright foreground pixels from the dialogue. Across all 120 frames, the maximum number of changed pixels was zero. Peak active particle usage was eight, against the configured capacity of ten, with one effect instance.
Validation and export produced no errors or warnings. Missing texture, material and sub-emitter references were empty, as were unsupported-module and unsupported-feature diagnostics. Switching to Mira reused the same instance. Silence and closing the dialogue stopped the ticker.
Pixel equality establishes that this animation did not alter this text region in this scene. It does not prove accessibility, readability on a smaller screen, or performance on a phone. The inspection path also performs extraction work that ordinary playback does not.
State the runtime limits
No unsupported settings are enabled in this effect. Lit particle shading requires the Three backend. Pixi depthWrite uses draw ordering and ink semantics rather than GPU depth-buffer writing; this source disables it.
The example does not test custom material graphs, trails, sub-emitters or Three.js rendering. It does not infer speech from audio. Real-editor visual review, mobile performance, reduced-motion preferences from the operating system, and a full accessibility audit remain untested. The supplied VFX switch is a local demo preference.
Run the source
Unpack the supplied source kit and enter its demo folder:
npm ci
npm run vfx:validate
npm run vfx:export
npm run dev
Open http://127.0.0.1:5199/. Inspect a fixed frame, run the 120-frame comparison, switch speakers, turn VFX off and close the dialogue. Download the complete source kit, including the authoring JSON, compiled bundle, screenshots and recorded checks.

Top comments (0)