Galois¶
Learn group theory by turning a Rubik's cube. Live: rj8228.github.io/galois · Code: github.com/rj8228/galois
Pitch (30 seconds)¶
Galois is a free web app that teaches group theory through a 3D Rubik's cube. You turn the cube, notice what happens, and then learn the name for it: identity, order, permutations, commutators, invariants, and finally why every position can be solved in 20 moves or fewer. It has a daily competition, Solve of the Day, with a speed and a fewest-moves track. It's a static PWA on GitHub Pages, tested on phone, tablet and laptop with Playwright.
Why: I taught myself the cube from YouTube at 13. Ten years later I wanted the version that explains the maths. The design rule is "turn first, name it later": every idea starts as something you do on the cube.
Get the framework right
Galois is built with Preact + @preact/signals, not React (the portfolio's skills chip groups it under
React). If asked: "Preact: same component and hooks API as React in 3 KB, which matters for a PWA on phones;
signals for fine-grained state so a cube turn doesn't re-render the whole tree. My React work is the Prayog
terminal and my day job."
Stack and why¶
| Choice | Why |
|---|---|
| TypeScript, Vite | Types for the maths (permutations, moves); fast dev server and build |
| Preact + signals | Small bundle for phones; signals update only what reads them |
| cubing.js | 3D cube, notation and puzzle maths, instead of writing a renderer |
| PWA (vite-plugin-pwa, service worker) | Works offline, installable |
| Vitest + Playwright | Unit tests for the maths; E2E on phone, tablet and laptop viewports |
| Biome | Lint + format in one fast tool |
GitHub Pages, phases tagged v0.N.0 |
Every push to main deploys; each phase is a branch → PR → tag |
The maths in interview-sized pieces¶
| Idea | On the cube | Elsewhere in software |
|---|---|---|
| Group | Moves with "do one then another", an identity (do nothing), and inverses (undo) | Undo stacks, Git, CRDTs |
| Order of a move | How many times you repeat it to get back to start (R has order 4; R U has order 105) |
Cyclic schedules |
| Permutation | Every position is a rearrangement of the pieces | Shuffling, sorting networks |
Commutator A B A⁻¹ B⁻¹ |
Changes only a few pieces: the basis of most solving algorithms | "Do, change, undo" refactoring |
| Invariant | Some positions are impossible (a single flipped edge) because a parity is always preserved | Loop invariants, consistency checks |
| God's number = 20 | Every position can be solved in at most 20 face turns (proved 2010) | Graph diameter |
Good story: the update that never arrived (service worker)¶
- Symptom: after v0.3.0 shipped, the live site kept showing the old "Arrives in Phase 3" page, even after reloads.
- Cause: v0.3.0 switched the service worker to
promptmode, where the new worker waits until the page tells it toskipWaiting(). But the code that sends that message was in the new app; the browser was still running the old one, which didn't know how. On phones the old tab is rarely closed, so it waited forever. - Fix: a new worker takes over by itself as soon as it's installed (PR #4).
- Lesson: an update mechanism must work with the previous version's code, not just the new one. Same idea as backward-compatible API and schema changes.
How does a service worker update?
The browser checks sw.js for changes (on navigation, at most every 24 h by default). If it changed, it installs
the new worker, which then waits until all tabs controlled by the old worker are closed, unless it calls
self.skipWaiting(). On activate, clients.claim() makes it control open pages. Cache strategies: cache-first for
hashed assets, network-first for index.html.
How do you test a 3D app?
Unit-test the logic (moves, permutations, analysis) with Vitest, independent of rendering. Playwright E2E on three viewports for journeys (turn a face, complete a lesson). Visual checks with screenshots where needed. The renderer (cubing.js) is trusted; my code around it is tested.