Skip to content

Latest commit

ย 

History

381 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

๐Ÿ”ฎ Scrymat

CI License: GPL TypeScript PWA Ready

Scrymat turns your Magic: The Gathering decks into a shared virtual playmat โ€” offline, at the table, or remotely with no account required.


Scrymat is a free, open-source Progressive Web App (PWA) for MTG goldfishing, couch play, and casual remote games. It runs entirely in your browser, works offline after first load, and is designed for use on a phone lying flat on a game store table.

Note

The production TLD and some AWS infrastructure may still use the legacy scryglass identifier until the final DNS and deploy cutover is complete.

๐Ÿš€ Quick Start (Play Tonight!)

No installation. No account. Just open and play.

Local / pass-and-play

  1. Open the deployed app (today that is still scryglass.cards).
  2. Paste your deck list (Scrymat format, or import from Moxfield/Archidekt/MTGO).
  3. Load each deck, deal opening hands, and start moving cards around the table.

Remote Host / Join

  1. One player clicks Generate Room Code to host a match.
  2. Share the room code or /match/:roomCode invite URL with the guest.
  3. The guest enters the code, Scrymat completes the WebRTC handshake, and both browsers connect peer-to-peer.
  4. Once connected, the host can resync the guest with a full GameState snapshot after reconnects or divergence.

โœจ Features

  • ๐Ÿƒ Shared virtual playmat โ€” The Scrymat pivot expands the state engine from library-only actions to generic card movement across public and private zones (ADR-012)
  • ๐Ÿช‘ Dumb Table philosophy โ€” Scrymat gives you zones, cards, and movement, but it does not enforce turn structure or comprehensive Magic rules. You stay in control of takes-backs, shortcuts, and house rules.
  • ๐Ÿค Peer-to-peer remote play โ€” WebRTC data channels plus stateless signaling synchronize matches without a central game server (ADR-014)
  • ๐Ÿ” Hidden-information protection โ€” Commit-reveal hashing keeps opponents from trivially peeking at concealed cards during remote games. Hidden cards stay committed until you intentionally reveal them.
  • ๐ŸŽฒ Deterministic shared shuffling โ€” Seeded PRNG support lets every client derive the same deck order for a match (ADR-013)
  • ๐Ÿ“ฑ Offline-first local play โ€” Install to your home screen and keep using it for goldfishing or pass-and-play even without WiFi
  • ๐Ÿ–ผ๏ธ Card images via Scryfall โ€” Background prefetch with IndexedDB caching and JIT priority loading
  • ๐Ÿ“ฅ Multi-format import โ€” Supports Moxfield, Archidekt, and MTGO/Arena deck formats
  • ๐Ÿ“ค Multi-format export โ€” Export your deck to any supported format

๐Ÿ’ญ Why Scrymat?

Scrymat began as a simple answer to the frustration that shuffling takes too long. I originally built it so I could play Magic with my kids. Previously, I was spending half of our "playtime" physically manipulating our cards. I wanted a way to let the computer handle the mechanics of the library so we could focus on actually playing the game together.

The pivot to Scrymat keeps that original goal, then pushes further into shared remote play:

  • Protecting High-Value Collectibles: Play with your physical dual lands, foils, and reserved list cards without subjecting them to the wear and tear of constant mash-shuffling.
  • Accessibility: 100-card Commander decks are physically difficult to manipulate. Scrymat removes the physical barrier of dexterity required to shuffle, fetch, and handle a massive deck.
  • Frictionless Goldfishing: Test your latest brews instantly. Just paste your Moxfield link and start drawing hands without needing to sleeve up a single card.
  • Remote Table Presence: Share a synchronized tabletop over the web without a full rules engine or webcam rig.

๐Ÿค Trust Model

Scrymat is designed for trusted casual play, not for adversarial tournament enforcement:

  • The table is dumb by design. Scrymat tracks where cards are, not whether a play was legal.
  • Private information uses commit-reveal. Remote peers receive commitments for hidden cards first, then the real card only when it becomes public.
  • Reconnects prefer snapshots over logs. When peers reconnect, the host can send the guest the current serialized GameState so both sides converge quickly.

๐Ÿ› ๏ธ For Builders

Architecture Decisions

All significant decisions are documented as Architecture Decision Records:

ADR Title
ADR-001 Use Architecture Decision Records (ADRs) to Document Decisions
ADR-002 Preact + Vite for the PWA
ADR-003 Scryfall API integration & compliance
ADR-004 Fisher-Yates shuffle with Web Crypto API
ADR-005 Action/Reducer state management โ€” agent-ready game engine
ADR-006 Semicolon-delimited deck import format
ADR-007 Monorepo structure (core/PWA separation)
ADR-008 TypeScript & Zod for strict typing
ADR-009 Client-side routing strategy
ADR-010 Local storage strategy for decklists
ADR-011 End-to-end testing strategy
ADR-012 Expand state engine to full playmat
ADR-013 Deterministic seeded PRNG for shared shuffling
ADR-014 WebRTC data channels & stateless signaling
ADR-015 URL-based match routing

