Skip to Content
Quarks
DocumentationFAQ & Troubleshooting

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:

  1. 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);
  1. 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); }, );
  1. 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:

  1. 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 });
  1. 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:

  1. Creating serialization adapters for your node editor
  2. Using the ParticleSystem.fromJSON and toJSON 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:

For direct support, join our community discussion on [Discord/Slack/etc.].

Last updated on