1 comments

  • CarlsJrMints 2 hours ago

    There are a good number of tools for creating custom MTG cards, a few of them open source. But so far there haven't been any self-contained, composable packages. There are 3 main modules to the package, a parser with associated relevant interfaces, the rendering engine itself, and a React component for displaying rendered cards.

    For the parser I was inspired by the lightweight text format that scryfall uses, like[1]

      Llanowar Elves {G}
      Creature — Elf Druid
      {T}: Add {G}.
      1/1
    
    Crucible's text format [2] is a superset that allows specifying "non-functional" parts of the card, like flavortext, rarity, art, etc. The text format gets parsed into a structured CardData interface. One challenge was accomodating different structured abilites for different card types, like Planeswalkers, Sagas, etc. which I solved using TypeScript discriminated unions. This gives a fair amount of flexibility for consumers since if you are building a UI, you can use the structured ability interfaces, or you can use the raw string interface for abilities.

    The rendering engine uses the Canvas API to compose the various visual elements. Implementing the rendering engine made me a lot more aware of card formatting details. For example hybrid frames vs. gold frames, accent colors, legend crowns, snow & nyx borders, information in the footer. There are a number of styling rule edge cases that had to be solved (a 2-color artifact uses those 2 colors in the accent, but gold for the type, name, and power/toughness boxes. But if the 2-colors are from hybrid mana, the accent is the same, but the name line uses the standard artifact color for the boxes).

    This package also solves the problem of reinventing the wheel when displaying cards in a browser. It provides a React component which provides invisible text to make the cards ctrl+f-able, a context menu for copying the image or text of the card, and rotations for dual-faced cards.

    This project had a lot of scope creep. MTG cards come in a variety of layouts, but it is heavily skewed towards "normal" (covering about 99% of cards) [3]. I had an MVP for rendering standard (or normal) framed cards in about a day. But I figured I should support all the primary card types, so I implemented support for Planeswalkers and Battles (about .1% and .01% respectively). Since Battles are dual-faced, I figured I might as well support transform cards and modal dual-faced. Then the Prepare mechanic came out in Secrets of Strixhaven so I should support that as well. In then end I supported just about every card layout, including split, flip, omen, adventure, aftermath, prototype, leveler. This greatly complicated the parser, rendering engine, and react component, but I am happy with how comprehensive the package is.

    I've used this package for three projects so far (not including the playground page https://domainellipticlanguage.com/project/mtg-crucible-play...):

    thismagiccarddoesnotexist.com [4] - a site for generating cards with AI

    obsidian-custom-mtg [5] - an Obsidian plugin for making custom cards in markdown using a variant of the Scryfall spoiler text format

    command-tower-mcp [6] - an MCP server for managing decks and custom cards on Archidekt.

    If you build anything, I would love to know. Open a PR on the GitHub repo to give yourself a shoutout in the README https://github.com/domainellipticlanguage/mtg-crucible#in-th...

    [1] https://api.scryfall.com/cards/73542493-cd0b-4bb7-a5b8-8f889...

    [2] https://github.com/domainellipticlanguage/mtg-crucible#text-...

    [3] https://scryfall.com/search?q=layout%3Anormal

    [4] https://thismagiccarddoesnotexist.com/

    [5] https://github.com/domainellipticlanguage/obsidian-custom-mt...

    [6] https://github.com/domainellipticlanguage/command-tower-mcp