A few days into making One Million Checkboxes I thought I’d been hacked. What was that doing in my database?

A few hours later I was tearing up, proud of some brilliant teens.

But let’s back up.

Note - I’m trying something new; I’ve also made a YouTube video that tells this story. I’m trying to decide whether I’m interested in making videos; check it out if you’d like!

What was One Million Checkboxes?

On June 26th 2024, I launched a website called One Million Checkboxes (OMCB). It had one million global checkboxes on it - checking (or unchecking) a box changed it for everyone on the site, instantly.

The site, 30 minutes after launch

My expectations for the site were very low and very wrong. I thought hundreds of players would check thousands of boxes - instead, 500,000 players checked over 650,000,000 boxes in the two weeks that I kept the site online. The site made it into the New York Times and the Washington Post; it’s on Know Your Meme and Wikipedia. The whole thing was a wild ride.

A separate blog of mine covers the technical details behind OMCB. This blog covers my favorite story from running the site.

But to tell you that story, I need to give you two bits of context.

Bit of context 1: OMCB made it hard to draw

I like to make games that help people interact on the internet. Some people are assholes when they interact on the internet. So when I make games like this1 I try to add constraints to make the average interaction a little more pleasant.

I’ve been around long enough to know what people will draw if you put an unrestricted canvas on the public internet, so for OMCB I wanted to constrain drawing.

To do this I scaled the number of checkboxes in a row to the size of the browser window. Here’s what that looks like in practice:

The text 'EXAMPLE' is written using checkboxes. The message disappears unless the browser window is fully stretched out.

excuse the kerning

Here I’ve written “EXAMPLE” - but you can only see it if the browser is exactly the right width! As soon as the number of checkboxes in a row changes the message disappears.

This meant that if you drew something rude on your phone it wouldn’t show up for me on my laptop and vice-versa: your graffiti was only visible to people with the exact same display as your own. This constraint was particularly nice because it was subtle; you might never realize that your writing wasn’t visible to anyone else on the site!

I got a lot of comments asking for me to “fix” this so that people could draw. But the choice here was very intentional.

I’ve found that the best stories from my stranger-interaction games come from how people work around the constraints that I add.

That was foreshadowing.

Bit of context 2: how I stored state

OMCB had a million checkboxes on it. One million is a big number! So I wanted to store and transmit my state efficiently.

To do this I said - a checkbox has two valid states. It’s checked or it’s unchecked. That’s like a bit - a bit is either 0 or 1. So I just stored the state for each checkbox as a bit. Bit 3 was “1” if checkbox 3 was checked and “0” otherwise.

That’s a million bits. There are 8 bits in a byte, so that’s 125,000 bytes, which is 125KB - not even the size of an MP3! Totally workable.

An example of how data was stored. Checked boxes are stored as 1s, unchecked boxes as 0s.

checked boxes are 1s, unchecked boxes are 0s

I stored this data in Redis (an easy to use database) and base64-encoded2 it when transmitting it to clients.

I promise this matters. Let’s get back to the story.

Have I been hacked?

A few days after launching OMCB, I rewrote the backend in go (with the help of my friend Eliot) to keep up with the load. And then, for some reason, I dumped an ascii encoding of the raw bytes in my database. I don’t know why - I just did it.

The data I saw looked like this:

raw binary data from the OMCB database. The url 'https://catgirls.win/omcb' repeatedly shows up

what in the world

And my reaction to the data looked something like this:

The same image as above, with 'what the fuck, am I hacked' on it

sheer terror

I panicked. There were URLs in my database! There were URLs pointing to catgirls.win in my database!! Something was very very wrong.

I assumed I’d been hacked. I poured over my logs, looking for evidence of an intrusion. I read and re-read my code, searching for how somebody could be stuffing strings into a database that should have just contained 0s and 1s.

I couldn’t find anything. My access logs looked fine. My (very simple) code was ok. My heart rate increased. My girlfriend patiently waited for me to join her for dinner. And then - wait.

Wait!

I saw it.

The hidden message

I looked at the checkboxes that corresponded to the sketchy URLs in my database.

An arrow pointing from the 'h' in https://catgirls.win/omcb to the 8 checkboxes on the website that comprise the byte that represents that h

the "h" is one byte - 8 bits. 8 checkboxes. those 8 checkboxes

That H - it represented one byte. One byte represented 8 bits. 8 bits represented 8 checkboxes.

Those chunks of 8 checkboxes formed a repeating pattern that lined up with the URLs. And if I changed something - if I unchecked a box - the pattern immediately reappeared.

A repeating pattern of checkboxes. When the user unchecks some boxes the boxes are immediately re-checked

spooky

I hadn’t been hacked.

Someone was writing me a message in binary.

What does that mean?

When I dumped my database, redis converted the data to ascii.

To do that, it read the data one byte - 8 bits - at a time. It converted that byte to a number between 0 and 255 (2^8 - 1). And then it checked whether that number was in the printable ascii character range (32 - 127). If it was, it printed out the corresponding character; otherwise it printed the byte’s hex representation (e.g. \x00 for 0).

8 checkboxes and their binary representation. Checkboxes forming the pattern 01101000 represent an h, checkboxes forming the pattern 01110100 represent a t. an ascii table

104 represents an 'h' and '116' represents a 't'

