Sealed Deck: The Hobbit
Open six boosters, build a deck, and play a friend in real time on a shared virtual table.
- What
- Web app, live
- Status
- Live
- Stack
- TypeScript, React, Cloudflare Workers, Durable Objects, Supabase
What it is
A Sealed Deck simulator for "The Hobbit" (HOB) set of Magic: The Gathering. Each player opens a pool of six Play Boosters, builds a deck in the browser, then plays a 1v1 match against a friend in real time.
The deck builder shows a mana curve and a colour breakdown, groups cards in different ways and exports the list.
How it works
- Front end: React and Vite, hosted on Cloudflare Pages.
- API and matches: a Cloudflare Worker. Each match lives in its own Durable Object, which is SQLite-backed and talks to both players over WebSockets. It uses the Hibernation API, so a match that is waiting for a move doesn't keep the Worker running.
- Card images: fetched from Scryfall once by an ingest script, stored in Cloudflare R2 and served through the Worker.
- Accounts and data: Supabase. The Worker verifies Supabase JWTs against the project's public keys (JWKS), and Postgres uses row-level security.
- Shared code: booster generation, the game protocol and the card types live in one shared package, used by both the front end and the Worker.
Interesting bits
- Booster generation follows Wizards' official Play Booster structure. The generator is seeded, so a pool can be reproduced. It has its own tests and a small command-line tool to inspect a generated pool without the UI.
- The table is manual on purpose. The server only guarantees the game state: life totals, zones, a hidden hand, tapped and untapped cards. Players apply card effects themselves, like on Cockatrice or Untap.in. That keeps the project small and still makes for a real game.
- The layout follows the Magic Tournament Rules (section 4.7). Lands sit on the player's edge, other cards sit between the two players, and library, graveyard and exile are separate visible zones.