A Magic: The Gathering playtesting simulator designed for the MTG community.
Check it out at https://arcanetable.app/
- π 3D Card Simulation: Test your Magic: The Gathering decks in a fully interactive 3D environment.
- π€ Local Multiplayer Playtesting: Playtest with friends using local connections or peer-to-peer networking.
- π Deck Management: Import, export, and manage your decks seamlessly.
- π§ Customizable Settings: Tailor the app to fit your playstyle and preferences.
Arcanetable is designed in a generic way that could support other trading card games, but is currently hardcoded to point at scryfall
- Browser: A modern browser (Chrome, Firefox, or Edge recommended).
- Node.js: Version 16+.
- pnpm: Installed globally for package management:
npm install -g pnpm
- Git: Installed for version control.
- Clone the repository:
git clone https://github.com/odama626/arcanetable.git cd arcanetable - Install dependencies:
pnpm install
- Start the development server:
pnpm dev
Visit http://localhost:3000 to start playtesting!
- Build the app:
pnpm build
- Serve the app (e.g., using Vercel, Netlify, or a Node.js server, or the supplied dockerfile).
Contributions are what make this project thrive! Hereβs how you can help:
- Fork the repository.
- Create your feature branch:
git checkout -b feature/YourFeatureName
- Commit your changes:
git commit -m "Add Your Feature" - Push to the branch:
git push origin feature/YourFeatureName
- Open a Pull Request.
This app is free to use and relies on your generosity. You can support us by:
- Donating on Patreon
- Contributing to the codebase or documentation.
This project is licensed under the GNU Affero General Public License (AGPL). See LICENSE for details.
Contributors should ensure that all additions comply with AGPL requirements.
For questions, suggestions, or just to say hi:
@sparkstonepdx.com- Discord: Join our Community Server
Play. Contribute. Evolve.
Together, we can build the ultimate playtesting experience.
Here's the updated README with the card system documentation added:
# Arcanetable
A Magic: The Gathering playtesting simulator designed for the MTG community.
Check it out at https://arcanetable.app/

