Slow Browser Detection
ParticleText.js includes intelligent performance monitoring that automatically detects slow browsers or devices and optimizes the animation to maintain smooth performance. This ensures your animation works well on both high-end desktops and older mobile devices without manual intervention.
How It Works
Section titled “How It Works”ParticleText.js continuously monitors frame render time during animation. When it detects that rendering is taking too long (exceeding renderTimeThreshold), it automatically applies performance optimizations.
Performance Monitoring
Section titled “Performance Monitoring”// Monitoring happens in three phases:
// 1. Initial test (first frame)// → Checks render time immediately
// 2. Early monitoring (first 10 seconds)// → Checks every 2 seconds (5 checks total)
// 3. Ongoing monitoring (after 10 seconds)// → Checks every 30 seconds to catch performance degradationDetection Criteria:
- If a single frame takes longer than
renderTimeThreshold(default: 15ms) - System automatically switches to optimized mode
- Once triggered, optimization remains active (no switching back)
Automatic Optimizations
Section titled “Automatic Optimizations”When slow browser/device is detected, ParticleText.js automatically:
- Reduces Canvas Resolution - Renders at 1/3 scale (3x smaller canvas) and upscales with CSS
- Simplifies Particle Rendering - Reduces particle size to minimum (1-2px)
- Adjusts Physics - Increases acceleration and reduces friction for faster convergence
- Optimizes Explosion Radius - Scales down explosion radius proportionally
Basic Configuration
Section titled “Basic Configuration”Default Behavior
Section titled “Default Behavior”// Performance monitoring is always activeconst instance = initParticleJS('#canvas', {text: 'AUTO OPTIMIZE',colors: ['#695aa6']// renderTimeThreshold: 15 (default)// Automatically optimizes if frame time > 15ms});Custom Threshold
Section titled “Custom Threshold”const instance = initParticleJS('#canvas', {text: 'CUSTOM',colors: ['#695aa6'],
// More aggressive optimization (triggers sooner)renderTimeThreshold: 10 // Optimize if frame > 10ms});
// Less aggressive (allows lower performance before optimizing)const instance2 = initParticleJS('#canvas2', {text: 'LENIENT',colors: ['#4ECDC4'],
renderTimeThreshold: 25 // Only optimize if frame > 25ms});Slow Browser Callback
Section titled “Slow Browser Callback”Get notified when optimization is triggered:
const instance = initParticleJS('#canvas', {text: 'NOTIFICATION',colors: ['#695aa6'],renderTimeThreshold: 15,
slowBrowserDetected: function() { console.log('Performance optimization activated');
// Notify user (optional) showNotification('Optimizing animation for your device...');
// Track analytics if (window.gtag) { gtag('event', 'performance_optimization', { event_category: 'ParticleText', event_label: 'Slow Browser Detected' }); }}});Render Time Threshold
Section titled “Render Time Threshold”The renderTimeThreshold determines how long (in milliseconds) a single frame can take before triggering optimization.
Choosing the Right Threshold
Section titled “Choosing the Right Threshold”| Threshold | Target FPS | When to Use | Performance Impact |
|---|---|---|---|
| 8-10ms | 100-125 FPS | High-refresh displays, gaming monitors | Very aggressive optimization |
| 12-15ms | 66-83 FPS | Standard displays (default) | Balanced approach |
| 16-20ms | 50-62 FPS | Older devices, complex scenes | Conservative optimization |
| 25-30ms | 33-40 FPS | Very slow devices only | Minimal optimization |
Frame Time Targets
Section titled “Frame Time Targets”// Target 60 FPS (16.67ms per frame)renderTimeThreshold: 15 // Optimize before dropping below 60 FPS
// Target 30 FPS (33.33ms per frame)renderTimeThreshold: 30 // Allow lower performance before optimizing
// Target 120 FPS (8.33ms per frame)renderTimeThreshold: 8 // Optimize aggressively for high refresh rateRule of Thumb: Set threshold slightly below your target frame time to optimize before performance drops.
Optimization Details
Section titled “Optimization Details”Canvas Resolution Scaling
Section titled “Canvas Resolution Scaling”// Normal mode (full resolution)canvas.width = 1800;canvas.height = 400;canvas.style.transform = 'scale(1)';
// Optimized mode (1/3 resolution, scaled up)canvas.width = 600; // 1800 / 3canvas.height = 133; // 400 / 3canvas.style.transform = 'scale(3)'; // CSS upscalecanvas.style.transformOrigin = '0 0';
// Exception: Very small screens (≤320px)// scale = 1 (no optimization needed)Performance Gain: Rendering 1/9 the pixels (3x smaller width × 3x smaller height)
Particle Size Reduction
Section titled “Particle Size Reduction”// Normal modeparticleRadius: { xs: { base: 2, rand: 1 } } // 2-3px particles
// Optimized mode (automatically applied)// Screen ≤ 2048px: radius = 1px (fixed)// Screen 2048-3000px: radius = rand * 1 + 2 (2-3px)// Screen > 3000px: radius = rand * 3 + 3 (3-6px)Physics Acceleration
Section titled “Physics Acceleration”// Normal modeparticle.accX = (dest.x - particle.x) / 100;particle.accY = (dest.y - particle.y) / 100;
// Optimized modeparticle.accX = (dest.x - particle.x) / 100 * 8; // 8x fasterparticle.accY = (dest.y - particle.y) / 100 * 8;
// Explosion physics// Normal: acc = (particle - mouse) / 10// Optimized: acc = (particle - mouse) / 10 * 4 // 4x fasterEffect: Particles reach destinations faster, reducing animation complexity
Friction Adjustment
Section titled “Friction Adjustment”// Normal modefriction: { base: 0.9, rand: 0.05 } // 0.85-0.95
// Optimized modefriction = friction / 1.1 // Reduces to ~0.77-0.86
// Lower friction = faster settling = less computationExplosion Radius Scaling
Section titled “Explosion Radius Scaling”// Explosion radius scaled proportionally// Normal: explosionRadius from config// Optimized: explosionRadius / scaleToFit (1/3 the radius)
// Example:explosionRadius: { xs: 50, lg: 100 }
// On slow device:// Effective radius: xs: ~16px, lg: ~33pxUse Cases
Section titled “Use Cases”1. Mobile-First Applications
Section titled “1. Mobile-First Applications”Optimize aggressively for mobile devices:
const instance = initParticleJS('#canvas', {text: 'MOBILE APP',colors: ['#695aa6'],
// Aggressive optimization for mobilerenderTimeThreshold: 12, // Trigger at 12ms (83 FPS)
slowBrowserDetected: function() { console.log('Mobile optimization active');
// Optionally reduce particle count further // (handled automatically, but you can track it)}});2. Desktop Hero Sections
Section titled “2. Desktop Hero Sections”Allow more complex animations on desktop:
const instance = initParticleJS('#canvas', {text: 'HERO',colors: ['#FF6B6B', '#4ECDC4'],fontSize: 150,
// Less aggressive - allow complex visualsrenderTimeThreshold: 20, // Trigger at 20ms (50 FPS)
particleRadius: { xs: { base: 3, rand: 2 }, // Larger particles lg: { base: 5, rand: 3 }},
slowBrowserDetected: function() { // Even on slow devices, maintain decent quality console.log('Optimized for smooth performance');}});3. Analytics Tracking
Section titled “3. Analytics Tracking”Track optimization events:
const instance = initParticleJS('#canvas', {text: 'ANALYTICS',colors: ['#695aa6'],renderTimeThreshold: 15,
slowBrowserDetected: function() { // Track in Google Analytics if (window.gtag) { gtag('event', 'slow_device_detected', { event_category: 'Performance', event_label: 'ParticleText Optimization', value: 1 }); }
// Track in Mixpanel if (window.mixpanel) { mixpanel.track('Slow Device Detected', { component: 'ParticleText', threshold: 15, userAgent: navigator.userAgent }); }
// Log to server fetch('/api/analytics/performance', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ event: 'slow_browser_detected', userAgent: navigator.userAgent, timestamp: new Date().toISOString() }) });}});4. User Notification
Section titled “4. User Notification”Inform users about optimization:
function showOptimizationNotice() {const notice = document.createElement('div');notice.className = 'optimization-notice';notice.innerHTML = ` <p>🎨 Animation optimized for your device</p> <button onclick="this.parentElement.remove()">Got it</button>`;notice.style.cssText = ` position: fixed; bottom: 20px; right: 20px; background: #4CAF50; color: white; padding: 15px 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); font-family: sans-serif; z-index: 1000;`;document.body.appendChild(notice);
// Auto-hide after 5 secondssetTimeout(() => notice.remove(), 5000);}
const instance = initParticleJS('#canvas', {text: 'NOTIFICATION',colors: ['#695aa6'],slowBrowserDetected: showOptimizationNotice});5. Conditional Features
Section titled “5. Conditional Features”Disable advanced features on slow devices:
let isOptimized = false;
const instance = initParticleJS('#canvas', {text: 'FEATURES',colors: ['#695aa6'],
slowBrowserDetected: function() { isOptimized = true;
// Disable expensive features disableParallaxEffect(); disableBackgroundBlur(); reduceParticleColors();}});
function disableParallaxEffect() {document.querySelectorAll('.parallax').forEach(el => { el.style.transform = 'none';});}
function reduceParticleColors() {if (isOptimized) { // Use fewer colors for better performance instance.colors = ['#695aa6']; // Single color}}Performance Monitoring
Section titled “Performance Monitoring”Check Optimization Status
Section titled “Check Optimization Status”const instance = initParticleJS('#canvas', {text: 'STATUS',colors: ['#695aa6']});
// Check if optimization is active// Note: No direct API, but you can track via callbacklet isOptimized = false;
const instance2 = initParticleJS('#canvas2', {text: 'TRACKED',colors: ['#695aa6'],
slowBrowserDetected: function() { isOptimized = true;}});
// Later in your codeif (isOptimized) {console.log('Running in optimized mode');}Display Performance Indicator
Section titled “Display Performance Indicator”const instance = initParticleJS('#canvas', {text: 'INDICATOR',colors: ['#695aa6'],renderTimeThreshold: 15,
slowBrowserDetected: function() { const indicator = document.getElementById('perf-indicator'); indicator.textContent = '⚡ Optimized Mode'; indicator.style.background = '#FFC107';}});
// HTML:// <div id="perf-indicator">🚀 Full Quality</div>Debug Performance
Section titled “Debug Performance”const DEBUG = true;
const instance = initParticleJS('#canvas', {text: 'DEBUG',colors: ['#695aa6'],renderTimeThreshold: 15,
slowBrowserDetected: function() { if (DEBUG) { console.group('Performance Optimization Triggered'); console.log('Threshold:', 15, 'ms'); console.log('Canvas Resolution:', 'Reduced to 1/3'); console.log('Particle Size:', 'Reduced to 1-2px'); console.log('Physics:', 'Accelerated 4-8x'); console.log('User Agent:', navigator.userAgent); console.log('Screen Size:', window.innerWidth + 'x' + window.innerHeight); console.groupEnd(); }}});Threshold Guidelines by Device
Section titled “Threshold Guidelines by Device”Mobile Devices
Section titled “Mobile Devices”// iOS devices (iPhone, iPad)renderTimeThreshold: 12 // Optimize at 12ms (83 FPS)
// Android devicesrenderTimeThreshold: 15 // Optimize at 15ms (66 FPS)
// Older mobile devices (iPhone 6, Android 4.x)renderTimeThreshold: 10 // Optimize aggressively at 10ms (100 FPS)Desktop Browsers
Section titled “Desktop Browsers”// Modern desktop (Chrome, Firefox, Safari, Edge)renderTimeThreshold: 20 // Allow complex visuals, optimize at 20ms (50 FPS)
// Older desktop browsers (IE11, old Safari)renderTimeThreshold: 15 // Standard optimization
// High-refresh monitors (120Hz, 144Hz)renderTimeThreshold: 8 // Target high frame rates (125 FPS)Adaptive Threshold
Section titled “Adaptive Threshold”// Detect device type and set appropriate thresholdfunction getOptimalThreshold() {const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);const isOldMobile = /iPhone [1-6]|Android [1-4]/i.test(navigator.userAgent);const isHighRefresh = window.screen.refreshRate > 60;
if (isOldMobile) return 10; // Aggressiveif (isMobile) return 12; // Mobile standardif (isHighRefresh) return 8; // High refreshreturn 15; // Desktop standard}
const instance = initParticleJS('#canvas', {text: 'ADAPTIVE',colors: ['#695aa6'],renderTimeThreshold: getOptimalThreshold()});Best Practices
Section titled “Best Practices”1. Use Default Threshold
Section titled “1. Use Default Threshold”// ✅ Good: Trust the default (15ms)const instance = initParticleJS('#canvas', {text: 'DEFAULT',colors: ['#695aa6']// renderTimeThreshold: 15 (default)});
// ❌ Bad: Arbitrary threshold without testingconst instance = initParticleJS('#canvas', {text: 'ARBITRARY',colors: ['#695aa6'],renderTimeThreshold: 50 // Too high, allows poor performance});2. Monitor Callback Usage
Section titled “2. Monitor Callback Usage”// ✅ Good: Use callback for analytics/loggingslowBrowserDetected: function() {console.log('Optimization triggered');trackAnalytics('slow_browser');}
// ❌ Bad: Heavy operations in callbackslowBrowserDetected: function() {// Don't do expensive operations here!recalculateEntireLayout();reloadAllImages();reinitializeApp();}3. Test on Real Devices
Section titled “3. Test on Real Devices”// ✅ Good: Test threshold on target devices// Test on:// - iPhone 6/7 (older iOS)// - Android 5.0 devices// - Desktop with throttled CPU (Chrome DevTools)
renderTimeThreshold: 15 // Verified on real devices
// ❌ Bad: Only test on powerful development machinerenderTimeThreshold: 30 // Allows terrible performance on real devices4. Don’t Disable Monitoring
Section titled “4. Don’t Disable Monitoring”// ✅ Good: Let monitoring happenconst instance = initParticleJS('#canvas', {text: 'MONITORED',colors: ['#695aa6']// Monitoring always active});
// ❌ Bad: Setting threshold too high effectively disables itconst instance = initParticleJS('#canvas', {text: 'DISABLED',colors: ['#695aa6'],renderTimeThreshold: 1000 // Will never trigger (1 second!)});5. Consider User Experience
Section titled “5. Consider User Experience”// ✅ Good: Notify users of optimizationslowBrowserDetected: function() {showNotification('Animation optimized for smooth performance');}
// ✅ Also good: Silent optimizationslowBrowserDetected: function() {console.log('Optimized silently');// User doesn't need to know}
// ❌ Bad: Disruptive notificationslowBrowserDetected: function() {alert('Your device is slow! Animation quality reduced!');}Advanced Techniques
Section titled “Advanced Techniques”Progressive Threshold
Section titled “Progressive Threshold”// Start with lenient threshold, tighten over timelet currentThreshold = 25;
function createAdaptiveInstance() {const instance = initParticleJS('#canvas', { text: 'PROGRESSIVE', colors: ['#695aa6'], renderTimeThreshold: currentThreshold,
slowBrowserDetected: function() { console.log(`Optimized at threshold: ${currentThreshold}ms`);
// Don't tighten further - optimization is active }});
// Reduce threshold every 5 seconds (first 20s)let iterations = 0;const interval = setInterval(() => { iterations++; currentThreshold = Math.max(15, 25 - (iterations * 5));
if (iterations >= 4 || currentThreshold === 15) { clearInterval(interval); }}, 5000);
return instance;}Device-Specific Optimization
Section titled “Device-Specific Optimization”function getDeviceProfile() {const ua = navigator.userAgent;const width = window.innerWidth;
// Detect specific devicesif (/iPhone [1-7]/.test(ua)) { return { threshold: 10, particleLimit: 2000 };}
if (/iPad/.test(ua)) { return { threshold: 12, particleLimit: 3000 };}
if (/Android [4-6]/.test(ua)) { return { threshold: 12, particleLimit: 2500 };}
if (width < 768) { return { threshold: 15, particleLimit: 3000 };}
return { threshold: 15, particleLimit: 5000 };}
const profile = getDeviceProfile();
const instance = initParticleJS('#canvas', {text: 'DEVICE PROFILE',colors: ['#695aa6'],renderTimeThreshold: profile.threshold,maxParticles: profile.particleLimit});A/B Testing Thresholds
Section titled “A/B Testing Thresholds”// A/B test different thresholdsfunction getExperimentThreshold() {const userId = getUserId();const variant = userId % 3;
switch(variant) { case 0: return 10; // Aggressive case 1: return 15; // Standard case 2: return 20; // Lenient}}
const instance = initParticleJS('#canvas', {text: 'EXPERIMENT',colors: ['#695aa6'],renderTimeThreshold: getExperimentThreshold(),
slowBrowserDetected: function() { // Track which variant triggered optimization trackExperiment('threshold_test', { threshold: getExperimentThreshold(), optimized: true });}});Troubleshooting
Section titled “Troubleshooting”Optimization triggers immediately?
- Your device/browser is genuinely slow
- Threshold is too low (increase from 15ms)
- Too many particles (reduce with
maxParticles) - Very large canvas (reduce
fontSize)
Optimization never triggers?
- Threshold is too high
- Device is very fast
- Canvas is very small
- Few particles in scene
Performance still poor after optimization?
- Reduce
maxParticles(default 5000) - Use smaller
fontSize - Simplify colors (fewer colors = better performance)
- Consider disabling animation on very old devices
Callback not firing?
- Check console for errors
- Verify callback is a function
- Ensure animation is actually running
- Wait a few seconds (monitoring takes time)
Canvas looks blurry after optimization?
- This is expected (1/3 resolution scaled up)
- Trade-off for smooth performance
- On very slow devices, this is preferable to stuttering
Performance Impact
Section titled “Performance Impact”Before Optimization
Section titled “Before Optimization”// Full resolution renderingCanvas: 1800 × 400 = 720,000 pixelsParticles: ~5,000 (based on text)Frame Time: ~18-25ms (40-55 FPS)CPU Usage: HighAfter Optimization
Section titled “After Optimization”// Optimized renderingCanvas: 600 × 133 = 79,800 pixels (11% of original)Particles: Same count but 1px sizeFrame Time: ~8-12ms (83-125 FPS)CPU Usage: Low-Medium
Performance Gain: 2-3x faster renderingRelated Documentation
Section titled “Related Documentation”- renderTimeThreshold (API Reference) - Configuration option
- slowBrowserDetected (API Reference) - Callback configuration
- Performance Optimization - General performance tips
- maxParticles - Limit particle count
- Manual Control - Control animation lifecycle