Skip to content

Instantly share code, notes, and snippets.

@westc
Last active August 1, 2025 21:48
Show Gist options
  • Save westc/8bc02b961d684a9b2837cadce87b172f to your computer and use it in GitHub Desktop.
Save westc/8bc02b961d684a9b2837cadce87b172f to your computer and use it in GitHub Desktop.
Use JavaScript to set the favicon on a page so that it cycles through the characters (code points) in a string in similar way to how a marquee would.
/**
* Sets the favicon for the page to show one letter per second of the
* MARQUEE_TEXT constant. The background will be a solid color that slowly
* changes, cycling through different hues.
*/
addEventListener('DOMContentLoaded', () => {
const MARQUEE_TEXT = 'A TEST ';
// The canvas that will be used to generate the image that will show as the
// favicon once every second.
const size = 64;
const canvas = document.createElement('canvas');
canvas.width = canvas.height = size;
const ctx = canvas.getContext('2d');
// Inject favicon
const link = document.createElement('link');
link.rel = 'icon';
link.type = 'image/png';
// Remove all `<link>` elements that are for icons and then just add our
// favicon `<link>` element.
document.querySelectorAll("link[rel~='icon']").forEach(el => el.remove());
document.head.appendChild(link);
// The array of characters to show.
// NOTE:
// Using Array.from() instead of String#.split() so that code points will be
// split correctly.
let marquee = Array.from(MARQUEE_TEXT);
// Update the favicon once every second.
setInterval(() => {
// Draw triangle with horizontal hypotenuse (bottom edge)
ctx.fillStyle = `hsl(${new Date().getSeconds() * 12}deg, 100%, 30%)`;
ctx.beginPath();
ctx.moveTo(0, 0); // Top-left (right angle)
ctx.lineTo(0, size); // Top-right
ctx.lineTo(size, size); // Bottom-right (hypotenuse edge)
ctx.lineTo(size, 0); // Bottom-right (hypotenuse edge)
ctx.closePath();
ctx.fill();
// Draw one letter at a time of the marquee.
let [first, ...rest] = marquee;
marquee = [...rest, first];
ctx.fillStyle = 'white';
ctx.font = `bold ${size * 7 / 8}px sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(first, size / 2, size / 2);
// Update the favicon.
link.href = canvas.toDataURL('image/png');
}, 1e3);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment