Animated Favicons - Real Uses and How They Work

A technical guide to animated favicons, exploring real-world use cases, browser support, and implementation approaches.

April 30, 2026 • 10 min read

The small icon at the left side of your browser tab might be static, or it might be quietly animating right now. Gmail uses animated favicons to show unread mail counts. Discord uses them for notification indicators. But animated favicons aren’t just for notifications, they’re useful in specific situations where the tab itself sits in the background and users need feedback without refocusing the window.

This isn’t new technology. The technique has existed for years, but it’s still underutilized because most websites don’t have a legitimate need for it. Before you add an animated favicon to your project, you should understand when they actually help users and when they just drain battery.

When Animated Favicons Actually Help

Be honest about your use case. These are the scenarios where an animated favicon genuinely adds value:

Loading and Processing States If your app handles long-running operations uploads, exports, video processing, database migrations, an animated favicon tells users the work is still happening even if they’ve switched to another tab. A subtle pulse or rotation is enough. Users don’t need to refocus the window to check progress.

Notification Badges A static red dot isn’t as attention-grabbing as a pulsing one. If your application notifies users of new messages, mentions, or alerts, a gently animating badge gets noticed faster than a static one. This matters for chat applications, email clients, and collaboration tools that users keep in the background.

Live Data and Monitoring Trading dashboards, system monitoring tools, sports score trackers, applications where data changes constantly and users switch tabs while monitoring. An animated element tells them “this is live data, it’s updating right now” without forcing them to refocus.

Time-Sensitive Countdown or Status If your application shows a countdown timer (meeting starting in 5 minutes, session expiring soon), an animated favicon can reflect that urgency while the tab is in the background.

When NOT to Use Them

Just for decoration. If it doesn’t represent meaningful state, don’t animate. A spinning icon on every tab gets annoying fast.

Constant animation during normal browsing. If the favicon animates the entire time a user has your page open, you’re just burning battery and CPU. Stop animating when the operation completes.

On Safari and mobile browsers. Safari doesn’t animate favicons smoothly, and mobile browsers have limited support. If your primary audience uses Safari or mobile, the effort-to-benefit ratio is poor.

When a static icon would work. If showing “1 unread message” is your goal, just change to a static red-dot favicon. No animation needed.

How It Actually Works

The technique is surprisingly simple, three core steps:

// 1. Get or create the favicon link element
let link = document.querySelector('link[rel~="icon"]');
if (!link) {
  link = document.createElement('link');
  link.rel = 'icon';
  document.head.appendChild(link);
}

// 2. Draw a frame to a hidden canvas
const canvas = document.createElement('canvas');
canvas.width = 32;
canvas.height = 32;
const ctx = canvas.getContext('2d');

function drawFrame(t) {
  // Draw whatever you want  shapes, text, patterns
  const scale = 0.5 + 0.5 * Math.abs(Math.sin(t / 400));
  ctx.clearRect(0, 0, 32, 32);
  ctx.fillStyle = '#ef4444';
  ctx.beginPath();
  ctx.arc(16, 16, 14 * scale, 0, Math.PI * 2);
  ctx.fill();

  // 3. Export the canvas to a data URL and assign it
  link.href = canvas.toDataURL('image/png');

  requestAnimationFrame(drawFrame);
}

requestAnimationFrame(drawFrame);

That’s roughly 15 lines for a working pulsing circle. Everything fancier is just different canvas drawing commands.

Canvas Size Matters

Use 16×16 or 32×32 pixels. The favicon is rendered tiny in the browser tab anyway, so larger canvases just create bigger data URLs and waste CPU. A 32×32 canvas with crisp pixels is the practical sweet spot visible enough to see detail, small enough to not waste resources.

PNG Over ICO

Always output PNG from your canvas: canvas.toDataURL('image/png'). Don’t try to generate ICO files yourself. PNG encoding is reliable across all browsers, and the browser handles the rest.

Restore the Original Favicon

Before you start animating, save the original favicon URL. When the operation completes or the user navigates away, restore it. Tabs showing half-broken animations after navigation look buggy.

const originalHref = link.href;

// ... animate ...

// Restore when done
link.href = originalHref;

Browser Support Reality

This is where it gets messy. Different browsers handle favicon animation very differently:

Firefox Firefox animates smoothly even when the tab isn’t focused. It’s the only browser that treats favicon updates like any other DOM change. If you need smooth 60fps animation, Firefox is your baseline.

Chrome / Edge These browsers animate while the tab is active. When you switch away, requestAnimationFrame throttles down to roughly once per second, so animations slow or pause completely. This is intentional browsers don’t want background tabs burning CPU.

Safari Safari animates while the tab is focused, but doesn’t guarantee smooth updates. Don’t count on 60fps animation. Safari on iOS has even more limited support.

What This Means Practically For notification dots and progress indicators, this is fine, you only need an update every second or so anyway. For smooth spinners and rotation effects, accept that they’ll be smooth in Firefox and slow in Chrome/Edge/Safari. That’s a tradeoff worth making if your use case justifies the feature.

The Web Worker Approach (Keeping Favicons Alive in Background Tabs)

If you need animation to continue even when the tab is in the background, use a Web Worker. The browser doesn’t throttle requestAnimationFrame inside workers, and OffscreenCanvas lets workers render without touching the DOM.

The pattern looks like this:

Main page:

const worker = new Worker('favicon-worker.js');

worker.onmessage = (e) => {
  if (e.data.type === 'updateFavicon') {
    document.querySelector('link[rel~="icon"]').href = e.data.dataUrl;
  }
};

worker.postMessage({ type: 'init', images: ['icon-a.png', 'icon-b.png'] });

In favicon-worker.js:

const canvas = new OffscreenCanvas(16, 16);
const ctx = canvas.getContext('2d');

async function animationLoop() {
  // Draw a frame
  ctx.clearRect(0, 0, 16, 16);
  ctx.fillStyle = '#ef4444';
  ctx.arc(8, 8, 7, 0, Math.PI * 2);
  ctx.fill();

  // Convert to blob and send data URL
  const blob = await canvas.convertToBlob();
  const reader = new FileReader();
  reader.onloadend = () => {
    self.postMessage({ 
      type: 'updateFavicon', 
      dataUrl: reader.result 
    });
  };
  reader.readAsDataURL(blob);

  setTimeout(animationLoop, 50); // 20fps
}

animationLoop();

This approach is worth the extra code if your users park your app in a background tab, chat clients, build dashboards, monitoring tools. For everything else, the main-thread version is simpler.

Real-World Library: Aymkdn/animated-favicon

The Aymkdn/animated-favicon library implements the Web Worker pattern out of the box. It’s one of the few well-maintained libraries for this because most sites don’t need it. If you find yourself needing this functionality, that library is worth studying, it handles browser quirks and provides pre-built animations.

Performance Considerations

Battery and CPU Cost Even a subtle 50fps animation drains battery on mobile. If your user is on a phone and your app is in the background, they’ll notice. Stop animating when:

  • The loading state ends
  • The user reads the notification
  • The tab loses focus
  • A timeout expires (don’t animate indefinitely)

Network Overhead Each data URL gets encoded into the favicon link. There’s no additional network request, but if you’re encoding a complex canvas drawing, the string size can grow. Keep canvas size small and avoid re-encoding the same frame repeatedly.

Quick Checklist Before Implementing

  • ✓ Does your use case match one of the legitimate scenarios above?
  • ✓ Is your primary audience on browsers that support smooth favicon animation? (if not, is the degraded experience acceptable?)
  • ✓ Will animation stop when it’s no longer needed?
  • ✓ Have you tested in Firefox, Chrome, Safari, and checked mobile behavior?
  • ✓ Does the favicon restore properly when navigation happens?

If you can’t check most of these boxes, a static favicon is probably the right choice.

Live Demo Patterns

If you were to implement animated favicons in your application, here are the animations you’d actually see in the browser tab:

Pulsing Circle A subtle circle that scales in and out continuously. Useful for “processing” states where you want to indicate activity without being aggressive.

const scale = 0.5 + 0.5 * Math.abs(Math.sin(timestamp / 400));
ctx.arc(16, 16, 14 * scale, 0, Math.PI * 2);

