FAQ & Troubleshooting
This page addresses frequently asked questions and common issues when working with three.quarks. If you encounter problems not covered here, please open an issue on GitHub.
Common Issues
Particles aren’t visible in my scene
Problem: You’ve set up a particle system but can’t see any particles.
Possible Solutions:
- Check camera position: Make sure your camera isn’t inside the emitter or too far away.
// Position camera to view particles
camera.position.set(0, 0, 10);
camera.lookAt(0, 0, 0);
- Verify texture loading: Ensure your particle texture is loading properly.
const textureLoader = new THREE.TextureLoader();
textureLoader.load(
"particle.png",
(texture) => {
// Texture loaded successfully
particleSystem.material.map = texture;
particleSystem.material.needsUpdate = true;
},
undefined,
(error) => {
console.error("Error loading texture:", error);
},
);
- Check blending mode: For bright/glowing particles, make sure you’re using the correct blending mode.
particleSystem.material.blending = THREE.AdditiveBlending;
particleSystem.material.transparent = true;
particleSystem.material.depthWrite = false;
Poor performance with many particles
Problem: Your application slows down when using large numbers of particles.
Possible Solutions:
- Reduce max particle count: The simplest solution is to use fewer particles.
// Use fewer particles
const emitter = new SphereEmitter({
// ...other settings
particleCount: 500, // Reduce from higher values
});
- Optimize behaviors: Limit the number of complex behaviors.
// Instead of many separate behaviors
emitter.addBehavior(new ColorOverLife([]));
emitter.addBehavior(new SizeOverLife([]));
emitter.addBehavior(new RotationOverLife([]));
emitter.addBehavior(new VelocityOverLife([]));
// Combine effects into fewer behaviors
emitter.addBehavior(new CombinedBehavior({
color: [...],
size: [...],
// Other properties
}));
Particles all emit at once instead of over time
Problem: All particles appear simultaneously instead of gradually.
Solution: Check your emission rate and burst settings.
// Continuous emission
const emitter = new PointEmitter({
// ...other settings
duration: 5, // Emit over 5 seconds
emissionOverTime: 20, // 20 particles per second
burstCount: 0, // No initial burst
});
// Or for burst-only emission
const emitter = new PointEmitter({
// ...other settings
duration: 0.1, // Short duration
emissionOverTime: 0, // No continuous emission
burstCount: 100, // Initial burst of 100 particles
});
Particles aren’t affected by scene lighting
Problem: Scene lights don’t affect the particles.
Solution: Particle systems typically use unlit materials by default. If you want lighting:
import { ParticleSystem, LitParticleMaterial } from "three.quarks";
// Create a particle system with a lit material
const particleSystem = new ParticleSystem({
// ...other settings
material: new MeshStandardMaterial({
color: new THREE.Color(1, 1, 1),
map: new THREE.TextureLoader().load("particle.png"),
}),
});
// Add lights to your scene
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1);
scene.add(directionalLight);
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
Advanced Topics
How do I serialize/deserialize a particle system?
three.quarks supports saving and loading particle systems as JSON:
// Save a particle system
const json = particleSystem.toJSON();
localStorage.setItem("savedParticleEffect", JSON.stringify(json));
// Load a particle system
const savedJSON = JSON.parse(localStorage.getItem("savedParticleEffect"));
const loadedSystem = ParticleSystem.fromJSON(savedJSON);
scene.add(loadedSystem);
Creating custom value generators
Value generators provide dynamic values for particle properties over time:
import { ValueGenerator, Particle } from "three.quarks";
/**
* Custom value generator that produces oscillating values
*/
export class OscillatingValue implements ValueGenerator<number> {
/**
* @param baseValue - Base value
* @param amplitude - Oscillation amplitude
* @param frequency - Oscillation frequency
*/
constructor(
private baseValue: number = 1.0,
private amplitude: number = 0.5,
private frequency: number = 1.0,
) {}
/**
* Generate a value based on particle age
*/
genValue(particle: Particle): number {
const t = particle.age / particle.lifetime;
return (
this.baseValue +
this.amplitude * Math.sin(t * Math.PI * 2 * this.frequency)
);
}
/**
* Clone this generator
*/
clone(): OscillatingValue {
return new OscillatingValue(this.baseValue, this.amplitude, this.frequency);
}
}
// Usage
const sizeGenerator = new OscillatingValue(1.0, 0.5, 2.0);
emitter.addBehavior(new SizeOverLife(sizeGenerator));
Responding to particle lifecycle events
You can hook into particle lifecycle events:
import { Behavior, Particle } from "three.quarks";
/**
* Behavior that triggers events at specific points in a particle's lifecycle
*/
export class LifecycleEventBehavior implements Behavior {
/**
* @param onBorn - Callback when particle is created
* @param onDeath - Callback when particle dies
* @param thresholds - Array of [time, callback] pairs for lifecycle events
*/
constructor(
private onBorn?: (particle: Particle) => void,
private onDeath?: (particle: Particle) => void,
private thresholds: Array<[number, (particle: Particle) => void]> = [],
) {
// Sort thresholds by time
this.thresholds.sort((a, b) => a[0] - b[0]);
}
private lastThresholdIndex: Map<number, number> = new Map();
initialize(particle: Particle): void {
if (this.onBorn) {
this.onBorn(particle);
}
this.lastThresholdIndex.set(particle.id, -1);
}
update(particle: Particle, deltaTime: number): void {
// If particle is dying
if (particle.age + deltaTime >= particle.lifetime) {
if (this.onDeath) {
this.onDeath(particle);
}
return;
}
// Check for threshold crossing
const normalizedAge = particle.age / particle.lifetime;
const lastIndex = this.lastThresholdIndex.get(particle.id) || -1;
for (let i = lastIndex + 1; i < this.thresholds.length; i++) {
const [threshold, callback] = this.thresholds[i];
if (normalizedAge >= threshold) {
callback(particle);
this.lastThresholdIndex.set(particle.id, i);
} else {
break;
}
}
}
clone(): LifecycleEventBehavior {
return new LifecycleEventBehavior(this.onBorn, this.onDeath, [
...this.thresholds,
]);
}
}
// Usage
const soundEffects = new LifecycleEventBehavior(
(p) => playSound("spawn.mp3"),
(p) => playSound("vanish.mp3"),
[
[0.5, (p) => playSound("midpoint.mp3")],
[0.75, (p) => playSound("almostDone.mp3")],
],
);
emitter.addBehavior(soundEffects);
Performance Optimization
When should I use instanced rendering?
Instanced rendering significantly improves performance for large numbers of particles (1000+). Enable it with:
const particleSystem = new ParticleSystem({
// ...other settings
useInstancing: true,
});
Note that instanced rendering has some limitations on custom vertex manipulation.
How can I optimize particles for mobile devices?
// Mobile-friendly particle system
const mobileParticleSystem = new ParticleSystem({
capacity: 200, // Fewer particles
useInstancing: true, // Instanced rendering
textureSize: new THREE.Vector2(32, 32), // Smaller textures
blending: THREE.NormalBlending, // Less expensive blending
transparent: true,
depthWrite: false,
});
// Use simpler behaviors
emitter.addBehavior(new BasicMovement()); // Instead of complex physics
What’s the difference between burst and continuous emission?
Burst emission creates many particles at once, while continuous emission creates them over time:
// Continuous emission (e.g., smoke)
const smokeEmitter = new PointEmitter({
particleCount: 0, // No initial burst
emissionOverTime: 10, // 10 particles per second
duration: Infinity, // Emit forever
useAttraction: false, // Simple movement
behaviors: [
// Behaviors
],
});
// Burst emission (e.g., explosion)
const explosionEmitter = new SphereEmitter({
particleCount: 100, // Initial burst of 100 particles
emissionOverTime: 0, // No continuous emission
duration: 0.1, // Very short duration
burstCount: 100, // Burst at start
speed: 10, // High initial speed
behaviors: [
// Explosion behaviors
],
});
Integration with Other Libraries
Using with React Three Fiber
You can use three.quarks with React Three Fiber:
import React, { useRef, useEffect } from "react";
import { useFrame } from "@react-three/fiber";
import { ParticleSystem, PointEmitter } from "three.quarks";
import * as THREE from "three";
const ParticleEffect = ({ position }) => {
const meshRef = useRef();
const systemRef = useRef<ParticleSystem>();
// Create particle system on component mount
useEffect(() => {
const particleSystem = new ParticleSystem({
duration: 5,
looping: true,
capacity: 1000,
});
const emitter = new PointEmitter({
position: new THREE.Vector3(0, 0, 0),
particleCount: 50,
duration: 2,
size: [0.5, 0],
});
particleSystem.addEmitter(emitter);
meshRef.current.add(particleSystem);
systemRef.current = particleSystem;
return () => {
// Cleanup
if (meshRef.current) {
meshRef.current.remove(particleSystem);
}
particleSystem.dispose();
};
}, []);
// Update particle system on each frame
useFrame((state, delta) => {
if (systemRef.current) {
systemRef.current.update(delta);
}
});
return <group ref={meshRef} position={position} />;
};
export default ParticleEffect;
Can I use three.quarks with node-based editors?
While three.quarks doesn’t include a visual editor, you can integrate it with node-based editors by:
- Creating serialization adapters for your node editor
- Using the
ParticleSystem.fromJSON
andtoJSON
methods
// Example adapter for a hypothetical node editor
class NodeEditorAdapter {
// Convert from node graph to three.quarks JSON
static toQuarksJSON(nodeGraph) {
// Implementation depends on your node editor
const quarksJSON = {
type: "ParticleSystem",
emitters: [],
// ...other properties
};
// Process nodes and convert to three.quarks format
for (const node of nodeGraph.nodes) {
if (node.type === "emitter") {
quarksJSON.emitters.push({
type: node.emitterType,
// Map node properties to emitter properties
});
}
// Process other node types...
}
return quarksJSON;
}
// Create a particle system from the node editor
static createSystem(nodeGraph) {
const json = this.toQuarksJSON(nodeGraph);
return ParticleSystem.fromJSON(json);
}
}
Next Steps
If you still have questions or issues, consider checking:
- Core Components - Documentation on the fundamental building blocks
- Advanced Features - More complex features and customization options
- Examples - Working examples of different particle effects
- GitHub Issues - Search for similar issues or open a new one
For direct support, join our community discussion on [Discord/Slack/etc.].