Skip to content

Latest commit

 

History

History
409 lines (329 loc) · 11.6 KB

README.md

File metadata and controls

409 lines (329 loc) · 11.6 KB

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 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:

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:

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

<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:

<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:

<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:

<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:

<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:

<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:

<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:

<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:

<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:

<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:

<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:

<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:

<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:

<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:

// 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