Behaviors
Behaviors are components that control how particles evolve over time in the three.quarks particle system. They modify particle properties such as position, velocity, color, size, and rotation throughout the particle’s lifetime, allowing you to create complex and realistic effects.
Overview
In three.quarks, behaviors are modular components that you add to a particle system. Each behavior focuses on a specific aspect of particle animation, and multiple behaviors can be combined to create sophisticated effects.
Using Behaviors
Behaviors are added to a particle system through the behaviors
array in the ParticleSystem constructor:
const particleSystem = new ParticleSystem({
// ... other particle system properties
behaviors: [
new SizeOverLife(new PiecewiseBezier([[new Bezier(1, 1.5, 0.5, 0), 0]])),
new ColorOverLife(
new Gradient([
[new Vector3(1, 0.5, 0.1), 0],
[new Vector3(0.1, 0.1, 0.1), 1],
]),
),
new ApplyForce(new Vector3(0, 5, 0), new ConstantValue(1)),
],
});
Behavior Categories
Behaviors in three.quarks can be grouped into three main categories:
Movement Behaviors
These behaviors affect the position, velocity, or rotation of particles.
Behavior | Description |
---|---|
ApplyForce | Applies a constant force to particles |
GravityForce | Applies gravitational force toward a point |
OrbitOverLife | Makes particles orbit around an axis |
SpeedOverLife | Controls particle speed throughout lifetime |
LimitSpeedOverLife | Caps particle speed throughout lifetime |
TurbulenceField | Applies realistic turbulence to particle movement |
Noise | Adds random noise to position and rotation |
ForceOverLife | Applies a varying force based on lifetime |
ApplyCollision | Handles collisions with objects or surfaces |
Visual Behaviors
These behaviors affect the appearance of particles.
Behavior | Description |
---|---|
ColorOverLife | Changes particle color throughout lifetime |
ColorBySpeed | Changes particle color based on speed |
SizeOverLife | Changes particle size throughout lifetime |
SizeBySpeed | Changes particle size based on speed |
RotationOverLife | Controls particle rotation throughout lifetime |
Rotation3DOverLife | Controls 3D particle rotation throughout lifetime |
RotationBySpeed | Controls particle rotation based on speed |
FrameOverLife | Animates sprite sheets throughout lifetime |
WidthOverLength | Controls the width of particle trails over their length |
Advanced Behaviors
These behaviors offer more complex functionality.
Behavior | Description |
---|---|
EmitSubParticleSystem | Emits child particle systems from parent particles |
ChangeEmitDirection | Modifies the emission direction of particles |
ApplySequences | Controls sequences of particle movements |
Detailed Behavior Documentation
Movement Behaviors
ApplyForce
Applies a constant force to particles, affecting their velocity over time.
new ApplyForce(
direction, // Vector3: Direction of the force
magnitude, // ValueGenerator: Magnitude of the force
);
Example:
// Apply an upward force
new ApplyForce(new Vector3(0, 1, 0), new ConstantValue(5));
GravityForce
Applies a gravitational force that pulls particles toward a central point.
new GravityForce(
center, // Vector3: The point particles are attracted to
magnitude, // number: Strength of the gravity
);
Example:
// Create a gravitational pull toward the origin
new GravityForce(new Vector3(0, 0, 0), 10);
OrbitOverLife
Makes particles orbit around an axis.
new OrbitOverLife(
orbitSpeed, // ValueGenerator or FunctionValueGenerator: Speed of orbit
axis, // Vector3: Axis to orbit around (default is y-axis)
);
Example:
// Make particles orbit around the y-axis
new OrbitOverLife(
new ConstantValue(Math.PI / 2), // Half a rotation per second
new Vector3(0, 1, 0),
);
SpeedOverLife
Controls particle speed throughout their lifetime.
new SpeedOverLife(
speed, // FunctionValueGenerator: Speed multiplier over lifetime
);
Example:
// Particles start fast and slow down over time
new SpeedOverLife(new PiecewiseBezier([[new Bezier(1, 0.8, 0.4, 0), 0]]));
LimitSpeedOverLife
Caps particle speed throughout their lifetime.
new LimitSpeedOverLife(
speed, // FunctionValueGenerator: Maximum speed over lifetime
dampen, // number: Damping factor (0-1)
);
Example:
// Limit particle speed with increasing restriction over time
new LimitSpeedOverLife(
new PiecewiseBezier([[new Bezier(10, 8, 5, 2), 0]]),
0.5,
);
TurbulenceField
Applies realistic turbulence to particle movement, creating natural flowing motion.
new TurbulenceField(
scale, // Vector3: Scale of the turbulence field
octaves, // number: Number of noise octaves (detail level)
velocityMultiplier, // Vector3: Strength of turbulence per axis
timeScale, // Vector3: Speed of turbulence change per axis
);
Example:
// Create realistic air turbulence
new TurbulenceField(
new Vector3(2, 2, 2),
3,
new Vector3(1, 2, 1),
new Vector3(0.1, 0.1, 0.1),
);
Noise
Adds random noise to particle position and rotation.
new Noise(
frequency, // ValueGenerator: Noise frequency
power, // ValueGenerator: Noise strength
positionAmount, // ValueGenerator: Amount to affect position (0-1)
rotationAmount, // ValueGenerator: Amount to affect rotation (0-1)
);
Example:
// Add gentle noise to particle position
new Noise(
new ConstantValue(1),
new ConstantValue(0.5),
new ConstantValue(0.3),
new ConstantValue(0),
);
ForceOverLife
Applies a force that varies based on particle lifetime.
new ForceOverLife(
x, // FunctionValueGenerator: X-axis force over lifetime
y, // FunctionValueGenerator: Y-axis force over lifetime
z, // FunctionValueGenerator: Z-axis force over lifetime
);
Example:
// Force that varies over lifetime
new ForceOverLife(
new PiecewiseBezier([[new Bezier(0, 0.5, -0.5, 0), 0]]),
new PiecewiseBezier([[new Bezier(0, 1, 1, 0), 0]]),
new ConstantValue(0),
);
ApplyCollision
Handles collisions with objects or surfaces, with bounce effects.
new ApplyCollision(
resolver, // PhysicsResolver: Object that detects collisions
bounce, // number: Bounciness factor (0-1)
);
Example:
// Resolve collisions with ground plane with medium bounce
const resolver = {
resolve: (pos, normal) => {
// Simple ground plane collision
if (pos.y < 0) {
normal.set(0, 1, 0);
return true;
}
return false;
},
};
new ApplyCollision(resolver, 0.7);
Visual Behaviors
ColorOverLife
Changes particle color throughout lifetime.
new ColorOverLife(
color, // FunctionColorGenerator: Color gradient over lifetime
);
Example:
// Particles fade from yellow to red to black
new ColorOverLife(
new Gradient(
[
[new Vector3(1, 0.8, 0.2), 0], // Yellow
[new Vector3(1, 0.2, 0.1), 0.5], // Red
[new Vector3(0.1, 0.1, 0.1), 1], // Black
],
[
[1, 0], // Fully opaque
[0.8, 0.5], // Slightly transparent
[0, 1], // Fully transparent
],
),
);
ColorBySpeed
Changes particle color based on their speed.
new ColorBySpeed(
color, // FunctionColorGenerator: Color gradient based on speed
speedRange, // IntervalValue: Min and max speed range
);
Example:
// Particles change color based on speed
new ColorBySpeed(
new Gradient([
[new Vector3(0, 0, 1), 0], // Blue (slow)
[new Vector3(0, 1, 0), 0.5], // Green (medium)
[new Vector3(1, 0, 0), 1], // Red (fast)
]),
new IntervalValue(0, 10), // Speed range 0-10
);
SizeOverLife
Changes particle size throughout lifetime.
new SizeOverLife(
size, // FunctionValueGenerator or Vector3Generator: Size over lifetime
);
Example:
// Particles grow then shrink
new SizeOverLife(new PiecewiseBezier([[new Bezier(0.5, 1, 1, 0), 0]]));
For different scaling per axis:
// Non-uniform scaling over lifetime
new SizeOverLife(
new Vector3Function(
new PiecewiseBezier([[new Bezier(1, 1.5, 1, 0.5), 0]]), // X axis
new PiecewiseBezier([[new Bezier(1, 0.5, 1.5, 1), 0]]), // Y axis
new ConstantValue(1), // Z axis
),
);
SizeBySpeed
Changes particle size based on their speed.
new SizeBySpeed(
size, // FunctionValueGenerator or Vector3Generator: Size based on speed
speedRange, // IntervalValue: Min and max speed range
);
Example:
// Faster particles are larger
new SizeBySpeed(
new PiecewiseBezier([[new Bezier(0.5, 1, 1.5, 2), 0]]),
new IntervalValue(0, 10), // Speed range 0-10
);
RotationOverLife
Controls 2D particle rotation throughout lifetime.
new RotationOverLife(
angularVelocity, // ValueGenerator or FunctionValueGenerator: Rotation speed
);
Example:
// Particles rotate at constant speed
new RotationOverLife(
new ConstantValue(Math.PI), // Half rotation per second
);
Rotation3DOverLife
Controls 3D particle rotation throughout lifetime. Works with mesh particles.
new Rotation3DOverLife(
angularVelocity, // RotationGenerator: 3D rotation control
);
Example:
// Particles rotate around the y-axis
new Rotation3DOverLife(
new AxisAngleGenerator(new Vector3(0, 1, 0), new ConstantValue(Math.PI)),
);
RotationBySpeed
Controls particle rotation based on speed.
new RotationBySpeed(
angularVelocity, // ValueGenerator or FunctionValueGenerator: Rotation speed
speedRange, // IntervalValue: Min and max speed range
);
Example:
// Faster particles rotate more quickly
new RotationBySpeed(
new PiecewiseBezier([[new Bezier(0, Math.PI / 2, Math.PI, Math.PI * 2), 0]]),
new IntervalValue(0, 10), // Speed range 0-10
);
FrameOverLife
Animates sprite sheets throughout particle lifetime.
new FrameOverLife(
frame, // FunctionValueGenerator: Frame index over lifetime
);
Example:
// Animate through a 4x4 sprite sheet
new FrameOverLife(new PiecewiseBezier([[new Bezier(0, 5, 10, 15), 0]]));
WidthOverLength
Controls the width of particle trails over their length. Only works with TrailParticle.
new WidthOverLength(
width, // FunctionValueGenerator: Width over trail length
);
Example:
// Trail width tapers off
new WidthOverLength(new PiecewiseBezier([[new Bezier(1, 0.8, 0.4, 0), 0]]));
Advanced Behaviors
EmitSubParticleSystem
Emits child particle systems from parent particles.
new EmitSubParticleSystem(
particleSystem, // IParticleSystem: Parent particle system
useVelocityAsBasis, // boolean: Whether to align with particle velocity
subParticleSystem, // IEmitter: Child particle system to emit
mode, // SubParticleEmitMode: When to emit (Death, Birth, Frame)
emitProbability, // number: Probability of emission (0-1)
);
Example:
// Emit explosion sub-system when particles die
new EmitSubParticleSystem(
particleSystem,
true,
explosionSystem.emitter,
SubParticleEmitMode.Death,
1,
);
ChangeEmitDirection
Modifies the emission direction of particles.
new ChangeEmitDirection(
angle, // ValueGenerator: Angle to change direction by
);
Example:
// Randomize emission direction within a cone
new ChangeEmitDirection(
new IntervalValue(0, Math.PI / 6), // 0-30 degrees
);
ApplySequences
Controls sequences of particle movements, useful for complex motion paths.
new ApplySequences(
delayBetweenParticles, // number: Delay between particle sequence starts
);
Example creating a text sequence:
const sequence = new ApplySequences(0.05);
// Add a sequence where particles form text
sequence.appendSequencer(
new IntervalValue(0, 2), // Time range
new TextureSequencer(10, 10, new Vector3(0, 0, 0)),
);
// Add the sequence behavior
particleSystem.behaviors.push(sequence);
Combining Behaviors
Multiple behaviors can be combined to create complex effects. Behaviors are processed in the order they appear in the array:
// Create a fire effect with multiple behaviors
const particleSystem = new ParticleSystem({
// ... other properties
behaviors: [
// Size change over lifetime
new SizeOverLife(new PiecewiseBezier([[new Bezier(0.5, 1, 1, 0), 0]])),
// Color change over lifetime
new ColorOverLife(
new Gradient(
[
[new Vector3(1, 0.8, 0.2), 0], // Yellow
[new Vector3(1, 0.2, 0.1), 0.5], // Red
[new Vector3(0.1, 0.1, 0.1), 1], // Black
],
[
[1, 0],
[0.8, 0.5],
[0, 1],
],
),
),
// Upward force
new ApplyForce(new Vector3(0, 5, 0), new ConstantValue(1)),
// Random rotation
new RotationOverLife(new IntervalValue(-Math.PI, Math.PI)),
// Natural turbulence
new TurbulenceField(
new Vector3(2, 2, 2),
2,
new Vector3(1, 2, 1),
new Vector3(0.1, 0.1, 0.1),
),
],
});
Creating Custom Behaviors
You can create custom behaviors by implementing the Behavior interface:
class MyCustomBehavior {
// Type identifier for the behavior
type = "MyCustomBehavior";
// Constructor for your behavior
constructor(param1, param2) {
this.param1 = param1;
this.param2 = param2;
}
// Called once when a particle is created
initialize(particle, particleSystem) {
// Initialize particle properties
}
// Called every frame for each particle
update(particle, delta) {
// Update particle properties based on delta time
}
// Called once per frame before updating particles
frameUpdate(delta) {
// Update behavior state before processing particles
}
// Called when you want to reset behavior state
reset() {
// Reset internal state
}
// For serialization
toJSON() {
return {
type: this.type,
param1: this.param1,
param2: this.param2,
};
}
// Create a new instance of this behavior
clone() {
return new MyCustomBehavior(this.param1, this.param2);
}
}
// Use your custom behavior
particleSystem.behaviors.push(new MyCustomBehavior(10, "custom"));
Performance Considerations
-
Behavior Complexity: Some behaviors (like TurbulenceField) are more computationally expensive than others. Use them judiciously.
-
Order Matters: Behaviors are processed in the order they appear in the array. Put more important or prerequisite behaviors earlier.
-
Batch Similar Systems: If you have multiple particle systems with identical behaviors, consider using a single system with more particles.
-
Balance Quantity vs. Quality: For mobile or performance-critical applications, reduce the number of behaviors per particle system.
Next Steps
- Learn about Particle System Configuration for more options
- Explore Emitter Shapes to control where particles spawn
- Discover Value Generators for creating dynamic particle properties