I Stored a Website in a Favicon
Hiding a Whole Website Inside a Favicon
By Tim Wehrle | Experiments | 19 | June 2026 | 4 min read
After a previous project where I managed to tuck two bytes of data into a mouse's DPI register, I developed a bit of an obsession. Once you realize you can hide information in places it doesn't belong, the entire digital world starts looking like potential storage.
Usually, a favicon is something you upload once and completely forget about ignore for the rest of the site's lifespan. Naturally, I wondered: Could I store an entire website inside one?
The Art of Steganography
At its core, steganography is the practice of concealing data within another file—like an image—without alerting the observer. Typically, this involves subtly altering a few bits of a photograph to embed a secret message.
In my case, the favicon didn't even need to look like a professional logo. To store text, I simply took the UTF-8 bytes of my content and mapped them directly onto the RGB channels of the image's pixels.
🛠️ The Implementation Process
I began with a minimal HTML payload:
<h1>Website in a Favicon</h1><p>Everything you're reading right now was decoded from favicon pixels.</p>
One critical detail was the length header. Because an image might have trailing unused pixels, the decoder needs to know exactly where the actual payload ends. Without a length value, the system wouldn't know when to stop reading.
The encoding logic followed a simple sequence:
- Byte 1 Red channel of Pixel 1
- Byte 2 Green channel of Pixel 1
- Byte 3 Blue channel of Pixel 1
- Byte 4 Red channel of Pixel 2... and so on.
// Conceptual logic for pixel encoding
const pixels = [];
for (let i = 0; i < htmlBytes.length; i += 3) {
pixels.push([htmlBytes[i], htmlBytes[i+1], htmlBytes[i+2]]);
}
The Mathematics of Tiny Storage
I was honestly surprised by how efficient this was. Since each pixel holds three bytes of data, the calculation for the required space is:
To fit 71 pixels into a square image, the smallest possible dimension is (which provides 81 pixels).
Final Storage Statistics:
| Metric | Value |
|---|---|
| Payload Size | 208 bytes |
| Image Dimensions | pixels |
| Total Capacity | 239 bytes |
| Utilization | 87% |
It is wild that a functional (albeit tiny) website fits into an image smaller than a standard favicon.
A representation of the tiny 9x9 pixel grid.
Retrieving the Data
Storing the data is only half the battle; you also have to get it back out.
The Important Catch: The favicon does not act as a website on its own. Without JavaScript, it is simply a PNG file containing encoded data.
To demonstrate the concept, I added a "Render Website" button to the page. When clicked, the script:
- Fetches the favicon image.
- Decodes the RGB values back into HTML.
- Replaces the current page content with the reconstructed markup.
Final Thoughts & Alternatives
While this is likely the smallest website I'll ever build, it's certainly not the most practical way to distribute HTML. However, there are other interesting paths one could take:
- Store markup directly inside an SVG favicon and parse it on load.
- Utilize the
.icoformat to leverage multiple resolutions for even more storage.
Explore the project here:
- Live Demo: https://www.timwehrle.de/labs/favicon-site/
- Source Code: https://github.com/timwehrle/favicon