Анимированные favicon: оживите вкладку браузера (с живым демо)
Пока вы читали это, скорее всего, что-то двигалось в верхней части вкладки. Это анимированный favicon — тот самый приём, которым Gmail показывает количество новых писем, а Discord рисует точки уведомлений. Только вы можете нарисовать на canvas, по сути, что угодно.
В этой статье есть живое демо, в которое можно потыкать. Нажмите кнопку — и реальная вкладка вашего браузера изменится. Никаких скриншотов, никаких встроенных видео: favicon, на который вы сейчас смотрите, и есть демо.
Зачем вообще анимировать favicon?
Честно говоря, большинству сайтов это и не нужно. Крутящаяся иконка в каждой вкладке быстро начинает раздражать и поедает CPU. Но есть случаи, когда это действительно полезно:
- Состояния загрузки или обработки. Долгие загрузки, экспорты или сборки. Пользователи переключаются между вкладками, пока ждут, и анимированный favicon подсказывает: работа всё ещё идёт.
- Бейджи уведомлений. Новые сообщения, упоминания, оповещения. Аккуратно пульсирующая красная точка замечается быстрее статичной.
- Живые потоки данных. Торговые дашборды, мониторинг, спортивные счета — везде, где одного заголовка вкладки недостаточно.
- Бренд-моменты. Праздничный спиннер, день запуска. Использовать дозированно.
Если ваш случай не из этих, лучше обойтись без анимации. Хороший статический SVG-favicon выигрывает по размеру файла, поддержке тёмной темы и экономии батареи.
Живое демо: попробуйте прямо сейчас
Выберите анимацию. А затем посмотрите на вкладку браузера — крошечная иконка наверху перерисовывается прямо сейчас.
Реальный favicon — 16×16 пикселей. На таком размере детали разглядеть сложно, поэтому окошко слева отображает тот же canvas с увеличением 8× и отключённым сглаживанием.
Статус: ожидание
Цикл анимации теперь полностью крутится внутри Web Worker — точно как favicon_worker.js в библиотеке Aymkdn. Каждые 20 мс (50 кадров в секунду) worker рисует на своём OffscreenCanvas, экспортирует результат через convertToBlob + FileReader и отправляет получившийся data URL обратно на страницу. Главный поток делает только одно: присваивает эту строку в faviconLink.href. Вот почему иконка теперь движется так же плавно, как в демо GitHub.
Хотите посмотреть, как этот приём работает в реальном продукте? Random Picker Wheel синхронизирует свой анимированный favicon с вращающимся колесом — UI, построенный на вращении, один из немногих случаев, когда движущаяся иконка действительно подходит продукту. Откройте страницу, крутаните колесо и посмотрите, как иконка во вкладке крутится вместе с ним.
Как это работает на самом деле
Три шага. Это и есть весь приём:
// 1. Получаем или создаём link-элемент favicon
let link = document.querySelector('link[rel~="icon"]');
if (!link) {
link = document.createElement('link');
link.rel = 'icon';
document.head.appendChild(link);
}
// 2. Рисуем кадр на скрытом canvas
const canvas = document.createElement('canvas');
canvas.width = 32;
canvas.height = 32;
const ctx = canvas.getContext('2d');
function drawFrame(t) {
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. Экспортируем canvas в data URL и присваиваем favicon
link.href = canvas.toDataURL('image/png');
requestAnimationFrame(drawFrame);
}
requestAnimationFrame(drawFrame);
Около 15 строк — и у вас уже работает пульсирующая точка. Всё, что сложнее, — это просто другие фигуры на том же canvas.
Реальность поддержки браузерами
Тут начинаются нюансы. Браузеры довольно сильно различаются в том, насколько усердно они анимируют favicon:
- Firefox: анимирует плавно даже когда вкладка не в фокусе. Эталон.
- Chrome / Edge: анимируют, пока вкладка активна. Когда переключаетесь,
requestAnimationFrameтротлится примерно до одного раза в секунду — анимация замедляется или ставится на паузу. - Safari: анимирует в фокусе, но иногда обновляет иконку с большими интервалами. Не рассчитывайте на плавное движение.
На самом деле для большинства сценариев этого хватает: точкам уведомлений и индикаторам прогресса достаточно обновляться раз в секунду. Плавный 60-fps-спиннер — это в основном косметика.
Трюк с Web Worker (для фоновых вкладок)
Кнопка «Переворот в стиле GitHub» выше — прямой порт фирменной анимации из библиотеки Aymkdn/animated-favicon: держим иконку A 3 секунды, по косинусу сжимаем её ширину до нуля, меняем на иконку B и разворачиваем обратно. Математика взята прямо из favicon_worker.js библиотеки — width = canvas.width * Math.abs(Math.cos(progress * Math.PI)), и вторая картинка перехватывает управление, как только progress пересекает 0.5.
Библиотека Aymkdn идёт ещё на шаг дальше: она крутит этот цикл внутри Web Worker. Worker'ы не тротлятся, когда вкладка в фоне, поэтому анимация продолжается, а OffscreenCanvas позволяет worker'у рисовать кадры, не трогая DOM.
Паттерн примерно такой:
// На странице
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'] });
// В favicon-worker.js
const canvas = new OffscreenCanvas(16, 16);
const ctx = canvas.getContext('2d');
// ...рисуем кадр...
const blob = await canvas.convertToBlob();
const reader = new FileReader();
reader.onloadend = () => self.postMessage({ type: 'updateFavicon', dataUrl: reader.result });
reader.readAsDataURL(blob);
Стоит заморачиваться, если ваше приложение из тех, что пользователи держат в фоновой вкладке: чаты, дашборды сборок, мониторинг. В остальных случаях версия на основном потоке проще и достаточно хороша.
Несколько вещей, которые стоит знать
Используйте 16×16 или 32×32, не больше. Favicon всё равно рендерится крошечным, а большие canvas'ы означают более длинные data URL и больше CPU на кадр. 32×32 с чёткими пикселями — золотая середина.
Устанавливайте favicon как PNG, а не ICO. canvas.toDataURL('image/png') — единственное, что работает надёжно. Не пытайтесь самостоятельно кодировать ICO.
Восстанавливайте оригинальный favicon, когда останавливаетесь. Сохраните link.href перед стартом анимации и верните на beforeunload или по завершении операции. Вкладки, в которых после навигации болтается недоигранная анимация, выглядят сломанно.
Не анимируйте вечно. Даже лёгкая пульсация сажает батарею на мобильных. Останавливайте анимацию, когда состояние загрузки закончилось, пользователь прочитал уведомление или вкладка потеряла фокус.
Для простых случаев пропустите анимацию. Если нужно показать «1 непрочитанное сообщение», просто меняйте favicon на статичную версию с красной точкой. Анимация не нужна. Большинство применений анимированных favicon — оверкилл.
Когда стоит за это браться
Анимированные favicon хорошо работают, когда:
- Анимация отражает реальное состояние, важное пользователю (загрузка, обработка, новое сообщение)
- Страница из тех, что живут в фоновой вкладке
- Статичный бейдж или просто смена заголовка вкладки не передадут того же
И плохо подходят, когда:
- Это просто украшение
- Анимация крутится всё время, пока открыта вкладка
- Аудитория сидит на Safari или мобильных устройствах, где это едва работает
Возьмите демо выше, перенесите в свой проект, замените цвета и формы под бренд и выкатите. Полный исходник прямо здесь, на этой странице — открывайте view source, копируйте, адаптируйте.
Источники
- Aymkdn/animated-favicon на GitHub — библиотека анимированных favicon на Web Worker, которая продолжает анимироваться в неактивных вкладках
- The Making of an Animated Favicon — CSS-Tricks — разбор техники canvas-to-favicon от Криса Койера
- How to animate a favicon? — Stack Overflow — классический тред с разными подходами и заметками о поддержке в браузерах
- OffscreenCanvas — MDN — API, благодаря которому возможен паттерн анимации через Web Worker
Use favicon.im to quickly check if your favicon is configured correctly. Our free tool ensures your website's favicon displays properly across all browsers and devices.
Free Public Service
Favicon.im is a completely free public service trusted by developers worldwide.