So someone was:

  • Checking boxes
  • To flip bits
  • To form numbers
  • That formed letters
  • That spelled out the URL

And they were doing it with thousands of other people on the site.

I was impressed.

Down the rabbit hole

So.

https://catgirls.win/omcb

catgirls dot win

I hemmed and hawed. I googled around. And then I clicked the link.

An invite to a discord called 'Checking Boxes'

interesting

The link went to a discord! And the discord was called “Checking Boxes”3. I joined the discord.

Discord chat logs. Nolen says 'hi, i made the website.' Someone else says 'WHOA, YES, ITS YOU'

saying hello

And someone was really excited to see me! We chatted for a bit. And then they asked me something that blew my mind:

"Have you seen your checkboxes as a 1000x1000 image yet?"

I said no. They showed me what they were up to:

A black and white grid. There is a banner on the grid that spells out 'be gay do crime' as well as a QR code.

goodness

They were downloading data for all million checkboxes and rendering them as a 1000x1000 grid (the unchecked boxes are white; the checked boxes are black).

There’s a lot going on here! We’ve got “be gay do crime” - love that - but there’s some interesting technical stuff here too.

An annotated version of the same grid

double goodness

The repeated noise at the bottom is the binary message I found. Above that is a base64 version of the same message - remember that base64 is what I used for transport. And on the left side is a QR code (with full error correction!). All of these messages linked to the discord.

The discord was full of some very sharp teens4, and they were writing these secret messages to gather other very sharp teens to talk about botting the site. Anyone who was writing a bot would probably be looking at either the base64’d version of the data, the binary version, or the 1000x1000 image version; they were covering all the bases.

And this worked! The discord grew from under 20 people when I joined to over 60 by the time I shut the site down.

So what’d they do?

Well, they drew a whole lot! As they built better systems for drawing (and better reverse-engineered my rate limits5) their drawings became more complex.

A drawing of a windows blue screen of death on one million checkboxes

A windows blue screen of death - pre CrowdStrike incident!

Over time they experimented with animations and even tried out some protocols for adding color - like treating adjacent cells as the red, green, and blue channels of a color and drawing to a smaller grid.

Lots of drawings on one million checkboxes, including several of Jake Gyllenhaal

The grid at its most chaotic - someone was a Jake Gyllenhaal fan.

I gave the discord a warning before I sunset the site. The night before doing it I removed all my rate limits to see how much traffic the site could handle and what they could do. We ended up with some really cool animations - my favorite was a Rickroll (this clip is not sped up).

An animated rickroll drawn on one million checkboxes

Look at Rick go

Is botting good?

Lots of people were mad about bots on OMCB. I’m not going to link to anything here - I don’t want to direct negative attention at anyone - but I got hundreds of messages about bots. The most popular tweet about OMCB complained about bots. People…did not like bots.

And I get it! The typical ways that folks - especially folks who don’t program - bump into bots are things like ticket scalping and restaurant reservation bots. Bots that feel selfish and unfair and antisocial.

And there certainly was botting that you could call antisocial. Folks wrote tiny javascript bots to uncheck every box that they could - I know this because they excitedly told me.

a reddit comment from a user sharing a script with me

ok!

I expected this - I’m a programmer! - but people told me that it ruined the site for them and I suppose I understand that.

So, sure. This drawing probably degraded the experience for “regular” users - although the botters did have some rules around where to bot, and I would occasionally chime in to ask them to dial things down.

A message from me asking for folks to tone down botting because OMCB is in the Washington Post

debatable how well this worked

So there are some caveats. I understand why people don’t like bots; maybe this wasn’t an unassailable good. But man…

I found this so moving!

In highschool, I wrote a recursive mail rule that sent a friend of mine millions of messages as a joke. I (accidentally!) repeatedly crashed the school’s mail server6

The adults in my life7 were largely not mad at me. They asked me to knock it off, but also made me a t-shirt. I don’t think I’d be doing what I do now without the encouragement that I received then.

What this discord did was so cool - so surprising - so creative. It reminded me of me - except they were 10 times the developer I was then (and frankly, better developers than I am now). Getting to watch it live - getting to provide some encouragement, to see what they were doing and respond with praise and pride instead of anger - was deeply meaningful to me. I still tear up when I think about it.

I’m proud to have made something that this discord decided was worth playing with, and I’m even more proud of what they did with it.

I can’t wait to see what they go on to make.

Again - I’ve made my first YouTube video to tell this story. Given how emotional I found the whole thing, I thought being able to use my voice would be nice. Check it out if you’re interested!

  1. The data I put together for Talk Paper Scissors is that ~4.5% of games involved someone trying to ruin things 

  2. Just an easy way to pass binary data around on the internet. There were more efficient choices here but I wasn’t too fussed about that when I made OMCB, especially since I didn’t expect it to be super popular. 

  3. The original discord is now locked down and the URL points to something else. 

  4. Well, not exclusively teens! But certainly “sharp people who are younger than me” 

  5. At one point I offered to tell them what the rate limits were and the leader of the discord told me they preferred to figure them out themselves! 

  6. I’ll write a different post about this. 

  7. Including my computer science teacher - the incredible Richard Eisenberg, who’d go on to be a brilliant functional programming researcher and my future coworker at Jane Street!