Twomillioncheckboxes
A reimplementation of the origin One Million Checkboxes page
Install / Use
/learn @PJUllrich/TwomillioncheckboxesREADME
A clone of OneMillionCheckboxes, written in Elixir and scaled to TWO million checkboxes ;)
You can play with it on twomillioncheckboxes.com
Open Issues
This project is currently work-in-progress. There are a few major problems that need fixing. PRs and suggestions are much appreciated:
- [x] Fix checkbox update payload.
- Currently, whenever a checkbox changes, all players receive an
:updateevent and patch their state of the game. But thePageLiveLiveView currently stores the visible checkboxes as one long list. Whenever that list is updated all checkboxes are re-rendered, leading to a message payload of ~56kb for every checkbox change. - This needs to be optimised.
LiveComponentsmight help, but we can't afford to spin up 1000s of processes for every player. - Fixed: By moving to streams, I could use
stream_insert/3to update individual checkboxes. The payload size went down to 152 bytes per update.
- Currently, whenever a checkbox changes, all players receive an
- [x] Fix UX issues with inifinity scrolling
- The current scroll behaviour is jumpy and doesn't work for scrolling to the top. This needs fixing by somebody smarter than me (please).
- Fixed: I tuned the padding of the board and added enough checkboxes so that the infinity scrolling works now. Ideally, the
prev-pageandnext-pageevents would kick not only when the end of the viewport is reached, but when e.g. 115% of the viewport is reached. That way, the loading would happen just a bit earlier and become just a bit smoother. But it's okay as it is right now.
- [x] (Maybe) get Streams to work.
- My first implementation was using LiveView streams. That worked alright, but I could not remove old elements, so only new ones were added. When I added a
limit: -3000to thestream(:checkboxes, checkboxes, at: at, limit: limit)call inPageStreamLive, the re-render time in the client went from <10ms to almost a second. If we could fix that, streams could work BUT: - How can we handle updates to single elements inside a stream? That's the same issue as with the current
assignimplementation inPageLive. We'd need to re-render the whole board. - Fixed: By reducing the size down to batches of
500checkboxes, the streams worked eventually. The biggest problem here was to get the infinity scrolling to work with larger displays. I needed to render e.g. 2000 elements at first to fill the whole screen. If i didn't fill it, theprev-pageandnext-pagewould fire as soon as I'd scroll down one percent. The board needed to be big enough to span the whole window.
- My first implementation was using LiveView streams. That worked alright, but I could not remove old elements, so only new ones were added. When I added a
- [x] Fix the shift of the board when the rows have an uneven numer of checkboxes
- Fixed: I calculate now the number of columns in the client using a Hook and send back that
column_countto the LiveView. When I fetch new data, I only fetch "full rows" (e.g. if there are 18 columns, I'd only fetch 18 * 27 = 486 checkboxes instead of 500). I don't remove or add "partial rows" this way which prevents a layout shift and keeps all checkboxes in place.
- Fixed: I calculate now the number of columns in the client using a Hook and send back that
- [x] Ignore events for checkboxes that are not currently visible on the player's board. Currently, LiveView will add them to the board, which is weird.
- [ ] General QA and testing would be very much appreciated!
Optional Fixes
These fixes are nice-to-have and might become important in the future.
- [x] Replace the
MapSetin theStateGenServer with an:etstable to allow better read concurrency. - [x] When using
:ets, usetab2Listto dump the state into the db instead if passing a potentially very long array from theStateto theDumpergenserver. - [ ] Store current position as URL parameter to prevent board reload for all users when the server restarts
Local development
To start your Phoenix server:
- Run
mix setupto install and setup dependencies - Start Phoenix endpoint with
mix phx.serveror inside IEx withiex -S mix phx.server
Now you can visit localhost:4000 from your browser.
Related Skills
node-connect
341.8kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
84.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
341.8kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
84.6kCommit, push, and open a PR
