Rune Duel
A real-time 3D wizard duel played by drawing runes with a finger or a mouse — in any browser, with the server re-drawing every stroke so a client cannot lie about it.

- Type
- Artifact
- Where
- Personal
- When
- Jul 2026 → Present
- Duration
- 2 mo
- Status
- Live on web
- Source
- GitHub
The problem
Gesture spellcasting is a VR idea. I wanted the feel of it — draw a shape, a spell fires — on the surfaces people actually carry: a phone screen and a trackpad. No headset, no install, no download between wanting to play and playing.
Taking it online turns a rendering problem into a trust problem. The moment two strangers duel over the internet, the number that decides how much damage a spell does is being computed on a machine I do not control.
What I built
The game is React Three Fiber over Three.js: a first-person arena, an opponent across from you, and a $1 gesture recognizer that scores a single-stroke rune against a template. Power is accuracy weighted against speed, so a clean slow rune and a fast sloppy one are both punished — that weighting is the whole game, and it is why drawing has to feel like skill rather than a menu with extra steps.
The recognizer and the combat resolution live in a shared workspace that both sides import. Offline practice runs them in the browser; an online duel runs them again on a Node and Socket.io server that re-recognizes the raw stroke points itself and ignores whatever the client claimed the draw was worth. Same code on both sides of the socket, so the two can never disagree about what a drawing meant.
Accounts, ranked Elo and the leaderboard sit in Supabase — Postgres with row-level security and a function that settles both players' ratings in one transaction — while guests keep an unranked record on device and never see a sign-in wall. The same web build is wrapped with Capacitor for iOS, so the phone app and the browser are one codebase drawing on one pool of opponents.
Screens

What I considered
Rejected. Better frame pacing and store-native tooling, but it forks the product into two codebases and splits the player pool — fatal for a 1v1 game nobody has heard of yet.
Rejected. It is the obvious reading of a spellcasting game, and it shrinks the audience to people who own a headset and are willing to put it on before they know whether the game is any good.
Chosen. One build, one match server, one matchmaking pool; the phone gets an installable app and everyone else gets the same game at a URL. The cost is the WebGL ceiling on graphics, which is the cheapest thing in the list to give up.
Results
- Playable in a browser today: practice against three AI tiers offline, or duel someone through quick match or a six-letter room code.
- Online 1v1 verified end to end over the internet, with the server scoring strokes rather than trusting the client's claim.
- Ranked accounts, Elo and a leaderboard on Supabase, with guests kept playable and unranked on device.
- Packaged for iOS with Capacitor from the same build and submitted to the App Store.
Architecture
In hindsight
Rooms live in one server process's memory. That kept the matchmaking code small and honest, and it makes the match server a single point of failure — when its hosting lapsed, online play went dark while practice and pass-and-play carried on regardless. Moving rooms behind Redis is the difference between a game that survives a restart and one that does not.