A snapshot from May 2026. The tended, current-state version lives in the garden: dnd-obsidian-ai-workflow.
I run D&D 5e for a small group, and over the past year I’ve built a workflow around Obsidian that’s become central to how I prep and run sessions. Claude Code skills handle encounter generation and homebrew publishing. A set of Python scripts call Google’s Gemini API to render battlemaps, tokens, and portraits. The vault is the source of truth for all campaign content; everything else is renders, exports, or platform integrations.
Obsidian as the DM hub
Everything lives in an Obsidian vault. Campaign notes, NPC profiles, monster stat blocks, loot tables, location descriptions, session logs. I can write “the party enters [[Iron-Root Mine]]” in a session log and it links directly to the location file with read-aloud text, encounter details, and DM secrets. Every proper noun in the campaign is a wikilink.
The vault is organized spatially, not chronologically. Each campaign gets a directory with subdirectories for locations, NPCs, monsters, and loot. A location file contains everything I need when the party arrives: read-aloud text in blockquotes, skill check DCs in callout blocks, NPC dialog, loot with checkboxes for tracking what’s been revealed, and DM-only notes in [!tip] callouts. When I’m running a session, I open the location file and everything is in front of me.
Campaign structure: the novel, the chapters, the log
I treat each campaign like a novel outline. A top-level campaign file lays out the story arcs, acts, and active plot threads — the “what could happen” document. Location files are the chapters: self-contained units the party moves through. A DM log captures what actually happened, in reverse chronological order. After each session, I fold any improvised content back into the location and NPC files so they stay current.
This separation matters. The campaign file tells me where the story is going. The location files tell me what’s in front of the party right now. The DM log tells me what already happened. No single file tries to do all three.
Obsidian Bases for bookkeeping
Obsidian Bases — structured database views built on note metadata — handle the bookkeeping. An XP Tracker base pulls from all party member notes, calculates XP needed for the next level, and shows progress percentages. A Party base shows AC, HP, initiative, and passive perception in a table I can glance at during combat. The data lives in each character’s frontmatter; the bases just query and display it.
AI-powered asset generation
Four Python scripts in a standalone repo (~/dnd-tools/) call Google’s Gemini API to generate battlemaps, monster tokens, NPC portraits, and atmospheric scenes from prompt markdown files in the vault.
For maps, I describe the layout in a markdown file using a DOT graph for topology. Nodes represent areas, edges represent connections, and edge styles indicate whether a path is normal, significant, or hidden. The script sends the description and graph to Gemini, which generates a 2048x2048 battlemap. A second Gemini pass removes all text and labels from the art. Then Pillow overlays a precise grid, scale bar, and column labels onto both versions: a DM copy with area labels and a clean player copy for the VTT. The player copy is designed for fog-of-war — a single image layer that the VTT can mask and reveal as the party explores.
The DOT graph is the key innovation. Without it, Gemini generates beautiful art with nonsensical spatial relationships. Rooms that should be connected aren’t, or paths loop in ways that break gameplay. The graph constrains the topology while the model handles the aesthetics.
Monster tokens and NPC portraits follow a simpler pipeline. Each gets a prompt file with style instructions. The system prompt enforces a consistent dark fantasy aesthetic: painterly style, heavy texture, atmospheric lighting. Monsters render at 2048x2048 for full illustrations and 256x256 circular tokens ready to drop into a VTT token bar. NPCs get waist-up portraits at 1000px wide and 256x256 tokens. A fourth script generates atmospheric scene illustrations for locations that don’t need a full battlemap but benefit from a mood image.
D&D Beyond integration
Monsters and magic items live in D&D Beyond’s homebrew library as the mechanical source of truth. Each monster and loot file in Obsidian includes a dndbeyond URL in its frontmatter, so I can jump from the narrative description straight to the stat block. Obsidian handles flavor, tactics, and DM notes; D&D Beyond handles the mechanical details and dice rolling.
A Claude Code skill (dnd-beyond) automates the publishing side. It pushes vault homebrew to D&D Beyond via Playwright browser automation, including rollable dice tags that turn “2d6 fire damage” into clickable dice in the platform’s character sheets. No more copy-pasting stat blocks between systems.
AI-powered encounter generation
A Claude Code skill (dnd-encounter-gen) generates balanced 5e encounters tailored to the current party. It pulls the party snapshot (level, class composition, equipment), the active campaign hooks, and the XP budget rules from skill reference files. It produces an encounter with stat blocks, environmental hazards, and a story tie-in that connects to active plot threads.
A third skill, dnd-vault-cleaner, audits for broken wikilinks, redundant headers, and missing entity files. The three skills compose with the asset scripts into a session prep flow: generate encounters, clean up broken links the new content introduced, render maps and tokens for new monsters, push homebrew to D&D Beyond.
Session prep in practice
A typical prep session:
- Review the campaign file for active plot threads. Figure out what the party is likely to do next.
- Open the relevant location files. Write or revise read-aloud text, place encounters, set skill check DCs.
- Run the encounter generator for new combat encounters.
- Generate maps, tokens, portraits, and scene illustrations with the asset scripts.
- Create or update monster and NPC files. Push homebrew to D&D Beyond via
dnd-beyond. - Run the vault cleaner to catch broken links and missing entity files.
- Review the combat cheat sheet — pre-computed one-turn action recipes for each party member so I can suggest options when players stall.
- Skim the Party base for AC, HP, and passive perception values I’ll need during play.
During the session, I keep the location file open and work through it. Checkboxes track what’s been revealed. After the session, I update the DM log and fold any improvised content back into the canonical files.
What works
The wikilink graph is the killer feature. When I’m mid-session and a player asks about an NPC they met three sessions ago, I type the name and the note is right there. No searching, no flipping through notebooks.
Spatial organization means I never wonder where to put something. If it’s about a place, it goes in that location’s file. If it’s about a person, it goes in their NPC file. One canonical place to look.
The asset scripts cut a map from a multi-hour job to a 15-minute one. The art is AI-made; the creative direction — the layout, the connections, the secrets — is mine. The maps aren’t as polished as hand-drawn ones, but they’re consistent in style across the entire campaign and good enough for play. The same pattern applies to encounter generation — the AI handles XP budgeting and stat-block scaffolding so I can focus on making the encounter fit the narrative.
The whole system is just files. Markdown, images, and a few Python scripts. No database, no proprietary format, no subscription. It syncs across devices with Syncthing and it’ll outlast whatever note-taking app is trendy next year. The same agentic pattern that runs my homelab runs my D&D prep.