Last active
August 1, 2025 21:48
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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