Generate Now

Animated & Dynamic Favicons Guide

Master animated favicons: JavaScript techniques, Canvas API, SVG animation, notification badges, and best practices for dynamic browser tab icons.

Why Animate Favicons?

Notifications
Alert users to updates

Status
Show loading/progress

Attention
Draw focus to tab

Activity
Real-time updates

Browser Support & Limitations

What Works & What Doesn't

Browser Canvas Animation GIF Animation SVG Animation Notes
Chrome ? Yes ? No ? No Canvas-based only
Firefox ? Yes ? No ? No Static GIF frame only
Safari ? Yes ? No ? No Limited support
Edge ? Yes ? No ? No Same as Chrome
Key Takeaway: Only Canvas-based JavaScript animation works reliably. GIF and SVG animations are NOT supported in favicons.

Canvas-Based Animation (Recommended)

JavaScript Canvas Technique

Basic Notification Badge Example:

// Simple notification badge
function updateFavicon(count) {
    const canvas = document.createElement('canvas');
    canvas.width = 32;
    canvas.height = 32;
    const ctx = canvas.getContext('2d');
    
    // Load original favicon
    const img = new Image();
    img.onload = () => {
        // Draw original icon
        ctx.drawImage(img, 0, 0, 32, 32);
        
        if (count > 0) {
            // Draw notification badge
            ctx.fillStyle = '#FF0000';
            ctx.beginPath();
            ctx.arc(24, 8, 8, 0, 2 * Math.PI);
            ctx.fill();
            
            // Draw count
            ctx.fillStyle = '#FFFFFF';
            ctx.font = 'bold 12px Arial';
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            ctx.fillText(count > 9 ? '9+' : count, 24, 8);
        }
        
        // Update favicon
        const link = document.querySelector("link[rel*='icon']") || 
                     document.createElement('link');
        link.type = 'image/x-icon';
        link.rel = 'shortcut icon';
        link.href = canvas.toDataURL();
        document.getElementsByTagName('head')[0].appendChild(link);
    };
    img.src = '/favicon.ico';
}

// Usage
updateFavicon(5);  // Show badge with "5"
updateFavicon(0);  // Remove badge

Animated Loading Spinner:

let angle = 0;

function animateLoading() {
    const canvas = document.createElement('canvas');
    canvas.width = 32;
    canvas.height = 32;
    const ctx = canvas.getContext('2d');
    
    // Clear canvas
    ctx.clearRect(0, 0, 32, 32);
    
    // Draw rotating arc
    ctx.strokeStyle = '#0066CC';
    ctx.lineWidth = 3;
    ctx.beginPath();
    ctx.arc(16, 16, 12, angle, angle + Math.PI * 1.5);
    ctx.stroke();
    
    angle += 0.1;
    
    // Update favicon
    const link = document.querySelector("link[rel*='icon']") || 
                 document.createElement('link');
    link.href = canvas.toDataURL();
    if (!link.parentNode) {
        document.head.appendChild(link);
    }
}

// Start animation
const intervalId = setInterval(animateLoading, 50);

// Stop animation
// clearInterval(intervalId);

Progress Bar Favicon:

function showProgress(percentage) {
    const canvas = document.createElement('canvas');
    canvas.width = 32;
    canvas.height = 32;
    const ctx = canvas.getContext('2d');
    
    // Background
    ctx.fillStyle = '#E0E0E0';
    ctx.fillRect(0, 0, 32, 32);
    
    // Progress bar
    ctx.fillStyle = '#4CAF50';
    ctx.fillRect(0, 0, 32 * (percentage / 100), 32);
    
    // Percentage text
    ctx.fillStyle = '#000000';
    ctx.font = 'bold 14px Arial';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(percentage + '%', 16, 16);
    
    // Update favicon
    const link = document.querySelector("link[rel*='icon']") || 
                 document.createElement('link');
    link.href = canvas.toDataURL();
    if (!link.parentNode) {
        document.head.appendChild(link);
    }
}

// Usage: showProgress(75);

Favicon Animation Libraries

Favico.js

Features: Badges, animations, video

Size: ~7KB minified

Installation:
<script src="favico.min.js"></script>
Usage:
const favicon = new Favico({
    animation: 'slide'
});
favicon.badge(12);  // Show badge
favicon.reset();    // Remove badge
View Documentation

Tinycon

Features: Lightweight badges

Size: ~2KB minified

Installation:
<script src="tinycon.min.js"></script>
Usage:
Tinycon.setOptions({
    background: '#FF0000'
});
Tinycon.setBubble(7);   // Show "7"
Tinycon.reset();        // Clear
View Documentation

