@codexo/exojs-particles 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/dist/esm/ParticleSystem.d.ts +212 -0
  4. package/dist/esm/ParticleSystem.js +570 -0
  5. package/dist/esm/ParticleSystem.js.map +1 -0
  6. package/dist/esm/distributions/BoxArea.d.ts +17 -0
  7. package/dist/esm/distributions/BoxArea.js +48 -0
  8. package/dist/esm/distributions/BoxArea.js.map +1 -0
  9. package/dist/esm/distributions/CircleArea.d.ts +19 -0
  10. package/dist/esm/distributions/CircleArea.js +33 -0
  11. package/dist/esm/distributions/CircleArea.js.map +1 -0
  12. package/dist/esm/distributions/ColorGradient.d.ts +40 -0
  13. package/dist/esm/distributions/ColorGradient.js +72 -0
  14. package/dist/esm/distributions/ColorGradient.js.map +1 -0
  15. package/dist/esm/distributions/ConeDirection.d.ts +28 -0
  16. package/dist/esm/distributions/ConeDirection.js +44 -0
  17. package/dist/esm/distributions/ConeDirection.js.map +1 -0
  18. package/dist/esm/distributions/Constant.d.ts +17 -0
  19. package/dist/esm/distributions/Constant.js +35 -0
  20. package/dist/esm/distributions/Constant.js.map +1 -0
  21. package/dist/esm/distributions/Curve.d.ts +30 -0
  22. package/dist/esm/distributions/Curve.js +53 -0
  23. package/dist/esm/distributions/Curve.js.map +1 -0
  24. package/dist/esm/distributions/Distribution.d.ts +45 -0
  25. package/dist/esm/distributions/LineSegment.d.ts +15 -0
  26. package/dist/esm/distributions/LineSegment.js +27 -0
  27. package/dist/esm/distributions/LineSegment.js.map +1 -0
  28. package/dist/esm/distributions/Range.d.ts +12 -0
  29. package/dist/esm/distributions/Range.js +19 -0
  30. package/dist/esm/distributions/Range.js.map +1 -0
  31. package/dist/esm/distributions/VectorRange.d.ts +20 -0
  32. package/dist/esm/distributions/VectorRange.js +31 -0
  33. package/dist/esm/distributions/VectorRange.js.map +1 -0
  34. package/dist/esm/distributions/index.d.ts +12 -0
  35. package/dist/esm/gpu/ParticleGpuState.d.ts +57 -0
  36. package/dist/esm/gpu/ParticleGpuState.js +535 -0
  37. package/dist/esm/gpu/ParticleGpuState.js.map +1 -0
  38. package/dist/esm/index.d.ts +1 -0
  39. package/dist/esm/index.js +32 -0
  40. package/dist/esm/index.js.map +1 -0
  41. package/dist/esm/modules/AlphaFadeOverLifetime.d.ts +24 -0
  42. package/dist/esm/modules/AlphaFadeOverLifetime.js +64 -0
  43. package/dist/esm/modules/AlphaFadeOverLifetime.js.map +1 -0
  44. package/dist/esm/modules/ApplyForce.d.ts +20 -0
  45. package/dist/esm/modules/ApplyForce.js +48 -0
  46. package/dist/esm/modules/ApplyForce.js.map +1 -0
  47. package/dist/esm/modules/AttractToPoint.d.ts +27 -0
  48. package/dist/esm/modules/AttractToPoint.js +73 -0
  49. package/dist/esm/modules/AttractToPoint.js.map +1 -0
  50. package/dist/esm/modules/BurstSpawn.d.ts +53 -0
  51. package/dist/esm/modules/BurstSpawn.js +93 -0
  52. package/dist/esm/modules/BurstSpawn.js.map +1 -0
  53. package/dist/esm/modules/ColorOverLifetime.d.ts +22 -0
  54. package/dist/esm/modules/ColorOverLifetime.js +65 -0
  55. package/dist/esm/modules/ColorOverLifetime.js.map +1 -0
  56. package/dist/esm/modules/ColorOverSpeed.d.ts +27 -0
  57. package/dist/esm/modules/ColorOverSpeed.js +86 -0
  58. package/dist/esm/modules/ColorOverSpeed.js.map +1 -0
  59. package/dist/esm/modules/DeathModule.d.ts +24 -0
  60. package/dist/esm/modules/DeathModule.js +25 -0
  61. package/dist/esm/modules/DeathModule.js.map +1 -0
  62. package/dist/esm/modules/Drag.d.ts +20 -0
  63. package/dist/esm/modules/Drag.js +43 -0
  64. package/dist/esm/modules/Drag.js.map +1 -0
  65. package/dist/esm/modules/OrbitalForce.d.ts +28 -0
  66. package/dist/esm/modules/OrbitalForce.js +65 -0
  67. package/dist/esm/modules/OrbitalForce.js.map +1 -0
  68. package/dist/esm/modules/RateSpawn.d.ts +41 -0
  69. package/dist/esm/modules/RateSpawn.js +75 -0
  70. package/dist/esm/modules/RateSpawn.js.map +1 -0
  71. package/dist/esm/modules/RepelFromPoint.d.ts +24 -0
  72. package/dist/esm/modules/RepelFromPoint.js +76 -0
  73. package/dist/esm/modules/RepelFromPoint.js.map +1 -0
  74. package/dist/esm/modules/RotateOverLifetime.d.ts +20 -0
  75. package/dist/esm/modules/RotateOverLifetime.js +41 -0
  76. package/dist/esm/modules/RotateOverLifetime.js.map +1 -0
  77. package/dist/esm/modules/ScaleOverLifetime.d.ts +26 -0
  78. package/dist/esm/modules/ScaleOverLifetime.js +59 -0
  79. package/dist/esm/modules/ScaleOverLifetime.js.map +1 -0
  80. package/dist/esm/modules/SpawnModule.d.ts +30 -0
  81. package/dist/esm/modules/SpawnModule.js +31 -0
  82. package/dist/esm/modules/SpawnModule.js.map +1 -0
  83. package/dist/esm/modules/SpawnOnDeath.d.ts +24 -0
  84. package/dist/esm/modules/SpawnOnDeath.js +47 -0
  85. package/dist/esm/modules/SpawnOnDeath.js.map +1 -0
  86. package/dist/esm/modules/Turbulence.d.ts +30 -0
  87. package/dist/esm/modules/Turbulence.js +122 -0
  88. package/dist/esm/modules/Turbulence.js.map +1 -0
  89. package/dist/esm/modules/UpdateModule.d.ts +95 -0
  90. package/dist/esm/modules/UpdateModule.js +66 -0
  91. package/dist/esm/modules/UpdateModule.js.map +1 -0
  92. package/dist/esm/modules/VelocityOverLifetime.d.ts +30 -0
  93. package/dist/esm/modules/VelocityOverLifetime.js +84 -0
  94. package/dist/esm/modules/VelocityOverLifetime.js.map +1 -0
  95. package/dist/esm/modules/WgslContribution.d.ts +81 -0
  96. package/dist/esm/modules/WgslContribution.js +34 -0
  97. package/dist/esm/modules/WgslContribution.js.map +1 -0
  98. package/dist/esm/modules/index.d.ts +22 -0
  99. package/dist/esm/particlesBuildInfo.d.ts +15 -0
  100. package/dist/esm/particlesBuildInfo.js +8 -0
  101. package/dist/esm/particlesBuildInfo.js.map +1 -0
  102. package/dist/esm/particlesExtension.d.ts +24 -0
  103. package/dist/esm/particlesExtension.js +49 -0
  104. package/dist/esm/particlesExtension.js.map +1 -0
  105. package/dist/esm/public.d.ts +9 -0
  106. package/dist/esm/register.d.ts +1 -0
  107. package/dist/esm/register.js +43 -0
  108. package/dist/esm/register.js.map +1 -0
  109. package/dist/esm/renderers/WebGl2ParticleRenderer.d.ts +45 -0
  110. package/dist/esm/renderers/WebGl2ParticleRenderer.js +325 -0
  111. package/dist/esm/renderers/WebGl2ParticleRenderer.js.map +1 -0
  112. package/dist/esm/renderers/WebGpuParticleRenderer.d.ts +48 -0
  113. package/dist/esm/renderers/WebGpuParticleRenderer.js +553 -0
  114. package/dist/esm/renderers/WebGpuParticleRenderer.js.map +1 -0
  115. package/dist/esm/renderers/glsl/particle.frag.js +4 -0
  116. package/dist/esm/renderers/glsl/particle.frag.js.map +1 -0
  117. package/dist/esm/renderers/glsl/particle.vert.js +4 -0
  118. package/dist/esm/renderers/glsl/particle.vert.js.map +1 -0
  119. package/package.json +55 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Codexo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # @codexo/exojs-particles
