API reference

Every public class, method, and event in @codexo/exojs. Generated from source.

C

classParticleSystem

@codexo/exojs-particles / particles / stable

The central coordinator of the particle pipeline. `ParticleSystem` is a Drawable that owns: - **SoA particle storage** — one typed array per attribute (position, velocity, scale, rotation, color, lifetime, ...), sized to a fixed capacity at construction. User code reads/writes via `system.posX[slot]`, `system.velX[slot]`, etc. - **Spawn modules** — write new particles into freshly allocated slots. - **Update modules** — mutate the live range each frame (forces, color blends, scale curves, drag, ...). Built-in modules ship both CPU and WGSL implementations; custom modules can opt into GPU acceleration by implementing `wgsl()`. - **Death modules** — fire once per dying particle, before its slot is recycled (sub-emitters, event hooks). **Auto-routing CPU vs GPU:** at first update, the system checks: if a `WebGpuBackend` was supplied AND every registered update module has `wgsl()`, the GPU path engages — a composite compute pipeline runs integration plus all module bodies in one dispatch and writes directly into the renderer's instance buffer (no CPU readback). Otherwise the CPU path runs the existing per-module `apply()` loops. **Per-frame order in update (CPU mode):** 1. Run every spawn module. 2. Integrate position from velocity, rotation from rotationSpeed, advance `elapsed`. 3. Run every update module on the live range. 4. Compact: scan `[0, liveCount)` forward, fire death modules on expired slots, copy survivors down. `liveCount` shrinks to the survivor count. **Per-frame order in update (GPU mode):** 1. Run every spawn module (CPU writes initial values into the spawn slot). 2. Detect expiries on CPU (via `elapsed >= lifetime`); fire death modules; set `lifetime[slot] = -1` sentinel + clear `alive[slot]` so the GPU shader skips them. **No compaction** — slots are recycled on next spawn. 3. Dispatch the composite compute pipeline. Integration + update modules + pack-instances run in one pass; the instance buffer is written directly. CPU SoA stays as-is for spawn writes. **Coordinate space:** particle positions are LOCAL to the system. The system's `getGlobalTransform()` is applied on top during rendering — both the WebGL2 and WebGPU shaders multiply `projection * translation * rotated`. Setting world-space positions on individual particles double-translates. Position the system itself via `system.setPosition(...)` and emit relative to `(0, 0)`.

55
props
45
methods
9
events
Import
import { ParticleSystem } from '@codexo/exojs-particles'

The central coordinator of the particle pipeline. `ParticleSystem` is a Drawable that owns:

- **SoA particle storage** — one typed array per attribute (position, velocity, scale, rotation, color, lifetime, ...), sized to a fixed capacity at construction. User code reads/writes via `system.posX[slot]`, `system.velX[slot]`, etc. - **Spawn modules** — write new particles into freshly allocated slots. - **Update modules** — mutate the live range each frame (forces, color blends, scale curves, drag, ...). Built-in modules ship both CPU and WGSL implementations; custom modules can opt into GPU acceleration by implementing `wgsl()`. - **Death modules** — fire once per dying particle, before its slot is recycled (sub-emitters, event hooks).

**Auto-routing CPU vs GPU:** at first update, the system checks: if a `WebGpuBackend` was supplied AND every registered update module has `wgsl()`, the GPU path engages — a composite compute pipeline runs integration plus all module bodies in one dispatch and writes directly into the renderer's instance buffer (no CPU readback). Otherwise the CPU path runs the existing per-module `apply()` loops.

**Per-frame order in update (CPU mode):** 1. Run every spawn module. 2. Integrate position from velocity, rotation from rotationSpeed, advance `elapsed`. 3. Run every update module on the live range. 4. Compact: scan `[0, liveCount)` forward, fire death modules on expired slots, copy survivors down. `liveCount` shrinks to the survivor count.

**Per-frame order in update (GPU mode):** 1. Run every spawn module (CPU writes initial values into the spawn slot). 2. Detect expiries on CPU (via `elapsed >= lifetime`); fire death modules; set `lifetime[slot] = -1` sentinel + clear `alive[slot]` so the GPU shader skips them. **No compaction** — slots are recycled on next spawn. 3. Dispatch the composite compute pipeline. Integration + update modules + pack-instances run in one pass; the instance buffer is written directly. CPU SoA stays as-is for spawn writes.

