Manual Animation Control
ParticleText.js provides complete programmatic control over the animation lifecycle. Instead of starting automatically, you can control when animations start, stop, and even render individual frames manually. This is essential for creating sophisticated interactions, optimizing performance, and coordinating with other page elements.
Understanding Auto Animation
Section titled “Understanding Auto Animation”By default, ParticleText.js starts animating immediately after initialization. Setting autoAnimate: false disables this behavior, giving you full control.
Default Behavior (Auto Animation)
Section titled “Default Behavior (Auto Animation)”// Starts animating immediatelyconst instance = initParticleJS('#canvas', {text: 'AUTO',colors: ['#695aa6']// autoAnimate: true (default)});
// Animation loop is runningconsole.log(instance.isAnimating); // trueManual Control Mode
Section titled “Manual Control Mode”// Waits for your commandconst instance = initParticleJS('#canvas', {text: 'MANUAL',colors: ['#695aa6'],autoAnimate: false // Don't start automatically});
// Animation not running yetconsole.log(instance.isAnimating); // false
// Start when you're readyinstance.startAnimation();Core Control Methods
Section titled “Core Control Methods”startAnimation()
Section titled “startAnimation()”Starts the animation loop. Safe to call multiple times - will not create duplicate animation loops.
const instance = initParticleJS('#canvas', {text: 'START',colors: ['#695aa6'],autoAnimate: false});
// Start the animationinstance.startAnimation();
// Safe to call again - no effectinstance.startAnimation();instance.startAnimation(); // Still only one loop runningWhen to use:
- Delayed animation start
- Restart after stopping
- Conditional animation based on user action
- Animation triggered by scroll position
destroy()
Section titled “destroy()”Stops the animation loop and cleans up resources. This cancels the requestAnimationFrame and prevents memory leaks.
const instance = initParticleJS('#canvas', {text: 'STOP',colors: ['#695aa6']});
// Stop the animationinstance.destroy();
console.log(instance.isAnimating); // false
// Can restart if neededinstance.startAnimation();When to use:
- Pause animation temporarily
- Clean up when component unmounts
- Stop animation when tab is not visible
- Performance optimization
forceRequestAnimationFrame()
Section titled “forceRequestAnimationFrame()”Renders a single frame without starting the animation loop. Useful for manual frame-by-frame control or static rendering.
const instance = initParticleJS('#canvas', {text: 'FRAME',colors: ['#695aa6'],autoAnimate: false // Don't animate});
// Render a single frameinstance.forceRequestAnimationFrame();
// Render another frameinstance.forceRequestAnimationFrame();
// Each call renders one frame, no continuous loopWhen to use:
- Frame-by-frame animation
- Step debugging
- Synchronize with external timing
- Screenshot/export functionality
Properties
Section titled “Properties”isAnimating
Section titled “isAnimating”Boolean property indicating whether the animation loop is currently running.
const instance = initParticleJS('#canvas', {text: 'STATUS',colors: ['#695aa6'],autoAnimate: false});
console.log(instance.isAnimating); // false
instance.startAnimation();console.log(instance.isAnimating); // true
instance.destroy();console.log(instance.isAnimating); // falseparticleList
Section titled “particleList”Array containing all particle objects. Useful for debugging or custom manipulation.
const instance = initParticleJS('#canvas', {text: 'PARTICLES',colors: ['#695aa6']});
console.log('Particle count:', instance.particleList.length);
// Access individual particlesinstance.particleList.forEach((particle, index) => {console.log(`Particle ${index}:`, { x: particle.x, y: particle.y, color: particle.color, radius: particle.r});});Common Use Cases
Section titled “Common Use Cases”1. Start on User Interaction
Section titled “1. Start on User Interaction”Start animation only when user clicks a button:
const instance = initParticleJS('#canvas', {text: 'CLICK TO START',colors: ['#695aa6'],autoAnimate: false});
document.getElementById('start-btn').addEventListener('click', () => {instance.startAnimation();});2. Scroll-Based Animation
Section titled “2. Scroll-Based Animation”Start animation when element enters viewport:
const instance = initParticleJS('#canvas', {text: 'SCROLL',colors: ['#695aa6'],autoAnimate: false});
const observer = new IntersectionObserver((entries) => {entries.forEach(entry => { if (entry.isIntersecting) { instance.startAnimation(); } else { instance.destroy(); // Stop when not visible }});});
observer.observe(document.getElementById('canvas'));3. Tab Visibility Optimization
Section titled “3. Tab Visibility Optimization”Pause animation when tab is not visible:
const instance = initParticleJS('#canvas', {text: 'VISIBILITY',colors: ['#695aa6']});
document.addEventListener('visibilitychange', () => {if (document.hidden) { instance.destroy(); console.log('Tab hidden - animation paused');} else { instance.startAnimation(); console.log('Tab visible - animation resumed');}});4. Conditional Animation Based on Device
Section titled “4. Conditional Animation Based on Device”Only animate on desktop (performance optimization):
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
const instance = initParticleJS('#canvas', {text: 'RESPONSIVE',colors: ['#695aa6'],autoAnimate: !isMobile // Auto-start only on desktop});
// Mobile users can click to enableif (isMobile) {document.getElementById('enable-btn').addEventListener('click', () => { instance.startAnimation();});}5. Synchronized Multi-Canvas Animation
Section titled “5. Synchronized Multi-Canvas Animation”Control multiple canvas instances together:
const canvas1 = initParticleJS('#canvas1', {text: 'ONE',colors: ['#FF6B6B'],autoAnimate: false});
const canvas2 = initParticleJS('#canvas2', {text: 'TWO',colors: ['#4ECDC4'],autoAnimate: false});
const canvas3 = initParticleJS('#canvas3', {text: 'THREE',colors: ['#FFD93D'],autoAnimate: false});
// Start all at oncefunction startAll() {canvas1.startAnimation();canvas2.startAnimation();canvas3.startAnimation();}
// Stop all at oncefunction stopAll() {canvas1.destroy();canvas2.destroy();canvas3.destroy();}
document.getElementById('start-all').onclick = startAll;document.getElementById('stop-all').onclick = stopAll;6. Frame-by-Frame Scrubbing
Section titled “6. Frame-by-Frame Scrubbing”Control animation frame-by-frame (e.g., timeline scrubber):
const instance = initParticleJS('#canvas', {text: 'SCRUB',colors: ['#695aa6'],autoAnimate: false});
const slider = document.getElementById('timeline-slider');
slider.addEventListener('input', (e) => {const frame = parseInt(e.target.value);
// Render specific number of framesfor (let i = 0; i < frame; i++) { instance.forceRequestAnimationFrame();}});7. Timed Animation Sequence
Section titled “7. Timed Animation Sequence”Run animation for specific duration:
const instance = initParticleJS('#canvas', {text: 'TIMED',colors: ['#695aa6'],autoAnimate: false});
function animateForDuration(ms) {instance.startAnimation();
setTimeout(() => { instance.destroy(); console.log(`Animation ran for ${ms}ms`);}, ms);}
// Animate for 5 secondsanimateForDuration(5000);8. Custom Animation Loop
Section titled “8. Custom Animation Loop”Integrate with custom game loop or animation framework:
const instance = initParticleJS('#canvas', {text: 'CUSTOM',colors: ['#695aa6'],autoAnimate: false // We'll control the loop});
let lastTime = 0;const targetFPS = 30; // Custom frame rateconst frameInterval = 1000 / targetFPS;
function customLoop(currentTime) {const deltaTime = currentTime - lastTime;
if (deltaTime >= frameInterval) { // Render ParticleText frame instance.forceRequestAnimationFrame();
// Your other animations updateOtherAnimations(deltaTime);
lastTime = currentTime;}
requestAnimationFrame(customLoop);}
requestAnimationFrame(customLoop);9. Pause/Resume Toggle
Section titled “9. Pause/Resume Toggle”Create a pause/resume button:
const instance = initParticleJS('#canvas', {text: 'TOGGLE',colors: ['#695aa6']});
let isPaused = false;
document.getElementById('toggle-btn').addEventListener('click', () => {if (isPaused) { instance.startAnimation(); isPaused = false; console.log('Resumed');} else { instance.destroy(); isPaused = true; console.log('Paused');}});10. Battery-Aware Animation
Section titled “10. Battery-Aware Animation”Stop animation when battery is low:
const instance = initParticleJS('#canvas', {text: 'BATTERY',colors: ['#695aa6']});
if ('getBattery' in navigator) {navigator.getBattery().then(battery => { function updateAnimationState() { if (battery.level < 0.2 || !battery.charging) { instance.destroy(); console.log('Low battery - animation paused'); } else { instance.startAnimation(); } }
battery.addEventListener('levelchange', updateAnimationState); battery.addEventListener('chargingchange', updateAnimationState);
updateAnimationState();});}Performance Optimization Patterns
Section titled “Performance Optimization Patterns”Lazy Loading
Section titled “Lazy Loading”Don’t start animation until canvas is near viewport:
const instance = initParticleJS('#canvas', {text: 'LAZY',colors: ['#695aa6'],autoAnimate: false});
const observer = new IntersectionObserver((entries) => {entries.forEach(entry => { if (entry.isIntersecting) { instance.startAnimation(); observer.disconnect(); // Only start once }});}, {rootMargin: '100px' // Start 100px before entering viewport});
observer.observe(document.getElementById('canvas'));Throttled Animation
Section titled “Throttled Animation”Reduce frame rate on slow devices:
const instance = initParticleJS('#canvas', {text: 'THROTTLE',colors: ['#695aa6'],autoAnimate: false});
const isSlowDevice = /iPhone 6|iPhone 7|Android 4/i.test(navigator.userAgent);const frameSkip = isSlowDevice ? 2 : 1; // Render every 2nd frame on slow devices
let frameCount = 0;
function throttledLoop() {frameCount++;
if (frameCount % frameSkip === 0) { instance.forceRequestAnimationFrame();}
requestAnimationFrame(throttledLoop);}
throttledLoop();Memory Cleanup
Section titled “Memory Cleanup”Properly clean up when component unmounts:
// React exampleuseEffect(() => {const instance = initParticleJS('#canvas', { text: 'CLEANUP', colors: ['#695aa6']});
// Cleanup functionreturn () => { instance.destroy(); console.log('Animation cleaned up');};}, []);
// Vue exampleexport default {mounted() { this.instance = initParticleJS('#canvas', { text: 'CLEANUP', colors: ['#695aa6'] });},beforeUnmount() { this.instance.destroy();}};
// Vanilla JS - page navigationwindow.addEventListener('beforeunload', () => {instance.destroy();});Debugging and Monitoring
Section titled “Debugging and Monitoring”Animation State Monitoring
Section titled “Animation State Monitoring”const instance = initParticleJS('#canvas', {text: 'DEBUG',colors: ['#695aa6']});
// Monitor state changessetInterval(() => {console.log({ isAnimating: instance.isAnimating, particleCount: instance.particleList.length, currentBreakpoint: instance.getCurrentBreakpoint()});}, 1000);Frame Rate Monitoring
Section titled “Frame Rate Monitoring”let frameCount = 0;let lastTime = performance.now();
const instance = initParticleJS('#canvas', {text: 'FPS',colors: ['#695aa6']});
setInterval(() => {const currentTime = performance.now();const elapsed = currentTime - lastTime;const fps = Math.round((frameCount * 1000) / elapsed);
console.log(`FPS: ${fps}`);
frameCount = 0;lastTime = currentTime;}, 1000);
// Count framesconst originalRender = instance.forceRequestAnimationFrame;instance.forceRequestAnimationFrame = function() {frameCount++;originalRender.call(instance);};Best Practices
Section titled “Best Practices”1. Always Clean Up
Section titled “1. Always Clean Up”// ✅ Good: Clean up when doneconst instance = initParticleJS('#canvas', {text: 'CLEANUP',colors: ['#695aa6']});
// When component unmounts or page changesinstance.destroy();
// ❌ Bad: Memory leak - animation keeps runningconst instance = initParticleJS('#canvas', {text: 'LEAK',colors: ['#695aa6']});// Never calls destroy() - animation loop continues forever2. Check State Before Acting
Section titled “2. Check State Before Acting”// ✅ Good: Check before actionif (!instance.isAnimating) {instance.startAnimation();}
// ❌ Unnecessary: startAnimation() already checks// But doesn't hurt - method is safe to call multiple timesinstance.startAnimation();instance.startAnimation(); // Safe but redundant3. Use Visibility API
Section titled “3. Use Visibility API”// ✅ Good: Respect tab visibilitydocument.addEventListener('visibilitychange', () => {if (document.hidden) { instance.destroy();} else { instance.startAnimation();}});
// ❌ Bad: Waste resources when tab is hidden// Animation runs even when user can't see it4. Coordinate Multiple Instances
Section titled “4. Coordinate Multiple Instances”// ✅ Good: Manage all instancesconst instances = [];
instances.push(initParticleJS('#canvas1', { /* ... */ }));instances.push(initParticleJS('#canvas2', { /* ... */ }));instances.push(initParticleJS('#canvas3', { /* ... */ }));
// Stop all at oncefunction cleanupAll() {instances.forEach(instance => instance.destroy());}
// ❌ Bad: Lose reference to instancesinitParticleJS('#canvas1', { /* ... */ }); // Can't control laterinitParticleJS('#canvas2', { /* ... */ }); // Can't control later5. Handle Errors Gracefully
Section titled “5. Handle Errors Gracefully”// ✅ Good: Defensive programmingtry {const instance = initParticleJS('#canvas', { text: 'SAFE', colors: ['#695aa6']});
if (instance && instance.startAnimation) { instance.startAnimation();}} catch (error) {console.error('Animation failed:', error);}
// ❌ Bad: Assumes successconst instance = initParticleJS('#canvas', { /* ... */ });instance.startAnimation(); // Might fail if canvas not foundTroubleshooting
Section titled “Troubleshooting”Animation doesn’t start?
- Check
autoAnimateis not false - Verify
startAnimation()is called - Ensure canvas element exists
- Check browser console for errors
Can’t stop animation?
- Call
destroy()method - Check
isAnimatingproperty - Ensure you have reference to instance
Multiple animation loops running?
startAnimation()is safe to call multiple times- Only one loop will run per instance
- Check you’re not creating multiple instances
Memory leak issues?
- Always call
destroy()when done - Use visibility API to pause hidden tabs
- Clean up in component unmount hooks
Frame rate too low?
- Use
autoAnimate: falseand custom loop - Implement frame skipping for slow devices
- Reduce particle count with
maxParticles
Related Documentation
Section titled “Related Documentation”- autoAnimate (API Reference) - Configuration option
- Performance Optimization - Optimize animation performance
- Visibility API - Tab visibility control
- Multiple Canvases - Manage multiple instances