Architecture

Scrymat is built as a monorepo with strict separation of concerns:

Package Purpose Browser Dependencies
@scrymat/core Pure game logic: deck parsing, shared shuffling, zone-based state management, mulligan/setup flows, and card movement โŒ None โ€” runs in Node.js and browsers
@scrymat/pwa Preact + Vite frontend: UI rendering, Scryfall API integration, IndexedDB caching, Service Worker, and WebRTC match sync โœ… Browser APIs required

The @scrymat/core module uses a strict JSON-in/JSON-out action-reducer pattern with Zod schema validation, making it suitable for consumption by AI agents, CLI tools, or any TypeScript/JavaScript consumer.

Monorepo Structure

repository-root/
โ”œโ”€โ”€ packages/
โ”‚   โ”œโ”€โ”€ core/             # @scrymat/core โ€” game logic library
โ”‚   โ”‚   โ”œโ”€โ”€ src/
โ”‚   โ”‚   โ”œโ”€โ”€ package.json
โ”‚   โ”‚   โ””โ”€โ”€ tsconfig.json
โ”‚   โ””โ”€โ”€ pwa/              # @scrymat/pwa โ€” Preact + Vite frontend
โ”‚       โ”œโ”€โ”€ src/
โ”‚       โ”œโ”€โ”€ package.json
โ”‚       โ””โ”€โ”€ tsconfig.json
โ”œโ”€โ”€ meta/                 # Development philosophy, ADRs, and plans
โ”‚   โ”œโ”€โ”€ adr/              # Architecture Decision Records
โ”‚   โ””โ”€โ”€ plans/            # Implementation plans and roadmaps
โ”œโ”€โ”€ docs-src/             # Documentation source files (MkDocs)
โ”œโ”€โ”€ scripts/              # Utility and automation scripts
โ”œโ”€โ”€ .github/              # GitHub-specific configuration
โ””โ”€โ”€ package.json          # Root workspace configuration

Deployment (AWS S3 + CloudFront)

The repository includes a GitHub Actions workflow (.github/workflows/deploy-aws.yml) that builds the PWA and deploys it to AWS S3, fronted by CloudFront.

Required AWS resources:

  • S3 bucket (static site hosting)
  • CloudFront distribution (CDN, HTTPS, custom error pages for SPA routing)
  • GitHub OIDC identity provider in IAM
  • IAM role with S3 put/delete and CloudFront invalidation permissions

Required GitHub repository variables (Settings โ†’ Secrets and variables โ†’ Actions โ†’ Variables):

Variable Description
AWS_ROLE_ARN ARN of the IAM deploy role
AWS_REGION AWS region of the S3 bucket
S3_BUCKET_NAME Name of the S3 bucket
CLOUDFRONT_DISTRIBUTION_ID CloudFront distribution ID
CLOUDFRONT_DOMAIN (optional) Domain for post-deploy smoke test

The workflow runs automatically on push to main and can be triggered manually via workflow_dispatch. See docs-src/deployment.md for the full step-by-step setup guide, including the IAM policy JSON and CloudFront configuration.

Important

Final Scrymat cutover still requires manual infrastructure follow-up: rename or redeploy the AWS bucket / CloudFront resources as desired, update GitHub Actions variables if those names change, rotate any environment-specific tokens tied to the legacy identifier, and repoint the public TLD when the new deployment is ready.


Contributing

Contributions are welcome! Please read CONTRIBUTING.md for guidelines. Note that this is primarily a hobby project for my own benefit - if there's something you'd like to see improved please LMK, but also know that maintaining Scrymat isn't my day job.

Scryfall Attribution

Card images and data are provided by Scryfall. Scrymat respects Scryfall's API guidelines by rate-limiting requests, caching aggressively, and including a descriptive User-Agent header. See our ROBOT_ETHICS.md policy.

License

This project is licensed under a GPL License.

Fan Content

Scrymat is unofficial Fan Content permitted under the Fan Content Policy. Not approved/endorsed by Wizards. Portions of the materials used are property of Wizards of the Coast. ยฉWizards of the Coast LLC.

About

A lightweight, serverless virtual playmat for MTG. Protect your physical cards from the wear and tear of shuffling during local play, or connect peer-to-peer for frictionless remote matches without the hassle of webcams.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages