This was a fun project that pulled inspiration from a number of places. First, in
my last two campaigns I turned MTG land cards into hex maps. I love the art in
Magic and it provides such great inspiration — my next project is a series of
TikToks using random land cards to build a world. Second, at a creator meet-up
someone had codified MTG keywords for Mörk Borg. Third, I really want someone to
make a TCG version of a TTRPG. But also, please don’t. I’ll go bankrupt.
Intro aside, the tool’s below and everything after it is how it works.
Draw a few and you’ll spot a pattern: while these are perfectly plausible
monsters, they’re sometimes a bit too strong or weak for their level. They
average out fine, but you’ll find the odd three-drop that swings like a level 5,
how loose it gets depends on the stat.
See the Pen MTG TTRPG Monster Creator by Gregory (@worldsbywally) on CodePen.
I actually think this is a feature and not a bug. There are a few reasons it
happens: keywords that let you cheat a creature out early (Convoke, Affinity,
Delve) leave it underpowered for its printed cost, and multicolour cards run a
bit stronger. Both are exactly what you’d expect from a Magic card, so I decided
not to over-correct for either.
The core stats
The most boring way to look at a monster is its AC, HP, attack bonus and damage.
That’s where I started. Magic gives you two numbers for this; classic TTRPGs use
four.
I did some analysis to find the averages by level in both, then built a
transformation mapping average power by mana value onto average attack bonus by
level, and the same for toughness onto AC. That gave me two formulas:
Attack bonus = card powerAC = 11 + toughness/2 (round up) + Dex adjustment
The attack one genuinely surprised me. There is no transformation. Card power
climbs +0.82 a level; the attack bonus of a real monster climbs +0.71. Magic’s
cost curve and an OSR threat curve are, near enough, the same curve. I tried some
more complicated ideas before realizing that using it exactly as-is worked best.
AC was the opposite. Toughness climbs +0.79 a level where AC climbs +0.36 —
almost exactly double. That means raw toughness is simultaneously too low at
level 1 and too high at level 10, which no amount of adding a constant will fix.
It had to be halved.
For HP I found it’s roughly 4.5 per level, so that’s the base, plus Constitution
scaled by tier, since I wanted Con to actually matter at high level. Damage and
number of attacks came from the same kind of look at averages by level.
The next set of adjustments came from the colour pie.
The colour pie
Magic is built on ally and enemy colours, and colour tends to define a card’s
abilities and archetypes. We could debate endlessly how to map this, but I
decided to put the stats on the colour pie in between the colours:
W --- Wis/Int --- U
/ \
Con Dex
\ /
G B
\ /
-- Str --- Cha --
R
Reading around the wheel: White–Blue is Wis/Int, Blue–Black is Dex,
Black–Red is Cha, Red–Green is Str, Green–White is Con.
Then each colour votes: its two neighbouring edges go high, and the edge
directly opposite, between its two enemies, goes low.
| Colour | Votes high | Votes low |
|---|---|---|
| White | Con, Wis | Cha |
| Blue | Dex, Int | Str |
| Black | Cha, Dex | Con |
| Red | Cha, Str | Wis |
| Green | Con, Str | Dex |
Add up the votes from a card’s colour identity: positive is high, zero is mid,
negative is low. Allied pairs stack their votes and come out spiky. Enemy pairs
cancel each other and come out flat. Colourless votes for nothing and sits on mid
across the board.
(White takes Wis off the shared White–Blue edge and Blue takes Int, so each ends
up with exactly two highs. Red, sitting opposite, dumps Wis on its own. Six stats
didn’t work perfectly with five colors.)
That told me which stats should be high, mid and low. Next I had to find what
those are actually worth, which got a bit more convoluted but ended up here:
| Level | High | Mid | Low |
|---|---|---|---|
| 1 | +1 | 0 | −2 |
| 2 | +2 | 0 | −2 |
| 3 | +2 | +1 | −2 |
| 4 | +3 | +1 | −2 |
| 5 | +3 | +1 | −2 |
| 6 | +4 | +1 | −2 |
| 7 | +4 | +1 | −2 |
| 8 | +4 | +2 | −1 |
| 9 | +5 | +3 | +1 |
| 10 | +5 | +3 | −1 |
The high and low numbers took the highest and lowest stat for each monster, then
the mode (most common) for the high, and the median (the middle) for the low.
The mid value is just the median across all stats for monsters of that level.
Unlike monsters in Pathfinder or D&D, OSR monsters mostly don’t derive AC and HP
from their stats the way players do. But I wanted to carry some of that theme
over, so each stat now pulls a real lever:
| Stat | What it does |
|---|---|
| Str | Picks the damage die — low, middle or high column for its level |
| Dex | Adjusts AC by its distance from the level’s mid value |
| Con | Adds to HP, scaled by tier |
| Int / Wis / Cha | The best of the three becomes the casting stat |
The Dex adjustment is deliberately lopsided: a creature gets the full bonus
upward but only half the penalty downward. Green dumps Dex, and without that
softening a 6/6 trampler was landing at AC 11. That just felt wrong, so I
adjusted it.
Creature types and keywords
The final layer adds abilities from creature type and keywords.
First, casters. Wizards get arcane spells, Clerics get divine ones, and their
casting modifier comes off the colour pie like everything else. Each gets one
random spell per spell tier it could reach at its level, and because they’re
rolled fresh every draw, pulling the same card twice shuffles its spellbook.
Then I looked at the most common creature types and keywords and started throwing
out abilities. That needed a couple of small formulas:
- Ability DC = 10 + half level, rounded down
- Area attacks (a dragon’s breath, say) = ⌊level ÷ 3⌋d8, minimum 1d8
Here’s a sample of what’s in there now:
| Trigger | Ability | Effect |
|---|---|---|
| Deathtouch | Deathtouch | First attack also deals 1d4 Con damage |
| Trample | Trample | Once per turn on a hit, move to another target within near and attack at disadvantage |
| Vigilance | Vigilant | Once per round, strike a target moving into or out of close |
| Lifelink | Lifelink | Regains half the damage it deals |
| Regenerate | Regenerate | Regains HP at the start of its turn |
| Defender | Defender | Damage die steps down once, +1 AC |
| Double strike | Double Strike | One more attack than its level would give it |
| Haste | Haste | Advantage on initiative |
| Hexproof | Hexproof | Advantage on saves against spells targeting it |
| Infect | Poison | DC save or take 1d4 damage of monsters best stat |
| Undying / Persist | Undying | On death, DC 10 Con save to return at 1 HP |
| Dragon | Breath Weapon | Exhales over double near, DC save or take the area damage |
| Dwarf | Stout | Adds its level to its HP |
| Goblin | Wary | Can’t be surprised |
| Rogue | Sneak Attack | Extra damage against an unaware target |
| Vampire | Blood Drain | Drains Con and heals itself |
There are about forty of these now. Send me a note on other keywords and what you
think they should do, and I’ll consider adding them.
A couple of these are advice rather than mechanics. Abilities that let you cast
things faster — Convoke, Affinity, Delve — leave a creature small for its printed
cost, so those cards get a note suggesting you run them a level lower. The
discount is real and grows with cost: Delve creatures average 1.3 power below
the mean for their mana value.
The results
I haven’t playtested these yet, so I’m very open to feedback.
This is where that “depends which stat you look at” comes in, and it surprised
me. I plotted the mean and interquartile range by level for both attack bonus and
AC. The IQR is the distance from the 25th to the 75th percentile, and it does a
reasonable job of capturing dispersion. The two stats behave nothing alike.
Attack bonus lands almost exactly on the average but varies wildly. The
generator’s IQR is 1.90 against the bestiary’s 0.85 — more than twice as
wide. At mana value 7 the published monsters occupy a middle half of exactly
+6 to +6, no width at all, while the cards run +4 to +7.
AC does not do this. Its IQR comes out at 2.17 against 1.95, essentially the
same spread.
While untested, I think I like the variance. A monster with an attack a bit too
high or low, that fits the description better, feels right and leads to better
stories than a pile of monsters that are identical by level.


Magic: The Gathering is a trademark of Wizards of the Coast, which does not
endorse this tool. Card images and data come from Scryfall. Shadowdark is copyright of Arcane Library LLC, this post has no association with.