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.