@neutrinoparticles/js-v1.1-phaser4
v1.1.1
Published
Phaser 4 integration for NeutrinoParticles (JS export format v1.1, GPU geometry construction)
Maintainers
Readme
@neutrinoparticles/js-v1.1-phaser4
Phaser 4 integration for NeutrinoParticles real-time particle effects (format v1.1).
Using Phaser 3? Use
@neutrinoparticles/js-v1.1-phaserinstead — Phaser 3 and Phaser 4 have incompatible renderers, so each major version has its own package.
Installation
npm install @neutrinoparticles/js-v1.1-phaser4The core runtime (@neutrinoparticles/js-v1.1) is installed automatically as a dependency.
Peer dependency: Phaser 4.0+
Requirements
The v1.1 renderer reconstructs particle geometry on the GPU. It renders through
Phaser.GameObjects.Extern, the sanctioned Phaser 4 raw-GL hook: Phaser
flushes its batch and restores its own render state around the effect, so the
integration never touches Phaser's internal pipeline state. It works on:
- WebGL2 — the primary path;
- WebGL1 (the Phaser 4 default) — supported automatically on contexts
that provide
OES_texture_float, at least 2 vertex texture image units,highpfloat in fragment shaders, and pass a one-time vertex-float-texture smoke test at startup. The renderer detects the context version and picks the path by itself — no configuration needed, a plainnew Phaser.Game({ type: Phaser.WEBGL })works out of the box.
On a WebGL1 context missing those capabilities, the plugin's init throws an
Error naming the missing capability (for example
NeutrinoParticles (js-v1.1): WebGL1 context lacks OES_texture_float; WebGL2
or WebGL1 with vertex float textures is required.).
WebGL2 vs WebGL1: which to use
Prefer WebGL2 whenever it is available — it is the faster and leaner path. WebGL1 is a compatibility fallback for devices and embeds that cannot create a WebGL2 context. Rough comparison of the same effect on the two paths:
- Rendering speed: identical at typical particle counts (up to ~20k live particles the difference is not measurable); on very heavy effects (~100k live particles) WebGL1 costs roughly 10% more render time per frame — the vertex shader does almost twice as many texture reads and extra math to unpack data that WebGL2 reads directly.
- GPU memory: WebGL1 uses about 25% more per effect (an extra byte texture carries the data WebGL1 shaders cannot decode from the float texture).
- Features/visuals: identical — both paths render the same effects with full parity (quads and ribbons).
Phaser 4 creates a WebGL1 context by default. To run on the faster
WebGL2 path, create a WebGL2 context yourself and hand it to the game via
the canvas + context options — WebGL2 is a superset of WebGL1, so
Phaser's own rendering keeps working:
const canvas = document.createElement('canvas');
canvas.width = 800;
canvas.height = 600;
document.body.appendChild(canvas);
const gl2 = canvas.getContext('webgl2');
new Phaser.Game({
type: Phaser.WEBGL,
canvas: canvas,
context: gl2, // hand the WebGL2 context to Phaser
width: 800,
height: 600,
// ...
});Export Target in Editor
In the NeutrinoParticles Editor, set the export target to JavaScript v1.1.
Export the effect with "For XHR Request" format. After export you get a .js
file (e.g. my_effect.js) containing the effect model.
Quick Start
import Phaser from 'phaser'
import { GamePlugin } from '@neutrinoparticles/js-v1.1-phaser4'
class GameScene extends Phaser.Scene {
constructor() {
super({ key: 'GameScene' });
}
preload() {
// Load exported effect (must be exported "For XHR Request")
this.load.neutrino('myEffect', 'export_js/my_effect.js');
}
create() {
// Get effect model from cache (created by the loader)
const effectModel = this.cache.binary.get('myEffect');
// Create effect instance
this.effect = this.add.neutrino(effectModel, {
position: [this.cameras.main.centerX, this.cameras.main.centerY, 0]
});
}
}
new Phaser.Game({
type: Phaser.WEBGL, // Phaser's default WebGL1 context works out of the box;
// see "WebGL2 vs WebGL1" above for the faster WebGL2 setup
width: 800,
height: 600,
scene: GameScene,
plugins: {
global: [{
key: 'neutrino',
plugin: GamePlugin,
start: true,
data: {
texturesBasePath: 'textures/',
generateNoise: true
}
}]
}
});For plain <script> tags (no bundler), reference the plugin as
Phaser4Neutrino.GamePlugin and include the UMD builds of both
@neutrinoparticles/js-v1.1 and @neutrinoparticles/js-v1.1-phaser4.
How It Works
scene.load.neutrino(key, url) fetches the exported .js file via XHR and
creates an effect model internally. The model is then available via
this.cache.binary.get(key). Use this.add.neutrino(model, options) to create
an effect instance that auto-updates and renders as part of the Phaser scene.
The effect is a Phaser.GameObjects.Extern, so it participates in the display
list like any other game object — its depth, alpha, and scroll factor behave as
usual, and Phaser handles flushing/restoring the renderer around it.
Instant Position Change (Teleporting)
Moving the effect via position creates a smooth trail of particles. To instantly teleport the effect without a trail:
effect.resetPosition({ position: [newX, newY, 0] });
// With new angle
effect.resetPosition({ position: [newX, newY, 0], angle: 45 }); // degreesRestart
Restart destroys all existing particles and starts the effect from scratch:
effect.restart({ position: [newX, newY, 0] });Pause
Full pause — all particles freeze, nothing is generated:
effect.pause();
effect.unpause();Generators pause — existing particles continue to live, but no new particles are created:
effect.pauseGenerators();
effect.unpauseGenerators();Set the initial pause state in the constructor:
import { Pause } from '@neutrinoparticles/js-v1.1-phaser4'
const effect = this.add.neutrino(effectModel, {
position: [400, 300, 0],
pause: Pause.YES // starts paused
});
// ...later:
effect.unpause();Emitter Properties
Control exposed emitter properties at runtime. Properties are defined in Emitter Guide or Emitter Scheme in the Editor.
// Write to every emitter that declares the property...
effect.setPropertyInAllEmitters('MyParticlesPerSecond', 100);
effect.setPropertyInAllEmitters('MyColor', [1.0, 0.5, 0.2]);
// ...or address a single emitter by name.
effect.setPropertyInEmitter('Sparks', 'MyColor', [1.0, 0.5, 0.2]);Read the current values back. A scalar property returns a number, a vector
property returns a number[], and an unknown property returns undefined:
effect.getEmitterPropertyValue('Sparks', 'MyColor'); // [1.0, 0.5, 0.2]
effect.getEmitterPropertyValue('MyParticlesPerSecond'); // 100 — first emitter with it
effect.hasEmitterProperty('Sparks', 'MyColor'); // true
effect.hasEmitterProperty('NoSuchProperty'); // falseDiscover what an effect exposes without knowing the names up front:
for (const p of effect.getEmitterProperties()) {
// { emitterName: 'Sparks', name: 'MyColor', arity: 3, value: [1, 0.5, 0.2] }
console.log(p.emitterName, p.name, p.arity, p.value);
}Values are read live from the simulation buffer at call time.
Use only the documented methods above. Exported effects are minified, and the internal property names/objects they contain (
_globParamMeta, or whatever short name it was mangled to) are not a stable API — they change between editor versions and differ between commercial and non-commercial exports.
3D Perspective and Base Parent
Two options mirror the PIXI 8 integration:
projection— aPerspectiveProjectiongiving the effect a real 3D perspective (with near-plane culling) instead of the default orthographic projection. Effects authored with depth render with proper perspective.baseParent— aPhaser.GameObjects.Containerthe effect is anchored to. The container's world transform is read live on every render, so moving, rotating, or scaling the container moves the whole effect with it.scale— a uniformnumberor a per-axis[x, y, z]vector.
import { PerspectiveProjection } from '@neutrinoparticles/js-v1.1-phaser4'
const container = this.add.container(400, 300);
this.effect = this.add.neutrino(effectModel, {
position: [0, 0, 0],
scale: [1, 1, 1],
projection: new PerspectiveProjection(/* ... */),
baseParent: container
});Number of Particles
const numParticles = effect.getNumParticles();Common Issues
Error: ... WebGL1 context lacks <capability>at startup: the (rare) WebGL1 context does not support the render technique — see Requirements. Running on a WebGL2 context (thecanvas+contextgame options) lifts the requirement.- No particles visible: Make sure the export target in the editor is set to JavaScript v1.1. Verify
texturesBasePathpoints to the correct textures directory. - Turbulence not working: Set
generateNoise: truein the plugin config, or call the plugin'sgenerateNoise()before creating effects that use noise.
Documentation
Full documentation at neutrinoparticles.com.
License
Copyright (c) Yurii Miroshnyk. All rights reserved.
