npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

wawa-vfx

v1.2.10

Published

A simple and easy-to-use library for creating visual effects in React Three Fiber.

Downloads

774

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

or

yarn add wawa-vfx

Usage

Wawa VFX uses a two-component system:

  • VFXParticles: Defines the particle system and its rendering properties
  • VFXEmitter: 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 uniform
  • vProgress: 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.