2
+
3
+ Official ExoJS extension for GPU-accelerated particle systems.
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ npm install @codexo/exojs @codexo/exojs-particles
9
+ ```
10
+
11
+ This package requires `@codexo/exojs` as a peer dependency. Both must be the same version.
12
+
13
+ ## Core compatibility
14
+
15
+ | `@codexo/exojs-particles` | `@codexo/exojs` |
16
+ |---|---|
17
+ | 0.12.x | 0.12.x |
18
+
19
+ ## Usage — side-effect-free root entry
20
+
21
+ Import `ParticleSystem` and supply it explicitly to your Application via `extensions`:
22
+
23
+ ```ts
24
+ import { Application } from '@codexo/exojs';
25
+ import { ParticleSystem, particlesExtension } from '@codexo/exojs-particles';
26
+
27
+ const app = new Application({
28
+ extensions: [particlesExtension],
29
+ });
30
+ ```
31
+
32
+ Importing from the root entry (`@codexo/exojs-particles`) does **not** register the extension globally. You control exactly which Applications receive the Particles extension.
33
+
34
+ ## Extension descriptor
35
+
36
+ `particlesExtension` is the default descriptor. Use it when you want the default renderer batch size:
37
+
38
+ ```ts
39
+ import { particlesExtension } from '@codexo/exojs-particles';
40
+
41
+ const app = new Application({ extensions: [particlesExtension] });
42
+ ```
43
+
44
+ ## Custom batch size via `createParticlesExtension`
45
+
46
+ ```ts
47
+ import { createParticlesExtension } from '@codexo/exojs-particles';
48
+
49
+ const app = new Application({
50
+ extensions: [createParticlesExtension({ batchSize: 8192 })],
51
+ });
52
+ ```
53
+
54
+ ## `ApplicationOptions.extensions`
55
+
56
+ Pass any combination of descriptors:
57
+
58
+ ```ts
59
+ const app = new Application({
60
+ extensions: [particlesExtension],
61
+ });
62
+ ```
63
+
64
+ ## `/register` convenience entry
65
+
66
+ Importing `/register` registers the default `particlesExtension` descriptor in the global `ExtensionRegistry`. Subsequently created Applications that use global defaults will automatically receive the Particles extension. This is the only side-effectful entry in this package.
67
+
68
+ ```ts
69
+ // Side effect: registers particlesExtension in the global ExtensionRegistry.
70
+ import '@codexo/exojs-particles/register';
71
+
72
+ // All named exports are re-exported from /register:
73
+ import { ParticleSystem, particlesExtension } from '@codexo/exojs-particles/register';
74
+ ```
75
+
76
+ **Note:** `/register` does not use automatic discovery. It explicitly calls `ExtensionRegistry.register(particlesExtension)` at module evaluation time.
77
+
78
+ ## Minimal working example
79
+
80
+ ```ts
81
+ import { Application, Scene, Texture } from '@codexo/exojs';
82
+ import {
83
+ ParticleSystem,
84
+ particlesExtension,
85
+ RateSpawn,
86
+ Constant,
87
+ } from '@codexo/exojs-particles';
88
+
89
+ const app = new Application({ extensions: [particlesExtension] });
90
+ document.body.append(app.canvas);
91
+
92
+ class DemoScene extends Scene {
93
+ system!: ParticleSystem;
94
+
95
+ override async load(loader) {
96
+ await loader.load(Texture, { particle: '/particle.png' });
97
+ }
98
+
99
+ override create(loader) {
100
+ this.system = new ParticleSystem(loader.get(Texture, 'particle'), {
101
+ capacity: 1024,
102
+ });
103
+ this.system.addSpawnModule(
104
+ new RateSpawn({ rate: new Constant(120), lifetime: new Constant(2) }),
105
+ );
106
+ this.addChild(this.system);
107
+ }
108
+ }
109
+
110
+ app.scenes.start(DemoScene);
111
+ ```
112
+
113
+ ## WebGL2 and WebGPU support
114
+
115
+ - **WebGL2**: full instanced-draw renderer
116
+ - **WebGPU**: compute-shader GPU simulation when a WebGPU device is available; falls back to CPU simulation otherwise
117
+
118
+ ## Destruction and ownership
119
+
120
+ `ParticleSystem` owns its GPU resources. Call `system.destroy()` when you are done with it. The `Application` or parent scene does not automatically destroy nested systems.
121
+
122
+ ## Links
123
+
124
+ - [Official ExoJS Particles guide](https://exojs.dev/guides/extensions/particles)
125
+ - [API reference](https://exojs.dev/api/exojs-particles)
126
+
127
+ ## License
128
+
129
+ MIT
@@ -0,0 +1,212 @@
1
+ import type { Time } from '@codexo/exojs';
2
+ import { Rectangle } from '@codexo/exojs';
3
+ import { Drawable } from '@codexo/exojs';
4
+ import type { RenderPlanBuilder } from '@codexo/exojs/rendering';
5
+ import { Spritesheet } from '@codexo/exojs';
6
+ import { Texture } from '@codexo/exojs';
7
+ import { ParticleGpuState } from '#gpu/ParticleGpuState';
8
+ import type { DeathModule } from '#modules/DeathModule';
9
+ import type { SpawnModule } from '#modules/SpawnModule';
10
+ import type { UpdateModule } from '#modules/UpdateModule';
11
+ /**
12
+ * Options for {@link ParticleSystem}'s constructor — orthogonal config
13
+ * that's independent of the texture source. Texture / frames / spritesheet
14
+ * live in positional arguments to enforce mutual exclusivity at the type
15
+ * level (you can't pass both a texture and a spritesheet by accident).
16
+ */
17
+ export interface ParticleSystemOptions {
18
+ /** Maximum particle count. Fixed at construction. Default 4096. */
19
+ capacity?: number;
20
+ /**
21
+ * Direct GPU device. Lets advanced consumers wire a `GPUDevice` owned
22
+ * outside an `Application` (or a mock device in tests). When omitted,
23
+ * the backend reference is captured automatically on the first
24
+ * {@link ParticleSystem.render} call — `WebGpuBackend` ⇒ GPU mode,
25
+ * anything else (incl. WebGL2) ⇒ CPU mode.
26
+ */
27
+ device?: GPUDevice;
28
+ }
29
+ /**
30
+ * The central coordinator of the particle pipeline. `ParticleSystem` is a
31
+ * {@link Drawable} that owns:
32
+ *
33
+ * - **SoA particle storage** — one typed array per attribute (position,
34
+ * velocity, scale, rotation, color, lifetime, ...), sized to a fixed
35
+ * capacity at construction. User code reads/writes via
36
+ * `system.posX[slot]`, `system.velX[slot]`, etc.
37
+ * - **Spawn modules** — write new particles into freshly allocated slots.
38
+ * - **Update modules** — mutate the live range each frame (forces, color
39
+ * blends, scale curves, drag, ...). Built-in modules ship both CPU and
40
+ * WGSL implementations; custom modules can opt into GPU acceleration by
41
+ * implementing `wgsl()`.
42
+ * - **Death modules** — fire once per dying particle, before its slot is
43
+ * recycled (sub-emitters, event hooks).
44
+ *
45
+ * **Auto-routing CPU vs GPU:** at first {@link update}, the system checks:
46
+ * if a `WebGpuBackend` was supplied AND every registered update module has
47
+ * `wgsl()`, the GPU path engages — a composite compute pipeline runs
48
+ * integration plus all module bodies in one dispatch and writes directly
49
+ * into the renderer's instance buffer (no CPU readback). Otherwise the CPU
50
+ * path runs the existing per-module `apply()` loops.
51
+ *
52
+ * **Per-frame order in {@link update} (CPU mode):**
53
+ * 1. Run every spawn module.
54
+ * 2. Integrate position from velocity, rotation from rotationSpeed, advance `elapsed`.
55
+ * 3. Run every update module on the live range.
56
+ * 4. Compact: scan `[0, liveCount)` forward, fire death modules on expired
57
+ * slots, copy survivors down. `liveCount` shrinks to the survivor count.
58
+ *
59
+ * **Per-frame order in {@link update} (GPU mode):**
60
+ * 1. Run every spawn module (CPU writes initial values into the spawn slot).
61
+ * 2. Detect expiries on CPU (via `elapsed >= lifetime`); fire death modules;
62
+ * set `lifetime[slot] = -1` sentinel + clear `alive[slot]` so the GPU
63
+ * shader skips them. **No compaction** — slots are recycled on next spawn.
64
+ * 3. Dispatch the composite compute pipeline. Integration + update modules
65
+ * + pack-instances run in one pass; the instance buffer is written
66
+ * directly. CPU SoA stays as-is for spawn writes.
67
+ *
68
+ * **Coordinate space:** particle positions are LOCAL to the system. The
69
+ * system's `getGlobalTransform()` is applied on top during rendering — both
70
+ * the WebGL2 and WebGPU shaders multiply `projection * translation * rotated`.
71
+ * Setting world-space positions on individual particles double-translates.
72
+ * Position the system itself via `system.setPosition(...)` and emit relative
73
+ * to `(0, 0)`.
74
+ *
75
+ * @example
76
+ * // Backend-agnostic — runs CPU on WebGL2, GPU on WebGPU automatically.
77
+ * const system = new ParticleSystem(loader.get(Texture, 'spark'), {
78
+ * capacity: 8192,
79
+ * backend: app.backend,
80
+ * });
81
+ *
82
+ * system.addSpawnModule(new RateSpawn({ rate: new Constant(60), ... }));
83
+ * system.addUpdateModule(new ApplyForce(0, 980)); // gravity, GPU-eligible
84
+ * system.addUpdateModule(new ColorOverLifetime(fireGradient));
85
+ * scene.addChild(system);
86
+ */
87
+ export declare class ParticleSystem extends Drawable {
88
+ /** Maximum particle count this system will store. Fixed at construction. */
89
+ readonly capacity: number;
90
+ readonly posX: Float32Array;
91
+ readonly posY: Float32Array;
92
+ readonly velX: Float32Array;
93
+ readonly velY: Float32Array;
94
+ readonly scaleX: Float32Array;
95
+ readonly scaleY: Float32Array;
96
+ readonly rotations: Float32Array;
97
+ readonly rotationSpeeds: Float32Array;
98
+ readonly color: Uint32Array;
99
+ readonly elapsed: Float32Array;
100
+ readonly lifetime: Float32Array;
101
+ readonly textureIndex: Uint16Array;
102
+ /**
103
+ * Number of currently live particles. In CPU mode this is exact: slots
104
+ * `[0, liveCount)` are all alive after each `update()`. In GPU mode
105
+ * this is a high-water mark — slots `[0, liveCount)` may contain dead
106
+ * holes (filled in by future spawns); use {@link aliveCount} for the
107
+ * actual alive count.
108
+ */
109
+ liveCount: number;
110
+ /**
111
+ * Per-slot alive flag (1 = alive, 0 = dead). Maintained in both CPU
112
+ * and GPU mode. Custom modules iterating the live range should check
113
+ * this to skip dead slots in GPU mode.
114
+ */
115
+ readonly alive: Uint8Array;
116
+ private readonly _spawnModules;
117
+ private readonly _updateModules;
118
+ private readonly _deathModules;
119
+ private _backend;
120
+ private readonly _device;
121
+ private _gpuState;
122
+ private _gpuMode;
123
+ private _compiled;
124
+ private _spawnHint;
125
+ /**
126
+ * In GPU mode, slots whose CPU SoA values need re-uploading to the GPU
127
+ * (newly spawned, or just-expired with lifetime sentinel). Cleared
128
+ * after each compute dispatch. CPU never overwrites integrated GPU
129
+ * state — only dirty slots flow CPU → GPU.
130
+ */
131
+ private readonly _gpuDirtySlots;
132
+ private _texture;
133
+ private readonly _frames;
134
+ private readonly _textureFrame;
135
+ private readonly _vertices;
136
+ private readonly _texCoords;
137
+ private _updateTexCoords;
138
+ private _updateVertices;
139
+ /** No texture — particles render as solid-color quads on a 1×1 white default. */
140
+ constructor(options?: ParticleSystemOptions);
141
+ /** Single texture, no atlas — every particle uses the full texture as one frame. */
142
+ constructor(texture: Texture, options?: ParticleSystemOptions);
143
+ /** Multi-frame atlas — each particle's `textureIndex` selects a frame. */
144
+ constructor(texture: Texture, frames: readonly Rectangle[], options?: ParticleSystemOptions);
145
+ /** Spritesheet shorthand — texture + frames pulled from the sheet. */
146
+ constructor(spritesheet: Spritesheet, options?: ParticleSystemOptions);
147
+ get texture(): Texture;
148
+ set texture(texture: Texture);
149
+ get textureFrame(): Rectangle;
150
+ set textureFrame(frame: Rectangle);
151
+ /**
152
+ * Atlas frames declared on this system, or empty when the texture is
153
+ * used as a single frame. Each particle's `textureIndex[i]` selects
154
+ * an entry from this list; out-of-range indices are clamped to 0.
155
+ */
156
+ get frames(): readonly Rectangle[];
157
+ /** `true` when the system declares more than one atlas frame. */
158
+ get hasAtlas(): boolean;
159
+ get vertices(): Float32Array;
160
+ get texCoords(): Uint32Array;
161
+ /** `true` when the system is running on the GPU compute pipeline. */
162
+ get gpuMode(): boolean;
163
+ /** GPU-side state, or `null` in CPU mode. */
164
+ get gpuState(): ParticleGpuState | null;
165
+ /** Actual count of live particles (slots with `alive[i] === 1`). May differ from `liveCount` in GPU mode. */
166
+ get aliveCount(): number;
167
+ get spawnModules(): readonly SpawnModule[];
168
+ get updateModules(): readonly UpdateModule[];
169
+ get deathModules(): readonly DeathModule[];
170
+ setTexture(texture: Texture): this;
171
+ setTextureFrame(frame: Rectangle): this;
172
+ resetTextureFrame(): this;
173
+ addSpawnModule(mod: SpawnModule): this;
174
+ addUpdateModule(mod: UpdateModule): this;
175
+ addDeathModule(mod: DeathModule): this;
176
+ clearSpawnModules(): this;
177
+ clearUpdateModules(): this;
178
+ clearDeathModules(): this;
179
+ /**
180
+ * Allocates a particle slot and returns its index. Returns `-1` when
181
+ * the system is at {@link capacity}.
182
+ *
183
+ * **CPU mode:** slots are dense in `[0, liveCount)`. `spawn()` returns
184
+ * the next sequential slot; `liveCount++`.
185
+ *
186
+ * **GPU mode:** slots may have dead holes. `spawn()` finds the first
187
+ * `alive[i] === 0` slot via a round-robin hint pointer (amortised O(1),
188
+ * worst case O(capacity) on full systems).
189
+ */
190
+ spawn(): number;
191
+ /** Resets the system to zero live particles without destroying it. */
192
+ clearParticles(): this;
193
+ /**
194
+ * @internal
195
+ *
196
+ * Collect-hook: captures the active backend before this node is emitted so
197
+ * the next `update()` can compile a GPU pipeline if the backend turned out
198
+ * to be `WebGpuBackend`. Re-captures and rebuilds when the backend
199
+ * reference changes (e.g. after device-loss recovery).
200
+ */
201
+ /** @internal */
202
+ _collect(builder: RenderPlanBuilder, seq?: number): void;
203
+ /** Per-frame entry point. Routes to CPU or GPU pipeline based on auto-detection at first call. */
204
+ update(delta: Time): this;
205
+ destroy(): void;
206
+ private _compile;
207
+ private _spawnCpu;
208
+ private _spawnGpu;
209
+ private _updateCpu;
210
+ private _updateGpu;
211
+ private _copySlot;
212
+ }