This was not useful. This was not practical. But it did something unfortunate to my brain.
Once you successfully hide data somewhere it doesn’t belong, you start seeing everything as potential storage.
One monitor is storage.
There is a keyboard storage.
There is a BIOS splash screen (probably) storage.
Favicon storage.
And yes, we are here.
Every website has a favicon. It’s that little icon in your browser tab. Usually you upload it once and then never think about it again. But. A favicon is just an image. An image is just pixels. And pixels are just bytes.
So of course I wondered if I could store something inside it.
Idea
My first thought was steganography.
Steganography is basically about hiding data in an image without making it obvious. You take a perfectly normal photo and modify a few bits so that it secretly contains a message.
The favicon itself doesn’t need to look like an icon (at least in my demo). This can become pure storage.
Each pixel has a red, green and blue value. That’s three bytes.
If I wanted to store text, I could take the UTF-8 bytes of text and write them directly into RGB channels.
The browser doesn’t care what those bytes represent. Those are the colors for the browser. To me they are HTML.
Creating a Favicon Website
I started with a small HTML payload:
Everything you're reading right now was decoded from favicon pixels.
The process is very straightforward.
First I convert HTML to bytes using TextEncoder.
Then I prepend four bytes containing the payload length.
The length header is important because the end of the image may contain unused pixels. If there is no length value, there is no way to know where the actual payload stops.
Once I have the byte array, I start filling in the pixels.
The first byte becomes the red channel of the first pixel.
The second byte becomes the green channel.
The third byte becomes the blue channel.
Then the next pixel. and next. and next…
Ultimately the entire HTML document exists as colored pixels.
The resulting image looks like visual noise.
Very small
What surprised me most wasn’t that it worked, to be honest. That’s how small the resulting image was.
The payload eventually became 208 bytes.
Adding a 4-byte header brings the total to 212 bytes.
Since each pixel stores three bytes, I need:
- total 212 bytes
- 71 pixels
- A square image large enough to contain them
The smallest square that works is 9×9 pixels.
That’s only 81 pixels.
The final figures looked like this:
- Payload: 208 bytes
- Image size: 9×9 pixels
- Capacity: 239 bytes
- Used: 87%
Somehow an entire small website (well, HTML with some styling) fits inside an image that’s smaller than a normal favicon.
read back website
Storing data is only half the problem. The other half is getting it back.
Browsers already have everything they need for this.
- The favicon is loaded as an image.
- The image is engraved on canvas.
- The Canvas API lets JavaScript read each pixel.
Once I have the pixel data, I simply reverse the process.
- Read RGB values.
- Reconstruct the byte array.
- Read the first four bytes to determine the payload length.
- Remove payload.
- Decode UTF-8 text.
At that time I have the original HTML again.
The browser reads a website from its favicon.
important catch
A favicon doesn’t actually cover the entire website.
It contains the content of a website.
You still need a small bootstrap loader to decode the image.
Without JavaScript the favicon is just a PNG (containing your website’s content).
The site includes a “Submit Website” button to demonstrate this scenario. It reads the favicon, decodes the HTML, and replaces the page with the rebuilt content.
Is it useful?
No, not at all.
The amount of data you can store is very small. JavaScript is required to bootstrap the page. There are dozens of better ways to distribute a small HTML document.
But in the end it’s about testing the limits, right?
The favicon feels like a very specific thing. It is considered a symbol.
But in the end it might just be a PNG.
And a PNG file is basically just bytes.
And it’s probably the smallest website I’ve ever made…
Here is the link to the site: https://www.timwehrle.de/labs/favicon-site/
And if you want to see how it works: https://github.com/timwehrle/favicon
<a href