Yesterday I was base64-encoding an image so I could send it to CouchDB to test some code I'm working on for a client. It reminded me of something I did a while back to set cookies on a remote server.
Basically, a small PHP script was put on the remote server which took a couple of GET parameters and set some cookies based on their values. The script then output a 1x1 transparent GIF. A PHP script on the local server generated an IMG tag which linked to this image and set the parameters based on the COOKIES on the local server.
This process also had to happen in the reverse direction, so I had to send the script to developers on the other side. I wanted to keep it as simple as possible, so I put the actual image contents in the PHP file as a base64 encoded string. I used the GIMP to generate the smallest transparent GIF I could manage so there wouldn't be too huge of a nasty string in the file. I came up with something like the following:
Remembering this got me to wondering, how small could you make a GIF? The file generated by the GIMP was only 43 bytes, but it seemed to be that you should be able to make a file which is representing a single pixel a bit smaller than that.
So, with equal parts of determination and derangement, I set about finding out.
Though of somewhat dubious usefulness, I managed to generate a perfectly valid GIF of only 26 bytes in length, which has the potential to display completely differently in various different software.
Read on to see how I found my way to this point.
The file generated by the GIMP, as I said, was 43 bytes long. You can find it here: tinytrans.gif
To understand what was taking up all those bytes, I read through the GIF89a Spec. The file tinytrans.gif above consists of the following sections:
Header, 6 bytes: Consists of the bytes "GIF" and the version number, which is usually "89a".
Logical Screen Descriptor, 7 bytes: Without going into too much detail, this section of the file indicates the following:
There are 2 colors in the global color table, the second one should be used as the background color
Global Color Table, 6 bytes: Consists of 3 bytes per color, a byte for red, green, and blue, respectively. In our file, the first color is white an the second color is black.
Graphic Control Extension, 8 bytes: Used to indicate that the second color in the color table should be treated as transparent (can also be used for animation parameters, but isn't in this file)
Image Descriptor, 10 bytes: A GIF file can actually contain multiple "images" within it, which keeps you from having to specify image data for parts of the image which have the same color as the background color. Each image block has a position and size within the overall image size. In the above file, the position is 0,0 and the size is 1x1.
Image Data, 5 bytes: One LZW-encoded block of image data. It takes 5 bytes to represent the single pixel the image has in it. The compression algorithm wasn't designed to compress a single byte very well.
GIF Trailer, 1 byte: A single byte with a hex value of 3B (";" in ASCII) indicates the end of the GIF.
Based on the required structures for a transparent GIF, it turns out that 43 bytes is pretty close to as small as you can get.
But, I managed to figure out one trick to make it a bit smaller. It's mentioned in the standard that it is optional to have a global color table. Of course, it's undefined as to what happens when you make a GIF without a color table at all (there are also optional local color tables per image block).
When you have a color table index defined as transparent, however, GIF decoders don't seem to care that there isn't actually a color table.
So I changed the logical screen descriptor to indicate there was no global color table and removed the table itself, saving a total of six bytes, bringing the file size down to a mere 37 bytes.
Here it is in all its glory: handtinytrans.gif
Interestingly enough, Wordpress gave me a lovely list of error messages of GD complaining that this isn't a valid GIF file, despite the fact that Firefox and the GIMP both open and display (is it "displayed" when it's transparent?) the file just fine.
So there you go, the tiniest transparent GIF possible (if you can make one smaller, let me know).
But of course, there's room still for the tiniest GIF ever.
To make it even smaller, I looked to the biggest remaining "optional" block in the image, the graphic control extension. If you don't need transparency, this block is no longer needed, and that's another 8 bytes you can take away.
Sadly, I wanted a whilte image, and I couldn't get it to come out white without adding the global color table back in, so I only managed to trim 2 bytes off of the file.
Still, 35 bytes is indeed smaller: handtinywhite.gif
If I remove the constraint of having the image be white (or any color I choose), I can make it even smaller, though admittedly this becomes much less useful.
I removed both the global image table and the graphic control extension, and then no matter what color the pixel originally was, it comes out black. From there, I took it a step further, because the standard never says you have to actually set any pixels in the image, I removed the LZW encoded image data. I left the required lzw code-size header and block terminator, because without those Firefox and the GIMP both complained it wasn't a valid GIF, and then I had it.
The tiniest GIF ever, 26 bytes: handtinyblack.gif
Despite the fact that this is called "handtinyblack.gif", black is just the color that the GIMP chooses to display when there's no global color table. It's stated in the spec "If no color table is available at all, the decoder is free to use a system color table or a table of its own."
This means that any GIF decoder is free to pick whatever color it wants for this image. Opening this image in Firefox at various different times, it has show up as shades of blue, green and brown, as well as transparent if set as a background image.