r3f-particle-system
Version:
A declarative particle system for React Three Fiber. It uses an FBO Simulation to provide incremental updates to particles, allowing for an intuitive API with use of forces. **Note**: This package is experimental and may introduce breaking changes in futu
409 lines (329 loc) • 11.9 kB
Markdown
# r3f-particle-system
A declarative particle system for React Three Fiber. It uses an FBO Simulation to provide incremental updates to particles, allowing for an intuitive API with use of forces. **Note**: This package is experimental and may introduce breaking changes in future releases.
[Check out the demo](https://r3f-particle-system-examples.vercel.app/) to see it in action! This is just one of many possible configurations for this tool.
## Installation
This package relies on the following peer dependencies. You need to have them installed in your project:
| Package | Required Version |
|----------------------|------------------|
| `@react-three/drei` | `^9.101.0` |
| `@react-three/fiber` | `^8.15.19` |
| `react` | `^18` |
| `three` | `^0.170.0` |
### Install Peer Dependencies
To install all the required peer dependencies for with this package, run the following command:
```bash
npm install @react-three/drei@^9.101.0 @react-three/fiber@^8.15.19 react@^18 three@^0.170.0
```
### Install Package
Once peer dependencies are installed, install the package:
```bash
npm install r3f-particle-system
```
## Usage
The particle system consists of four main components: `ParticleSystem`, `Emitters`, `Forces`, and `Particles`. The `ParticleSystem` acts as a context wrapper and must be included for the system to function. Within it, you can use one `Emitter`, one `Particle`, and multiple `Forces` to define and manipulate the behavior of particles.
### Example
```jsx
<ParticleSystem>
<BoxEmitter speed={[ 0.8, 1 ]} life={[ 1, 10 ]} />
<DirectionalForce direction={[ 0, 1, 0 ]} randomAmt={ 0.1 } strength={ 2 } />
<RotationalForce strength={ 1.5 } />
<PointParticle
shape="circle"
color={[ "cyan", "magenta" ]}
size={[ 5, 25 ]}
alphaFade={[ 0.25, 1 ]}
/>
</ParticleSystem>
```
---
## Components
### ParticleSystem
The core context provider that manages data across the particle system.
**Props**:
- `simulationSpace`: Determines the coordinate space for particles (`"local"` | `"world"`)
- `normalizeForces`: When `true`, normalizes the sum of all forces (useful for maintaining predictable speeds).
Example w/ Default Values:
```jsx
<ParticleSystem
simulationSpace="local"
normalizeForces={ false }
/>
```
---
## Emitters
Emitters are responsible for spawning particles. Only one emitter can be used per system.
### BoxEmitter
Spawns particles from within a box-shaped volume.
**Props**:
- `size`: Particle buffer size (e.g., `128` = 16,384 particles).
- `spawnRate`: Number of particles spawned per second.
- `life`: Particle lifespan or min and max life (`life` | `[min, max]`).
- `speed`: Particle speed or man and max speed (`speed` | `[min, max]`).
- `bounds`: Dimensions of the box `[x, y, z]`.
Example w/ Default Values:
```jsx
<BoxEmitter
size={ 128 }
spawnRate={ 500 }
life={[ 1.0, 5.0 ]}
speed={[ 0.5, 2.0 ]}
bounds={[ 1, 1, 1 ]}
/>
```
### CurveEmitter
Spawns particles along a curve.
**Props**:
- `size`: Particle buffer size (e.g., `128` = 16,384 particles).
- `spawnRate`: Number of particles spawned per second.
- `life`: Particle lifespan or min and max life (`life` | `[min, max]`).
- `speed`: Particle speed or man and max speed (`speed` | `[min, max]`).
- `points`: Array of control points defining the curve.
- `controlOffset`: Smoothness of curve corners.
- `curve`: Optional `THREE.Curve` instance (overrides `points`).
- `sampleMode`: Distribution mode along the curve (`"random"` | `"sequential"`).
- `isLoop`: Whether the curve loops.
- `resolution`: How many points make up this curve.
- `randomOffset`: Random positional offset for particles.
- `debug`: When true shows the curve and control points.
Example w/ Default Values:
```jsx
<CurveEmitter
size={ 128 }
spawnRate={ 500 }
life={[ 1.0, 5.0 ]}
speed={[ 0.5, 2.0 ]}
points={[[ 0, 0, 0 ], [ 0, 1, 0 ]]}
controlOffset={ 0.1 }
curve={ null }
sampleMode="random"
isLoop={ false }
resolution={ 100 }
randomOffset={ 0.1 }
debug={ false }
/>
```
### CylinderEmitter
Spawns particles within a cylindrical volume.
**Props**:
- `size`: Particle buffer size (e.g., `128` = 16,384 particles).
- `spawnRate`: Number of particles spawned per second.
- `life`: Particle lifespan or min and max life (`life` | `[min, max]`).
- `speed`: Particle speed or man and max speed (`speed` | `[min, max]`).
- `innerRadius`: Inner radius of the cylinder (no particles spawn inside).
- `outerRadius`: Outer radius of the cylinder.
- `height`: Cylinder height.
- `arc`: Angular section of the cylinder (in radians).
Example w/ Default Values:
```jsx
<CylinderEmitter
size={ 128 }
spawnRate={ 500 }
life={[ 1.0, 5.0 ]}
speed={[ 0.5, 2.0 ]}
innerRadius={ 0.0 },
outerRadius={ 1.0 },
height={ 0.0 },
arc={ Math.PI * 2 }
/>
```
### MeshEmitter
Spawns particles from the surface of a mesh.
**Props**:
- `size`: Particle buffer size (e.g., `128` = 16,384 particles).
- `spawnRate`: Number of particles spawned per second.
- `life`: Particle lifespan or min and max life (`life` | `[min, max]`).
- `speed`: Particle speed or man and max speed (`speed` | `[min, max]`).
- `sampleMode`: Determines particle spawn positions (`"random"` | `"sequential"` | `"shuffled"`).
- `debug`: If `true`, renders the input mesh for visualization.
**Note**: The source mesh must be a child of this emitter.
Example w/ Default Values:
```jsx
<MeshEmitter
size={ 128 }
spawnRate={ 500 }
life={[ 1.0, 5.0 ]}
speed={[ 0.5, 2.0 ]}
sampleMode="random",
debug={ false }
>
<mesh>
<sphereGeometry>
<meshBasicMaterial>
</mesh>
</MeshEmitter>
```
### SphereEmitter
Spawns particles within a spherical volume.
**Props**:
- `size`: Particle buffer size (e.g., `128` = 16,384 particles).
- `spawnRate`: Number of particles spawned per second.
- `life`: Particle lifespan or min and max life (`life` | `[min, max]`).
- `speed`: Particle speed or man and max speed (`speed` | `[min, max]`).
- `radius`: Radius of the sphere.
- `surface`: If `true`, particles conform to the sphere's surface.
Example w/ Default Values:
```jsx
<SphereEmitter
size={ 128 }
spawnRate={ 500 }
life={[ 1.0, 5.0 ]}
speed={[ 0.5, 2.0 ]}
radius={ 1 },
surface={ false },
/>
```
---
## Forces
Forces affect the movement and behavior of particles. Multiple forces can be applied simultaneously.
### DirectionalForce
Applies a constant directional force.
**Props**:
- `direction`: Force direction vector (`[x, y, z]`).
- `randomAmt`: Random deviation from the base direction.
- `strength`: Strength of the force.
Example w/ Default Values:
```jsx
<DirectionalForce
direction={[ 0, 1, 0 ]},
randomAmt={ 0 },
strength={ 1 }
/>
```
### RotationalForce
Applies rotational force around a center.
**Props**:
- `center`: Center point of rotation.
- `axis`: Rotation axis (`"xy"` | `"xz"` | `"yz"`).
- `strength`: Strength of the force.
Example w/ Default Values:
```jsx
<RotationalForce
axis="xyz",
seed="random",
period={ 1 },
strength={ 1 }
/>
```
### NoiseForce
Applies a curl noise force to particles.
**Props**:
- `axis`: Which axes are affected by the force (`"xyz"` | `"xy"` | `"xz"` | `"yz"`).
- `seed`: Seed position (`[x, y, z]` | `"random"`).
- `period`: The period of the noise.
- `strength`: Strength of the force.
Example w/ Default Values:
```jsx
<NoiseForce
axis="xyz",
seed="random",
period={ 1 },
strength={ 1 }
/>
```
### CurveTangentForce
Applies a force along the tangent of a curve. Only has Eefect when using CurveEmitter.
**Props**:
- `direction`: The direction of the force along the curve (`1` | `-1`)
- `randomAmt`: Random deviation from the base direction.
- `strength`: Strength of the force.
Example w/ Default Values:
```jsx
<CurveTangentForce
direction={ 1 },
randomAmt={ 0.25 },
strength={ 1 }
/>
```
### NormalForce
Applies a force perpendicular to a surface. Only has effect when using the MeshEmitter.
**Props**:
- `strength`: Strength of the rotation.
Example w/ Default Values:
```jsx
<NormalForce
strength={ 1 }
/>
```
### PointForce
Dynamically applies a force from a point source up to an effective threshold.
**Props**:
- `position`: The center positions of the force.
- `direction`: The direction of the force relative to the center position (`"towards"` | `"away"`).
- `axis`: Which axes are affected by the force (`"xyz"` | `"xy"` | `"xz"` | `"yz"`).
- `effectiveDist`: The extents this force affects particles around its center.
- `strength`: Strength of the rotation.
- `returnForce`: Force particles back to their original position when outside effectiveDist.
- `returnForce`: The strength of the return force.
Example w/ Default Values:
```jsx
<PointForce
position={[ 0, 0, 0 ]},
direction="towards",
axis="xyz",
effectiveDist={ 1 },
strength={ 1 },
returnForce={ true },
returnStrength={ 1 }
/>
```
---
## Particles
### PointParticle
Defines individual particles rendered as points.
**Props**:
- `shape`: Particle shape (`"circle"` | `"square"` | `"softCircle"` | `"point"`).
- `color`: Particle color (single value or array for gradients).
- `colorMode`: Determines how colors are applied (`"overLife"` | `"random"`).
- `size`: Particle size or range (`size` | `[min, max]`).
- `alphaFade`: Fade-in and fade-out alpha values.
- `depthWrite`: depthWrite value for the particles.
- `depthTest`: depthTest value for the particles.
- `blending`: blending value for the particles.
Example w/ Default Values:
```jsx
<PointParticle
shape="circle"
color="blue"
colorMode="overLife",
size={ 50 }
alphaFade={ 1 }
depthWrite={ false },
depthTest={ true },
blending={ THREE.NormalBlending },
/>
```
---
## Imperative API & Particle Bursts
Refs of `ParticleSystem` can be used to expose it's imperative API. Available methods are `stop()`, `start()`, and `createBurst()`. After stopping the system, `createBurst(numParticles)` can be used to create a burst of particles at runtime.
### Example Usage:
```jsx
// create ref for particle system
const systemRef = useRef();
// pause the spawning of particles
useEffect(() => {
systemRef.current.pause();
}, []);
// create burst of 2000 particles using 'onpointerdown' event
useEffect(() => {
const burst = () => systemRef.current.createBurst(2000);
window.addEventListener('pointerdown', burst);
return () => window.removeEventListener('pointerdown', burst);
}, []);
return (
<ParticleSystem ref={ systemRef }>
...
</ParticleSystem>
);
```
---
## Roadmap
Planned features for future releases:
- **MeshParticle Component**: Instanced meshes as particles for more advanced visuals.
- **Color Sampling**: Allow particles to inherit colors from meshes or textures.
- **Lighting**: Allow particles to be affected by scene lighting.
- **Scene Collisions**: Allow for particle collisions with scene using depth buffer
- **Strength over time**: Option for strength over time to give move control with forces.
---
## License
MIT License
---