API reference

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

C

classTrailParticles

@codexo/exojs-particles / particles / stable

A motion trail behind every particle: each one drags a strip through the positions it recently occupied, and all of them are drawn in a single non-instanced draw. Where `RibbonParticles` connects the particles of one system into one band, this connects each particle to its own past - the shape a swarm of sparks, tracer rounds or comet debris wants, where every element needs its own streak. Layout of the per-vertex buffer build fills (20 bytes, 3 attributes): ``` a_position f32x2 (offset 0, 8 bytes) strip vertex (system-local) a_texcoord f32x2 (offset 8, 8 bytes) u along the trail, v across it a_color u8x4 (offset 16, 4 bytes) RGBA tint, alpha faded by age ``` **History is recorded on the particle's own clock.** A position is appended to a particle's ring buffer once its age has advanced by TrailParticlesOptions.interval, so a trail covers the same span of travel whatever the frame rate; the live position is always drawn as the head, which keeps the strip attached to the particle between two samples. **One instance per system.** The history is per-particle state carried across frames and addressed by simulation slot, so two systems sharing one instance would overwrite each other's trails. **The strips are built on the CPU**, so this mode is not GPU-eligible and a system using it stays on the CPU simulation path (observable through `ParticleSystem.gpuMode`). That is also what lets the history survive a death: CPU-mode slots are dense and compaction copies survivors forward stably, which is what build re-aligns the history rows against. It identifies a particle by its age and its lifetime, so an update module that rewrites a live particle's lifetime restarts that particle's trail.

8
props
5
methods
0
events
Import
import { TrailParticles } from '@codexo/exojs-particles'

A motion trail behind every particle: each one drags a strip through the positions it recently occupied, and all of them are drawn in a single non-instanced draw.

Where `RibbonParticles` connects the particles of one system into one band, this connects each particle to its own past - the shape a swarm of sparks, tracer rounds or comet debris wants, where every element needs its own streak.

Layout of the per-vertex buffer build fills (20 bytes, 3 attributes):

``` a_position f32x2 (offset 0, 8 bytes) strip vertex (system-local) a_texcoord f32x2 (offset 8, 8 bytes) u along the trail, v across it a_color u8x4 (offset 16, 4 bytes) RGBA tint, alpha faded by age ```

**History is recorded on the particle's own clock.** A position is appended to a particle's ring buffer once its age has advanced by TrailParticlesOptions.interval, so a trail covers the same span of travel whatever the frame rate; the live position is always drawn as the head, which keeps the strip attached to the particle between two samples.

**One instance per system.** The history is per-particle state carried across frames and addressed by simulation slot, so two systems sharing one instance would overwrite each other's trails.

**The strips are built on the CPU**, so this mode is not GPU-eligible and a system using it stays on the CPU simulation path (observable through `ParticleSystem.gpuMode`). That is also what lets the history survive a death: CPU-mode slots are dense and compaction copies survivors forward stably, which is what build re-aligns the history rows against. It identifies a particle by its age and its lifetime, so an update module that rewrites a live particle's lifetime restarts that particle's trail.

Constructors1
Methods5
_ensureCapacity(byteLength: number): void
Grow the scratch buffer to hold at least byteLength. Grow-only: a shrinking particle count reuses the larger buffer rather than reallocating, matching the renderers' existing buffer policy.
_onBufferGrown(data: ArrayBuffer): void
Re-create typed-array views after _ensureCapacity reallocates.
_setCount(count: number): void
Record the element count produced by a build.
Fill the scratch buffer from the system's live particles. particles is the simulation's own channel storage, handed in rather than read off the system: a render mode is the one consumer that legitimately works in bulk, and it runs at the point in the frame where those values are the ones being drawn.
destroy(): void
Optional cleanup, called from ParticleSystem.destroy.
Properties8
The draw covers whatever build emitted this frame. Non-indexed by construction: the strip's own vertex order is its topology, and an index list would pin the draw to a fixed index count.
floatsPerVertex: number
Floats spanned by one vertex. Exposed so callers reading data can step through it without hard-coding the stride.
gpuEligible: boolean
Whether this mode can run while the system is in GPU compute mode. Mirrors UpdateModule.wgsl(): a mode that cannot forces the whole system onto the CPU path, silently and observably via ParticleSystem.gpuMode. A GPU-eligible mode's layout must match what the compute pipeline emits into gpuState.instanceBuffer - the shared 40-byte per-instance layout (instanceAttributes). QuadParticles and MeshParticles both declare exactly that layout and differ only in the shape they expand it into, which is why both are eligible without the compute shader knowing either.
instanced: false
Draw model. true issues an instanced draw, false a plain draw over the built vertex buffer. Declared here because a layout carries topology but has no instancing concept.
Fixed per-vertex geometry this mode instances, or null when it derives its vertices in the shader from the vertex index - which is what QuadParticles and RibbonParticles do. When set, it supplies the topology, index list and index count for the draw in place of dataLayout's, and the executors bind its vertexData as a second vertex buffer stepping per vertex beside the per-instance one. Only meaningful on an instanced mode: without instancing both buffers would step per vertex and no draw is expressible. Attribute names must not collide with dataLayout's, since both sets bind into the same shader. Mutating the geometry is picked up on the next draw through its version, so invalidate() after an in-place edit is enough to reach the GPU.
count: number
Element count of the draw call this mode's last build produced. Draw-model relative: instance count when instanced, vertex count otherwise. Do not assume one meaning.
data: ArrayBuffer
The buffer the renderer uploads. Valid until the next build.
Source