A list of all the fun stuff I made on One Billion Checkboxes (OBCB)
You need to do npm install
in the directory.
The startIndex
, referenced in many files is the index of
the pixel you are referring to.
A formula to calculate the pixel would look something like:
const pixelsPerRow = 60;
const pixelsPerPage = 262144;
function getPixelInPage(row = 0, column = 0, page = 0) {
const pixelWithinPage = (row * pixelsPerRow) + column;
const pageStart = pixelsPerPage * page;
const pixelIndex = pageStart + pixelWithinPage;
return pixelIndex;
}
I think this should work, I think. I just hacked the javascript to show me the pixel indices of all checkboxes so I don't have to deal with this.
The recommended threshold is 128 for most stuff, but if your image has high contrast or is a black/white image, you can just go for a really high threshold value, maybe like 200-220.
You can play with the values, in most cases it is rendered in ./renders
so you can inspect it there.
run_generate_image.ts
, change the image, page, startIndex and other
stuff according to whatever you need.page
that you are trying to draw on.startIndex
.npx tsx run_generate_image.ts
Like the previous one, inside run_display.ts
has a list of images this time.
You follow all the steps above.
There's a lot of last-moment features in here since I was attempting to combat trolls.
If you toggle combat_mode
, it will continuously re-render the image. This
isn't very efficient on it's own, but it does it's best, and is probably more
effective with about 5 of these proceses running at once, in sync. I won't be
providing code for that though.
shuffleEnabled
shuffles the order of the display.
Putting an array of strings instead of a string for the image name is going to make it either randomly pick one or continuously render them all one by one.
Putting "random" for invert will make the invert
property become randomized.
startIndex
and page
npx tsx run_clock_websocket.ts
This is obviously a sped-up version but you get the idea.
ponggame
, run npm start
.
and modify
run_pong_websocket.ts
with your startIndex
and page
npx tsx run_pong_websocket.ts
run_tic_tac_toe_websocket.ts
with your startIndex
and page
.npx tsx run_tic_tac_toe_websoocket.ts
Inside the retrobot
directory is a modified fork of the retrobot
emulator discord bot.
Add a game of extension .gb
, .gba
or .nes
into the ./games
directory.
Open up run_pokemon_ts
. Modify the startIndex
, page
and game
.
Depending on the game, you will want to modify the threshold
.
Different thresholds work well for different games.
There are 2 modes to this, horizontal and vertical. Change the gameHorizontal
property to your liking.
Run npm run start-pokemon
We've played actual games on the emulator!
Nintendo Entertainment System (NES) Tetris
rom into the games
directory.runtetris.ts
, modify the same as above.npm run start-tetris
This does not actually play tetris by itself, but the original NES Tetris comes with a built-in auto-play screensaver, which plays games of tetris seemingly on it's own (even though it's likely pre-recorded).
runtetrisauto.ts
Like the original OMCB, this repository has code to render and draw on a 32768 x 32768 pixel square.
Go to the py
directory and run
python -m pip install websocket-client pillow numpy
Run python read_and_generate_map.py
This will take a considerable amount of memory usage. You'll need at least 4+ gigabytes of RAM free.
You can choose to create a dump
file to read from there instead of making requests
to the server next time. However, dump files are 1 gigabyte big.
The rendered output is saved in square_image.png
and looks like this:
Here comes the tricky part, I never actually solved this myself. I came up with a hacky fix and I'm still disappointed in the way that it works. I wouldn't consider this a reliable method but I no longer have the patience to fix this.
generate_dithered_image.ts
to link to your .png
image and modify
the threshold.npx tsx generate_dithered_image.ts
../renders/render_dither_grayscale.png
Open up run_generate_image_big.ts
Change the startIndex
, imagePath
, imageInvert
, imageThreshold
You can run run_generate_whatdoesitlooklike.ts
with the same settings
to see what the resulting image will look like. Keep increaseHeight
to
false to see it normally, true to see it's true elongated state at which it
is rendered.
Decide on the width of the image in widthImage
. Remember that the canvas
is 32k x 32k.
The most important step is to find the right targetHeightMultiplier
.
This is a weird thing that was introduced because I might've messed up my math, but GPT couldn't find the problem either so I gave up.
1) For a 1k x 1k image, the multiplier can be left at 1. 2) For a 5k x 5k image, the multiplier must go to 6 (or 6.5 also works). 3) For a 5k x 2.7k image, the multiplier can be 4 (or 4.5 is better). 4) For a 5k x 6.3k image, the multiplier must go to 7 or 7.5.
You see this weird pattern? I don't get it. Why does multiplying the height by such weird amounts give us the correct image we need on the board? I'll never know.
leaveUnfilledBlank
will not touch pixels that are of "unfilled" / 0 / white colour.
Run npx tsx run_generate_image_big.ts
After that you're done. You're going to want to re-render the board to see your result.
Here's what mine looked like
There was a popular website called One Million Checkboxes (OMCB). It was wow-ed at for it's ability to handle the state of a million checkboxes in real-time.
Later, the creator made this video, where he mentioned that a group of teenagers made art out of checkboxes. I missed it, and I probably never would've found it, but I found One Billion Checkboxes, it was made by someone in the same server that orchestrated the OMCB artists' operations.
Then, I immediately got to work to recreate what I would've had I laid my hands on OMCB. Out of excitement, I wrote this rushed gist about how I found this super cool alternative site.
And then, in a matter of a week or two, here I am, sharing the cool stuff I made on OBCB. I'm especially glad for the friends I made while making these silly projects, alongside the trolls I met that sabotaged me. It was short, but it was nice.