Disclosure: This article and demo were generated by a coding agent for a NixieFX marketing task. The code was validated in a synthetic button demo; this is not a production-game capture.
A button can have a hover effect without making hover its only way to communicate. The important state is whether the user is interacting with it: a pointer may be over the button, or keyboard focus may still be inside it after the pointer leaves.
This NixieFX tutorial uses a real HTML button over a PixiJS canvas. The button supplies native keyboard activation and focus. PixiJS renders a decorative ember layer that cannot receive pointer input. The button's layout stays independent of particle bounds.
Start with a small, exported effect
The accompanying source pins nixie-fx to 0.1.17 and pixi.js to 8.19.0. Its hover-ember authoring effect uses a box emitter, 22 requested particles per second, a 0.65-second lifetime, and a hard capacity of 24. Initial velocity moves particles upward in screen coordinates. These are authored values, not a performance claim.
The demo uses an exported manifest and compiled effect JSON. It calls loadVfxExportBundle with requiredBackend: "pixi2d", checks the backend support report, then creates a PixiVfxRenderer with a separate parent container.
The estimated steady population is about 14 particles before discrete emission and fade behavior. The live counter is the observation to inspect. Capacity is an upper bound, not the number that should be visible every frame.
Combine hover and focus as independent inputs
Do not stop the effect on every pointerleave. A keyboard user might still have focus on the same button. Conversely, losing focus should not stop the effect if a pointer remains over it.
let hovered = false;
let focused = false;
let instance = null;
const motion = matchMedia('(prefers-reduced-motion: reduce)');
function sync() {
const wanted = (hovered || focused) && !motion.matches;
if (wanted) {
if (!instance || !instance.isActive) {
if (instance) vfx.removeEffect(instance, true);
instance = vfx.createEffect(effect, { seed: 42 });
}
} else if (instance) {
instance.stop();
vfx.removeEffect(instance, true);
instance = null;
}
}
button.addEventListener('pointerenter', () => { hovered = true; sync(); });
button.addEventListener('pointerleave', () => { hovered = false; sync(); });
button.addEventListener('focus', () => { focused = true; sync(); });
button.addEventListener('blur', () => { focused = false; sync(); });
motion.addEventListener('change', sync);
This excerpt assumes the verified effect and renderer are already available. The complete source includes loading, position updates, disposal, and statistics. Its button click handler increments a demonstration counter; connect the existing game action in a real UI.
The focus ring remains visible whether motion is allowed or not. Particles are decoration, not the only indication that the button can be activated. On touch devices, the native button still works without requiring a hover state.
Resolve layout coordinates each frame
The native button is centered in a responsive stage. Its DOM rectangle must be converted into that stage's local CSS-pixel coordinates. The Pixi projection uses one pixel per unit and a downward Y axis.
const buttonRect = button.getBoundingClientRect();
const stageRect = stage.getBoundingClientRect();
instance?.setPosition([
buttonRect.left - stageRect.left + buttonRect.width / 2,
buttonRect.top - stageRect.top + buttonRect.height / 2,
0
]);
vfx.update(Math.min(ticker.deltaMS / 1000, 0.05));
Call the renderer update once per frame. The example caps the step at 0.05 seconds and skips hidden documents. It therefore chooses stable visual continuation over catching up every elapsed wall-clock second.
Stop the decoration and remove its owners
In the pinned PixiJS adapter, stop() clears the effect immediately. This example removes the stopped instance from the renderer and creates a fresh one on re-entry. It does not claim the Three.js adapter's graceful-completion API is available here. During teardown, remove event listeners and the ticker callback, destroy the VFX renderer, and destroy the Pixi application and its canvas.
The reduced-motion setting prevents fresh emission. If it changes while particles are alive, the example clears them immediately. A static focus ring remains available.
What this example does not transfer to PixiJS
The recipe uses a supported 2D billboard effect without file textures, custom materials, trails, or scene lighting. It does not implement GPU depth occlusion or a 3D collision response. A successful export is not evidence that every editor option will render identically across backends.
The NixieFX PixiJS particle-effects guide is the assigned reference for the integration. Use the exported support report for the actual authored effect and keep the installed package version with the source.
Reproduce and inspect
Run the supplied demo in hover mode. Tab to Continue, activate it with Enter, move the pointer away while focus remains, then move focus away too. Check that particles clear without disabling the button's existing action. Repeat with reduced motion enabled and at a narrow viewport.

Top comments (0)