JAMIE
A persistent AI development partner — part JARVIS, part operating system for how I work.
[ read case study ]I wanted to play D&D, but never had a committed group. Party Wipe is the combat half — a 20-minute single-player roguelike, no DM required.




D&D has a gatekeeping problem. A committed group, a willing DM, four-plus-hour sessions, paper bookkeeping, a rules tome longer than most novels. Two audiences bounce off it: gamers who'd be receptive to tabletop but won't commit, and non-gamers who'd try a shortplay if the rules barrier disappeared.
Most of that friction lives in combat. Story tracking is solvable — campaign wiki, DM-managed objective tree, prepared encounters. Combat is the harder problem: conditions, action economy, dice math, resource bookkeeping, damage-type interactions. New players bounce off the rules density before they ever feel the strategic loop underneath.
I wanted to find out what "consumable D&D combat" could look like, with no good way to learn it from the table. So I built Party Wipe — a single-player roguelike that runs the combat layer end-to-end without a DM, on a heavily curated SRD subset. Longer-term, this becomes the combat engine inside a DM/player dashboard that lowers the barrier for casual table play.
The game is five things working together.
Every game phase is a full-screen takeover with a persistent floating HUD. Phases: room-preview, combat, loot, rest, level-up, game-over. No inventory menu, no character-sheet dialog opened mid-combat. The current phase is the screen. Removes the rules-research detour that loses new players at tabletop.
Three abstract zones — front, mid, back. Distance determines melee (same zone), ranged (adjacent), or far (skip one). No grid tiles, no positioning within a zone. The tactical layer is which zone you're in, not which five-foot square. Loses tabletop's AoE-placement game; gains four-second turns and a battlefield that reads in one glance.
The D&D 5e SRD ships 304 monsters, 9 classes with 30+ subclasses, 400+ spells. Party Wipe runs on 18 monsters, 6 classes, 23 spells, 14 weapons, 6 consumables. Every survivor earns its slot by teaching a strategy loop — skeleton's bludgeoning vulnerability and poison immunity make weapon choice matter; mummy's fire vulnerability with a frighten-inflict turns a hard hitter into a softer save-or-suck. The trim is a prototype constraint. Breadth comes back when the engine's earned it.
Three boss templates — chimera, young black dragon, stone giant — cycle by floor and scale multiplicatively. A Floor 3 chimera and a Floor 23 chimera are the same fight beat-for-beat, statted bigger. Infinite-depth runs sustainable from three boss designs.
Combat events flow through one bus (data/combat-events.ts) and render as full-card overlays on the affected token. Three layers: scrim, one of ten family-themed flourishes (fire embers, ice shards, lightning forks, and so on), and a result composition (number-in-glyph + qualifier ribbon for CRIT / VULNERABLE / RESISTED). A new player learns "fire damage on a vulnerable enemy with a critical hit" without ever opening a rules reference — every element of that read is encoded visually. Sixty-nine CSS tokens feed per-concern TS registries (damage, condition, intent, class) that components consume via var(...).
Party Wipe is built on Loom, the same design-system pipeline behind Paperboy — one questionnaire in, two visually distinct products out.
Combat first, story later. Most D&D digital adaptations render the whole experience: narrative, exploration, character relationships, combat. Party Wipe deliberately doesn't. Story tracking is the solved-ish half (wikis, objective trees, prepared encounters); combat is where the friction lives. Building only the combat half means the engine gets sharpened on the actual hard problem before it has to plug into a larger system.
Zone abstraction over grid. Most digital D&D either keeps the grid (BG3, Solasta) or abstracts to text-only (most CRPG-likes). Three zones is the middle abstraction that preserves positioning as a tactical choice without making turns expensive. Newcomers read the battlefield instantly.
Qualifier as field, not event. CRIT, VULNERABLE, and RESISTED used to fire as separate floating popups offset from the damage number. Folded them into a qualifier field on the damage event itself, rendered as a small uppercase ribbon anchored above the number-in-glyph. Three events became one. The visual hierarchy is identical every time, so the pattern becomes recognizable instead of soup.
Visual death decoupled from data death. Combat resolvers flip isAlive: false immediately, but ZoneToken holds the visual alive for 1200ms after a kill event — long enough for the damage flourish to play through before the card grayscales. Five kill paths (player melee, player spell, enemy melee, enemy save-AoE, DoT) all emit the same event and get the same hold.
One source of truth per visual concern. Damage, condition, intent, and class colors each live as a TS registry reading from CSS custom properties in game-tokens.css. No component owns its own color map. Adding a new damage family is one file. Theming a new screen is reading existing tokens. The architecture survives expansion when the trimmed content grows back to full breadth.
The roster is the load-bearing artifact — every monster annotated with the strategy loop that earns it a slot.
/**
* V1 Roster — Curated game content
*
* Every entry here is distinct, functional, and intentional.
* Generators and UI filter against these lists.
* Nothing outside the roster appears in gameplay.
*
* Philosophy: FF GBA / BG1 / Fallout 1 — small scope, everything works.
*/
// ─── Monsters (18 curated from 304) ────────────────────────────
export const V1_MONSTERS = new Set([
// ─ Floor 1 — CR 0.125–0.25 ─
'giant-rat', // 0.125 — mob: vanilla cleanup fodder
'goblin', // 0.25 — mob: vanilla flexible attacker
'skeleton', // 0.25 — bludgeoning-vulnerable + poison-immune (damage-type teacher)
'wolf', // 0.25 — Bite inflicts prone (STR save)
// ─ Floor 2 — CR 0.5–1 ─
'shadow', // 0.5 — resists nonmagical physical, vulnerable to radiant
'ghoul', // 1 — Claws inflict paralyzed (CON save)
'giant-spider', // 1 — Bite inflicts poisoned (CON save); the poison creature
// ... 11 more, each carrying one strategy loop the engine actually wires
]);A persistent AI development partner — part JARVIS, part operating system for how I work.
[ read case study ]I kept burning out building UI foundations, so I built a pipeline that generates them.
[ read case study ]A daily news dashboard I built so I’d stop opening six apps every morning.
[ read case study ]