Common Use Cases

Perfect for: Gmail, Slack, messaging apps

// Show unread count
let unreadCount = 0;

function updateUnreadCounter(count) {
    unreadCount = count;
    updateFavicon(count);
    document.title = count > 0 ? 
        `(${count}) Messages` : 
        'Messages';
}

// WebSocket or polling update
socket.on('newMessage', () => {
    updateUnreadCounter(++unreadCount);
});

Perfect for: File uploads, cloud storage

// Track upload progress
xhr.upload.addEventListener('progress', (e) => {
    if (e.lengthComputable) {
        const percent = Math.round((e.loaded / e.total) * 100);
        showProgress(percent);
    }
});

xhr.addEventListener('load', () => {
    showProgress(100);
    setTimeout(resetFavicon, 2000);
});

Perfect for: Social media, alerts, dashboards

// Flash animation for attention
function flashFavicon() {
    let isOriginal = true;
    const flashInterval = setInterval(() => {
        if (isOriginal) {
            updateFavicon(1); // Show alert
        } else {
            resetFavicon();   // Show normal
        }
        isOriginal = !isOriginal;
    }, 500);
    
    // Stop after 5 seconds
    setTimeout(() => clearInterval(flashInterval), 5000);
}

Perfect for: Pomodoro, exam timers, auctions

// Countdown timer in favicon
let secondsLeft = 300; // 5 minutes

const timerInterval = setInterval(() => {
    const minutes = Math.floor(secondsLeft / 60);
    const seconds = secondsLeft % 60;
    updateFaviconText(`${'$'}{minutes}:${'$'}{seconds.toString().padStart(2, '0')}`);
    
    secondsLeft--;
    if (secondsLeft < 0) {
        clearInterval(timerInterval);
        flashFavicon(); // Alert when done
    }
}, 1000);

Animated Favicon Best Practices

? Do This

  • Use for meaningful notifications only
  • Reset favicon when tab gains focus
  • Keep animations subtle and professional
  • Test performance impact
  • Provide option to disable animations
  • Use requestAnimationFrame for smooth animation
  • Clear intervals/timeouts properly
  • Fallback to static icon if Canvas not supported

? Avoid This

  • Constant animation (annoying, battery drain)
  • Too fast animation (seizure risk)
  • Complex animations (performance)
  • Using GIF (doesn't animate)
  • Forgetting to stop animation loops
  • Animating on mobile (battery concern)
  • Excessive canvas operations
  • Ignoring accessibility concerns

Performance Considerations

Optimize Animation Performance

Best Practices for Performance:

  • Use requestAnimationFrame: Better than setInterval
  • Throttle updates: Max 10-15 FPS sufficient
  • Stop when tab inactive: Use Page Visibility API
  • Cache canvas: Don't recreate each frame
  • Limit canvas size: 32x32 or 16x16 max
  • Debounce updates: Don't update too frequently
  • Clean up properly: Clear intervals on unmount
  • Test battery impact: Especially on mobile

Optimized Animation Example:

let animationId;
let lastUpdate = 0;
const FPS = 10; // Limit to 10 FPS
const frameDelay = 1000 / FPS;

function animate(timestamp) {
    if (timestamp - lastUpdate >= frameDelay) {
        updateFaviconFrame();
        lastUpdate = timestamp;
    }
    animationId = requestAnimationFrame(animate);
}

// Start animation
animationId = requestAnimationFrame(animate);

// Stop when tab hidden
document.addEventListener('visibilitychange', () => {
    if (document.hidden) {
        cancelAnimationFrame(animationId);
    } else {
        animationId = requestAnimationFrame(animate);
    }
});

// Cleanup
function stopAnimation() {
    if (animationId) {
        cancelAnimationFrame(animationId);
    }
}

Accessibility & User Preferences

Respect User Preferences

Detect Motion Preferences:

// Check if user prefers reduced motion
const prefersReducedMotion = window.matchMedia(
    '(prefers-reduced-motion: reduce)'
).matches;

if (!prefersReducedMotion) {
    // Safe to animate
    startFaviconAnimation();
} else {
    // Use static badge instead
    updateFavicon(count);
}
Important: Always respect prefers-reduced-motion. Animated favicons can cause discomfort or trigger seizures in sensitive users.

Start with Static, Perfect Favicons

Generate professional static favicons first, then add animation with JavaScript

Generate Base Favicons

Related Articles

An unhandled error has occurred. Reload 🗙