← Back to news

I Stored a Website in a Favicon

timwehrle.de|258 points|90 comments|by theanonymousone|Jun 20, 2026

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:

  1. Byte 1 \rightarrow Red channel of Pixel 1
  2. Byte 2 \rightarrow Green channel of Pixel 1
  3. Byte 3 \rightarrow Blue channel of Pixel 1
  4. Byte 4 \rightarrow 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:

Total Bytes=212\text{Total Bytes} = 212 Required Pixels=212/3=71 pixels\text{Required Pixels} = \lceil 212 / 3 \rceil = 71 \text{ pixels}

To fit 71 pixels into a square image, the smallest possible dimension is 9×99 \times 9 (which provides 81 pixels).

Final Storage Statistics:

MetricValue
Payload Size208 bytes
Image Dimensions9×99 \times 9 pixels
Total Capacity239 bytes
Utilization87%

It is wild that a functional (albeit tiny) website fits into an image smaller than a standard favicon.

Favicon Concept 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:

  1. Fetches the favicon image.
  2. Decodes the RGB values back into HTML.
  3. 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 .ico format to leverage multiple resolutions for even more storage.

Explore the project here: