A hex-board tactics duel where balance is a theorem, not a patch note.
Two Voices meet on a small hex board. Everything they do lives on a 12-point
phase wheel. How hard a strike lands is sin(Δphase); how many points a Voice
scores is cos(Δ to a sweeping Axis). Because sine sums to zero around a circle,
every position is beaten by exactly as much as it beats — there is provably no
dominant move, no degenerate meta, and power creep is mathematically impossible.
Managing where you stand (space) and how you're tuned (phase) at the same time, against an opponent revealing simultaneously, is the whole game.
npm install
npm run dev # open the printed localhost URLBuild a static site:
npm run build # outputs to dist/
npm run preview # serve the production build locallyThere are no runtime dependencies beyond React — no game framework, no asset pipeline, no backend. Sound is generated with the Web Audio API.
- Skirmish — vs a searching AI (Calm / Even / Sharp) or 2-player hotseat with hidden-order hand-offs.
- Campaign — five authored battles with distinct boards and win conditions (outscore, protect your Cantor, race to a target, a Sharp-difficulty Nemesis).
- Puzzles — deterministic one-round tactics trials with solution checkers.
- Tutorial — the wheel, the Axis, the board, the classes, and comebacks in five illustrated steps.
- Settings — sound, colorblind shapes, high contrast. Play never depends on color alone.
| Glyph | Class | Trait |
|---|---|---|
| ◈ | Warden | Balanced fighter and scorer. |
| ▲ | Lancer | +1 strike, tough; hunts scorers. |
| ◐ | Cantor | Scores double, cannot strike — needs a guard. |
| ⤢ | Shifter | Moves two hexes; a flanker. |
| ⬟ | Anchor | Shields adjacent allies (−1 damage); counters Lancers. |
Every strength is paid for by a weakness — the same antisymmetry that governs the wheel.
- Clash =
sin(Δ)— antisymmetric and zero-summing around the circle. No dominant tuning can exist; a stronger matchup always implies an equal weakness in its mirror. - Score =
cos(Δ)to an Axis that sweeps 30° per round — a non-stationary optimum you can't solve or camp. - The AI generates candidate plans, predicts your best reply, simulates the round, and evaluates the board — then eases off when far ahead to hold the match near 50/50 (flow).
- Tension (the comeback system) buys information, never stats, so a comeback still has to be earned.
This repo ships a workflow at .github/workflows/deploy.yml that builds and
publishes on every push to main.
- Push the repo to GitHub (see below).
- In the repo: Settings → Pages → Build and deployment → Source: GitHub Actions.
- Push to
main. Your game goes live athttps://<your-username>.github.io/<repo>/.
Two things a static site genuinely can't do: real online multiplayer (needs a server to relay moves) and saves that persist across sessions (needs a backend or a storage layer). Everything else is here. Hotseat and single-device play are fully local.
MIT — see LICENSE. Yours to fork, retheme, and extend.