## π Features
- π **3D Card Simulation**: Test your Magic: The Gathering decks in a fully interactive 3D environment.
- π€ **Local Multiplayer Playtesting**: Playtest with friends using local connections or peer-to-peer networking.
- π **Deck Management**: Import, export, and manage your decks seamlessly.
- π§ **Customizable Settings**: Tailor the app to fit your playstyle and preferences.
- π§© **Custom Card Systems**: Bring your own card data source β perfect for proxies, custom cubes, or entirely different TCGs.
## π Getting Started
### Prerequisites
- **Browser**: A modern browser (Chrome, Firefox, or Edge recommended).
- **Node.js**: Version 16+.
- **pnpm**: Installed globally for package management:
```bash
npm install -g pnpm
```- Git: Installed for version control.
- Clone the repository:
git clone https://github.com/odama626/arcanetable.git cd arcanetable - Install dependencies:
pnpm install
- Start the development server:
pnpm dev
Visit http://localhost:3000 to start playtesting!
- Build the app:
pnpm build
- Serve the app (e.g., using Vercel, Netlify, or a Node.js server, or the supplied dockerfile).
Arcanetable supports custom card systems, so you can bring your own card data source. This is useful for:
- Proxies β point at your own server serving proxy card data
- Custom cubes β build and serve a curated card pool
- Other TCGs β use Arcanetable as a playtesting tool for entirely different card games
To use a custom card system, add a system query parameter to the URL pointing at a JSON endpoint that describes your card system:
https://arcanetable.app/?system=https://your-server.com/card-system.json
Your endpoint should return a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
name |
string |
An identifier for your card system (e.g. "mtg", "my-cube") |
cardDetailEndpoint |
string |
URL of an API endpoint that accepts ?exact=<card name> and optionally ?set=<set code> to return card detail objects |
cardBack |
string |
URL of the card back image to use |
popularity |
string |
The field name in your card detail response to use for sorting by popularity |
searchField |
object |
Configuration for how cards are indexed for local search (see below) |
The searchField object controls how card data is indexed for searching within the app:
{
"filterEmpty": false,
"searchFields": [
{ "field": "name" },
{ "field": "type_line" },
{ "field": "oracle_text" },
{ "field": "mana_cost", "transform": "stripBraces" },
{ "field": "card_faces", "recurse": true }
]
}| Property | Type | Description |
|---|---|---|
filterEmpty |
boolean |
Whether to omit empty/null fields from the search index |
searchFields |
array |
List of fields to include in the search index |
searchFields[].field |
string |
The field name from your card detail object |
searchFields[].transform |
string |
Optional transform to apply. Currently supported: stripBraces (removes { and } characters) |
searchFields[].recurse |
boolean |
If the field is an array of objects (e.g. card faces), recurse into each item using the same search config |
Your cardDetailEndpoint will be called with:
?exact=<card name>β the exact card name to look up?set=<set code>(optional) β a set/edition filter; the app will retry without it if the card isn't found
The endpoint should return a JSON object representing the card. Any fields you configure in searchField and popularity should be present in this response.
{
"name": "my-cube",
"cardDetailEndpoint": "https://my-server.com/api/cards/named",
"cardBack": "https://my-server.com/card-back.webp",
"popularity": "pick_rate",
"searchField": {
"filterEmpty": true,
"searchFields": [{ "field": "name" }, { "field": "type" }, { "field": "text" }]
}
}π‘ Know a popular TCG that should have built-in support? Open an issue or drop by the Discord and let us know!
Contributions are what make this project thrive! Here's how you can help:
- Fork the repository.
- Create your feature branch:
git checkout -b feature/YourFeatureName
- Commit your changes:
git commit -m "Add Your Feature" - Push to the branch:
git push origin feature/YourFeatureName
- Open a Pull Request.
This app is free to use and relies on your generosity. You can support us by:
- Donating on Patreon
- Contributing to the codebase or documentation.
This project is licensed under the GNU Affero General Public License (AGPL). See LICENSE for details.
Contributors should ensure that all additions comply with AGPL requirements.
For questions, suggestions, or just to say hi:
@sparkstonepdx.com- Discord: Join our Community Server
Play. Contribute. Evolve.
Together, we can build the ultimate playtesting experience.
Main changes:
- Added π§© custom card systems to the features list and removed the old note saying it was "hardcoded to Scryfall"
- Added a full **Custom Card Systems** section with schema reference, search config docs, and an example
- Kept everything else intactHere's the updated README with the card system documentation added:
```markdown
# Arcanetable
A Magic: The Gathering playtesting simulator designed for the MTG community.
Check it out at https://arcanetable.app/

## π Features
- π **3D Card Simulation**: Test your Magic: The Gathering decks in a fully interactive 3D environment.
- π€ **Local Multiplayer Playtesting**: Playtest with friends using local connections or peer-to-peer networking.
- π **Deck Management**: Import, export, and manage your decks seamlessly.
- π§ **Customizable Settings**: Tailor the app to fit your playstyle and preferences.
- π§© **Custom Card Systems**: Bring your own card data source β perfect for proxies, custom cubes, or entirely different TCGs.
## π Getting Started
### Prerequisites
- **Browser**: A modern browser (Chrome, Firefox, or Edge recommended).
- **Node.js**: Version 16+.
- **pnpm**: Installed globally for package management:
```bash
npm install -g pnpm
- Git: Installed for version control.
- Clone the repository:
git clone https://github.com/odama626/arcanetable.git cd arcanetable - Install dependencies:
pnpm install
- Start the development server:
pnpm dev
Visit http://localhost:3000 to start playtesting!
- Build the app:
pnpm build
- Serve the app (e.g., using Vercel, Netlify, or a Node.js server, or the supplied dockerfile).
Arcanetable supports custom card systems, so you can bring your own card data source. This is useful for:
- Proxies β point at your own server serving proxy card data
- Custom cubes β build and serve a curated card pool
- Other TCGs β use Arcanetable as a playtesting tool for entirely different card games
To use a custom card system, add a system query parameter to the URL pointing at a JSON endpoint that describes your card system:
https://arcanetable.app/?system=https://your-server.com/card-system.json
Your endpoint should return a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
name |
string |
An identifier for your card system (e.g. "my-cube") |
cardDetailEndpoint |
string |
URL of an API endpoint that accepts ?exact=<card name> and optionally ?set=<set code> to return card detail objects |
cardBack |
string |
URL of the card back image to use |
popularity |
string |
The field name in your card detail response to use for sorting by popularity |
imageUriFormat |
string |
Either "standard" (default) or "scryfall" β controls how image URLs are structured in card detail responses (see below) |
searchField |
object |
Configuration for how cards are indexed for local search (see below) |
Your cardDetailEndpoint will be called with:
?exact=<card name>β the exact card name to look up?set=<set code>(optional) β a set/edition filter; the app will retry without it if the card isn't found
The response should be a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
name |
string |
The card's display name |
type_line |
string |
The card's type (e.g. "Creature β Human Wizard") |
image_uris |
object |
Image URLs for this card (see Image URIs below) |
card_faces |
array |
For double-faced cards, an array of face objects each with their own image_uris |
all_parts |
array |
(optional) Related cards or tokens associated with this card |
[popularity field] |
number |
Whatever field you configured as popularity in your card system |
searchis generated internally by the app from yoursearchFieldconfig β you do not need to return it.
The structure of image_uris depends on the imageUriFormat set in your card system config.
For card systems with multiple printings or variants per card. image_uris has two keys β full and art β each a map of arbitrary printing identifiers to URLs. Arcanetable will pick a printing at random for display.
| Key | Usage |
|---|---|
full |
Map of printing keys to full card image URLs |
art |
Map of printing keys to art crop URLs |
{
"image_uris": {
"full": {
"1": "https://my-server.com/images/my-card-1.jpg",
"2": "https://my-server.com/images/my-card-2.jpg"
},
"art": {
"1": "https://my-server.com/images/my-card-1-art.jpg",
"2": "https://my-server.com/images/my-card-2-art.jpg"
}
}
}Keys can be anything β they just need to match between full and art so the same printing is used for both.
Mirrors the Scryfall API format, used automatically for the built-in MTG card system. Each card has a flat image_uris object with these keys:
| Key | Usage |
|---|---|
large |
The primary card image shown in the game |
art_crop |
A cropped art-only version, used for thumbnails and deck view |
{
"image_uris": {
"large": "https://my-server.com/images/my-card.jpg",
"art_crop": "https://my-server.com/images/my-card-art.jpg"
}
}For double-faced cards, include a card_faces array with at least two entries. Each face should have its own image_uris following whichever format your card system uses. The app will use card_faces[0] for the front face and card_faces[1] for the back.
{
"name": "My Double-Faced Card",
"card_faces": [
{
"name": "Front Face",
"image_uris": {
"full": { "1": "https://my-server.com/images/front-1.jpg" },
"art": { "1": "https://my-server.com/images/front-1-art.jpg" }
}
},
{
"name": "Back Face",
"image_uris": {
"full": { "1": "https://my-server.com/images/back-1.jpg" },
"art": { "1": "https://my-server.com/images/back-1-art.jpg" }
}
}
]
}The searchField object controls how card data is indexed for searching within the app:
{
"filterEmpty": false,
"searchFields": [
{ "field": "name" },
{ "field": "type_line" },
{ "field": "oracle_text" },
{ "field": "mana_cost", "transform": "stripBraces" },
{ "field": "card_faces", "recurse": true }
]
}| Property | Type | Description |
|---|---|---|
filterEmpty |
boolean |
Whether to omit empty/null fields from the search index |
searchFields |
array |
List of fields to include in the search index |
searchFields[].field |
string |
The field name from your card detail object |
searchFields[].transform |
string |
Optional transform to apply. Currently supported: stripBraces (removes { and } characters) |
searchFields[].recurse |
boolean |
If the field is an array of objects (e.g. card faces), recurse into each item using the same search config |
{
"name": "my-cube",
"cardDetailEndpoint": "https://my-server.com/api/cards/named",
"cardBack": "https://my-server.com/card-back.webp",
"popularity": "pick_rate",
"imageUriFormat": "standard",
"searchField": {
"filterEmpty": true,
"searchFields": [{ "field": "name" }, { "field": "type" }, { "field": "text" }]
}
}π‘ Know a popular TCG that should have built-in support? Open an issue or drop by the Discord and let us know!
Contributions are what make this project thrive! Here's how you can help:
- Fork the repository.
- Create your feature branch:
git checkout -b feature/YourFeatureName
- Commit your changes:
git commit -m "Add Your Feature" - Push to the branch:
git push origin feature/YourFeatureName
- Open a Pull Request.
This app is free to use and relies on your generosity. You can support us by:
- Donating on Patreon
- Contributing to the codebase or documentation.
This project is licensed under the GNU Affero General Public License (AGPL). See LICENSE for details.
Contributors should ensure that all additions comply with AGPL requirements.
For questions, suggestions, or just to say hi:
@sparkstonepdx.com- Discord: Join our Community Server
Play. Contribute. Evolve.
Together, we can build the ultimate playtesting experience.
Main changes:
- Added π§© custom card systems to the features list and removed the old note saying it was "hardcoded to Scryfall"
- Added a full **Custom Card Systems** section with schema reference, search config docs, and an example
- Kept everything else intact
