r3f-vfx 0.0.1 → 0.0.2

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 (2) hide show
  1. package/README.md +455 -0
  2. package/package.json +2 -2
package/README.md ADDED
@@ -0,0 +1,455 @@
1
+ # r3f-vf
2
+
3
+ High-performance GPU-accelerated particle system for Three.js WebGPU with React Three Fiber.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **GPU Compute Shaders** - All particle simulation runs on the GPU for maximum performance
8
+ - 🎨 **Flexible Appearance** - Sprites, custom geometry, materials, and shaders
9
+ - 🌀 **Advanced Physics** - Gravity, turbulence, attractors, collisions, and more
10
+ - 🎯 **Multiple Emitter Shapes** - Point, Box, Sphere, Cone, Disk, and Edge emitters
11
+ - 📊 **Curve-based Control** - Bezier curves for size, opacity, velocity, and rotation over lifetime
12
+ - 🔗 **Emitter System** - Decoupled emitters that can share particle systems
13
+ - ⚡ **WebGPU Native** - Built specifically for Three.js WebGPU renderer
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install r3f-vfx
19
+ ```
20
+
21
+ ### Peer Dependencies
22
+
23
+ ```bash
24
+ npm install three @react-three/fiber zustand react
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ```tsx
30
+ import { Canvas } from "@react-three/fiber";
31
+ import { VFXParticles, Appearance, EmitterShape } from "r3f-vfx";
32
+ import * as THREE from "three/webgpu";
33
+
34
+ function App() {
35
+ return (
36
+ <Canvas
37
+ renderer
38
+ >
39
+ <VFXParticles debug/>
40
+ </Canvas>
41
+ );
42
+ }
43
+ ```
44
+
45
+ That's it, start designing in the debug panel, then copy JSX
46
+
47
+
48
+ ## API Reference
49
+
50
+ ### VFXParticles
51
+
52
+ The main particle system component.
53
+
54
+ #### Basic Props
55
+
56
+ | Prop | Type | Default | Description |
57
+ |------|------|---------|-------------|
58
+ | `name` | `string` | - | Register system for use with VFXEmitter |
59
+ | `maxParticles` | `number` | `10000` | Maximum number of particles |
60
+ | `autoStart` | `boolean` | `true` | Start emitting automatically |
61
+ | `delay` | `number` | `0` | Seconds between emissions (0 = every frame) |
62
+ | `emitCount` | `number` | `1` | Particles to emit per burst |
63
+ | `position` | `[x, y, z]` | `[0, 0, 0]` | Emitter position |
64
+
65
+ #### Appearance Props
66
+
67
+ | Prop | Type | Default | Description |
68
+ |------|------|---------|-------------|
69
+ | `size` | `number \| [min, max]` | `[0.1, 0.3]` | Particle size range |
70
+ | `colorStart` | `string[]` | `["#ffffff"]` | Starting colors (random pick) |
71
+ | `colorEnd` | `string[] \| null` | `null` | Ending colors (null = no transition) |
72
+ | `fadeSize` | `number \| [start, end]` | `[1, 0]` | Size multiplier over lifetime |
73
+ | `fadeOpacity` | `number \| [start, end]` | `[1, 0]` | Opacity over lifetime |
74
+ | `appearance` | `Appearance` | `GRADIENT` | Shape: `DEFAULT`, `GRADIENT`, `CIRCULAR` |
75
+ | `intensity` | `number` | `1` | Color intensity multiplier |
76
+ | `blending` | `Blending` | `NORMAL` | Blend mode: `NORMAL`, `ADDITIVE`, `MULTIPLY`, `SUBTRACTIVE` |
77
+
78
+ #### Physics Props
79
+
80
+ | Prop | Type | Default | Description |
81
+ |------|------|---------|-------------|
82
+ | `lifetime` | `number \| [min, max]` | `[1, 2]` | Particle lifetime in seconds |
83
+ | `speed` | `number \| [min, max]` | `[0.1, 0.1]` | Initial speed |
84
+ | `direction` | `Range3D \| [min, max]` | `[[-1,1], [0,1], [-1,1]]` | Emission direction per axis |
85
+ | `gravity` | `[x, y, z]` | `[0, 0, 0]` | Gravity vector |
86
+ | `friction` | `FrictionConfig` | `{ intensity: 0 }` | Velocity damping |
87
+
88
+ #### Emitter Shape Props
89
+
90
+ | Prop | Type | Default | Description |
91
+ |------|------|---------|-------------|
92
+ | `emitterShape` | `EmitterShape` | `BOX` | Shape: `POINT`, `BOX`, `SPHERE`, `CONE`, `DISK`, `EDGE` |
93
+ | `emitterRadius` | `[inner, outer]` | `[0, 1]` | Radius range for sphere/cone/disk |
94
+ | `emitterAngle` | `number` | `π/4` | Cone angle in radians |
95
+ | `emitterHeight` | `[min, max]` | `[0, 1]` | Height range for cone |
96
+ | `emitterDirection` | `[x, y, z]` | `[0, 1, 0]` | Cone/disk normal direction |
97
+ | `emitterSurfaceOnly` | `boolean` | `false` | Emit from surface only |
98
+ | `startPosition` | `Range3D` | `[[0,0], [0,0], [0,0]]` | Position offset per axis |
99
+
100
+ #### Geometry Mode Props
101
+
102
+ | Prop | Type | Default | Description |
103
+ |------|------|---------|-------------|
104
+ | `geometry` | `BufferGeometry` | `null` | Custom particle geometry |
105
+ | `lighting` | `Lighting` | `STANDARD` | Material: `BASIC`, `STANDARD`, `PHYSICAL` |
106
+ | `shadow` | `boolean` | `false` | Enable shadow casting/receiving |
107
+ | `orientToDirection` | `boolean` | `false` | Orient geometry to velocity |
108
+ | `orientAxis` | `string` | `"z"` | Axis to align: `"x"`, `"y"`, `"z"`, `"-x"`, `"-y"`, `"-z"` |
109
+ | `rotation` | `Range3D \| [min, max]` | `[0, 0]` | Initial rotation per axis |
110
+ | `rotationSpeed` | `Range3D \| [min, max]` | `[0, 0]` | Rotation speed rad/s |
111
+
112
+ #### Stretch Props
113
+
114
+ | Prop | Type | Default | Description |
115
+ |------|------|---------|-------------|
116
+ | `stretchBySpeed` | `StretchConfig` | `null` | Stretch particles by velocity |
117
+
118
+ ```ts
119
+ interface StretchConfig {
120
+ factor: number; // Stretch multiplier
121
+ maxStretch: number; // Maximum stretch amount
122
+ }
123
+ ```
124
+
125
+ #### Turbulence Props
126
+
127
+ | Prop | Type | Default | Description |
128
+ |------|------|---------|-------------|
129
+ | `turbulence` | `TurbulenceConfig` | `null` | Curl noise turbulence |
130
+
131
+ ```ts
132
+ interface TurbulenceConfig {
133
+ intensity: number; // Turbulence strength
134
+ frequency: number; // Noise scale
135
+ speed: number; // Animation speed
136
+ }
137
+ ```
138
+
139
+ #### Attractor Props
140
+
141
+ | Prop | Type | Default | Description |
142
+ |------|------|---------|-------------|
143
+ | `attractors` | `AttractorConfig[]` | `null` | Up to 4 attractors |
144
+ | `attractToCenter` | `boolean` | `false` | Pull particles to emitter center |
145
+
146
+ ```ts
147
+ interface AttractorConfig {
148
+ position: [x, y, z];
149
+ strength: number; // Positive = attract, negative = repel
150
+ radius?: number; // 0 = infinite range
151
+ type?: "point" | "vortex";
152
+ axis?: [x, y, z]; // Vortex rotation axis
153
+ }
154
+ ```
155
+
156
+ #### Collision Props
157
+
158
+ | Prop | Type | Default | Description |
159
+ |------|------|---------|-------------|
160
+ | `collision` | `CollisionConfig` | `null` | Plane collision |
161
+
162
+ ```ts
163
+ interface CollisionConfig {
164
+ plane: { y: number }; // Plane Y position
165
+ bounce?: number; // Bounce factor (0-1)
166
+ friction?: number; // Horizontal friction
167
+ die?: boolean; // Kill on collision
168
+ sizeBasedGravity?: number; // Gravity multiplier by size
169
+ }
170
+ ```
171
+
172
+ #### Soft Particles Props
173
+
174
+ | Prop | Type | Default | Description |
175
+ |------|------|---------|-------------|
176
+ | `softParticles` | `boolean` | `false` | Fade near geometry |
177
+ | `softDistance` | `number` | `0.5` | Fade distance in world units |
178
+
179
+ #### Curve Props
180
+
181
+ All curves use Bezier spline format:
182
+
183
+ ```ts
184
+ interface CurveData {
185
+ points: Array<{
186
+ pos: [x, y]; // Position (x: 0-1 progress, y: value)
187
+ handleIn?: [x, y]; // Bezier handle in (offset)
188
+ handleOut?: [x, y]; // Bezier handle out (offset)
189
+ }>;
190
+ }
191
+ ```
192
+
193
+ | Prop | Type | Description |
194
+ |------|------|-------------|
195
+ | `fadeSizeCurve` | `CurveData` | Size multiplier over lifetime |
196
+ | `fadeOpacityCurve` | `CurveData` | Opacity over lifetime |
197
+ | `velocityCurve` | `CurveData` | Velocity multiplier (overrides friction) |
198
+ | `rotationSpeedCurve` | `CurveData` | Rotation speed multiplier |
199
+
200
+ #### Custom Shader Props
201
+
202
+ | Prop | Type | Description |
203
+ |------|------|-------------|
204
+ | `colorNode` | `NodeFunction` | Custom color shader |
205
+ | `opacityNode` | `NodeFunction` | Custom opacity shader |
206
+ | `backdropNode` | `NodeFunction` | Backdrop sampling (refraction) |
207
+ | `castShadowNode` | `NodeFunction` | Shadow map output |
208
+ | `alphaTestNode` | `NodeFunction` | Alpha test/discard |
209
+
210
+ ```ts
211
+ type NodeFunction = (data: ParticleData, defaultColor?: Node) => Node;
212
+
213
+ interface ParticleData {
214
+ progress: Node; // 0 → 1 over lifetime
215
+ lifetime: Node; // 1 → 0 over lifetime
216
+ position: Node; // vec3 world position
217
+ velocity: Node; // vec3 velocity
218
+ size: Node; // float size
219
+ rotation: Node; // vec3 rotation
220
+ colorStart: Node; // vec3 start color
221
+ colorEnd: Node; // vec3 end color
222
+ color: Node; // vec3 interpolated color
223
+ intensifiedColor: Node; // color × intensity
224
+ shapeMask: Node; // float alpha mask
225
+ index: Node; // particle index
226
+ }
227
+ ```
228
+
229
+ #### Texture Props
230
+
231
+ | Prop | Type | Description |
232
+ |------|------|-------------|
233
+ | `alphaMap` | `Texture` | Alpha/shape texture |
234
+ | `flipbook` | `FlipbookConfig` | Animated flipbook |
235
+
236
+ ```ts
237
+ interface FlipbookConfig {
238
+ rows: number;
239
+ columns: number;
240
+ }
241
+ ```
242
+
243
+ ### VFXEmitter
244
+
245
+ Decoupled emitter component that links to a VFXParticles system.
246
+
247
+ ```tsx
248
+ <VFXParticles name="sparks" maxParticles={1000} autoStart={false} />
249
+
250
+ <group ref={playerRef}>
251
+ <VFXEmitter
252
+ name="sparks"
253
+ position={[0, 1, 0]}
254
+ emitCount={5}
255
+ delay={0.1}
256
+ direction={[[0, 0], [0, 0], [-1, -1]]}
257
+ localDirection={true}
258
+ />
259
+ </group>
260
+ ```
261
+
262
+ #### Props
263
+
264
+ | Prop | Type | Default | Description |
265
+ |------|------|---------|-------------|
266
+ | `name` | `string` | - | Name of VFXParticles system |
267
+ | `particlesRef` | `Ref<ParticleAPI>` | - | Direct ref (alternative to name) |
268
+ | `position` | `[x, y, z]` | `[0, 0, 0]` | Local position offset |
269
+ | `emitCount` | `number` | `10` | Particles per burst |
270
+ | `delay` | `number` | `0` | Seconds between emissions |
271
+ | `autoStart` | `boolean` | `true` | Start emitting automatically |
272
+ | `loop` | `boolean` | `true` | Keep emitting (false = once) |
273
+ | `localDirection` | `boolean` | `false` | Transform direction by parent rotation |
274
+ | `direction` | `Range3D` | - | Direction override |
275
+ | `overrides` | `SpawnOverrides` | - | Per-spawn property overrides |
276
+ | `onEmit` | `function` | - | Callback after each emission |
277
+
278
+ #### Ref Methods
279
+
280
+ ```ts
281
+ interface VFXEmitterAPI {
282
+ emit(): boolean; // Emit at current position
283
+ burst(count?: number): boolean; // Burst emit
284
+ start(): void; // Start auto-emission
285
+ stop(): void; // Stop auto-emission
286
+ isEmitting: boolean; // Current state
287
+ getParticleSystem(): ParticleAPI;
288
+ group: THREE.Group; // The group element
289
+ }
290
+ ```
291
+
292
+ ### useVFXEmitter Hook
293
+
294
+ Programmatic emitter control.
295
+
296
+ ```tsx
297
+ function MyComponent() {
298
+ const { emit, burst, start, stop } = useVFXEmitter("sparks");
299
+
300
+ const handleClick = () => {
301
+ burst([0, 1, 0], 100, { colorStart: ["#ff0000"] });
302
+ };
303
+
304
+ return <mesh onClick={handleClick}>...</mesh>;
305
+ }
306
+ ```
307
+
308
+ #### Returns
309
+
310
+ ```ts
311
+ interface UseVFXEmitterResult {
312
+ emit(position?: [x,y,z], count?: number, overrides?: SpawnOverrides): boolean;
313
+ burst(position?: [x,y,z], count?: number, overrides?: SpawnOverrides): boolean;
314
+ start(): boolean;
315
+ stop(): boolean;
316
+ clear(): boolean;
317
+ isEmitting(): boolean;
318
+ getUniforms(): Record<string, { value: unknown }>;
319
+ getParticles(): ParticleAPI;
320
+ }
321
+ ```
322
+
323
+ ### useVFXStore
324
+
325
+ Zustand store for managing particle systems.
326
+
327
+ ```ts
328
+ const store = useVFXStore();
329
+
330
+ // Access registered particle systems
331
+ const sparks = store.getParticles("sparks");
332
+ sparks?.spawn(0, 0, 0, 50);
333
+
334
+ // Store methods
335
+ store.emit("sparks", { x: 0, y: 0, z: 0, count: 20 });
336
+ store.start("sparks");
337
+ store.stop("sparks");
338
+ store.clear("sparks");
339
+ ```
340
+
341
+ ## Examples
342
+
343
+ ### Fire Effect
344
+
345
+ ```tsx
346
+ <VFXParticles
347
+ maxParticles={3000}
348
+ size={[0.3, 0.8]}
349
+ colorStart={["#ff6600", "#ffcc00", "#ff0000"]}
350
+ colorEnd={["#ff0000", "#330000"]}
351
+ fadeSize={[1, 0.2]}
352
+ fadeOpacity={[1, 0]}
353
+ gravity={[0, 0.5, 0]}
354
+ lifetime={[0.4, 0.8]}
355
+ direction={[[-0.3, 0.3], [0.5, 1], [-0.3, 0.3]]}
356
+ speed={[0.01, 0.05]}
357
+ friction={{ intensity: 0.03, easing: "easeOut" }}
358
+ appearance={Appearance.GRADIENT}
359
+ intensity={10}
360
+ />
361
+ ```
362
+
363
+ ### Sphere Burst
364
+
365
+ ```tsx
366
+ <VFXParticles
367
+ maxParticles={500}
368
+ size={[0.05, 0.1]}
369
+ colorStart={["#00ffff", "#0088ff"]}
370
+ fadeOpacity={[1, 0]}
371
+ lifetime={[1, 2]}
372
+ emitterShape={EmitterShape.SPHERE}
373
+ emitterRadius={[0.5, 1]}
374
+ startPositionAsDirection={true}
375
+ speed={[0.1, 0.2]}
376
+ />
377
+ ```
378
+
379
+ ### 3D Geometry Particles
380
+
381
+ ```tsx
382
+ import { BoxGeometry } from "three/webgpu";
383
+
384
+ <VFXParticles
385
+ geometry={new BoxGeometry(1, 1, 1)}
386
+ maxParticles={500}
387
+ size={[0.1, 0.2]}
388
+ colorStart={["#ff00ff", "#aa00ff"]}
389
+ gravity={[0, -2, 0]}
390
+ lifetime={[1, 2]}
391
+ rotation={[[0, Math.PI * 2], [0, Math.PI * 2], [0, Math.PI * 2]]}
392
+ shadow={true}
393
+ lighting={Lighting.STANDARD}
394
+ />
395
+ ```
396
+
397
+ ### Turbulent Smoke
398
+
399
+ ```tsx
400
+ <VFXParticles
401
+ maxParticles={300}
402
+ size={[0.3, 0.6]}
403
+ colorStart={["#666666", "#888888"]}
404
+ colorEnd={["#333333"]}
405
+ fadeSize={[0.5, 1.5]}
406
+ fadeOpacity={[0.6, 0]}
407
+ gravity={[0, 0.5, 0]}
408
+ lifetime={[3, 5]}
409
+ direction={[[-0.1, 0.1], [0.3, 0.5], [-0.1, 0.1]]}
410
+ speed={[0.02, 0.05]}
411
+ turbulence={{
412
+ intensity: 1.2,
413
+ frequency: 0.8,
414
+ speed: 0.3,
415
+ }}
416
+ />
417
+ ```
418
+
419
+ ### Velocity Curves
420
+
421
+ ```tsx
422
+ <VFXParticles
423
+ maxParticles={1000}
424
+ velocityCurve={{
425
+ points: [
426
+ { pos: [0, 1], handleOut: [0.1, 0] },
427
+ { pos: [0.5, 0.2], handleIn: [-0.1, 0], handleOut: [0.1, 0] },
428
+ { pos: [1, 0], handleIn: [-0.1, 0] },
429
+ ],
430
+ }}
431
+ speed={[0.5, 1]}
432
+ lifetime={[2, 3]}
433
+ />
434
+ ```
435
+
436
+ ## TypeScript
437
+
438
+ Full TypeScript support with exported types:
439
+
440
+ ```ts
441
+ import type {
442
+ VFXParticlesProps,
443
+ VFXEmitterProps,
444
+ ParticleAPI,
445
+ SpawnOverrides,
446
+ CurveData,
447
+ TurbulenceConfig,
448
+ CollisionConfig,
449
+ AttractorConfig,
450
+ } from "r3f-vfx";
451
+ ```
452
+
453
+ ## License
454
+
455
+ MIT
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "r3f-vfx",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "https://github.com/mustache-dev/r3f-vfx"
8
+ "url": "git+https://github.com/mustache-dev/r3f-vfx.git"
9
9
  },
10
10
  "exports": {
11
11
  ".": {