Skip to content

SuppliedOrange/obcb-contraptions

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

obcb-contraptions

A list of all the fun stuff I made on One Billion Checkboxes (OBCB)

Prerequisites

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.

  • Pixels go from 0 to 1073741823.
  • There are 262144 pixels per page, and there are only 60 pixels in a row.
  • There are 4096 pages in total.

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.

Contents

Generating an image on a page one time

  • Modify run_generate_image.ts, change the image, page, startIndex and other stuff according to whatever you need.
  • You will need to set the page that you are trying to draw on.
  • You will need to set the startIndex.
  • You will need to set an image path, and configure it's properties.
  • Run npx tsx run_generate_image.ts

an image being rendered on checkboxes

  • Ta-da! Image.

Generating a slideshow of images

  • 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.

a series of images being rendered one after another on checkboxes

Generating a digital clock

  • Add in your startIndex and page
  • Run npx tsx run_clock_websocket.ts

a digital clock rendered on checkboxes

This is obviously a sped-up version but you get the idea.

Generating an infinite game of pong

  • Go to ponggame, run npm start
  • In another terminal or your server, go to directory root . and modify run_pong_websocket.ts with your startIndex and page
  • Run npx tsx run_pong_websocket.ts
  • As long as your terminal is open, an infinite game of pong is generated.

a digital clock rendered on checkboxes

Generating working game of tic-tac-toe

  • Modify run_tic_tac_toe_websocket.ts with your startIndex and page.
  • Run npx tsx run_tic_tac_toe_websoocket.ts

a digital clock rendered on checkboxes

Running retro game emulators

Inside the retrobot directory is a modified fork of the retrobot emulator discord bot.

1: How to run your own retro game

  • 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!

  • Pokemon looked like this

a digital clock rendered on checkboxes

  • But I recommend Bomberman, it works best for the low resolution:

a digital clock rendered on checkboxes

2: Run tetris, it works great

  • I have optimized tetris to work with portait mode on this website.
  • Add the Nintendo Entertainment System (NES) Tetris rom into the games directory.
  • Open up runtetris.ts, modify the same as above.
  • Run npm run start-tetris

a digital clock rendered on checkboxes

3: Make tetris play by itself

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).

  • Do the same steps as above for runtetrisauto.ts

Rendering images on the grand scale

Like the original OMCB, this repository has code to render and draw on a 32768 x 32768 pixel square.

Rendering the 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:

a digital clock rendered on checkboxes

Drawing on the large scale

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.

1: Dither an image

  • Modify generate_dithered_image.ts to link to your .png image and modify the threshold.
  • Run npx tsx generate_dithered_image.ts.
  • The resulting image will appear in ./renders/render_dither_grayscale.png

a digital clock rendered on checkboxes

2: Draw the image

  • 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

a digital clock rendered on checkboxes

Motive

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.