A black cat in 2032 bytes

by
, posted

Taper describes itself as “an online literary magazine for small computational pieces”. Submissions need to be 2048 bytes or fewer.

I submitted this little piece, “Light Switch”, to their 13th issue:

The rest of this post describes how I did it.

Shrinking the image

The black cat is the most significant part of the piece. It’s a highly-edited version of a photo I took:

The original photo of the cat.

This is a cute picture but it’s 2.5 megabytes, which is over 1000 times too big.

After a lot of editing, I crushed it down to a 502-byte PNG. Here’s what it looks like in the final submission:

The compressed image of the cat.

I went through several steps to achieve this.

  1. I started by using iOS’s “lift a subject” feature to isolate the cat and remove the background. I still needed to do some editing, but it was a helpful first pass.
  2. I used Squoosh to change the color palette to just black and transparent. Black-and-white PNGs are generally smaller because each pixel can be stored with just one bit (0 for black, 1 for white/transparent)1. I also shrunk the image to just 200×193.
  3. I made minor adjustments in JS Paint. For example, the right eye looked weird after resizing and removing so many colors.
  4. Zopfli helped me squeeze every last byte out of the image. I ran zopflipng --iterations=1000 --filters=01234mepb --lossy_transparent uncompressed.png compressed.png.

I’m sure there’s some tool that does all of those things at once, but I was happy using my hodgepodge of different tools.

Putting the image on the page

Next, I needed to put the image on the page. I can’t load external resources, so I used a base64-encoded data URL.

The HTML looks something like this:

<img src="data:image/png;base64,iVBORw0KG..." />

This data URL is 694 bytes long. This was a big increase from 502 bytes. This concerned me and I had a few ideas for improvements:

I vowed to address these concerns when they became a problem…they never did, and I stuck with the base64-encoded data URL.

I added pixelated image rendering and moved the image to the bottom-right of the screen.

Now that I had the cat sitting still on the screen, I needed to make it a little more lifelike. I needed to make it blink.

After some enjoyable moments looking up GIFs of cats blinking, I settled on this solution:

The cat's blinking eyes. How the cat's blinking eyes work: little circles representing the eyelids.

The cat has four eyelids, two for each eye. Each is a black circle that periodically moves over part of the eye. These circles sit on a <canvas> directly atop the image.

While I was developing it, I thought there was no way something so simple would work. But when I increased the speed, it looked totally fine! Yet another case where my primitive solution was good enough.

I added some additional polish to make the cat occasionally blink twice, which I felt made it look better.

Turning off the lights

My partner recommended I add some additional pizzazz, so I added the ability to turn the lights on and off.

I immediately ran into a problem: the cat’s eyes are transparent in the image. This meant that I couldn’t change the background color without changing the color of the cat’s eyes, and I wanted the cat’s eyes to be visible in the dark.

The image of the cat on a brown background. Because the eyes are transparent, the cat's eyes are brown, which isn't what I wanted.

I tried making a new version of the image with yellow eyes. Unfortunately, this made the image too large, presumably because each pixel could no longer be encoded with a single bit.

My partner had another good suggestion: just put a yellow box behind the eyes. That’s what I did.

The cat image with the yellow eyes behind.

Another primitive solution that was very effective.

Shaving off bytes

Taper doesn’t allow “pieces using exec or regex eval functions”, which rules out compressors like RegPack. In addition to the image compression I described above, I used a combination of tricks to keep my JavaScript under the size limit:

I could’ve done much more, but these solutions were enough to keep me well under the 2048 byte limit with minimal effort.

Thank you

If you’re curious, you can see the source code here.

Make sure to check out all the other great submissions on the Taper website!

Thanks to my partner for constant help and suggestions, thanks to the Taper team for their hard work…and biggest thanks of all to Kyppie the cat, who is now world famous.


  1. If you’re interested in learning more about this, I wrote “The world’s smallest PNG” which explains this in much more detail. ↩︎