**Coordinate space:** particle positions are LOCAL to the system. The system's `getGlobalTransform()` is applied on top during rendering — both the WebGL2 and WebGPU shaders multiply `projection * translation * rotated`. Setting world-space positions on individual particles double-translates. Position the system itself via `system.setPosition(...)` and emit relative to `(0, 0)`.

Constructors 4
new(options?: ParticleSystemOptions): ParticleSystem
new(texture: Texture, options?: ParticleSystemOptions): ParticleSystem
new(texture: Texture, frames: readonly Rectangle[], options?: ParticleSystemOptions): ParticleSystem
new(spritesheet: Spritesheet, options?: ParticleSystemOptions): ParticleSystem
Methods 45
_invalidateBoundsCascade(): void
_invalidateChildrenTransform(): void
_invalidateSubtreeTransform(): void
addDeathModule(mod: DeathModule): this
addFilter(filter: Filter): this
addSpawnModule(mod: SpawnModule): this
addUpdateModule(mod: UpdateModule): this
clearDeathModules(): this
clearFilters(): this
clearParticles(): this
clearSpawnModules(): this
clearUpdateModules(): this
collidesWith(target: Collidable): CollisionResponse | null
contains(x: number, y: number): boolean
destroy(): void
getBounds(): Rectangle
getGlobalTransform(): Matrix
getLocalBounds(): Rectangle
getNormals(): Vector[]
getTransform(): Matrix
intersectsWith(target: Collidable): boolean
invalidateCache(): this
inView(view: View): boolean
move(x: number, y: number): this
project(axis: Vector, result: Interval): Interval
removeFilter(filter: Filter): this
render(backend: RenderBackend): this
resetTextureFrame(): this
rotate(degrees: number): this
setAnchor(x: number, y: number): this
setBlendMode(blendMode: BlendModes): this
setOrigin(x: number, y: number): this
setPosition(x: number, y: number): this
setRotation(degrees: number): this
setScale(x: number, y: number): this
setSkew(x: number, y: number): this
setTexture(texture: Texture): this
setTextureFrame(frame: Rectangle): this
setTint(color: Color): this
spawn(): number
update(delta: Time): this
updateBounds(): this
updateParentTransform(): this
updateTransform(): this
setInternalSpriteFactory(factory: object | null): void
Properties 55
alive: Uint8Array
capacity: number
clip: boolean
clipShape: Rectangle | Geometry | null
collisionType: CollisionType
color: Uint32Array
cursor: string | null
draggable: boolean
elapsed: Float32Array
flags: Flags<SceneNodeTransformFlags>
lifetime: Float32Array
liveCount: number
posX: Float32Array
posY: Float32Array
preserveDrawOrder: boolean
rotations: Float32Array
rotationSpeeds: Float32Array
scaleX: Float32Array
scaleY: Float32Array
textureIndex: Uint16Array
velX: Float32Array
velY: Float32Array
aliveCount: number
anchor: ObservableVector
blendMode: BlendModes
cacheAsBitmap: boolean
cullable: boolean
deathModules: readonly DeathModule[]
filters: readonly Filter[]
frames: readonly Rectangle[]
gpuMode: boolean
gpuState: ParticleGpuState | null
hasAtlas: boolean
interactive: boolean
isAlignedBox: boolean
mask: MaskSource
origin: ObservableVector
parent: Container | null
pixelSnapMode: PixelSnapMode
position: ObservableVector
rotation: number
scale: ObservableVector
skewX: number
skewY: number
spawnModules: readonly SpawnModule[]
texCoords: Uint32Array
texture: Texture
textureFrame: Rectangle
tint: Color
updateModules: readonly UpdateModule[]
vertices: Float32Array
visible: boolean
x: number
y: number
zIndex: number
Events 9
onDrag: Signal<[InteractionEvent]>
onDragEnd: Signal<[InteractionEvent]>
onDragStart: Signal<[InteractionEvent]>
onPointerDown: Signal<[InteractionEvent]>
onPointerMove: Signal<[InteractionEvent]>
onPointerOut: Signal<[InteractionEvent]>
onPointerOver: Signal<[InteractionEvent]>
onPointerTap: Signal<[InteractionEvent]>
onPointerUp: Signal<[InteractionEvent]>
Source