This is a web app I built for my own decks in Magic: The Gathering, Wizards of the Coast’s card game. It keeps decks by format and answers questions I used to guess at: is the mana right, how often does the deck do what it was built to do, how does it fare against what people play in tournaments, and what should I change. It runs on my home server, with Cloudflare in front and the household sign-in on the door.
The problem
Deck-building questions come in two kinds. Some have exact answers: the chance of holding three lands by turn three is a sum anyone can compute. Others need the game played, and the rules cover about 30,000 distinct cards. The tools I had used either stopped at the first kind or gave one win percentage with no sense of how far to trust it. I wanted both kinds of answer in one place, each saying how it was produced.
What I built
- A collection by format (Commander, Standard, Pioneer, Modern and Pauper): plain-text import and export, every save a version, versions compared, legality checked.
- Measurements: the curve, card types, land kinds, colour sources against a published benchmark, and exact draw odds.
- A goldfish: the deck played on its own with simplified rules (mulligans, land drops, mana by colour, casting, a damage clock), 10,000 games a run.
- Full-game simulations against a chosen deck, the rest of the collection or tournament decks, with a computer player in every seat, in two-player games or four-player Commander.
- A Meta page: recent tournament results grouped into archetypes, with each one’s share and win rate.
- An assessment: each card’s roles, the deck’s strengths and weaknesses with the number and threshold behind each, and suggested changes with their evidence. A change can be tested on a hidden copy of the deck, in the goldfish or in full games, before I keep it.
- Extras: Commander Spellbook combos, a Commander power check, prices with cheaper cards of the same role, ban and rotation alerts, a matchup matrix with the expected win rate against the field, sideboard plans and a keep rule for opening hands.
How it works
One TypeScript engine package holds the parsing, the statistics, the exact odds, the goldfish and the assessment’s rules. It does no network or disk access of its own, so the same code runs in the browser for instant figures and on the server for batch runs: 10,000 goldfish games of a Commander deck took 1,420 ms on the server.
Full games run on Forge, an open-source Magic rules engine, as a separate process. A worker on the home server takes queued runs one at a time, runs 12 Forge processes for two-player games or 6 for four-player Commander, and stores each game as it ends, so the report fills in while the run plays. A run in which no clock fired repeats exactly from its seed.
A nightly job reads recent tournaments in five formats from TopDeck.gg’s API, keeps 120 days of them, and groups the decks into archetypes. No player’s name is stored. A run can play these decks too, and report the deck’s record against each.
In front, a Cloudflare Worker behind Cloudflare Access serves the web app, passes API calls to the home server through a Cloudflare Tunnel, and keeps a copy of each read, so the app stays readable when the server is down.
Decisions and trade-offs
- Borrowing a rules engine for full games, and writing a small one for the rest. The rules are by far the hardest part, and Forge already plays them, headless, with computer players. I gave up control of its AI and its speed, about 117 two-player games a minute on the home server, and didn’t write a rules engine. Questions that don’t need the full rules go to my own goldfish, which is far faster.
- A simulated win rate measures the engine’s AI, not the player. Forge’s AI plays aggressive decks well and combo decks badly, so every rate is labelled AI-piloted and shown with its 95% interval. At 100 games that interval is ±9.8 points, enough to tell a 70% matchup from a 30% one and nothing finer; at 1,000 it is ±3.1. One clean percentage would read as more certain than it is.
- Card roles from my own rules, not a community tag set. A versioned rules file reads each card’s rules text, rather than importing Scryfall’s community tags, so every tag names the rule that gave it and I can turn any tag on or off for a card. The cost is keeping those rules myself.
- A tournament source that asks for credit. TopDeck.gg’s documented API asks for a visible credit, and every page of the app carries it. The best-known decklist sites have no API, and one disallows deck downloads in its robots.txt, so scraping them was out.
What’s next
Owned cards, to show what each deck is missing; a sample-hand playtester; and a weighted mix of opponents in one run.
Built with
TypeScript, React, Vite, Hono, Node.js, Postgres 18, Forge (Java), Docker, Cloudflare Workers, Workers KV, Cloudflare Access, Cloudflare Tunnel, Scryfall card data, Commander Spellbook combo data, the TopDeck.gg API, Uptime Kuma.