How to Make a Rotating Favicon: Cycling Icon Frames with SVG Artwork
Learn how to build a favicon rotator that cycles through a set of icon frames with JavaScript, how to prepare the SVG artwork, and how to test the result.
Contents
The short answer
A rotating favicon is a small set of separate icon files, often called frames, plus a short script. The script changes the href attribute of the page's icon link element on a timer, and the browser redraws the tab icon each time. Browsers do not rotate icons on their own, and an animated file inside the icon link should not be relied on to play. Swapping static frames from a script is the dependable approach. Most of the quality of the result depends on the artwork: each frame has to read clearly at tab size and line up with the others so the change looks deliberate and not glitchy.
Two terms come up throughout. A favicon is the small icon a browser shows in tabs, bookmarks and history, declared in the page head with a link element whose rel attribute is icon. An SVG (Scalable Vector Graphics) file describes shapes as mathematical paths instead of a fixed grid of pixels, so it can be rendered sharply at any size. Its viewBox attribute sets the coordinate space the artwork is drawn in. Keeping that space identical across frames is what keeps a rotation steady.
Decide what the rotation is for
Settle on the purpose before drawing anything, because the purpose decides how many frames you need and when they should play. Common uses are a subtle brand loop, a status signal such as a change when new activity arrives, and an attention cue that runs only while the tab is in the background. A constantly changing tab icon can distract or annoy visitors, so a narrow, purposeful trigger is usually the better design choice. This is a recommendation, not a technical requirement.
Prepare the frame artwork
- Draw every frame on the same artboard with an identical square viewBox, so the browser scales each one the same way and nothing shifts between frames.
- Keep shapes simple and bold. Fine strokes, small counters (the enclosed spaces inside letters such as o or e) and thin gaps tend to vanish at tab size.
- Convert any lettering to outlines, meaning paths, so the result does not depend on fonts installed on the visitor's device.
- Change as little as possible between frames. Varying one element, such as a color, a rotation or a single mark, keeps the brand recognizable throughout the cycle.
- Align the frames optically, not just mathematically. Shapes of the same measured size can look different in weight, so compare the frames side by side at actual tab size.
- Remove editor metadata, hidden layers and unused definitions on export, so each file stays small and predictable.
- Check that each frame keeps enough contrast against both light and dark browser tab themes.
A file ending in .svg is not proof of clean vector content. An SVG can wrap an embedded raster image, and that will blur at small sizes. Open each file in a text editor or a vector application and confirm it contains paths, not an embedded bitmap. The file extension also says nothing about whether you are permitted to modify or reuse a logo. Confirm usage rights before building frames from someone else's mark.
Choose formats and fallbacks
Browser support for SVG favicons is not uniform. Check current compatibility data for the browsers your audience uses instead of assuming support. A common pattern is to declare an SVG icon alongside a raster fallback, either a PNG or the older ICO format. Note that the script below only swaps the link it targets. Browsers that ignore the SVG link and use the fallback will keep showing a static icon. If the rotation must work everywhere, export each frame as a PNG as well and rotate those instead, or in addition.
Add the icon link to the page head
<link id="favicon" rel="icon" type="image/svg+xml" href="icons/frame-1.svg">
<link rel="alternate icon" type="image/png" href="icons/frame-1.png">
The id attribute gives the script a reliable way to find the link it should update. File names and folder structure here are placeholders. Use whatever matches your project.
Write the rotator script
const frames = [
"icons/frame-1.svg",
"icons/frame-2.svg",
"icons/frame-3.svg"
];
const link = document.getElementById("favicon");
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
let index = 0;
let timer = null;
// Preload frames so each swap draws immediately
frames.forEach(src => {
const img = new Image();
img.src = src;
});
function nextFrame() {
index = (index + 1) % frames.length;
link.href = frames[index];
}
function start() {
if (reduceMotion || timer !== null) return;
timer = setInterval(nextFrame, 2000); // example interval; tune to taste
}
function stop() {
clearInterval(timer);
timer = null;
index = 0;
link.href = frames[0];
}
start();
The modulo operator (%) wraps the index back to the first frame after the last one, which is what makes the change cyclical. The interval value is only an example. Choose a pace that suits the design, and avoid rapid flashing. The stop function resets to the first frame, so the tab always settles on your primary icon. The script also respects the prefers-reduced-motion setting, which lets visitors who have asked their system for less motion see a static icon instead.
Optional: rotate only while the tab is hidden
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
start();
} else {
stop();
}
});
If you use this variant, remove the unconditional start() call from the earlier script. Be aware that browsers commonly throttle timers in background tabs to save resources, so the actual pace there may be slower than the interval you set. Treat timing in hidden tabs as approximate.
Verify the result
- Load the page and watch the tab icon cycle through every frame and return to the first one.
- Open the browser developer tools, inspect the icon link element and confirm its href attribute changes on schedule.
- Check the network panel. After preloading, frames should come from cache instead of being downloaded again on every swap.
- Test in each browser you support, including one that uses the raster fallback, so you know where the rotation actually appears.
- Switch your operating system to reduced motion and confirm the icon stays static.
- Try both light and dark browser themes and confirm every frame stays legible.
- If an old icon persists during development, clear the browser cache or open a private window. Browsers cache favicons aggressively, independent of your script.
Gaps in this guide
This guide was not based on any specific measurements or test data. It does not state exact browser version support, required pixel dimensions or benchmarked timer behavior. Confirm those details against current browser documentation and your own testing before release.