wawa-vfx
v1.2.10
Published
A simple and easy-to-use library for creating visual effects in React Three Fiber.
Downloads
774
Maintainers
Readme
wawa-vfx
A simple and easy-to-use library for creating visual effects in React Three Fiber.
Live demo - Fireworks demo - Wizard Game demo
This powerful VFX particle system was developed as part of the comprehensive VFX & Advanced Rendering Chapter in my React Three Fiber: The Ultimate Guide to 3D Web Development course.
In the course, we break down every aspect of this system, explaining the mathematics, optimization techniques, and design patterns that make it work.
Installation
npm install wawa-vfxor
yarn add wawa-vfxUsage
Wawa VFX uses a two-component system:
VFXParticles: Defines the particle system and its rendering propertiesVFXEmitter: Controls how and when particles are emitted into the scene
Basic Example
import { Canvas } from '@react-three/fiber';
import { VFXEmitter, VFXParticles, AppearanceMode } from 'wawa-vfx';
const MyEffect = () => {
return (
<>
{/* Step 1: Define your particle system */}
<VFXParticles
name="particles" // A unique identifier for this particle system
settings={{
nbParticles: 100000, // Maximum number of particles to allocate
gravity: [0, -9.8, 0], // Apply gravity (x, y, z)
fadeSize: [0, 0], // Size fade in/out settings
fadeAlpha: [0, 0], // Opacity fade in/out settings
renderMode: "billboard", // "billboard" or "mesh" or "stretchBillboard"
intensity: 3, // Brightness multiplier
appearance: AppearanceMode.Circular, // Define the default appearance to be plane (default) or circular
easeFunction: "easeLinear", // add easing to the particle animations
}}
/>
{/* Step 2: Define your emitter */}
<VFXEmitter
debug // Show debug visualization
emitter="particles" // Target the particle system by name
settings={{
loop: true, // Continuously emit particles (only if `spawnMode` is 'time')
duration: 1, // Emission cycle duration in seconds
nbParticles: 100, // Number of particles to emit per cycle
spawnMode: "time", // Emission mode: 'time' or 'burst'
delay: 0, // Time delay before starting emission
// Particle lifetime range [min, max]
particlesLifetime: [0.1, 1],
// Position range (min/max)
startPositionMin: [-0.1, -0.1, -0.1],
startPositionMax: [0.1, 0.1, 0.1],
// Rotation range (min/max)
startRotationMin: [0, 0, 0],
startRotationMax: [0, 0, 0],
// Rotation speed range (min/max)
rotationSpeedMin: [0, 0, 0],
rotationSpeedMax: [0, 0, 0],
// Direction range (min/max)
directionMin: [-1, 0, -1],
directionMax: [1, 1, 1],
// Particle size range [min, max]
size: [0.01, 0.25],
// Particle speed range [min, max]
speed: [1, 12],
// Color at start - an array of strings for random selection
colorStart: ["white", "skyblue"],
// Color at end - an array of strings for random selection
colorEnd: ["white", "pink"],
// When true, the emitter will emit particles using its local axes (transformed by its world rotation)
useLocalDirection: true,
}}
/>
</>
);
};
function App() {
return (
<Canvas>
<MyEffect />
</Canvas>
);
}Custom Geometry Example
You can use custom geometries for your particles:
import { useGLTF } from '@react-three/drei';
const CustomParticles = () => {
const { nodes } = useGLTF('/models/sword.glb');
return (
<>
<VFXParticles
name="swords"
geometry={<primitive object={nodes.Sword.geometry} />}
settings={{
nbParticles: 1000,
renderMode: "mesh",
intensity: 2,
}}
/>
<VFXEmitter
emitter="swords"
settings={{
spawnMode: "burst",
nbParticles: 100,
// ... other settings
}}
/>
</>
);
};API Reference
VFXParticles Component
| Property | Type | Description |
| ---------- | ------------- | ------------------------------------------------ |
| name | string | Unique identifier for this particle system |
| settings | object | Configuration options for particles |
| alphaMap | THREE.Texture | Optional texture for particle alpha/transparency |
| geometry | ReactElement | Optional custom geometry for particles |
VFXParticles Settings
| Setting | Type | Default | Description | | ------------- | ----------------------- | ----------------- | -------------------------------------------------------------- | | nbParticles | number | 1000 | Maximum number of particles | | intensity | number | 1 | Brightness multiplier | | renderMode | 'billboard' | 'mesh' | 'stretchBillboard' | 'mesh' | How particles are rendered | | stretchScale | number | 1.0 | Stretch factor for stretchBillboard mode | | fadeSize | [number, number] | [0.1, 0.9] | Size fade in/out range (0-1 of lifetime) | | fadeAlpha | [number, number] | [0, 1.0] | Opacity fade in/out range | | gravity | [number, number, number]| [0, 0, 0] | Gravity force applied to particles | | frustumCulled | boolean | true | Whether particles are frustum culled | | appearance | AppearanceMode | AppearanceMode.Square | Particle appearance (Square or Circular) | | easeFunction | EaseFunction | 'easeLinear' | Easing function for particle animations | | blendingMode | THREE.Blending | AdditiveBlending | How particles blend with the scene | | shadingHooks | object | {} | Custom GLSL shader hooks for advanced rendering effects |
VFXEmitter Component
| Property | Type | Description |
| --------------- | ------- | ------------------------------------------- |
| emitter | string | Name of the target particle system |
| settings | object | Configuration options for emission behavior |
| debug | boolean | Show Leva controls to adjust settings |
| autoStart | boolean | Automatically start emitting |
| localDirection| boolean | Use emitter's local space for directions |
VFXEmitter Settings
| Setting | Type | Default | Description | | ----------------- | -------------------------- | ---------- | ------------------------------------------------ | | loop | boolean | true | Continuously emit particles | | duration | number | 1 | Emission cycle duration in seconds | | nbParticles | number | 100 | Number of particles to emit per cycle | | spawnMode | 'time' | 'burst' | 'time' | How particles are spawned | | delay | number | 0 | Time delay before starting emission | | particlesLifetime | [number, number] | [0.1, 1] | Particle lifetime range [min, max] | | startPositionMin | [number, number, number] | [-0.1, -0.1, -0.1] | Minimum start position | | startPositionMax | [number, number, number] | [0.1, 0.1, 0.1] | Maximum start position | | startRotationMin | [number, number, number] | [0, 0, 0] | Minimum start rotation | | startRotationMax | [number, number, number] | [0, 0, 0] | Maximum start rotation | | rotationSpeedMin | [number, number, number] | [0, 0, 0] | Minimum rotation speed | | rotationSpeedMax | [number, number, number] | [0, 0, 0] | Maximum rotation speed | | directionMin | [number, number, number] | [-1, 0, -1]| Minimum emission direction | | directionMax | [number, number, number] | [1, 1, 1] | Maximum emission direction | | size | [number, number] | [0.01, 0.25] | Particle size range [min, max] | | speed | [number, number] | [1, 12] | Particle speed range [min, max] | | colorStart | string[] | ['white'] | Colors at start (randomly selected) | | colorEnd | string[] | ['white'] | Colors at end (randomly selected) | | useLocalDirection | boolean | false | Use emitter's local space for directions |
Controlling Emitters with useVFX Hook
You can programmatically control emitters using the useVFX hook:
import { useVFX } from 'wawa-vfx';
const ControlledEffect = () => {
const { emit } = useVFX();
const handleClick = () => {
// Emit particles programmatically
emit('myParticles', 10, () => ({
position: [0, 0, 0],
direction: [0, 1, 0],
scale: [1, 1, 1],
rotation: [0, 0, 0],
rotationSpeed: [0, 0, 0],
lifetime: [1, 2],
colorStart: '#ff0000',
colorEnd: '#0000ff',
speed: [5],
}));
};
return (
<>
<VFXParticles name="myParticles" settings={{ nbParticles: 1000 }} />
<mesh onClick={handleClick}>
<boxGeometry />
<meshBasicMaterial color="red" />
</mesh>
</>
);
};Using with VFXEmitterRef
You can control emitters through refs:
import { useRef } from 'react';
import { VFXEmitter, VFXEmitterRef } from 'wawa-vfx';
const RefControlledEffect = () => {
const emitterRef = useRef<VFXEmitterRef>(null);
const handleStart = () => {
emitterRef.current?.startEmitting(true); // true to reset
};
const handleStop = () => {
emitterRef.current?.stopEmitting();
};
const handleEmitAt = (position: THREE.Vector3) => {
emitterRef.current?.emitAtPos(position, true);
};
return (
<>
<VFXParticles name="controlled" settings={{ nbParticles: 5000 }} />
<VFXEmitter
ref={emitterRef}
emitter="controlled"
autoStart={false}
settings={{ /* ... */ }}
/>
</>
);
};Features
✨ New Features
Stretch Billboard Mode
A new renderMode that renders particles as billboards that stretch along their velocity direction, ideal for effects like trails, speed lines, or fire streaks.
<VFXParticles
name="trails"
settings={{
renderMode: "stretchBillboard",
stretchScale: 2.0,
// ...
}}
/>Particle Easings
Apply easing functions for smooth transitions over the particle's lifetime. Includes 42 easing functions with TypeScript autocomplete support.
<VFXParticles
name="smooth"
settings={{
easeFunction: "easeInOutCubic", // TypeScript will suggest all 42 options
// ...
}}
/>Explicit Appearance Mode
Define whether particles appear as planes (default) or circular.
import { AppearanceMode } from 'wawa-vfx';
<VFXParticles
name="circles"
settings={{
appearance: AppearanceMode.Circular,
// ...
}}
/>Local Direction Control
Control whether particles emit in the emitter's local space or world space.
<VFXEmitter
emitter="particles"
settings={{
useLocalDirection: true, // Particles follow emitter rotation
// ...
}}
/>Custom Shader Hooks
Add custom GLSL shader code to modify particle rendering for advanced visual effects:
<VFXParticles
name="customShaderEffect"
settings={{
nbParticles: 5000,
renderMode: "billboard",
shadingHooks: {
fragmentBeforeOutput: /* glsl */ `
// Add a simple pulsing effect based on time
float pulse = 0.5 + 0.5 * sin(uTime * 10.0 + vProgress * 10.0);
finalColor.rgb *= pulse;
finalColor.r = 1.0; // Enhance red channel
`,
},
// ... other settings
}}
/>Available shader hooks:
- customUniforms: Add custom uniform declarations
- customVaryings: Add custom varying declarations
- vertexBeforeOutput: Modify vertex shader output before final gl_Position
- fragmentBeforeOutput: Modify the final color before it's written to the screen
Built-in shader variables accessible in hooks:
uTime: Current time uniformvProgress: Particle lifetime progress (0 to 1)finalColor: The computed particle color in fragment shader
Key Benefits
- 🚀 Easy to Use: Create complex particle effects with minimal code
- 🎨 Flexible Customization: Extensive settings for fine-tuning visual effects
- ⚡ Performance Optimized: Uses instanced rendering for efficient particle systems
- 🔧 TypeScript Support: Full type definitions for better development experience
- 🎮 Debug Mode: Built-in Leva controls for real-time tweaking
- 📦 Custom Geometry: Support for any Three.js geometry as particles
- 🎯 Programmatic Control: Full control through hooks and refs
Examples
Check out the examples directory in the repository for more complex implementations:
- Fire effects
- Fireworks
- Magic spells
- Environmental effects
- Custom geometry particles
For Vanilla Three.js
If you're using vanilla Three.js without React, check out our companion package: wawa-vfx-vanilla
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
