Every cube app I have used will happily solve the puzzle for you. Almost none of them will tell you that you took 23 moves when 9 was possible.
What It Is
A 2x2 speedcube in the browser. Scramble it, turn it by dragging the faces, and when you finish it tells you two numbers: how many moves you used, and how many were actually necessary.
It also knows how far you are from solved at any moment mid-solve, which is what makes the hints work.
The Number That Made It Worth Building
A 2x2 has 3,674,160 possible positions.
That sounds like a lot. It is nothing. A browser tab can visit every single one of them in about a third of a second and remember exactly how far each is from solved. The whole table fits in 3.5 megabytes.
So I built that. On load, a background thread walks the entire puzzle, and from then on “how far am I from solved” is a single array lookup. Fifty nanoseconds.
Everything interesting in the app is downstream of that one decision:
- Scrambles by exact difficulty. Not “twelve random turns”, which almost always lands on the same difficulty anyway. You ask for a puzzle that needs exactly six moves and you get one.
- Honest scoring, because the app knows what was possible.
- Hints that give you one move, then make you play it before you can have another.
- It works out which stage of the solve you are in by looking at the cube, not by tracking which buttons you pressed. Hand it to someone mid-solve and it still knows.
A 3x3 cannot do any of this. Its position count is roughly ten million times larger. This app exists because a 2x2 is small, and I am not going to pretend otherwise by promising a 3x3 later.
God’s Number, Which I Did Not Look Up
The largest number of moves any 2x2 position needs is 11. I knew that going in, because it is a well-known result.
I made the app compute it anyway, and it came out at 11.
That is not a wasted afternoon. The computation walks every position through every move table, so the histogram it produces is a fingerprint of the entire engine. It now sits in the test suite as a list of expected numbers. If I ever break a move table, one of the twelve counts changes and the whole thing goes red.
I would rather a test told me my cube maths was wrong than a player.
The Part I Got Wrong Three Times
three.js is about 850 kilobytes. Twenty-six other games on the hub do not use it, and I did not want those players downloading it for nothing.
So I made it load only when someone opens the cube. Easy.
Then I looked at what the site actually did, and the offline cache was quietly downloading it anyway, for everyone, in the background.
Fixing that took three attempts. The first two both looked correct in the config and produced correct-looking builds. The one that finally exposed the truth was recording every network request on the home page and reading who asked for it. It was the main bundle. A one-line optimisation I had added to name the file had silently turned an on-demand load into an always load.
The lesson I keep relearning: you cannot tell what a bundler did by reading its settings. Count the requests.
The Bug That Passed Every Test
At one point every turn was applied twice. Click “turn the right face” once and the
move log said R R.
The puzzle logic was correct. The solver was correct. The 3D maths was correct. Over a thousand tests passed. The bug was three lines of React state code that nobody had ever tested, because it was glue and glue feels too boring to test.
I only found it by opening the actual page and clicking the actual button.
The test I wrote afterwards has to run in React’s strict mode to catch it. Written any other way, it passes against the broken version too.
Colour, Because It Is The Only Information A Cube Has
You cannot solve a cube by shape. Colour is the entire signal, which makes it an accessibility problem with nowhere to hide.
So the six sticker colours are chosen so that the cube is still solvable with no colour vision at all. Convert the whole thing to greyscale and every face is still a different shade. That covers every form of colour blindness at once, rather than patching for one.
My first attempt at this was wrong, and I only know because I wrote the claim as a test instead of a comment. Two colours I had spaced carefully by lightness came out nearly identical in greyscale, in the wrong order, because perceptual lightness and screen brightness are not the same thing. A comment would have sat there being false for years.
It Also Turns Into A Head
The cube’s shape is a plug-in. The puzzle logic does not know or care what a piece looks like.
To prove that, there is a second option in the settings that turns the cube into a lumpy animal head with a snout and ears, generated from scratch in code. Scramble it and the head comes apart properly: the snout ends up on top, the ears point down.
Adding it changed nothing about how the puzzle works. No new logic, no special cases, no branching on which shape is active. That was the whole point, and it is the part I am most pleased with, because it is the part nobody will notice.
What It Does Not Do
No accounts. No leaderboards. No backend. No ads. No tracking. Nothing leaves your device, and everything works on a plane.
No 3x3, for the reason above.
No Bluetooth smart cube support, which I want and did not build.