sim/particles/ParticleChemistry

Summary: ParticleChemistry.js - Chemical Reaction System (GAP 38) GPU compute: valence-electron driven bond formation/breaking. Bonds form when electronegativity difference permits, release/absorb energy that feeds directly into the thermal system. Features: - Valence electron tracking per particle (from element table) - Bond formation: electronegativity difference → ionic vs covalent - Reaction energy ΔH → feeds thermal buffer - Temperature-dependent reaction rate (Arrhenius: k = A·e^(-Ea/RT)) - Bond dissociation at high temperature - Uses neighbor grid (GAP 25) for spatial queries - Outputs bond pairs to CPU for constraint creation (GAP existing) Two-pass approach: Pass 1 (GPU): scan neighbors, compute bond eligibility, write bond candidates Pass 2 (CPU): read back candidates, create constraints, update valence Usage: const chem = createChemistrySystem(device, maxParticles); initChemistryBindGroups(chem, device, posBuffer, velBuffer, gridBuffers, elementTable); executeChemistry(chem, device, particleCount, dt);


createChemistrySystem

Summary: Create the chemistry system.


initChemistryBindGroups

initChemistryBindGroups(system, device, positionBuffer, velocityBuffer, gridBuffers, elementTable, thermalBuffer)

Summary: Initialize bind groups.


initParticleValence

initParticleValence(system, device, elementData, startIndex, count)

Summary: Initialize valence electrons for a range of particles based on their element.


executeChemistry

executeChemistry(system, device, particleCount, dt)

Summary: Execute chemistry compute (bond formation + dissociation).


getValenceBuffer

Summary: Get the valence buffer for external use.


destroyChemistrySystem

Summary: Destroy.


sim/particles/ParticleClassifier

Summary: ParticleClassifier.js - GPU Particle Classification for Fluid Rendering Classifies each liquid particle into categories based on SPH density and velocity. Used by renderers to route particles to different visual styles: - Bulk fluid → SSFR surface rendering (smooth continuous surface) - Surface → SSFR + edge highlight - Spray → Small SDF billboard with fading alpha - Foam → White-tinted SDF billboard on surface - Bubble → Tiny transparent rising sphere Output: per-particle u32 classification buffer (binding for vertex shaders). Classification IDs: 0 = unclassified / non-liquid 1 = bulk (dense interior) 2 = surface (boundary) 3 = spray (isolated, fast) 4 = foam (isolated, slow, near surface) 5 = bubble (isolated, rising) Usage: const cls = createClassifierSystem(device, maxParticles); initClassifierBindGroups(cls, device, posBuffer, velBuffer, thermalBuffer, sphDensityBuffer); executeClassifier(cls, device, particleCount); // cls.classBuffer is a storage buffer readable by vertex shaders


createClassifierSystem

Summary: Create the classifier system.


initClassifierBindGroups

initClassifierBindGroups(system, device, positionBuffer, velocityBuffer, thermalBuffer, sphDensityBuffer)

Summary: Initialize bind groups.


executeClassifier

Summary: Execute the classification compute pass.


getClassificationBuffer

Summary: Get the classification buffer for use by renderers.


setClassifierParams

Summary: Update classifier thresholds at runtime.


destroyClassifierSystem

Summary: Destroy classifier system.


sim/particles/ParticleColorGradient

