Skip to content

@modoki/engine / index / ParticleEffectDef

Interface: ParticleEffectDef ​

A complete particle effect definition (the .particle.json payload).

v1 covers the core authoring surface (emission, shape, start values, gravity, size/color/opacity over life). Advanced behaviors (forces, collision, trails, sub-emitters, sprite-sheet animation) extend this in Phase 3 — additively, so old assets keep loading.

Particles ​

collision? ​

optional collision?: CollisionConfig

collision against a solid plane / sphere / box / cylinder collider


colorOverLife? ​

optional colorOverLife?: Gradient


drag? ​

optional drag?: number

linear velocity damping per second (0 = none)


duration ​

duration: number

loop period in seconds


emission ​

emission: object

Particles ​

bursts? ​

optional bursts?: EmissionBurst[]

fillPool? ​

optional fillPool?: boolean

Continuous full-pool emission: keep every slot alive at all times (ages are staggered at start so deaths spread over time), ignoring rateOverTime and bursts. Effective rate ≈ maxParticles ÷ lifetime. Both backends honor this identically — it is the only emission model the GPU compute backend implements, so an effect must set fillPool to be eligible for simulation: 'gpu'. Ideal for dense ambient fields (galaxies, starfields, drifting dust/motes).

rateOverTime ​

rateOverTime: number


forces? ​

optional forces?: ForceField[]

external force fields (wind, attractors/repellers)


gravity ​

gravity: number | [number, number, number]

Constant acceleration, world units / s². Two forms (see resolveGravity in simSpec.ts): a scalar g = a downward pull of magnitude g along -Y (legacy 3D authoring, maps to (0,-g,0)); or an explicit [x,y,z] vector applied as-is (axis-neutral). 2D effects use the vector form — [0,+G,0] falls toward screen-down (PixiJS +Y) with no Y flip, [0,-G,0] rises. normalizeParticleDef migrates a loaded scalar to [0,-g,0] so old assets re-save in vector form.


id? ​

optional id?: string

Stable asset GUID, stored in-file (same convention as mesh/material/prefab/scene id). Lets scenes + sub-emitters reference this effect by GUID so the reference survives the file being moved/renamed. Assigned on first save if absent.


looping ​

looping: boolean


maxParticles ​

maxParticles: number

hard cap on simultaneously-alive particles (sizes the instance buffer)


name? ​

optional name?: string


noise? ​

optional noise?: NoiseConfig

turbulence/curl noise acceleration


opacityOverLife? ​

optional opacityOverLife?: Curve


prewarm? ​

optional prewarm?: boolean

pre-simulate one duration so the effect starts "full"


render ​

render: RenderConfig


rotationOverLife? ​

optional rotationOverLife?: Curve


rotationSpeed? ​

optional rotationSpeed?: MinMax


shape ​

shape: EmitterShape


simulation? ​

optional simulation?: "cpu" | "gpu"

Simulation backend. 'cpu' (default) = deterministic JS sim, full feature set. 'gpu' = TSL compute shader for very high counts (100k+). The GPU backend only implements continuous full-pool emission, so a GPU effect must also set emission.fillPool. It supports forces, single-plane collision and mesh-primitive rendering, but NOT trails or sub-emitters. An effect that isn't GPU-eligible (no fillPool, uses trails/sub-emitters, or no WebGPU compute backend) transparently falls back to the CPU sim — which honors fillPool identically, so the look matches.


sizeOverLife? ​

optional sizeOverLife?: Curve


space? ​

optional space?: "3d" | "2d"

Editor-only authoring hint: which preview canvas the Particle Editor shows for this asset ('2d' = PixiJS, '3d' = Three.js) and which property sections it exposes. It does NOT affect runtime rendering — a live emitter renders in 2D iff it has a Canvas2D ancestor (the same rule as Renderable2D), and in 3D otherwise. Default '3d'. Stamped '2d' when a particle is created under a Canvas2D node.


startColor ​

startColor: RGB


startLifetime ​

startLifetime: MinMax


startOpacity? ​

optional startOpacity?: number


startRotation? ​

optional startRotation?: MinMax


startSize ​

startSize: MinMax


startSpeed ​

startSpeed: MinMax


subEmitters? ​

optional subEmitters?: SubEmitter[]

nested effects spawned on parent-particle birth/death (depth-1)


trail? ​

optional trail?: TrailConfig

motion trail drawn from each particle's recent position history


version ​

version: 1


worldSpace ​

worldSpace: boolean

true = particles persist in world space after emission; false = follow the emitter

Built with Modoki.