Particle system for ThreeJS.
- Easy integration with Three.js.
- Visual editor for creating and fine-tuning effects: THREE Particles Editor
- Highly customizable particle properties (position, velocity, size, color, alpha, rotation, etc.).
- Support for various emitter shapes and parameters.
- Force fields and attractors for dynamic particle behavior (point attraction/repulsion, directional wind).
- Sub-emitters triggered on particle birth or death events.
- Serialization support for saving and loading particle system configs.
- GPU instancing renderer (
RendererType.INSTANCED) — removesgl_PointSizehardware limit, ideal for large particles or high particle counts. - Trail / Ribbon renderer (
RendererType.TRAIL) — continuous ribbon trails behind particles with configurable width, opacity, and color tapering. - Mesh particle renderer (
RendererType.MESH) — render each particle as a 3D mesh (debris, gems, coins) using GPU instancing with full 3D rotation and simple directional lighting. - Soft particles — depth-based alpha fade near opaque geometry, eliminating hard intersection lines.
- TypeDoc API documentation available.
- Editor & Live Demo: https://newkrok.com/three-particles-editor/index.html
- CodePen Basic Example: https://codepen.io/NewKrok/pen/GgRzEmP
- CodePen Fire Animation: https://codepen.io/NewKrok/pen/ByabNRJ
- CodePen Projectile Simulation: https://codepen.io/NewKrok/pen/jEEErZy
- Video - Projectiles: https://youtu.be/Q352JuxON04
- Video - First Preview: https://youtu.be/dtN_bndvoGU
npm install @newkrok/three-particlesInclude the script directly in your HTML:
<script src="https://cdn.jsdelivr.net/npm/@newkrok/three-particles@latest/dist/three-particles.min.js"></script>
<!-- or -->
<script src="https://unpkg.com/@newkrok/three-particles@latest/dist/three-particles.min.js"></script>Here's a basic example of how to load and use a particle system:
// Create a particle system
const effect = {
// Your effect configuration here
// It can be empty to use default settings
};
const system = createParticleSystem(effect);
scene.add(system.instance);
// Update the particle system in your animation loop
// Pass the current time, delta time, and elapsed time
updateParticleSystems({now, delta, elapsed});
// Update configuration at runtime without recreating the system
system.updateConfig({
gravity: -9.8,
forceFields: [{ type: 'DIRECTIONAL', direction: { x: 1, y: 0, z: 0 }, strength: 5 }],
});The library works seamlessly with React Three Fiber. No additional wrapper package is needed — use createParticleSystem directly with React hooks:
import { useRef, useEffect } from "react";
import { useFrame } from "@react-three/fiber";
import {
createParticleSystem,
Shape,
type ParticleSystem,
} from "@newkrok/three-particles";
import * as THREE from "three";
function FireEffect({ config }: { config?: Record<string, unknown> }) {
const groupRef = useRef<THREE.Group>(null);
const systemRef = useRef<ParticleSystem | null>(null);
useEffect(() => {
const system = createParticleSystem({
duration: 5,
looping: true,
maxParticles: 200,
startLifetime: { min: 0.5, max: 1.5 },
startSpeed: { min: 1, max: 3 },
startSize: { min: 0.3, max: 0.8 },
startColor: {
min: { r: 1, g: 0.2, b: 0 },
max: { r: 1, g: 0.8, b: 0 },
},
gravity: -1,
emission: { rateOverTime: 50 },
shape: { shape: Shape.CONE, cone: { angle: 0.2, radius: 0.3 } },
renderer: {
blending: THREE.AdditiveBlending,
transparent: true,
depthWrite: false,
},
...config,
});
systemRef.current = system;
groupRef.current?.add(system.instance);
return () => {
system.dispose();
};
}, [config]);
useFrame((_, delta) => {
systemRef.current?.update({
now: performance.now(),
delta,
elapsed: 0,
});
});
return <group ref={groupRef} />;
}
// In your R3F Canvas:
// <Canvas>
// <FireEffect />
// </Canvas>Key points:
- Use
useEffectto create and dispose the particle system - Use
useFrameto drive updates each frame (callsystem.update()instead ofupdateParticleSystems()for per-system control) - Add the
system.instanceto a<group>ref so R3F manages the scene graph - Return a cleanup function from
useEffectthat callssystem.dispose()
Automatically generated TypeDoc: https://newkrok.github.io/three-particles/api/
The colorOverLifetime feature uses a multiplier-based approach (similar to Unity's particle system), where each RGB channel curve acts as a multiplier applied to the particle's startColor.
Formula: finalColor = startColor * colorOverLifetime
startColor to white { r: 1, g: 1, b: 1 }. If any channel in startColor is set to 0, that channel cannot be modified by colorOverLifetime.
Example - Rainbow effect:
{
startColor: {
min: { r: 1, g: 1, b: 1 }, // White - allows full color range
max: { r: 1, g: 1, b: 1 }
},
colorOverLifetime: {
isActive: true,
r: { // Red: full → half → off
type: 'BEZIER',
scale: 1,
bezierPoints: [
{ x: 0, y: 1, percentage: 0 },
{ x: 0.5, y: 0.5, percentage: 0.5 },
{ x: 1, y: 0, percentage: 1 }
]
},
g: { // Green: off → full → off
type: 'BEZIER',
scale: 1,
bezierPoints: [
{ x: 0, y: 0, percentage: 0 },
{ x: 0.5, y: 1, percentage: 0.5 },
{ x: 1, y: 0, percentage: 1 }
]
},
b: { // Blue: off → half → full
type: 'BEZIER',
scale: 1,
bezierPoints: [
{ x: 0, y: 0, percentage: 0 },
{ x: 0.5, y: 0.5, percentage: 0.5 },
{ x: 1, y: 1, percentage: 1 }
]
}
}
}