Rotating Icon A slowly rotating arrow or spinner. Common for upload/download progress or background sync operations.

ctx.save();
ctx.translate(16, 16);
ctx.rotate((timestamp / 40) % (Math.PI * 2));
ctx.drawImage(baseIcon, -8, -8, 16, 16);
ctx.restore();

Notification Badge A small colored dot (usually red) that appears or disappears. For unread counts or alerts.

ctx.fillStyle = '#ef4444';
ctx.beginPath();
ctx.arc(24, 8, 5, 0, Math.PI * 2);
ctx.fill();

Icon Flip (Two-Frame) Two different icons that smoothly transition between each other. Useful for state changes.

// Using cosine compression for smooth width animation
const progress = (timestamp / 600) % 1;
const width = canvas.width * Math.abs(Math.cos(progress * Math.PI));
// Draw first icon scaled to width, swap to second icon when progress > 0.5

These are actual patterns used by chat applications, monitoring dashboards, and productivity tools. The key difference between these and pure decoration is that each one communicates meaningful state.

Implementation Steps

If you want to add animated favicons to your project:

  1. Create a hidden canvas (32×32 pixels recommended)
  2. Write a drawFrame() function that renders your animation based on timestamp
  3. Convert the canvas to a PNG data URL
  4. Assign that data URL to your favicon link element
  5. Call drawFrame() repeatedly with requestAnimationFrame()
  6. Stop calling it when the state it represents is no longer relevant
function startFaviconAnimation(drawFn) {
  const canvas = document.createElement('canvas');
  canvas.width = 32;
  canvas.height = 32;
  const ctx = canvas.getContext('2d');
  
  const link = document.querySelector('link[rel~="icon"]') || 
    (() => {
      const el = document.createElement('link');
      el.rel = 'icon';
      document.head.appendChild(el);
      return el;
    })();

  const originalHref = link.href;
  let animationId;

  function animate(timestamp) {
    ctx.clearRect(0, 0, 32, 32);
    drawFn(ctx, timestamp);
    link.href = canvas.toDataURL('image/png');
    animationId = requestAnimationFrame(animate);
  }

  animationId = requestAnimationFrame(animate);

  return () => {
    cancelAnimationFrame(animationId);
    link.href = originalHref;
  };
}

// Usage:
const stopAnimation = startFaviconAnimation((ctx, t) => {
  const scale = 0.5 + 0.5 * Math.abs(Math.sin(t / 400));
  ctx.fillStyle = '#ef4444';
  ctx.beginPath();
  ctx.arc(16, 16, 14 * scale, 0, Math.PI * 2);
  ctx.fill();
});

// Stop when done
stopAnimation();

Implementation Takeaways

Animated favicons are a real technique with legitimate use cases, but they’re niche. The implementation is straightforward, canvas drawing + data URL export but the decision to use them should be deliberate. Users switching tabs expecting their work to continue in the background while getting visual feedback: that’s where this technique shines. Everything else is probably overengineering.

The code is simple enough to implement from scratch, and understanding how it works, drawing to canvas, converting to data URL, updating the link element is worth knowing for browser fundamentals even if you never use animated favicons in production.

References

  1. Animated Favicon Live Demo - favicon.im - The original article that inspired this post. Includes an interactive live demo, detailed browser support analysis, and the Web Worker optimization technique with working code examples.

  2. Aymkdn/animated-favicon on GitHub - A production-ready library that uses Web Workers for background tab animation. Good reference for the advanced pattern and pre-built animation implementations.

  3. The Making of an Animated Favicon - CSS-Tricks - Chris Coyier’s detailed walkthrough of the canvas-to-favicon technique and browser compatibility considerations.

  4. How to animate a favicon? - Stack Overflow - The classic discussion with multiple approaches, browser support observations, and real-world examples from developers who’ve shipped this.

  5. OffscreenCanvas - MDN Web Docs - Official documentation for the API that enables Web Worker-based favicon animation without DOM access.

  6. Canvas API - MDN Web Docs - Complete reference for canvas drawing methods, transformations, and pixel manipulation.

Share this post