Summary: ParticleColorGradient.js - N-point color gradient for particles Niagara/PopcornFX parity: arbitrary color ramps over particle lifetime. Replaces the 2-point linear (color → colorEnd) with full gradient control. CPU builds a 1D RGBA LUT texture, GPU samples with t = age/lifetime. Usage: const grad = createColorGradient([ { t: 0.0, color: [1, 0.6, 0.1, 0.0] },  // orange, transparent { t: 0.2, color: [1, 0.8, 0.2, 1.0] },  // bright yellow, full { t: 0.7, color: [0.8, 0.2, 0.0, 0.8] }, // red { t: 1.0, color: [0.3, 0.3, 0.3, 0.0] }, // grey smoke, fade out ]); const texture = uploadGradientTexture(device, grad);


createColorGradient

createColorGradient(stops, resolution = 64)

Summary: Create a color gradient from an array of color stops Each color is [r, g, b] or [r, g, b, a] with values 0-1


sampleGradient

Summary: Sample a color gradient at a given t value


uploadGradientTexture

Summary: Upload a color gradient as a 1D GPU texture for shader sampling


destroyGradientTexture

Summary: Destroy a gradient texture


gradientFromLegacy

gradientFromLegacy(color, colorEnd, alphaOverLife = [1, 1, 1])

Summary: Convert a legacy color/colorEnd pair to a gradient


sim/particles/ParticleCompression

Summary: ParticleCompression.js - Compression utilities for particle snapshots Extracted from ParticleSnapshotDelta.js for modularity Includes: - Run-length encoding - Variable-length integer packing - PCA + DCT compression - Position/velocity quantization - Interpolation algorithms


catmullRomInterpolate

catmullRomInterpolate(p0, p1, p2, p3, t, tension = 0.5)


sim/particles/ParticleConfig


sim/particles/ParticleConstraints

Summary: ParticleConstraints.js - GPU Constraint Solver for Rope/Wire/Cloth Unified constraint system for particle-based physics: - Distance constraints (maintain rest length between particles) - Attachment constraints (pin particle to world position) - Angle constraints (maintain angle between 3 particles) [future] - Bending constraints (maintain curvature) [future] Part of the Unified Rope-Particle System (Phase 2)


createRopeConstraintSystem

createRopeConstraintSystem(device, maxConstraints = 100000)

Summary: Create rope/constraint solver system


initRopeConstraintBindGroup

initRopeConstraintBindGroup(system, device, positionsBuffer, velocitiesBuffer, thermalBuffer)

Summary: Initialize constraint system bind group


addDistanceConstraint

addDistanceConstraint(system, device, particleA, particleB, restLength, stiffness = 0.9)

Summary: Add a distance constraint between two particles


addAttachmentConstraint

addAttachmentConstraint(system, device, particleIndex, worldPos, stiffness = 1.0)

Summary: Add an attachment constraint (pin to world position)


addRopeInextensibleConstraint

addRopeInextensibleConstraint(system, device, particleA, particleB, restLength, opts = {})

Summary: Add a rope inextensible constraint (mass-aware, with breaking support)


updateAttachmentTarget

updateAttachmentTarget(system, device, constraintIndex, worldPos)

Summary: Update an attachment constraint's target position


updateRopeConstraintPhysics

updateRopeConstraintPhysics(system, device, constraintIndex, opts)

Summary: Update rope constraint physics properties


addBendingConstraint

addBendingConstraint(system, device, particleA, particleB, particleC, restAngle = Math.PI, opts = {})

Summary: Add a bending/angle constraint between 3 particles - UNTESTED Maintains angle at center particle (particleB)


addSpringConstraint

addSpringConstraint(system, device, particleA, particleB, restLength, opts = {})

Summary: Add a spring constraint - UNTESTED Unlike distance, springs can compress AND extend (Hooke's law)


addStickyConstraint

addStickyConstraint(system, device, particleA, target, opts = {})

Summary: Add a sticky/adhesion constraint - UNTESTED Particle sticks to a world position or another particle


addCohesionConstraint

addCohesionConstraint(system, device, particleA, particleB, opts = {})

Summary: Add a cohesion constraint - UNTESTED Particles attract each other within a radius (like water droplets)


addCollisionConstraint

addCollisionConstraint(system, device, particleA, particleB, opts = {})

Summary: Add a collision constraint - UNTESTED Particle-particle collision with friction and restitution (bounce)


addWeldConstraint

addWeldConstraint(system, device, particleA, particleB, restDistance = 0)

Summary: Add a weld constraint - UNTESTED Permanently joins two particles (rigid connection)


applyMaterialPreset

applyMaterialPreset(system, device, presetName, constraintIndices)

Summary: Apply a material preset to particle constraints - UNTESTED


createRopeConstraints

createRopeConstraints(system, device, particleStart, particleCount, opts = {})

Summary: Create all constraints for a rope


solveRopeConstraints

solveRopeConstraints(system, device, iterations = 8, dt = 1/60, opts = {})

Summary: Solve all constraints with rope physics options


clearRopeConstraints

Summary: Clear all constraints for a rope


destroyRopeConstraintSystem

Summary: Destroy constraint system


sim/particles/ParticleDecalSpawner

Summary: ParticleDecalSpawner.js - Spawn Decals from Particle Collisions (GAP 22) Consumes GPU event readback data (collision events) to spawn projected texture decals at impact points. Integrates with the GPU event system (GAP 6) via CPU readback callbacks. Each collision event provides: position, velocity (impact direction), and event type. The spawner creates decal descriptors that the game's decal system can render (projected quads aligned to surface normal). Usage: const spawner = createDecalSpawner({ maxDecals: 200 }); // Register with event system: onParticleEvent(world.eventSystem, (events) => { feedCollisionEvents(spawner, events); }); // Each frame: const decals = getActiveDecals(spawner); // Render decals with your decal system


createDecalSpawner

Summary: Create a decal spawner with a fixed-size ring buffer pool.


feedCollisionEvents

feedCollisionEvents(spawner, eventData, eventCount, options = {})

Summary: Feed collision events from GPU event readback into the decal spawner. Events are expected to be Float32Array with 8 floats per event: [posX, posY, posZ, velX, velY, velZ, eventType, particleIndex]


updateDecals

Summary: Update decal ages, fade, and expire old decals.


getActiveDecals

Summary: Get all active decals as an array of descriptors.


getDecalCount

Summary: Get active decal count.


clearDecals

Summary: Clear all decals.


destroyDecalSpawner

Summary: Destroy the spawner.


sim/particles/ParticleEffects

Summary: Create force point buffer for attractors/repellers Each force point: [x, y, z, strength] where negative = repel


createForcePointBuffer

createForcePointBuffer(device, maxPoints = MAX_FORCE_POINTS)


updateForcePoints

Summary: Update force points in the buffer


calculateDissolve

calculateDissolve(startTime, duration, direction = 'in')

Summary: Calculate dissolve factor for fade in/out


processSubEmitters

Summary: Process sub-emitter spawning from dead particles This CPU fallback scans all slots and has 1-2 frame latency from GPU readback.


createTrailBuffer

createTrailBuffer(device, maxParticles, historyLength = 8)

Summary: Create trail history buffer Stores N previous positions per particle for trail rendering


generateParticleAtlas

generateParticleAtlas(device, size = 512, tilesPerRow = 4)

Summary: Generate a procedural particle texture atlas


Summary: Sub-emitter configuration


Summary: Particle texture atlas configuration


sim/particles/ParticleElectromagnetic

Summary: ParticleElectromagnetic.js - Electromagnetic Forces (GAP 35) GPU compute: Coulomb electrostatic + Lorentz magnetic force. F_coulomb = k · q₁·q₂ / r²  (along r) F_lorentz = q · (v × B)       (perpendicular to v and B) Per-particle charge from element table (GAP 33) charge buffer. Optional: sample E/H fields from FDTDSolver.js if attached. Uses neighbor grid (GAP 25) for O(N) spatial queries. Debye shielding at high density to prevent runaway forces. Usage: const em = createElectromagneticSystem(device, maxParticles); initEMBindGroups(em, device, positionBuffer, velocityBuffer, gridBuffers, chargeBuffer); setExternalField(em, { Ex, Ey, Ez, Bx, By, Bz }); executeElectromagnetic(em, device, particleCount, dt);


createElectromagneticSystem

Summary: Create the electromagnetic system.


initEMBindGroups

initEMBindGroups(system, device, positionBuffer, velocityBuffer, gridBuffers, chargeBuffer, thermalBuffer)

Summary: Initialize bind groups.


setExternalField

Summary: Set external E and B fields.


executeElectromagnetic

executeElectromagnetic(system, device, particleCount, dt)

Summary: Execute the electromagnetic compute pass.


destroyElectromagneticSystem

Summary: Destroy.


sim/particles/ParticleElementRegistry

Summary: ParticleElementRegistry.js - Unified Element Registry Bridges the two previously disconnected element systems: - Particle ELEMENTS (fire, water, magic, smoke) from ParticleEmitterSystem.js - Spell SPELL_ELEMENTS (fire, ice, lightning, arcane, nature, dark, holy) from SpellGenerator.js Each unified element defines particle properties (color, materialIndex, temperature, physics modifiers), spell properties (spellColors, effects, damageType), and combination rules (from SpellElementMixer). Also provides bridge functions: - elementMixToEmitterConfig(elements[]) → full emitter config derived from mix - getElementCombination(a, b) → combination result


setRegistryTrace

setRegistryTrace(enabled, throttleMs = 500)

Summary: Enable or disable element registry trace logging. When enabled, logs every element mix derivation with full pipeline details.


getRegistryElement

Summary: Get a registry element by ID. Handles backward-compat aliases (e.g., "magic" → "arcane"). Falls back to REACTION_PRODUCTS for byproduct IDs (e.g., "smoke").


isPrimaryElement

Summary: Check if an element ID is a primary element (user-selectable) vs reaction product.


getAllRegistryElements

Summary: Get all registry elements as an array.


getRegistryElementsByCategory

Summary: Get element IDs grouped by category.


getElementCombination

Summary: Look up the combination result for two elements.


getCombinationsFor

Summary: Get all combinations involving a specific element.


elementMixToEmitterConfig

Summary: Given an array of active element layers [{id, power, enabled}], derive a complete emitter property set: color, colorEnd, temperature, materialIndex, gravityMod, and combination info. This replaces the separate deriveColorFromElements + deriveTemperatureFromElements + deriveMaterialIndexFromElements calls with one unified function.


metaSlidersToPhysics

Summary: Convert meta-slider values (Intensity 0-1, Spread 0-1, Lifetime 0-1) into concrete emitter physics parameters. Used by Simple Mode to provide easy controls.


elementMixToSpellParams

Summary: Convert an active element mix into spell generator parameters. Used by the "Create Spell From This" button.


sim/particles/ParticleElementTable

Summary: ParticleElementTable.js - Element Property System / Periodic Table (GAP 33) Data foundation for universal matter simulation. Every other physics system (Lennard-Jones, Coulomb, SPH, Chemistry) reads element properties from here. Provides: - Full periodic table data for 118 elements - Per-particle element buffer (GPU storage, u32 per particle) - Element lookup by atomic number or symbol - Common molecule/compound presets - LJ parameters (epsilon, sigma) per element - Integration with existing thermal material system Usage: const table = createElementTable(device, maxParticles); setParticleElement(table, device, particleIndex, ELEMENTS.Fe); const iron = getElement(26); // by atomic number const gold = getElementBySymbol('Au');


getElement

Summary: Get element by atomic number (1-118).


getElementBySymbol

Summary: Get element by symbol ('H', 'Fe', 'Au', etc.).


getAllElements

Summary: Get all elements.


ljMixingRule

Summary: Compute Lennard-Jones cross-interaction parameters using Lorentz-Berthelot rules.


createElementTable

Summary: Create the element table system with per-particle element buffer.


setParticleElements

setParticleElements(system, device, startIndex, count, atomicNumber)

Summary: Set element type for a range of particles.


setParticleCharges

setParticleCharges(system, device, startIndex, count, charge)

Summary: Set charge for a range of particles (overrides element default).


destroyElementTable

Summary: Destroy the element table system.


ELEMENTS

Summary: Quick access: ELEMENTS.H = 1, ELEMENTS.Fe = 26, etc.


Summary: Common molecule presets — arrays of { element, count, charge }.


Summary: WGSL struct and accessor for element LUT. Bind elementLUT as storage buffer, elementTypes and charges as storage buffers.


sim/particles/ParticleEmitterLOD

Summary: ParticleEmitterLOD.js - Emitter-Level LOD System (GAP 19) Distance/screen-size based LOD for particle emitters. Unlike per-particle quality fade, this operates at the EMITTER level: - Reduce spawn rate by LOD tier - Disable expensive features (collision, turbulence) at distance - Switch renderer (billboard → point → cull) by distance - Scale particle lifetime to reduce total particle count Matches Niagara scalability / PopcornFX LOD layers. Usage: const lod = createEmitterLOD({ tiers: [...] }); // Each frame: const tier = evaluateEmitterLOD(lod, cameraPos, emitterPos); // Use tier.spawnRateScale, tier.features, etc. to drive emitter config


createEmitterLOD

Summary: Create an emitter LOD controller.


evaluateEmitterLOD

evaluateEmitterLOD(lod, cameraPos, emitterPos, dt = 1/60)

Summary: Evaluate LOD tier for an emitter based on camera distance. Returns the active tier config with interpolated values.


evaluateEmitterLODByScreenSize

Summary: Evaluate LOD by screen-space size instead of distance. Useful for effects that vary in world scale.


getCurrentLODTier

Summary: Get the current LOD tier without re-evaluation.


setForcedLODTier

Summary: Force a specific LOD tier.


sim/particles/ParticleEmitterSystem

Summary: Resolve a substance string to { atomicNumber, temperature, charge, element }. Accepts element symbols ("Fe"), compound keys ("H2O"), preset names ("fire"), or null.


deriveState

Summary: Derive the state of matter from temperature and element melt/boil points.


derivePhysicsProfile

Summary: Derive which physics systems an emitter needs based on substance + temperature. Returns a physics profile object.


deriveColorFromElements

Summary: Derive color from element layers (weighted blend)


deriveMaterialIndexFromElements

Summary: Derive materialIndex from element layers (picks highest-power element's material)


getStates

Summary: Get available states of matter


getElements

Summary: Get available elements


getPresetList

Summary: Get all presets grouped by category for UI consumption.


applyPreset

Summary: Apply a preset to an emitter config object, mutating it in place. Sets substance, temperature, charge, emitter type, elements, and physics.


getEmitterTypes

Summary: Get available emitter type IDs.


getEmitterPreset

Summary: Get emitter type preset by ID.


buildEffectKeyFromElements

buildEffectKeyFromElements(elementPower, options = {})


computeVisualPower

computeVisualPower(elementPower, options = {})


createEmittersForEffect

createEmittersForEffect(effectId, options = {})


createEmitter

Summary: ============================================================================ CREATE PARTICLE EMITTER ============================================================================ Creates an emitter instance from a preset type. The emitter controls all aspects of its particles: visual appearance, physics, and timing.


stepEmitters

stepEmitters(emitters, particleWorld, dt, options = {})

Summary: Step all emitters and emit particles into the particle world. Returns the number of new particles emitted.


updateParticleCounts

Summary: Update particle counts by scanning for dead particles. Adds dead slots to freeSlots array and updates live/dead counts. Call this periodically (e.g., every 0.5s) to refresh counts.


ageAndCullParticles

ageAndCullParticles(particles, hiddenDuration, options = {})

Summary: Age all particles by a given duration and cull those that died. Call this when resuming from hidden/minimized to skip dead particles.


initSlotTracking

Summary: Initialize slot tracking for a particle system. Call once after creating particles state.


buildColliderBuffer

buildColliderBuffer(particleWorld, entities, options = {})

Summary: Build AABB collider buffer from ECS entities for particle collisions. Returns the number of colliders written.


buildFluidSources

buildFluidSources(emitters, options = {})

Summary: Build fluid sources from emitters for density splatting.


sim/particles/ParticleEulerianFluid

Summary: ParticleEulerianFluid.js - Self-Contained Eulerian Fluid Solver (GAP 32) GPU grid-based 3D fluid simulation using the Stable Fluids algorithm: 1. Advect velocity field (semi-Lagrangian) 2. Apply external forces (buoyancy, source injection) 3. Compute divergence 4. Pressure solve (Jacobi iteration) 5. Subtract pressure gradient (make divergence-free) 6. Advect density/temperature fields Particles can sample this velocity field via the existing fluidVelocity binding in ParticleSimWorld (attachFluidWorld). Matches Niagara Fluids Grid3D Gas simulation. Usage: const fluid = createEulerianFluidSolver(device, { gridSize: [32, 32, 32] }); addFluidSource(fluid, { position: [0, 0, 0], density: 1, temperature: 500 }); // Each frame: stepEulerianFluid(fluid, device, dt); // Connect to particles: attachFluidWorld(particleWorld, fluid, worldMin, worldMax);


createEulerianFluidSolver

createEulerianFluidSolver(device, config = {})

Summary: Create a self-contained Eulerian fluid solver.


initFluidBindGroups

Summary: Build bind groups (call once after creation).


addFluidSource

Summary: Add a fluid source (emitter of density/temperature/velocity).


stepEulerianFluid

Summary: Step the fluid simulation.


destroyEulerianFluidSolver

Summary: Destroy the solver.


sim/particles/ParticleEventSpawn

Summary: ParticleEventSpawn.js - GPU-driven sub-emitter spawning from particle events Exceeds Niagara parity: Niagara events are CPU-only, this is fully GPU. Zero-frame latency — sub-particles appear the same frame as the trigger event. Pipeline: 1. Main sim shader appends events to eventBuffer (death, collision, kill zone) 2. IndirectDispatch builds freeList of dead particle slot indices 3. THIS SHADER reads events + consumes free slots + writes new particles Each workgroup handles one event. Threads within the workgroup spawn sub-particles. Free slots are consumed atomically from the free list (no CPU involvement).


createEventSpawnSystem

createEventSpawnSystem(device, options = {})

Summary: Create the GPU event spawn system


initEventSpawnBindGroups

Summary: Initialize bind groups for the event spawn system


setEventSpawnConfig

setEventSpawnConfig(system, config = {})

Summary: Update sub-emitter spawn configuration


executeEventSpawn

Summary: Execute the event spawn compute pass Call AFTER stepParticleSimWorld (events generated) and AFTER executeIndirectScan (free list built)


destroyEventSpawnSystem

Summary: Destroy event spawn system


sim/particles/ParticleEventSystem

Summary: ParticleEventSystem.js - GPU Particle Event System (exceeds Niagara parity) Niagara events are CPU-only. This system is fully GPU-driven with zero-frame latency. Architecture: 1. Main sim shader appends events to eventBuffer (death, collision, kill zone) 2. IndirectDispatch builds freeList of dead particle slot indices 3. EventSpawn compute shader reads events + consumes free slots + writes new particles 4. (Optional) CPU readback for gameplay callbacks (sound, damage, etc.) Usage: const evtSys = initParticleEventSystem(world); registerEventHandler(evtSys, { event: 'death', subCount: 4, speed: 2 }); // In render loop (after stepParticleSimWorld + executeIndirectScan): stepEventSystem(evtSys, world); // Optional async readback for gameplay: const events = await readbackEvents(evtSys);


initParticleEventSystem

initParticleEventSystem(world, options = {})

Summary: Initialize the GPU particle event system on a particle world. Call after createParticleSimWorld + initAllAdvancedSystems (needs indirectSystem.freeList).


registerEventHandler

registerEventHandler(system, config = {})

Summary: Register/update the sub-emitter event handler configuration.


onParticleEvent

Summary: Register a CPU callback for a specific event type (called after readback)


offParticleEvent

Summary: Remove all callbacks for an event type


stepEventSystem

Summary: Step the event system: execute GPU event spawn. Call AFTER stepParticleSimWorld + executeIndirectScan.


readbackEvents

Summary: Async readback of particle events from GPU for gameplay callbacks. Returns array of events. Tolerates 1-2 frame latency.


getLastEvents

Summary: Get the last readback events (non-async, returns cached result)


sim/particles/ParticleFMM

Summary: Uniform-octree Fast Multipole Method for open-boundary gravitational forces. The GPU path is deliberately self-contained and opt-in. It performs the full FMM sequence P2M -> M2M -> M2L -> L2L -> L2P + P2P. The first implementation uses monopole source expansions and first-order Cartesian local expansions; the direct solver remains the accuracy oracle and small-N fallback.


createFmmSystem

createFmmSystem(device, maxParticles, options = {})


initFmmBindGroups

initFmmBindGroups(system, device, positionBuffer, velocityBuffer, metaBuffer, thermalBuffer,)


executeFmm

executeFmm(system, device, particleCount, dt, options = {})


sim/particles/ParticleFlocking

Summary: ParticleFlocking.js - GPU Flocking / Boids System (GAP 27) Dedicated GPU compute shader implementing classic boid rules: 1. Separation — steer away from nearby neighbors 2. Alignment — match velocity direction of nearby neighbors 3. Cohesion — steer toward center of mass of nearby neighbors 4. Goal seeking — steer toward target position(s) 5. Obstacle avoidance — steer away from repulsion points 6. Speed regulation — maintain target speed range Uses the NeighborGrid (GAP 25) for O(N) spatial queries instead of O(N²). Each force has independent radius + weight for fine-tuned behavior. Matches PopcornFX CParticleEvolver_Flocking. Usage: const flock = createFlockingSystem(device, maxParticles); initFlockingBindGroups(flock, device, positionBuffer, velocityBuffer, gridBuffers); setFlockingParams(flock, { separationWeight: 1.5, alignmentWeight: 1.0, ... }); // Each frame (after neighbor grid is built): executeFlocking(flock, device, particleCount, dt);


createFlockingSystem

Summary: Create the flocking system.


initFlockingBindGroups

initFlockingBindGroups(system, device, positionBuffer, velocityBuffer, gridBuffers)

Summary: Initialize bind groups. Requires neighbor grid buffers.


setFlockingParams

Summary: Update flocking parameters.


executeFlocking

executeFlocking(system, device, particleCount, dt)

Summary: Execute the flocking compute pass.


destroyFlockingSystem

Summary: Destroy the system.


sim/particles/ParticleFxApi


spawnParticleEmitterEntity

spawnParticleEmitterEntity(world, options = {})


sim/particles/ParticleIndirectDispatch

Summary: ParticleIndirectDispatch.js - GPU-driven indirect draw + dispatch Replaces CPU-side particle count tracking for rendering and compute dispatch. A small "scan alive" compute pass runs once per frame, atomically counting alive particles and finding the highest alive slot. A "finalize" micro-pass writes the results into draw indirect and dispatch indirect buffers. Benefits: - Eliminates CPU→GPU sync for particle counts - drawIndirect uses GPU-computed instance count - dispatchWorkgroupsIndirect uses GPU-computed workgroup count - Prepares infrastructure for full alive list compaction (Phase 3) Ref: Wicked Engine GPU particle architecture, UE5 Niagara indirect dispatch


createIndirectDispatchSystem

createIndirectDispatchSystem(device, maxParticles, workgroupSize = 256)

Summary: Create the indirect dispatch system.


initIndirectDispatchBindGroups

initIndirectDispatchBindGroups(system, device, positionsBuffer, velocitiesBuffer)

Summary: Initialize bind groups. Call after position/velocity buffers are available.


executeIndirectScan

executeIndirectScan(system, device, options = {})

Summary: Execute the scan + finalize passes. Run ONCE per frame, before any other particle work. After this call, drawIndirectBuffer and dispatchIndirectBuffer contain GPU-computed values.


updateIndirectMaxSlots

Summary: Update maxSlots (e.g., when particle capacity changes).


destroyIndirectDispatchSystem

Summary: Destroy indirect dispatch system resources.


sim/particles/ParticleLennardJones

Summary: ParticleLennardJones.js - Lennard-Jones Intermolecular Potential (GAP 34) GPU compute shader implementing the Lennard-Jones 12-6 potential: V(r) = 4ε[(σ/r)¹² - (σ/r)⁶] F(r) = 24ε/r [2(σ/r)¹² - (σ/r)⁶] This single equation creates realistic solid/liquid/gas behavior: - r < σ: strong repulsion (Pauli exclusion) - r = σ·2^(1/6): equilibrium (minimum energy) - r > σ·2^(1/6): weak attraction (van der Waals) - r > 2.5σ: effectively zero (cutoff) Per-element ε and σ from element table (GAP 33). Cross-element interactions use Lorentz-Berthelot mixing rules. Uses neighbor grid (GAP 25) for O(N) spatial queries. Usage: const lj = createLennardJonesSystem(device, maxParticles); initLJBindGroups(lj, device, positionBuffer, velocityBuffer, gridBuffers, elementTable); executeLennardJones(lj, device, particleCount, dt);


createLennardJonesSystem

Summary: Create the Lennard-Jones system.


initLJBindGroups

initLJBindGroups(system, device, positionBuffer, velocityBuffer, gridBuffers, elementTable, thermalBuffer)

Summary: Initialize bind groups. Requires neighbor grid + element table buffers.


executeLennardJones

executeLennardJones(system, device, particleCount, dt)

Summary: Execute the Lennard-Jones compute pass.


destroyLennardJonesSystem

Summary: Destroy the system.


sim/particles/ParticleLifetimeCurves

Summary: ParticleLifetimeCurves.js - GPU N-point Bezier curves for particle properties over lifetime Extends the color gradient LUT pattern to size, alpha, and velocity. Generates a 1D RGBA16float texture (256 texels): R = size multiplier G = alpha multiplier B = velocity multiplier A = reserved (1.0) Sampled in vertex shader (size), fragment shader (alpha), and optionally sim shader (velocity). Supports N-point curves with Catmull-Rom interpolation for smooth results.


Summary: Convert a legacy 3-point array [start, mid, end] to a curve.


generateCurveLUT

Summary: Generate the RGBA LUT data (Float16 or Float32 depending on use).


createCurveLUTTexture

createCurveLUTTexture(device, config = {})

Summary: Create a GPU 1D LUT texture for lifetime curves.


updateCurveLUTTexture

updateCurveLUTTexture(device, texture, config = {})

Summary: Update an existing LUT texture with new curve data.


createLifetimeCurvesSystem

createLifetimeCurvesSystem(device, config = {})

Summary: Create a lifetime curves system for a particle world.


setLifetimeCurves

Summary: Update curves at runtime.


destroyLifetimeCurvesSystem

Summary: Destroy the system.


sim/particles/ParticleLightEmission

Summary: ParticleLightEmission.js - Particles as dynamic point lights Niagara/PopcornFX parity: emissive particles contribute point lights to the scene. Pipeline: 1. GPU compute shader finds the top-N brightest/hottest particles 2. Results are read back to CPU as an array of point light descriptors 3. Any lighting system can consume these as dynamic lights This avoids tight coupling to a specific lighting implementation. The consumer (LightingPass, voxel lighting, etc.) reads the light array each frame.


createParticleLightSystem

createParticleLightSystem(device, options = {})

Summary: Create particle light emission system


initParticleLightBindGroup

Summary: Initialize bind group (call once when particle buffers are ready)


extractParticleLights

Summary: Execute the light extraction compute pass


readbackParticleLights

Summary: Read back extracted lights from GPU (async, call after command buffer submit) Returns array of { position: [x,y,z], radius, color: [r,g,b], intensity }


getParticleLights

Summary: Get the current particle lights (non-async, returns last readback result)


destroyParticleLightSystem

Summary: Destroy particle light system


sim/particles/ParticleLocalSpace

Summary: ParticleLocalSpace.js - Local/World Space Toggle (GAP 30) Per-emitter transform for local-space simulation. When enabled, particles are simulated in the emitter's local coordinate frame and transformed to world space only for rendering. Moving the emitter moves all its particles. When disabled (default), particles are emitted in world space and the emitter can move without affecting existing particles. GPU compute shader transforms between local and world space using the emitter's model matrix each frame. Matches PopcornFX CParticleEvolver_Localspace. Usage: const ls = createLocalSpaceSystem(device, maxParticles); initLocalSpaceBindGroups(ls, device, positionBuffer, velocityBuffer); setEmitterTransform(ls, modelMatrix); // Before sim step: transform world→local executeWorldToLocal(ls, device, particleCount); // ... run simulation in local space ... // After sim step: transform local→world executeLocalToWorld(ls, device, particleCount);


createLocalSpaceSystem

Summary: Create local space transform system.


initLocalSpaceBindGroups

initLocalSpaceBindGroups(system, device, positionBuffer, velocityBuffer)

Summary: Initialize bind groups.


setEmitterTransform

Summary: Set the emitter transform (4x4 column-major matrix). Automatically computes the inverse.


setEmitterTransformFromPosRot

Summary: Set emitter transform from position + rotation (Euler angles in radians).


executeWorldToLocal

Summary: Transform particles from world space to local space (run before sim step).


executeLocalToWorld

Summary: Transform particles from local space to world space (run after sim step).


destroyLocalSpaceSystem

Summary: Destroy the system.


sim/particles/ParticleLongRangeMath

Summary: Pure configuration, layout, and validation math shared by long-range particle solvers.


fmmSelectDepth

fmmSelectDepth(particleCount, options = {})


generateProlateWindowTable

Summary: Samples the positive, even, order-zero PSWF. Inverse iteration solves the spheroidal Sturm-Liouville operator in an orthonormal even-Legendre basis; the GPU linearly interpolates the resulting half-window.


generateProlateSplitTable

Summary: Builds the normalized radial density and complementary local kernel used by the PSWF Ewald split. The density integrates to one on [0, 1], while the complement falls from one to zero at the real-space cutoff.


prolateSplitFourierResponse

Summary: Cosine transform of the normalized even PSWF splitting kernel.


directLongRangeAccelerations

directLongRangeAccelerations(positions, sources, options = {})


sim/particles/ParticleMeshEwald

Summary: Periodic particle-mesh Ewald solver with cubic B-spline PME and an experimental numerically sampled prolate-spheroidal (ESP) split and window. The solver uses exact integer cell counting/scatter, a separable 3D complex FFT, reciprocal-space screened Poisson solve, interpolated mesh gradients, and the complementary short-range Ewald force.


createParticleMeshEwaldSystem

createParticleMeshEwaldSystem(device, maxParticles, options = {})


initParticleMeshEwaldBindGroups

initParticleMeshEwaldBindGroups(system, device, positionBuffer, velocityBuffer, metaBuffer, thermalBuffer,)


setParticleMeshEwaldParams

setParticleMeshEwaldParams(system, config = {})


executeParticleMeshEwald

executeParticleMeshEwald(system, device, particleCount, dt, options = {})


sim/particles/ParticleNBody

Summary: ParticleNBody.js - N-Body Gravitational Dynamics (GAP 37) GPU compute: every particle attracts every other via gravity. F = G · m₁·m₂ / (r² + ε²)^(3/2) Direct summation O(N²) — suitable for up to ~100K particles on modern GPUs. Softening parameter ε prevents singularities at close range. Configurable G for different scales (molecular, macro, celestial). Uses tiled shared-memory approach: each workgroup loads a tile of particles into shared memory, computes interactions, then loads next tile. Usage: const nb = createNBodySystem(device, maxParticles); initNBodyBindGroups(nb, device, positionBuffer, velocityBuffer, metaBuffer); executeNBody(nb, device, particleCount, dt);


createNBodySystem

Summary: Create the N-body gravity system.


initNBodyBindGroups

initNBodyBindGroups(system, device, positionBuffer, velocityBuffer, metaBuffer, thermalBuffer)

Summary: Initialize bind groups.


setNBodyParams

Summary: Set N-body parameters at runtime.


executeNBody

executeNBody(system, device, particleCount, dt, options = {})

Summary: Execute the N-body compute pass.


destroyNBodySystem

Summary: Destroy.


sim/particles/ParticleNeighborGrid

Summary: ParticleNeighborGrid.js - General-Purpose Neighbor Grid (GAP 25) GPU spatial query structure for arbitrary particle-to-particle interactions. Unlike the collision-specific spatial grid, this is a general-purpose 3D grid that any system can query: flocking, color transfer, interaction, etc. Matches Niagara Neighbor Grid 3D and PopcornFX Spatial Layers. Architecture (hash-modulo bucket approach — inspired by NVIDIA CUDA Particles): 1. Clear cell counts via encoder.clearBuffer (GPU memset, no dispatch limit) 2. Insert particles into hash buckets (compute pass, atomic append) 3. Query neighbors from any other compute pass via grid lookup Instead of allocating one bucket per 3D cell (can exceed millions), we use numBuckets = min(totalCells, maxParticles). The flat cell index is modulo'd into this smaller range. Hash collisions are harmless — consumer shaders already do distance checks that filter false positives. Grid stores up to maxNeighborsPerCell particle indices per bucket. Query returns indices of particles in same + adjacent cells (27-cell neighborhood).


createNeighborGridSystem

createNeighborGridSystem(device, config = {})

Summary: Create a general-purpose neighbor grid.


initNeighborGridBindGroups

initNeighborGridBindGroups(system, device, positionBuffer, velocityBuffer)

Summary: Initialize bind groups (call after particle buffers are created).


setNeighborGridParticleCount

Summary: Update the particle count in params (call each frame if count changes).


buildNeighborGrid

Summary: Execute clear + insert passes. Call once per frame before any queries.


getNeighborGridBuffers

Summary: Get the grid buffers for use in consumer compute shaders. Consumer shaders should bind cellCountsBuffer and cellEntriesBuffer as read-only storage.


destroyNeighborGridSystem

Summary: Destroy the system.


sim/particles/ParticleNoiseTexture

Summary: ParticleNoiseTexture.js - Pre-baked 3D curl noise volume texture Replaces expensive per-particle gradientNoise calls with one filtered sample from a two-pass vector-potential -> discrete-curl 3D texture bake. 6-24× cheaper turbulence depending on octave count. The noise is tileable: baked with periodic boundary conditions and sampled with repeat addressing mode for seamless spatial tiling. Ref: GPU Gems 3 Ch.1 "Generating Complex Procedural Terrains Using the GPU"


createNoiseTextureSystem

async createNoiseTextureSystem(device, resolution = 64, period = DEFAULT_NOISE_PERIOD)

Summary: Create the 3D noise texture system.


bakeNoiseTexture

Summary: Bake the noise volume texture. Call once after creation. This is a one-time GPU operation (~1ms for 64³, ~8ms for 128³).


createDummyNoiseTexture

Summary: Create a 1×1×1 dummy noise texture for use when the real texture isn't ready. Returns { textureView, sampler, paramsBuffer } suitable for bind group creation.


destroyNoiseTextureSystem

Summary: Destroy noise texture system resources.


sim/particles/ParticleRadixSort

Summary: ParticleRadixSort.js - GPU 4-way radix sort for particle distance sorting Replaces bitonic sort (O(n log²n) dispatches) with radix sort (O(n), fixed 8 passes). For 100K particles: bitonic = ~289 dispatches, radix = ~24 dispatches (12× fewer). Architecture: - 4-bit radix: 16 buckets per pass, 8 passes for 32-bit keys - Float-to-sortable-uint conversion for correct float ordering - Per-pass: histogram → prefix sum → scatter (3 dispatches) - Double-buffered keys+values for ping-pong between passes - "Order check" optimization: skip sort when data is already sorted Ref: "Fast 4-way parallel radix sorting on GPUs" (Ha et al.) kishimisu/WebGPU-Radix-Sort


createRadixSortSystem

Summary: Create GPU radix sort system for transparent particle rendering. Drop-in replacement for the bitonic sort system.


executeRadixSort

executeRadixSort(system, device, particleCount, cameraPos)

Summary: Execute GPU radix sort. Drop-in replacement for executeGPUSort.


sim/particles/ParticleReactionTable

Summary: ParticleReactionTable.js - Cross-Emitter Particle Reaction System GPU-driven reaction table: when particles with different materialIdx collide, the collision shader scans this table for a matching (matA, matB) rule. If found: apply energy, optionally kill reactants, emit reaction event (type 5). EventSpawn reads the rule index to spawn product particles with correct properties. Exceeds Niagara/PopcornFX/Unity VFX Graph — none support particle-particle reactions. Usage: const table = createReactionTable(device); addReaction(table, MATERIAL.FIRE, MATERIAL.WATER, { ... }); registerDefaultReactions(table); // Bind table.buffer to sim shader group


createReactionTable

Summary: Create a reaction table with GPU uniform buffer.


destroyReactionTable

Summary: Destroy the reaction table and release GPU resources.


addReaction

addReaction(table, matA, matB, config = {})

Summary: Add a reaction rule to the table.


removeReaction

Summary: Remove a reaction rule by index.


clearReactions

Summary: Remove all reaction rules.


uploadReactionTable

Summary: Upload the reaction table to the GPU uniform buffer. Call once after adding/removing reactions (not every frame).


registerDefaultReactions

Summary: Register built-in reaction presets.


getReactions

Summary: Get the current reaction rules.


getMaterialName

Summary: Get a material name from its ID.


getMaterialForSubstance

Summary: Look up the material ID for a substance preset key.


sim/particles/ParticleRibbonTrail

Summary: ParticleRibbonTrail.js - GPU ribbon/trail rendering for particles Connects particle position history into continuous camera-facing triangle strips for smoke trails, magic effects, and motion streaks. Architecture: 1. Trail Update (compute): shifts history ring buffer back, inserts current position 2. Ribbon Render (vertex+fragment): reads trail history, generates camera-facing triangle strip with width tapering and alpha fade Each particle trail is rendered as one triangle strip instance. Vertex count per instance = historyLength × 2. No intermediate vertex buffer needed — the vertex shader reads history directly. Ref: GPU Gems 3 Ch.23, UE5 Niagara ribbon renderer


createRibbonTrailSystem

createRibbonTrailSystem(device, maxParticles, historyLength = 8, targetFormat = 'bgra8unorm', options = {})

Summary: Create ribbon trail system.


initRibbonTrailBindGroups

initRibbonTrailBindGroups(system, device, positionsBuffer, velocitiesBuffer)

Summary: Initialize bind groups for the trail system. Call after the position buffer is available.


updateTrailHistory

Summary: Update trail history for all particles (compute pass). Call once per frame AFTER the main sim step.


renderRibbonTrails

renderRibbonTrails(pass, system, viewProjMatrix, cameraPos, instanceCount)

Summary: Render ribbon trails into the current render pass.


destroyRibbonTrailSystem

Summary: Destroy ribbon trail system resources.


sim/particles/ParticleSDFAttraction

Summary: ParticleSDFAttraction.js - SDF Attraction/Repulsion Force (GAP 15) Attract or repel particles toward/from SDF surfaces. Unlike SDF collision (hard boundary), this applies a smooth force field based on signed distance, so particles orbit, hover near, or flee from surfaces. Force = -gradient(SDF) × strength / (|SDF| + softness) Modes: - attract: particles pulled toward surface (strength > 0) - repel: particles pushed away from surface (strength < 0) - orbit: attract + tangential velocity (strength > 0, orbitalFactor > 0) - surface-lock: very strong attraction with damping (particles stick to surface) Uses a GPU compute shader that evaluates SDF primitives and applies forces.


createSDFAttractionSystem

Summary: Create an SDF attraction system.


addSDFAttractor

Summary: Add an SDF attractor.


updateSDFAttractor

Summary: Update an existing attractor.


initSDFAttractionBindGroup

initSDFAttractionBindGroup(system, device, positionBuffer, velocityBuffer)

Summary: Initialize bind group (call after particle buffers are created).


executeSDFAttraction

executeSDFAttraction(system, device, particleCount, dt = 1/60, time = 0)

Summary: Execute SDF attraction compute pass.


clearSDFAttractors

Summary: Clear all attractors.


destroySDFAttractionSystem

Summary: Destroy the system.


sim/particles/ParticleSPH

Summary: ParticleSPH.js - Smoothed Particle Hydrodynamics (GAP 36) True Lagrangian particle-based fluid simulation where particles ARE the fluid. Complements Eulerian grid solver (GAP 32) for free-surface liquids. Two-pass GPU compute: Pass 1 (density): estimate density at each particle via Poly6 kernel Pass 2 (forces):  pressure (Spiky), viscosity, surface tension, XSPH correction Navier-Stokes in SPH form: ρᵢ = Σⱼ mⱼ W(rᵢ-rⱼ, h)           (density) aᵢ = -Σⱼ mⱼ(Pᵢ/ρᵢ² + Pⱼ/ρⱼ²)∇W  (pressure) aᵢ += μ Σⱼ mⱼ(vⱼ-vᵢ)/ρⱼ ∇²W      (viscosity) Per-element rest density and viscosity from element table (GAP 33). Uses neighbor grid (GAP 25) for O(N) spatial queries. Usage: const sph = createSPHSystem(device, maxParticles); initSPHBindGroups(sph, device, posBuffer, velBuffer, gridBuffers); executeSPH(sph, device, particleCount, dt);


createSPHSystem

Summary: Create the SPH fluid system.


initSPHBindGroups

initSPHBindGroups(system, device, positionBuffer, velocityBuffer, gridBuffers, thermalBuffer)

Summary: Initialize bind groups. Requires neighbor grid buffers.


setSPHParams

Summary: Set SPH parameters at runtime.


executeSPH

executeSPH(system, device, particleCount, dt)

Summary: Execute the two-pass SPH compute.


getSPHDensityBuffer

Summary: Get the density/pressure buffer for use by surface renderers.


destroySPHSystem

Summary: Destroy.


sim/particles/ParticleSchema

Summary: Pack meta.w value from individual components (full format with physics) Format: mass * 1e8 + drag * 1e6 + size * 1e4 + renderMode * 1e3 + shape * 10 + behavior


packParticleMeta

packParticleMeta(size, renderMode, shape, behavior = 0, mass = 1.0, drag = 0.02)


unpackParticleMeta

Summary: Unpack meta.w value to individual components


getBehaviorId

Summary: Get behavior ID from behavior name


validateField

Summary: Validate a value against a schema field


validateEmitterConfig

Summary: Validate an emitter config against schema


createDefaultEmitterConfig

Summary: Create a default emitter config


createDefaultParticleParams

Summary: Create a default particle params uniform buffer data


createEmptySnapshot

Summary: Create an empty particle snapshot


createEmitterSnapshot

Summary: Create an emitter snapshot from live emitter state


getShapeId

Summary: Get shape ID from shape name


getRenderModeId

Summary: Get render mode ID from mode name


getShapeName

Summary: Get shape name from ID


getRenderModeName

Summary: Get render mode name from ID


serializeEmitterConfig

Summary: Serialize emitter config to JSON-safe object


deserializeEmitterConfig

Summary: Deserialize emitter config from JSON


serializeSnapshot

Summary: Serialize particle snapshot to JSON-safe object


deserializeSnapshot

Summary: Deserialize particle snapshot from JSON


Summary: Per-particle data layout in GPU buffers. Each particle uses 3 vec4s (48 bytes total).


Summary: Frame uniforms passed to all particle shaders


Summary: Per-draw particle parameters


Summary: Force parameters uniform (group 2 binding 0 in main particle compute shader) Controls global wind, turbulence, force points, vortex, and velocity clamping.


Summary: Shape types for particle rendering


Summary: Render modes for particle blending


Summary: Behavior types for particle movement patterns


Summary: Complete emitter configuration schema


Summary: Element layer for emitter mixing


Summary: Single particle snapshot entry


Summary: Emitter state snapshot


Summary: Complete particle system snapshot


sim/particles/ParticleSharedMemCollision

Summary: ParticleSharedMemCollision.js - Workgroup shared memory particle collision Drop-in replacement for the grid collision `collide` pass that uses workgroup shared memory to eliminate random global memory reads. Architecture: - Uses the same grid infrastructure (clearGrid + binParticles are unchanged) - Dispatches one workgroup PER grid cell (cell-centric) - All threads in a workgroup share the same cell → same neighbor cells - Neighbor particles loaded into shared memory in tiles of TILE_SIZE - Each thread checks its particle against shared memory (fast L1/register access) Memory access improvement: - Old: Each thread reads random global positions/velocities for neighbors - New: Workgroup cooperatively loads sequential global reads → shared memory Then all threads read from shared memory (100× faster than global) Uses the SAME bind group layout as the existing grid collision system, so the existing bind group can be reused directly. Ref: GPU Gems 3 Ch.31 "Fast N-Body Simulation with CUDA"


createSharedMemCollidePipeline

createSharedMemCollidePipeline(device, bindGroupLayout, tileSize = 256)

Summary: Create the shared-memory collide pipeline. This pipeline uses the SAME bind group layout as the existing grid collision system.


dispatchSharedMemCollide

dispatchSharedMemCollide(pipeline, bindGroup, pass, gridDims)

Summary: Execute the shared-memory collide pass. Dispatches one workgroup per grid cell. Uses the existing grid bind group.


sim/particles/ParticleSimWorld

Summary: Initialize spatial grid and line buffers for advanced particle features. Call this after createParticleSimWorld to enable line connections and neighbor lookups.


createParticleSimWorld

async createParticleSimWorld(gpuDevice, options = {})


initAdvancedParticleBuffers

initAdvancedParticleBuffers(world, options = {})


initAllAdvancedSystems

async initAllAdvancedSystems(world, options = {})

Summary: Initialize all advanced particle systems (sorting, wire constraints, SDF collision, etc.) Call after createParticleSimWorld and initAdvancedParticleBuffers.


createHeatConductionSystem

Summary: Create heat conduction + phase transition compute system. Requires spatial grid to be initialized (gridCountsBuffer, gridIndicesBuffer).


stepHeatConduction

Summary: Execute heat conduction compute pass.


stepAdvancedSystems

stepAdvancedSystems(world, dt, options = {})

Summary: Execute all active advanced particle systems in the simulation step.


renderWorldRibbonTrails

renderWorldRibbonTrails(pass, world, viewProjMatrix, cameraPos, instanceCount)

Summary: Render ribbon trails into the given render pass. Call during the render loop AFTER particle rendering.


sortParticlesForRendering

Summary: Execute GPU sorting for transparent particle rendering. Call before rendering particles back-to-front. Respects world.sortFrameInterval to skip sorting on intermediate frames (e.g. interval=2 sorts every other frame, reducing GPU cost for large counts).


addParticleWire

addParticleWire(world, particleA, particleB, restLength, stiffness = 0.5)

Summary: Add a wire constraint between two particles.


createGPURope

createGPURope(world, startPos, endPos, segments, opts = {})

Summary: Create a GPU-simulated rope with particles and constraints


uploadParticleVelocities

Summary: Upload particle velocities to GPU buffer


updateRopeAttachment

updateRopeAttachment(world, entityId, which, worldPos)

Summary: Update attachment constraint target position


destroyGPURope

Summary: Destroy a GPU rope and release its resources


addParticleCollider

addParticleCollider(world, type, center, radius, halfExtents)

Summary: Add an SDF collider to the particle system.


clearParticleColliders

Summary: Clear all SDF colliders (call before per-frame rebuild).


rebuildParticleColliders

Summary: Batch-rebuild SDF colliders from an array of descriptors. Call each frame with entity SDF shapes for proper mesh collision.


seedParticles

seedParticles(world, count, options = {})

Summary: Seed particles on GPU with random positions/velocities.


setParticleAtlasMode

Summary: Set texture atlas sprite assignment mode.


initSnapshotSystem

Summary: Initialize snapshot system for time rewind.


updateSnapshots

Summary: Auto-capture snapshots during simulation (call each frame).


captureSnapshot

Summary: Manually take a snapshot.


beginRewind

Summary: Start time rewind mode.


endRewind

Summary: Stop time rewind and resume simulation.


updateRewind

Summary: Step rewind playback (call each frame while rewinding).


rewindToTime

Summary: Seek to a specific time in history.


rewindToFrame

Summary: Restore a specific frame from history.


setRewindSpeed

Summary: Set rewind playback speed (-1 = reverse, 1 = forward, 0.5 = slow, etc.)


getRewindInfo

Summary: Get snapshot/rewind status info.


isRewinding

Summary: Check if currently in rewind mode.


clearSnapshots

Summary: Clear all snapshot history.


updateAdaptiveQuality

Summary: Update adaptive quality based on frame time. Automatically adjusts activeParticleCount to maintain target FPS. TECHNIQUES FROM AAA GAME INDUSTRY (DRS Best Practices): 1. Frame-rate independent exponential smoothing 2. Spike filtering - only reduce after sustained poor performance 3. Panic mode - aggressive drop when critically over budget 4. Fast recovery - don't be over-cautious when headroom exists 5. Proportional response - bigger adjustments when further from target 6. Target slightly under budget for safety margin


getParticleStats

Summary: Get particle system stats for performance overlay.


readbackPositionsFromGPU

async readbackPositionsFromGPU(world, targetPositions, particleCount)


readbackVelocitiesFromGPU

async readbackVelocitiesFromGPU(world, targetVelocities, particleCount)


readbackThermalFromGPU

async readbackThermalFromGPU(world, targetThermal, particleCount)


attachFluidWorld

attachFluidWorld(particleWorld, fluidWorld, worldMin, worldMax)

Summary: Attach a fluid world to the particle simulation. Particles will sample and follow the fluid velocity field.


stepParticleSimWorld

stepParticleSimWorld(world, deltaSeconds, options = {})


createLineRenderPipeline

Summary: Create line rendering pipeline for particle connections


createLineComputePipelines

Summary: Create line compute pipelines for spatial grid and line generation


createLineUniformBuffer

Summary: Create line uniform buffer for compute and render


initLineRenderingSystem

Summary: Initialize complete line rendering system


stepLineGeneration

Summary: Execute line generation compute passes


renderParticleLines

Summary: Render particle connection lines


updateMouseInteraction

updateMouseInteraction(ndcX, ndcY, active = true)

Summary: Update mouse state for particle interaction


getMouseState

Summary: Get current mouse state


addForcePoint

Summary: Add an attractor or repeller force point


removeForcePoint

Summary: Remove a force point by index


clearForcePoints

Summary: Clear all force points


getForcePoints

Summary: Get all force points


updateForcePoint

updateForcePoint(index, x, y, z, strength)

Summary: Update force point position


attachWindSystem

Summary: Attach a WindSystem instance to the particle world. Once attached, stepParticleSimWorld will automatically read wind params each frame.


detachWindSystem

Summary: Detach wind system from particle world


updateKillZones

Summary: Update kill zones for a particle world. Particles inside kill zones are killed.


clearKillZones

Summary: Clear all kill zones


startDissolve

startDissolve(groupId, duration = 1.0, direction = 'out')

Summary: Start a dissolve effect for a particle group


getDissolve

Summary: Get dissolve factor for a group (0 = invisible, 1 = fully visible)


sampleCurlNoise

sampleCurlNoise(x, y, z, scale = 1, time = 0)

Summary: Sample curl noise at a position (CPU version for debugging)


initTrailSystem

initTrailSystem(world, historyLength = 8)

Summary: Initialize trail history buffer for particles


registerSubEmitter

Summary: Register a sub-emitter configuration


getSubEmitterConfig

Summary: Get sub-emitter config for a particle type


clearSubEmitters

Summary: Clear all sub-emitter configs


configureParticleLongRange

configureParticleLongRange(world, config = {})

Summary: Select exactly one long-range force backend without changing the legacy default.


ensureSystemEnabled

Summary: Lazily enable a particle system on an existing world if not already initialized. Call from editor/runtime when an emitter's physics profile requires a system that wasn't enabled at init time.


warmUpParticleWorld

warmUpParticleWorld(world, warmUpSeconds = 2.0, options = {})

Summary: Pre-simulate a particle world for N seconds before the first visible frame. Runs stepParticleSimWorld repeatedly with fixed timestep so effects look "established" immediately (e.g. fire already burning, smoke already rising).


reserveParticles

reserveParticles(particleWorld, count, backend = 'free')

Summary: Reserve a range of particles for a specific backend


releaseParticles

Summary: Release reserved particles (e.g., when rope is destroyed)


registerRopeChain

Summary: Register a rope chain in the particle system


unregisterRopeChain

Summary: Unregister a rope chain from the particle system


getTotalReservedParticles

Summary: Get total particle count across all backends


sim/particles/ParticleSnapshot

Summary: Create particle snapshot system for time rewind


createSnapshotSystem

createSnapshotSystem(device, maxParticles, options = {})


initSnapshotBindGroups

initSnapshotBindGroups(snapshot, positionBuffer, velocityBuffer, metaBuffer, thermalBuffer)

Summary: Initialize bind groups with particle buffers


takeSnapshot

Summary: Take a snapshot of current state


autoSnapshot

Summary: Auto-snapshot based on interval (call every frame)


restoreFrame

Summary: Restore exact frame from history


restoreInterpolated

restoreInterpolated(snapshot, emitters, frameA, frameB, t)

Summary: Restore with interpolation between two frames


seekToTime

Summary: Seek to a specific time in history


startRewind

Summary: Start rewind mode


stopRewind

Summary: Stop rewind and resume simulation


stepRewind

Summary: Step rewind playback (call each frame while rewinding)


setPlaybackSpeed

Summary: Set playback speed (-1 = reverse, 1 = forward, 0.5 = slow forward, etc.)


getSnapshotInfo

Summary: Get current playback info


clearHistory

Summary: Clear all history


destroySnapshotSystem

Summary: Destroy snapshot system


sim/particles/ParticleSnapshotDelta

Summary: ParticleSnapshotDelta.js - Delta-Compressed Particle State History Like the WorldTimeline system, uses delta compression to store only changes: - Keyframes: Full state snapshots at intervals - Delta frames: Only particles that moved significantly - No max frame limit - unlimited history with efficient storage Compression approach: - Track which particles changed since last frame - Store only changed particle indices + their new state - Periodic keyframes for random access


hashParticleKeyframeSemantic

hashParticleKeyframeSemantic(frame, sampleVec4Count = 1024)


createDeltaSnapshotSystem

Summary: Create delta-compressed particle snapshot system


captureFrame

async captureFrame(snapshot, particleState, emitters, currentTimeMs)

Summary: Capture a snapshot (keyframe or delta based on interval) Captures EVERYTHING: positions, velocities, meta, slotInfo, liveCount, freeSlots


autoCapture

async autoCapture(snapshot, particleState, emitters, currentTimeMs)

Summary: Auto-capture based on interval - NON-BLOCKING version Uses double-buffered GPU readback to avoid blocking the main thread


reconstructState

Summary: Reconstruct FULL particle state at a specific frame by applying deltas from keyframe Includes positions, velocities, meta, slotInfo, liveCount, freeSlots - everything needed for complete rewind Handles both quantized (Int16) and unquantized (Float32) data formats


restoreToFrame

restoreToFrame(snapshot, particleState, emitters, frameIndex)

Summary: Restore FULL particle state to a specific frame


restoreInterpolated

restoreInterpolated(snapshot, particleState, emitters, frameA, frameB, t)


seekToTime

seekToTime(snapshot, particleState, emitters, targetTimeMs)

Summary: Seek to specific time


startRewind

Summary: Start rewind mode


stopRewind

Summary: Stop rewind


stepRewind

stepRewind(snapshot, particleState, emitters, dtMs)

Summary: Step rewind playback - restores FULL particle state including spawn/death


setPlaybackSpeed

Summary: Set playback speed


setEasingType

Summary: Set easing type for interpolation


getEasingTypes

Summary: Get available easing types


getSnapshotInfo

Summary: Get snapshot info


clearHistory

Summary: Clear all history


sim/particles/ParticleSoftContainment

Summary: ParticleSoftContainment.js - Soft Containment Volumes (GAP 31) GPU compute shader that keeps particles inside shape volumes using soft bounce forces instead of hard kill zones. Particles approaching the boundary receive a proportional repulsion force pushing them back inside. Supports: sphere, box, cylinder containment volumes. Unlike kill zones (hard removal), soft containment preserves particles and creates natural-looking bounded effects. Matches PopcornFX CParticleEvolver_Containment. Usage: const cont = createSoftContainmentSystem(device, maxParticles); addContainmentVolume(cont, { type: 'sphere', center: [0,5,0], radius: 10 }); initContainmentBindGroups(cont, device, positionBuffer, velocityBuffer); executeSoftContainment(cont, device, particleCount, dt);


createSoftContainmentSystem

Summary: Create the soft containment system.


addContainmentVolume

Summary: Add a containment volume.


initContainmentBindGroups

initContainmentBindGroups(system, device, positionBuffer, velocityBuffer)

Summary: Initialize bind groups.


executeSoftContainment

executeSoftContainment(system, device, particleCount, dt)

Summary: Execute the soft containment compute pass.


clearContainmentVolumes

Summary: Clear all volumes.


destroySoftContainmentSystem

Summary: Destroy the system.


sim/particles/ParticleSpawnShapes

Summary: ParticleSpawnShapes.js - Spawn shape primitives for particle emission Niagara/PopcornFX/Houdini parity: spawn particles in structured shapes instead of just point + random spread. Each function returns { position: [x,y,z], normal: [nx,ny,nz] } Normal points outward from the shape surface — useful for initial velocity direction. ⚠ MULTIPLAYER: Uses deterministic RNG from AIRandom.js


spawnSphere

spawnSphere(center, radius, surfaceOnly = false)

Summary: Spawn inside or on a sphere


spawnHemisphere

spawnHemisphere(center, radius, axis = [0, 1, 0], surfaceOnly = false)

Summary: Spawn on a hemisphere (oriented along axis)


spawnBox

Summary: Spawn inside or on a box (AABB)


spawnBoxCenter

spawnBoxCenter(center, halfExtents, surfaceOnly = false)

Summary: Spawn inside or on a box defined by center + half-extents


spawnCylinder

spawnCylinder(center, axis, radius, height, surfaceOnly = false)

Summary: Spawn inside or on a cylinder


spawnCone

spawnCone(apex, axis, angle, height, surfaceOnly = false)

Summary: Spawn inside or on a cone


spawnRing

spawnRing(center, axis, radius, thickness = 0)

Summary: Spawn on a ring (circle in 3D space)


spawnTorus

spawnTorus(center, axis, majorRadius, minorRadius, surfaceOnly = false)

Summary: Spawn inside or on a torus


spawnLine

Summary: Spawn along a line segment


spawnDisc

spawnDisc(center, normal, radius, innerRadius = 0)

Summary: Spawn on a disc (filled circle in 3D space)


spawnGrid

spawnGrid(origin, axisU, axisV, countU, countV, index, jitter = 0)

Summary: Spawn at grid positions (deterministic placement, good for walls/floors of particles)


spawnFromShape

spawnFromShape(shape, emitterPos, index = 0)

Summary: Spawn a particle using a shape configuration object


velocityFromNormal

velocityFromNormal(normal, speed, spread = 0.2)

Summary: Generate initial velocity from spawn shape result


sim/particles/ParticleSplineFollow

Summary: ParticleSplineFollow.js - Spline/Path Following (GAP 28) GPU compute shader that steers particles along bezier or catmull-rom spline paths. Each particle tracks its progress (t parameter) along the curve and applies a steering force toward the next point on the path. Supports: - Cubic Bezier paths (4 control points per segment) - Catmull-Rom splines (N control points, smooth interpolation) - Per-particle speed along path - Spread: particles can deviate from path center by a configurable radius - Looping and ping-pong modes Control points are uploaded as a storage buffer (max 64 points). Each particle stores its path progress in thermalData.w (repurposed when not using thermal system) or a dedicated progress buffer. Usage: const spline = createSplineFollowSystem(device, maxParticles); setSplineControlPoints(spline, device, points); initSplineFollowBindGroups(spline, device, positionBuffer, velocityBuffer); executeSplineFollow(spline, device, particleCount, dt);


createSplineFollowSystem

Summary: Create the spline follow system.


setSplineControlPoints

Summary: Upload control points (array of [x, y, z]).


initSplineFollowBindGroups

initSplineFollowBindGroups(system, device, positionBuffer, velocityBuffer)

Summary: Initialize bind groups.


setSplineFollowParams

Summary: Update spline follow parameters.


executeSplineFollow

executeSplineFollow(system, device, particleCount, dt)

Summary: Execute the spline follow compute pass.


destroySplineFollowSystem

Summary: Destroy the system.


sim/particles/ParticleSplitting

Summary: ParticleSplitting.js - Adaptive Particle Refinement for Fluid When water stretches thin (low SPH density, high velocity, few neighbors), particles split into smaller child particles. When many small particles cluster in a dense area, they merge back. This is how water naturally breaks apart into spray/droplets and reforms. GPU compute shader scans liquid particles, identifies split candidates, and uses atomic counters to claim dead particle slots for children. Split trigger: density < threshold AND speed > threshold Merge trigger: two particles within close range, both small, low relative velocity Mass conservation: Split: parent shrinks to 0.7× size, child spawns at 0.7× size (2 × 0.7³ ≈ 0.686 volume — slight loss acceptable for stability) Merge: surviving particle grows by merged particle's volume^(1/3) Usage: const split = createSplittingSystem(device, maxParticles); initSplittingBindGroups(split, device, posBuffer, velBuffer, thermalBuffer, metaBuffer, sphDensityBuffer); executeSplitting(split, device, particleCount, dt);


createSplittingSystem

Summary: Create the splitting system.


initSplittingBindGroups

initSplittingBindGroups(system, device, positionBuffer, velocityBuffer, thermalBuffer, metaBuffer, sphDensityBuffer)

Summary: Initialize bind groups.


executeSplitting

executeSplitting(system, device, particleCount, dt)

Summary: Execute the splitting compute pass.


setSplittingParams

Summary: Update splitting parameters at runtime.


destroySplittingSystem

Summary: Destroy splitting system.


sim/particles/ParticleSurfaceProjection

Summary: ParticleSurfaceProjection.js - Surface Projection / Conform (GAP 29) GPU compute shader that projects particles onto shape surfaces. Particles are snapped or attracted to the nearest point on a surface, enabling effects like particles crawling along walls, flowing over terrain, or conforming to mesh silhouettes. Supports analytical shapes (sphere, box, plane, cylinder) on GPU. For arbitrary meshes, uses a signed distance field (SDF) with gradient descent to find the nearest surface point. Matches PopcornFX CParticleEvolver_Projection. Usage: const proj = createSurfaceProjectionSystem(device, maxParticles); addProjectionSurface(proj, { type: 'sphere', center: [0,0,0], radius: 5 }); initProjectionBindGroups(proj, device, positionBuffer, velocityBuffer); executeSurfaceProjection(proj, device, particleCount, dt);


createSurfaceProjectionSystem

Summary: Create the surface projection system.


addProjectionSurface

Summary: Add a projection surface.


initProjectionBindGroups

initProjectionBindGroups(system, device, positionBuffer, velocityBuffer)

Summary: Initialize bind groups.


executeSurfaceProjection

executeSurfaceProjection(system, device, particleCount, dt)

Summary: Execute the surface projection compute pass.


clearProjectionSurfaces

Summary: Clear all surfaces.


destroySurfaceProjectionSystem

Summary: Destroy the system.


sim/particles/ParticleTextureAtlas

Summary: Create texture atlas configuration


createAtlasConfig

Summary: Create texture atlas configuration


createTextureAtlasSystem

createTextureAtlasSystem(device, maxParticles, config = {})

Summary: Create texture atlas system


loadAtlasTexture

async loadAtlasTexture(atlasSystem, device, imageUrl)

Summary: Load texture atlas from image


createProceduralAtlas

createProceduralAtlas(atlasSystem, device, options = {})

Summary: Create procedural atlas texture (for testing)


initAtlasBindGroup

initAtlasBindGroup(atlasSystem, device, positionsBuffer, velocitiesBuffer)

Summary: Initialize sprite assignment bind group


assignSpritesGPU

assignSpritesGPU(atlasSystem, device, particleCount, time)

Summary: Assign sprites to particles via GPU


getAtlasUniforms

Summary: Get atlas uniforms for rendering


setAssignmentMode

Summary: Set sprite assignment mode


destroyTextureAtlasSystem

Summary: Destroy texture atlas system


sim/particles/ParticleVectorField

Summary: Create a filterable 3D vector-field texture compatible with linear sampling.


createVectorFieldTexture

createVectorFieldTexture(device, resolution, label = 'VectorField.texture')


createVectorFieldSystem

createVectorFieldSystem(device, options = {})

Summary: Create a vector field system.


loadVectorFieldFromData

Summary: Load raw velocity data into the vector field.


generateProceduralVectorField

generateProceduralVectorField(system, type = 'tornado')

Summary: Generate a procedural vector field.


setVectorFieldParams

setVectorFieldParams(system, params = {})

Summary: Update vector field parameters.


parseFGA

Summary: Parse FGA format (FluidNinja/Houdini vector field export). FGA is a simple text format: header lines then XYZ velocity per voxel.


destroyVectorFieldSystem

Summary: Destroy the vector field system.


sim/particles/ParticlesStateRegistry


sim/particles/RopeMaterial

Summary: RopeMaterial.js — Thermal & interaction properties for rope/chain fiber materials. Extends the visual-only FIBER_MATERIALS from RopeSchema with physics properties needed for particle↔rope interaction: flammability, moisture absorption, thermal conductivity, burn/break temperatures, mass modifiers. EVERY material (including steel and chain) participates in interactions — metal softens and breaks at high enough temperatures, organic materials burn, all materials conduct heat along the chain. Thresholds differ per material. Interaction types by substance: - Fire/lava/plasma → heat transfer → softening → burning/melting → chain split - Water/blood/honey → moisture absorption → heavier → sag → wet stiffness change - Ice/snow → cooling → stiffening → freeze-brittle - Acid → corrosion → integrity loss → chain split - Sparks/debris → minor heat transfer


getSubstanceInteraction

Summary: Map particle substance to interaction type and intensity. Returns null if the substance doesn't interact with rope.


createRopeNodeState

createRopeNodeState(nodeCount, ambientTemp = 293)

Summary: Create per-node interaction state for a rope.


getRopeInteractionMaterial

Summary: Get the interaction material for a fiber material name. Falls back to hemp if unknown.


sim/particles/RopeParticleInteraction

Summary: RopeParticleInteraction.js — CPU-side per-tick bridge between free particles and ropes. Runs each simulation frame and performs: 1. Spatial proximity query: find free particles near each rope node 2. Heat transfer: fire/lava/plasma → rope heats up; ice/snow → rope cools 3. Moisture absorption: water/blood/honey near rope → rope gets heavier, sags 4. Corrosion: acid particles eat away rope integrity 5. Thermal conduction: heat spreads along the rope between adjacent nodes 6. Burning: when node temperature exceeds burn threshold → ignite, char, break 7. Chain splitting: when integrity reaches 0 → break constraint → 2 separate chains Works with ALL materials including steel/chain — metal softens and eventually breaks at high enough temperatures (lava, plasma). Thresholds differ per material. This is a CPU-side system because it needs to: - Make topology decisions (break constraints, split chains) - Spawn new emitters (fire/smoke on burning rope) - Modify per-constraint mass values (moisture → heavier)


createRopeInteractionSystem

Summary: Create the rope↔particle interaction system. Attach to the particle world and call stepRopeInteraction() each tick.


registerRopeForInteraction

registerRopeForInteraction(system, entityId, ropeMetadata, fiberMaterial, ambientTemp = 293)

Summary: Register a rope for interaction tracking. Call this after createGPURope.


unregisterRopeInteraction

Summary: Unregister a rope (cleanup).


stepRopeInteraction

stepRopeInteraction(system, world, positionsCPU, thermalCPU, metaCPU, dt, callbacks = {})

Summary: Step the rope↔particle interaction system.


getRopeInteractionState

Summary: Get the current interaction state for a rope.


getRopeAverageTemperature

Summary: Get average temperature of a rope.


getRopeAverageMoisture

Summary: Get average moisture saturation of a rope.


destroyRopeInteractionSystem

Summary: Destroy the interaction system and release all state.


sim/particles/RopePhysics

Summary: RopePhysics.js - Advanced Rope Physics System GPU-accelerated rope physics with: - Tension/slack simulation - Attachment constraints (fixed points, entity attachments) - Rope cutting and rejoining - Stretching/compression limits - Wind/external force response


createRopePhysicsSystem

createRopePhysicsSystem(device, options = {})


sim/particles/SnapshotWorker

Summary: SnapshotWorker.js - Offload heavy snapshot compression to a separate thread This worker handles: - Delta calculation between frames - Quantization of position/velocity data - Compression of frame data - Memory management for snapshot history The main thread only does GPU readback (can't be done in workers) and sends the raw data here for processing.


sim/particles/SpellEffectComposer

Summary: SpellEffectComposer.js — Auto-blending multi-layer VFX composition ==================== ARCHITECTURE (for future AI/developers) ==================== Industry VFX standard (Niagara, Unity VFX Graph, PopcornFX, Houdini POP): A spell/effect is composed of MULTIPLE visual layers, each serving a distinct role: 1. CORE    — Main volumetric body (SDF particles, standard blend) 2. GLOW    — Larger, softer aura around core (bright color, low alpha, additive feel) 3. ACCENT  — Small bright particles: embers, snowflakes, sparkles, droplets 4. WISPS   — Secondary volumetric layer: smoke tendrils, mist, steam 5. RING    — Expanding shockwave/halo (burst spawn, grows outward) 6. TRAILS  — Ribbon trails following particles 7. LIGHT   — Dynamic point light matching effect color This system auto-generates these layers from an element mix. Given elements like [fire(0.8), ice(0.6)], it produces an array of emitter configs — one per visual layer. Each config is a complete Emitter component that can be passed to editor.createEntity(). USAGE: import { composeSpellEffect } from './SpellEffectComposer.js'; const layers = composeSpellEffect(activeElements, baseEmitterConfig); // layers = [{ name, layerType, emitterConfig }, ...] // Each layer.emitterConfig is a full Emitter component for createEntity The "Create Spell From This" button in EmitterInspectorCard.js calls this, then EditorParticles.createComposedSpellEffect() creates child emitter entities.


composeSpellEffect

composeSpellEffect(elements, baseConfig = {}, options = {})

Summary: Compose a multi-layer spell effect from an element mix.


getAvailableRecipes

Summary: Get available element recipe IDs.


sim/particles/data/index

Summary: data/index.js — Re-exports for schema, config, attributes, and compression.


sim/particles/effects/index

Summary: effects/index.js — Re-exports for visual effects, trails, and lines.


sim/particles/emitters/index

Summary: emitters/index.js — Organized re-exports for emitter creation, presets, and lifecycle. New code should import from here. Original files remain in parent directory.


sim/particles/events/index

Summary: events/index.js — Re-exports for event system, audio, and decals.


sim/particles/mesh/index

Summary: mesh/index.js — Re-exports for mesh-to-particle conversion and instancing.


sim/particles/physics/collision/index

Summary: physics/collision/index.js — Re-exports for collision systems.


sim/particles/physics/compute/index

Summary: physics/compute/index.js — Re-exports for compute utilities.


sim/particles/physics/constraints/index

Summary: physics/constraints/index.js — Re-exports for constraint solvers.


sim/particles/physics/fluids/index

Summary: physics/fluids/index.js — Re-exports for fluid solvers.


sim/particles/physics/forces/index

Summary: physics/forces/index.js — Re-exports for force systems.


sim/particles/physics/index

Summary: physics/index.js — Organized re-exports for GPU physics compute solvers. New code should import from here. Original files remain in parent directory.


sim/particles/rope/index

Summary: rope/index.js — Re-exports for rope/wire constraint systems.


sim/particles/snapshots/index

Summary: snapshots/index.js — Re-exports for snapshot, playback, and async management.


sim/particles/substances/SandboxMaterialCatalog

Summary: Engine-owned material facade for dense cellular sandboxes. Atomic numbers, particle material IDs, and sandbox cell codes are separate identities.  A sandbox code is only a compact runtime index.  The catalog keeps the canonical references beside it so the Editor, demos, and future authoring tools can share labels and properties without conflating those identity spaces. The generated behaviour is qualitative and mesoscale.  It does not claim that a grid cell is an atom or that periodic-table properties alone predict chemistry.  Only explicitly flagged reaction classes react in a solver.


createSandboxMaterialCatalog

Summary: Build an immutable shared element/substance/object view for dense sandboxes.


createSandboxSpeciesLut

Summary: Compile the shared catalog into one vec4<u32> record per cell code.


packSandboxCell

packSandboxCell(speciesCode, temperatureK = SANDBOX_REFERENCE_TEMPERATURE_K, life = 255, variant = 0)


packSandboxObject

packSandboxObject(objectId, sourceSpeciesCode = 0, strength = 255, direction = 0)


Summary: Pairwise interaction traits compiled into the high byte of LUT word 3.


sim/particles/substances/SubstanceMixer

Summary: SubstanceMixer.js — Multi-substance blending for mixed emitters. When an emitter uses multiple elements (e.g. fire 0.8 + smoke 0.3), this module blends their substance properties into a single effective definition. Replaces scattered mixing logic in ParticleEmitterSystem.js (deriveTemperatureFromElements, deriveMaterialIndexFromElements, elementMixToEmitterConfig).


mixSubstances

Summary: Mix multiple substances into a blended definition.


deriveFromElements

Summary: Derive a substance approximation from element layers (used by emitter system). Maps element IDs to substance IDs and mixes them.


sim/particles/substances/SubstanceRegistry

Summary: SubstanceRegistry.js — Central registry for all substance definitions. Provides a unified API to: - Register/unregister substance definitions - Look up substances by id, materialId, category, or tag - Derive physics profiles, phase state, thermal presets, SSFR tint, etc. Each substance is a plain object assembled from per-substance sub-files: { id, materialId, label, icon, category, tags, chemistry, thermal, physics, sph, visual, emitter, audio, decals, reactions, spell } Replaces scattered lookups across ParticleEmitterSystem, ParticleConstraints, ParticleReactionTable, EditorParticles, and ParticleElementRegistry.


registerSubstance

Summary: Register a substance definition. Overwrites if id already exists.


unregisterSubstance

Summary: Unregister a substance by id.


getSubstance

Summary: Get substance by id (e.g. 'water', 'lava', 'fire').


getSubstanceByMaterialId

Summary: Get substance by material ID number.


getAllSubstances

Summary: Get all registered substances.


getSubstancesByCategory

Summary: Get substances filtered by category.


getSubstancesByTag

Summary: Get substances filtered by tag.


getSubstanceIds

Summary: Get substance IDs as an array.


resolveSubstance

Summary: Resolve a substance key (preset name, element symbol, compound) to a substance definition. Falls back to partial matching.


deriveState

Summary: Derive the phase of matter for a substance at a given temperature.


derivePhysicsProfile

Summary: Derive which physics systems to enable for a substance at a given temperature.


getSSFRTint

Summary: Get SSFR tint for a substance.


getThermalPreset

Summary: Get thermal preset for a substance.


getSPHParams

Summary: Get SPH fluid parameters for a substance.


getEmitterDefaults

Summary: Get emitter defaults for a substance.


getAudioCues

Summary: Get audio cues for a substance.


getReactions

Summary: Get reactions for a substance.


getMaterialForSubstance

Summary: Get material ID for a substance key (replaces SUBSTANCE_TO_MATERIAL lookup).


buildThermalPresets

Summary: Build THERMAL_MATERIAL_PRESETS-compatible object from all registered substances. For backward compatibility with ParticleConstraints.js consumers.


buildSubstanceToMaterial

Summary: Build SUBSTANCE_TO_MATERIAL-compatible object from all registered substances. For backward compatibility with ParticleReactionTable.js consumers.


getSubstanceCount

Summary: Total number of registered substances.


sim/particles/substances/SubstanceSchema

Summary: SubstanceSchema.js — Shared constants and type definitions for the substance system. Single source of truth for: - Phase constants (PHASE_SOLID, PHASE_LIQUID, PHASE_GAS, PHASE_PLASMA) - Material ID enum (MATERIAL) - Reverse lookup helpers Replaces scattered definitions in ParticleConstraints.js and ParticleReactionTable.js. Those files re-export from here for backward compatibility.


getPhaseName

Summary: Get phase name string from phase constant.


getPhaseFromName

Summary: Get phase constant from name string.


getMaterialName

Summary: Get material name from material ID.


getMaterialId

Summary: Get material ID from name string.


sim/particles/substances/elements/MoleculePresets

Summary: MoleculePresets.js — Common molecule/compound definitions. Re-export of MOLECULE_PRESETS from ParticleElementTable.js. Lives in substances/elements/ for organizational clarity.


sim/particles/substances/elements/PeriodicTable

Summary: PeriodicTable.js — Full periodic table data for 118 elements. Re-export of the core element data from ParticleElementTable.js. This file lives in the new substances/elements/ folder for organizational clarity. The original ParticleElementTable.js remains functional and re-exports from here for backward compatibility. Provides: - ELEMENT_DATA raw array - ELEMENTS_BY_Z / ELEMENTS_BY_SYMBOL parsed lookups - getElement, getElementBySymbol, getAllElements - ELEMENTS shorthand constants - ljMixingRule for LJ cross-interactions - GPU buffer creation (createElementTable, etc.)


sim/particles/substances/index

Summary: substances/index.js — Barrel export for the substance system. Single import point: import { getSubstance, MATERIAL, PHASE_LIQUID, ... } from './substances/index.js';


sim/particles/substances/materials/acid/audio


sim/particles/substances/materials/acid/chemistry


sim/particles/substances/materials/acid/decals


sim/particles/substances/materials/acid/emitter


sim/particles/substances/materials/acid/identity


sim/particles/substances/materials/acid/index


sim/particles/substances/materials/acid/physics


sim/particles/substances/materials/acid/reactions


sim/particles/substances/materials/acid/spell


sim/particles/substances/materials/acid/sph


sim/particles/substances/materials/acid/thermal


sim/particles/substances/materials/acid/visual


sim/particles/substances/materials/blood/audio


sim/particles/substances/materials/blood/chemistry


sim/particles/substances/materials/blood/decals


sim/particles/substances/materials/blood/emitter


sim/particles/substances/materials/blood/identity


sim/particles/substances/materials/blood/index


sim/particles/substances/materials/blood/physics


sim/particles/substances/materials/blood/reactions


sim/particles/substances/materials/blood/spell


sim/particles/substances/materials/blood/sph


sim/particles/substances/materials/blood/thermal


sim/particles/substances/materials/blood/visual


sim/particles/substances/materials/debris/audio


sim/particles/substances/materials/debris/chemistry


sim/particles/substances/materials/debris/decals


sim/particles/substances/materials/debris/emitter


sim/particles/substances/materials/debris/identity


sim/particles/substances/materials/debris/index


sim/particles/substances/materials/debris/physics


sim/particles/substances/materials/debris/reactions


sim/particles/substances/materials/debris/spell


sim/particles/substances/materials/debris/sph


sim/particles/substances/materials/debris/thermal


sim/particles/substances/materials/debris/visual


sim/particles/substances/materials/fire/audio


sim/particles/substances/materials/fire/chemistry


sim/particles/substances/materials/fire/decals


sim/particles/substances/materials/fire/emitter


sim/particles/substances/materials/fire/identity


sim/particles/substances/materials/fire/index


sim/particles/substances/materials/fire/physics


sim/particles/substances/materials/fire/reactions


sim/particles/substances/materials/fire/spell


sim/particles/substances/materials/fire/sph


sim/particles/substances/materials/fire/thermal


sim/particles/substances/materials/fire/visual


sim/particles/substances/materials/glass/audio


sim/particles/substances/materials/glass/chemistry


sim/particles/substances/materials/glass/decals


sim/particles/substances/materials/glass/emitter


sim/particles/substances/materials/glass/identity


sim/particles/substances/materials/glass/index


sim/particles/substances/materials/glass/physics


sim/particles/substances/materials/glass/reactions


sim/particles/substances/materials/glass/spell


sim/particles/substances/materials/glass/sph


sim/particles/substances/materials/glass/thermal


sim/particles/substances/materials/glass/visual


sim/particles/substances/materials/honey/audio


sim/particles/substances/materials/honey/chemistry


sim/particles/substances/materials/honey/decals


sim/particles/substances/materials/honey/emitter


sim/particles/substances/materials/honey/identity


sim/particles/substances/materials/honey/index


sim/particles/substances/materials/honey/physics


sim/particles/substances/materials/honey/reactions


sim/particles/substances/materials/honey/spell


sim/particles/substances/materials/honey/sph


sim/particles/substances/materials/honey/thermal


sim/particles/substances/materials/honey/visual


sim/particles/substances/materials/ice/audio


sim/particles/substances/materials/ice/chemistry


sim/particles/substances/materials/ice/decals


sim/particles/substances/materials/ice/emitter


sim/particles/substances/materials/ice/identity


sim/particles/substances/materials/ice/index


sim/particles/substances/materials/ice/physics


sim/particles/substances/materials/ice/reactions


sim/particles/substances/materials/ice/spell


sim/particles/substances/materials/ice/sph


sim/particles/substances/materials/ice/thermal


sim/particles/substances/materials/ice/visual


sim/particles/substances/materials/index

Summary: materials/index.js — Auto-registers all substance definitions with SubstanceRegistry. Import this file to ensure all built-in substances are registered. Each substance folder exports a default object assembled from its sub-files.


sim/particles/substances/materials/lava/audio


sim/particles/substances/materials/lava/chemistry


sim/particles/substances/materials/lava/decals


sim/particles/substances/materials/lava/emitter


sim/particles/substances/materials/lava/identity


sim/particles/substances/materials/lava/index


sim/particles/substances/materials/lava/physics


sim/particles/substances/materials/lava/reactions


sim/particles/substances/materials/lava/spell


sim/particles/substances/materials/lava/sph


sim/particles/substances/materials/lava/thermal


sim/particles/substances/materials/lava/visual


sim/particles/substances/materials/mercury/audio


sim/particles/substances/materials/mercury/chemistry


sim/particles/substances/materials/mercury/decals


sim/particles/substances/materials/mercury/emitter


sim/particles/substances/materials/mercury/identity


sim/particles/substances/materials/mercury/index


sim/particles/substances/materials/mercury/physics


sim/particles/substances/materials/mercury/reactions


sim/particles/substances/materials/mercury/spell


sim/particles/substances/materials/mercury/sph


sim/particles/substances/materials/mercury/thermal


sim/particles/substances/materials/mercury/visual


sim/particles/substances/materials/metal/audio


sim/particles/substances/materials/metal/chemistry


sim/particles/substances/materials/metal/decals


sim/particles/substances/materials/metal/emitter


sim/particles/substances/materials/metal/identity


sim/particles/substances/materials/metal/index


sim/particles/substances/materials/metal/physics


sim/particles/substances/materials/metal/reactions


sim/particles/substances/materials/metal/spell


sim/particles/substances/materials/metal/sph


sim/particles/substances/materials/metal/thermal


sim/particles/substances/materials/metal/visual


sim/particles/substances/materials/oil/audio


sim/particles/substances/materials/oil/chemistry


sim/particles/substances/materials/oil/decals


sim/particles/substances/materials/oil/emitter


sim/particles/substances/materials/oil/identity


sim/particles/substances/materials/oil/index


sim/particles/substances/materials/oil/physics


sim/particles/substances/materials/oil/reactions


sim/particles/substances/materials/oil/spell


sim/particles/substances/materials/oil/sph


sim/particles/substances/materials/oil/thermal


sim/particles/substances/materials/oil/visual


sim/particles/substances/materials/plasma/audio


sim/particles/substances/materials/plasma/chemistry


sim/particles/substances/materials/plasma/decals


sim/particles/substances/materials/plasma/emitter


sim/particles/substances/materials/plasma/identity


sim/particles/substances/materials/plasma/index


sim/particles/substances/materials/plasma/physics


sim/particles/substances/materials/plasma/reactions


sim/particles/substances/materials/plasma/spell


sim/particles/substances/materials/plasma/sph


sim/particles/substances/materials/plasma/thermal


sim/particles/substances/materials/plasma/visual


sim/particles/substances/materials/sand/audio


sim/particles/substances/materials/sand/chemistry


sim/particles/substances/materials/sand/decals


sim/particles/substances/materials/sand/emitter


sim/particles/substances/materials/sand/identity


sim/particles/substances/materials/sand/index


sim/particles/substances/materials/sand/physics


sim/particles/substances/materials/sand/reactions


sim/particles/substances/materials/sand/spell


sim/particles/substances/materials/sand/sph


sim/particles/substances/materials/sand/thermal


sim/particles/substances/materials/sand/visual


sim/particles/substances/materials/smoke/audio


sim/particles/substances/materials/smoke/chemistry


sim/particles/substances/materials/smoke/decals


sim/particles/substances/materials/smoke/emitter


sim/particles/substances/materials/smoke/identity


sim/particles/substances/materials/smoke/index


sim/particles/substances/materials/smoke/physics


sim/particles/substances/materials/smoke/reactions


sim/particles/substances/materials/smoke/spell


sim/particles/substances/materials/smoke/sph


sim/particles/substances/materials/smoke/thermal


sim/particles/substances/materials/smoke/visual


sim/particles/substances/materials/snow/audio


sim/particles/substances/materials/snow/chemistry


sim/particles/substances/materials/snow/decals


sim/particles/substances/materials/snow/emitter


sim/particles/substances/materials/snow/identity


sim/particles/substances/materials/snow/index


sim/particles/substances/materials/snow/physics


sim/particles/substances/materials/snow/reactions


sim/particles/substances/materials/snow/spell


sim/particles/substances/materials/snow/sph


sim/particles/substances/materials/snow/thermal


sim/particles/substances/materials/snow/visual


sim/particles/substances/materials/sparks/audio


sim/particles/substances/materials/sparks/chemistry


sim/particles/substances/materials/sparks/decals


sim/particles/substances/materials/sparks/emitter


sim/particles/substances/materials/sparks/identity


sim/particles/substances/materials/sparks/index


sim/particles/substances/materials/sparks/physics


sim/particles/substances/materials/sparks/reactions


sim/particles/substances/materials/sparks/spell


sim/particles/substances/materials/sparks/sph


sim/particles/substances/materials/sparks/thermal


sim/particles/substances/materials/sparks/visual


sim/particles/substances/materials/steam/audio


sim/particles/substances/materials/steam/chemistry


sim/particles/substances/materials/steam/decals


sim/particles/substances/materials/steam/emitter


sim/particles/substances/materials/steam/identity


sim/particles/substances/materials/steam/index


sim/particles/substances/materials/steam/physics


sim/particles/substances/materials/steam/reactions


sim/particles/substances/materials/steam/spell


sim/particles/substances/materials/steam/sph


sim/particles/substances/materials/steam/thermal


sim/particles/substances/materials/steam/visual


sim/particles/substances/materials/stone/audio


sim/particles/substances/materials/stone/chemistry


sim/particles/substances/materials/stone/decals


sim/particles/substances/materials/stone/emitter


sim/particles/substances/materials/stone/identity


sim/particles/substances/materials/stone/index


sim/particles/substances/materials/stone/physics


sim/particles/substances/materials/stone/reactions


sim/particles/substances/materials/stone/spell


sim/particles/substances/materials/stone/sph


sim/particles/substances/materials/stone/thermal


sim/particles/substances/materials/stone/visual


sim/particles/substances/materials/water/audio


sim/particles/substances/materials/water/chemistry


sim/particles/substances/materials/water/decals


sim/particles/substances/materials/water/emitter


sim/particles/substances/materials/water/identity


sim/particles/substances/materials/water/index


sim/particles/substances/materials/water/physics


sim/particles/substances/materials/water/reactions


sim/particles/substances/materials/water/spell


sim/particles/substances/materials/water/sph


sim/particles/substances/materials/water/thermal


sim/particles/substances/materials/water/visual


sim/particles/substances/materials/wax/audio


sim/particles/substances/materials/wax/chemistry


sim/particles/substances/materials/wax/decals


sim/particles/substances/materials/wax/emitter


sim/particles/substances/materials/wax/identity


sim/particles/substances/materials/wax/index


sim/particles/substances/materials/wax/physics


sim/particles/substances/materials/wax/reactions


sim/particles/substances/materials/wax/spell


sim/particles/substances/materials/wax/sph


sim/particles/substances/materials/wax/thermal


sim/particles/substances/materials/wax/visual


sim/particles/substances/materials/wood/audio


sim/particles/substances/materials/wood/chemistry


sim/particles/substances/materials/wood/decals


sim/particles/substances/materials/wood/emitter


sim/particles/substances/materials/wood/identity


sim/particles/substances/materials/wood/index


sim/particles/substances/materials/wood/physics


sim/particles/substances/materials/wood/reactions


sim/particles/substances/materials/wood/spell


sim/particles/substances/materials/wood/sph


sim/particles/substances/materials/wood/thermal


sim/particles/substances/materials/wood/visual


sim/particles/thermal/index

Summary: thermal/index.js — Re-exports for heat, phase transitions, and chemistry.


sim/physics/ConnectivityCompute

Summary: ConnectivityCompute.js - GPU-Accelerated Voxel Connectivity Analysis Problem: When voxels are destroyed, we need to detect which parts are still connected to anchors (ground) vs floating (should fall). CPU flood-fill is O(n) and too slow for real-time destruction. Solution: GPU wave propagation using ping-pong buffers. Each iteration, voxels adopt the minimum component ID of their neighbors. After log(n) iterations, all connected voxels share the same ID. Algorithm: 1. Initialize: Each solid voxel gets unique ID, anchors get ID=1 2. Propagate: Each voxel takes min(self, neighbors) if solid 3. Repeat until no changes (typically 10-20 iterations for 32³) 4. Result: All voxels connected to anchors have ID=1, others form islands Performance Target: <1ms for 32³ chunk (10-20 iterations)


Summary: Convert 1D index to 3D position


positionToIndex

Summary: Convert 3D position to 1D index


Summary: Maximum iterations before forcing stop (prevents infinite loops)


Summary: Workgroup size for compute shaders


Summary: Component ID for anchored voxels (ground, supports)


Summary: Component ID for empty/air voxels


sim/physics/ConvexDecomposition

Summary: ConvexDecomposition.js - V-HACD Style Convex Decomposition Implements Volumetric Hierarchical Approximate Convex Decomposition (V-HACD) for breaking concave meshes into convex pieces suitable for physics collision. This is essential for: - Complex mesh colliders (characters, vehicles, props) - Hollow objects (cups, bowls, tubes) - Concave terrain features The algorithm: 1. Voxelize the mesh at a given resolution 2. Recursively split voxel regions using optimal cutting planes 3. Compute convex hulls for each region 4. Simplify hulls to reduce vertex count


voxelizeMesh

voxelizeMesh(vertices, indices, resolution = 64)

Summary: Voxelize a mesh into a 3D grid


decomposeMesh

decomposeMesh(vertices, indices, options = {})

Summary: Decompose a mesh into convex hulls


hullsToCompoundCollider

Summary: Convert decomposition result to PhysX compound collider format


createDecomposedCollider

createDecomposedCollider(vertices, indices, options = {})

Summary: High-level function: Decompose mesh and create PhysX collider


Summary: Default decomposition parameters


sim/physics/DynamicMeshCollider


getColliderConfigForMesh

getColliderConfigForMesh(meshId, halfExtents = [0.5, 0.5, 0.5])