The problem: toBlob() has never heard of ICO
Our favicon generator and the Icon Studio export PNGs in all the common sizes — the Canvas API handles that with a single call. But the classic favicon.ico that browsers have been requesting from the domain root since 1999 is an ICO: a container format that Microsoft introduced in 1985 for Windows 1.0. No browser offers a function to produce one. If you want to offer it browser-locally, you have to assemble the file yourself — byte by byte.
Anatomy of an ICO file
The good news: ICO is delightfully simple once you've understood the specification. The file consists of three parts:
- The header (6 bytes): two zero bytes, then the type (1 = icon), then the number of images contained. That's it.
- The directory (16 bytes per image): for each contained image, an entry with width, height, color depth, data length, and the byte offset where the image data begins. Fun quirk on the side: the size field is only 1 byte wide — so the value 0 means "256 pixels". A format older than the idea that icons could ever get bigger.
- The image data: historically uncompressed BMP blocks — but since Windows Vista, complete PNG files are allowed too.
The shortcut that saved us: PNG-in-ICO
The third point is the decisive one. The BMP route would have meant reordering pixel data (BMP stores rows bottom-up), swapping color channels, and computing a separate transparency mask — all sources of bugs. Instead we use the Vista rule: the PNGs the Canvas API delivers anyway go into the container unchanged. Our ICO builder only calculates the header and directory entries, appends the PNG blocks after them, and hands the result off as a download. Around fifty lines of code — and every byte of it traceable.
The trade-off, to be honest: ancient software (Internet Explorer on Windows XP) expects BMP blocks and cannot read PNG-in-ICO. We decided that a format detail from twenty years ago is no longer a blocker — every browser and operating system that matters today reads PNG entries without a hitch.
Which sizes belong in the file
A good favicon.ico contains several resolutions for the system to pick from: 16 px (browser tab), 32 px (bookmarks, taskbar), 48 px (Windows shortcuts). Packing in more bloats a file that is requested on every page load — the large resolutions are the job of the PNG and Apple touch variants the generator ships alongside (bundled as a ZIP, also generated locally).
The real surprise: 16 pixels are a design question
The technical side was solved after a few evenings. What stuck with us was a different lesson: no algorithm can save a logo at 16 pixels. When you downscale automatically, thin lines disappear, lettering turns into gray haze, details into noise. The big brands show the way: their favicons aren't the logo but a symbol reduced to the bare minimum — one letter, one basic shape, two colors. That is exactly why the Icon Studio got a live preview at actual size: the decision about what survives at 16 pixels can only be made by a human eye. More on this in the post Creating a favicon.
What we take away
Three lessons from this stretch of building: first, old formats are often easier to implement than their reputation suggests — ICO can be understood in an afternoon, while modern containers would cost weeks. Second, it pays to read specifications in the original instead of adopting half-knowledge from forums; the 0-means-256 detail appears in none of the three tutorials we found first. And third: the hardest problems in image processing are rarely technical in nature — they live in the 256 pixels of a 16-by-16 grid.
Sources
Microsoft — Icons (original documentation) · Wikipedia — ICO file format · MDN — rel="icon".