An open-source Magic: The Gathering rules engine and game client
Quick Start · Features · Architecture · Development · Discord ·
A Rust-native MTG engine compiling to native and WASM, powering a Tauri desktop app, browser PWA, and WebSocket multiplayer. Implements comprehensive MTG rules using functional architecture — pure reducers, discriminated unions, and immutable state with structural sharing — with an Arena-quality React/TypeScript UI.
I'm Matt — a millennial software engineer who loves Magic. My six-year-old son asks me to play with him all the time, but the real game is just too complicated for a kid his age.
So I built Alchemy (source) — a simplified, kid-friendly version of MTG we could play together on our iPads. Fewer keywords, no lands, energy that builds each turn, and an adaptive learning mode where he solves math problems for combat bonuses. I had a working version in a single afternoon and fleshed it out over the following week.
After that I wanted to see how far I could push it — a real MTG rules engine in Rust, compiling to WASM, with the same React frontend so the whole thing runs in a browser. This whole project went from nothing to where it is now in a matter of weeks.
I'm not trying to make money off this. There are no ads. I'm just a dude who likes Magic.
- Rules engine — Turns, priority, stack, combat, state-based actions, layers, triggers, replacement effects
- 34,300+ cards — Parsed from MTGJSON with format support (Commander, Modern, Pioneer, Standard, and more)
- AI opponent — Per-card decision logic, game tree search, and evaluation heuristics
- Game UI — Battlefield, hand, stack, targeting overlays, mana payment, animations, and ambient audio
- Multiplayer — WebSocket server with hidden information, lobby system, and WebRTC peer-to-peer
- Metagame feeds — Automated scraping of top decks from MTGGoldfish, updated daily
- Deck builder — Card search, visual builder, and
.dck/.decimport - Cross-platform — Tauri desktop (Windows, macOS, Linux), browser PWA, and tablet
- Card images — Scryfall integration with IndexedDB caching
Thousands of cards are still unimplemented. If you use Claude Code, Codex CLI, or a similar agent, you can "lend your LLM" an hour and ship a real PR — even if you don't have a Rust toolchain. The LLM does all the work; you just paste a prompt.
Hand this to your LLM:
Read https://raw.githubusercontent.com/phase-rs/phase/main/docs/AI-CONTRIBUTOR.md
and follow it end-to-end to implement {a card I name, or pick one for me}.
Use medium thinking. Don't stop for my input. Open a PR when done.
Full procedure, two tracks (developer / non-developer), and copy-paste prompts for LLM UIs without web fetch: docs/AI-CONTRIBUTOR.md.
- Rust toolchain
- wasm32 target:
rustup target add wasm32-unknown-unknown(Windows: see below) - wasm-bindgen-cli:
cargo install wasm-bindgen-cli@0.2.114 - wasm-opt (optional):
brew install binaryenorapt install binaryen - Node.js 22+ and pnpm:
npm i -g pnpm
rustup on Windows defaults to the GNU toolchain, which requires dlltool.exe and fails with "error calling dlltool 'dlltool.exe': program not found". You need Visual Studio Build Tools with the Desktop development with C++ workload, then switch to the MSVC host:
rustup set default-host x86_64-pc-windows-msvc
rustup toolchain install nightly --target wasm32-unknown-unknownVerify with rustup show active-toolchain — it should end in x86_64-pc-windows-msvc.
The setup scripts also require jq and curl, which are not installed by default on Windows. Install them before running ./scripts/setup.sh:
winget install jqlang.jq
winget install curl.curl # skip if curl is already on your PATHOpen a new terminal after installing so the updated PATH takes effect.
git clone https://github.com/phase-rs/phase && cd phase
./scripts/setup.sh # Downloads card data, builds WASM, installs deps
cd client && pnpm dev # Start dev server at localhost:5173./scripts/gen-card-data.sh # generate card-data.json
./scripts/build-wasm.sh # Build WASM bindings
cd client && pnpm install && pnpm dev # Start frontend| Crate | Description |
|---|---|
engine |
Core rules engine: types, game logic, parser, card database |
phase-ai |
AI opponent: evaluation, legal actions, search |
engine-wasm |
WASM bindings via wasm-bindgen + tsify |
server-core |
Server-side game session management |
phase-server |
Axum WebSocket server for multiplayer |
feed-scraper |
Metagame deck scraper (MTGGoldfish) |
Dependency flow: engine <- phase-ai <- engine-wasm / server-core <- phase-server (feed-scraper is standalone)
React + TypeScript + Tailwind v4 + Zustand + Framer Motion + Vite
Transport-agnostic EngineAdapter interface with multiple implementations:
- WasmAdapter — Direct WASM calls (browser/PWA)
- TauriAdapter — Tauri IPC (desktop)
- WebSocketAdapter — WebSocket (multiplayer)
- P2PHostAdapter / P2PGuestAdapter — WebRTC peer-to-peer via PeerJS
- Pure reducers —
apply(state, action) -> ActionResultwith no mutation - Discriminated unions — Rust enums serialize to tagged TS unions via serde + tsify
- Structural sharing — Immutable state via rpds persistent data structures
# Rust (uses cargo-nextest for test execution)
cargo test-all # Run all tests (nextest)
cargo clippy --all-targets -- -D warnings # Lint
cargo fmt --all -- --check # Format check
# WASM
./scripts/build-wasm.sh # Build WASM (release)
./scripts/build-wasm.sh debug # Build WASM (debug)
# Frontend
cd client
pnpm install # Install dependencies
pnpm dev # Vite dev server
pnpm build # TypeScript check + Vite build
pnpm lint # ESLint
pnpm test # Vitestcargo test-all # Run all tests (nextest)
cargo clippy-strict # Lint with -D warnings
cargo export-cards # Run card data exporter
cargo coverage # Card support coverage report
cargo wasm # Build WASM (debug)
cargo wasm-release # Build WASM (release)
cargo serve # Run multiplayer server
cargo scrape-feeds # Scrape metagame feeds
crates/
engine/ Core rules engine
engine-wasm/ WASM bindings
phase-ai/ AI opponent
server-core/ Server session management
phase-server/ Axum WebSocket server
feed-scraper/ Metagame deck scraper
client/ React frontend
scripts/ Build and setup scripts
- Email: matt@phase-rs.dev
- Discord: discord.gg/dUZwhYHUyk
phase.rs is a non-commercial fan project built under the spirit of the Wizards of the Coast Fan Content Policy. It exists for hobbyist, educational, and research use only.
- No bundled Wizards assets. This repository does not distribute MTG card images, card art, mana symbol artwork, card-frame graphics, or the Comprehensive Rules document. Card images and mana symbols are fetched from Scryfall at runtime by the user's browser. Card metadata is sourced from MTGJSON.
- No affiliation. phase.rs is not affiliated with, endorsed by, sponsored by, or approved by Wizards of the Coast LLC or Hasbro, Inc.
Magic: The Gathering, Planeswalker, the mana symbols, and all associated names, text, and imagery are trademarks and copyrights of Wizards of the Coast LLC. All rights reserved by their respective owners.
If you believe content in this repository infringes your rights, please see DMCA.md.
- MTGJSON — Card data (MIT licensed)
- Scryfall — Card images and search API
- MTGGoldfish — Metagame deck data
- Wizards of the Coast — Magic: The Gathering
Dual-licensed under MIT or Apache 2.0, at your option.

