A modern 5E compatible character builder and compendium built with Next.js and MySQL.
- Step-by-step character creation — Guided workflow through species, class, ability scores, background, gear, spells, and details
- Multi-class support — Build characters with multiple classes and track levels independently
- Real-time preview — Live character sheet with Summary, Combat, Features, and Custom tabs
- Point buy & standard array — Multiple methods for determining ability scores
- Repeatable feats — Feats marked repeatable can fill more than one milestone slot; duplicate ASI feats combine into a shared bonus pool on the Abilities step
- Background proficiencies — Tools, vehicles, weapons, armor, and languages from backgrounds flow into preview and saved characters
- Automatic calculations — HP, AC, weapon attacks, saving throws, skills, and modifiers calculated automatically
- SRD content — Seed the full SRD 5.2.1 compendium (classes, species, spells, equipment, and more)
- Custom content creation — Create and manage species, classes, subclasses, backgrounds, feats, spells, equipment, and custom abilities
- Common Modifier Effects — A permanent, editable system catalog under Custom Abilities that merges class-feature activation templates with characteristic modifiers; class/subclass features, feats, species traits, and choice options pick from this searchable list instead of defining effects inline
- SRD modifier enrichment — Bundled SRD classes, subclasses, feats, and species traits ship with linked common-modifier presets (class resources, cast spell, movement types, Metamagic/Eldritch Invocations catalogs, unarmed die scaling, and more); run
pnpm dlx tsx scripts/audit-srd-class-features.tsto list gaps - Card background graphics — Every compendium entry can have a hero image for selection cards and full-screen detail overlays (upload or URL in the editor header area)
- Cinematic selection UI — Builder class/species/background pickers and compendium detail views use full-bleed artwork with gold-framed cards inspired by D&D Beyond
- Class resources — Dedicated compendium tab for per-class resource pools (Rage, Ki, etc.) linked from feature limited uses
- Enable / disable content — Toggle compendium entries off for the builder, with prompts to disable dependents (subclasses, attached abilities, etc.)
- Unified editor header — Icon picker (inline with name field), name, source, and source link on one row across all compendium editors
- Background proficiencies editor — Structured tools & vehicles (SRD dropdown + custom), weapon categories, armor checkboxes, and languages
- Background granted spells — Assign spells by overall character level (1st–20th), not spell level
- Spell editor — Casting time, range, and duration presets with “Other” custom values; ritual and concentration on the same row as level and school
- Section export & clear — Export or wipe an entire compendium tab from the gear menu
- Filtering & search — Find content quickly with search and category filters
- Save & load characters — Persist characters to MySQL; resume editing from the builder
- Character sheet — Condensed sheet with skills grouped by ability, merged proficiencies, subclass features, banner/portrait, and in-sheet HP tracking
- Export options — Download character and compendium data as JSON
- SRD seed — One-click SRD import from bundled JSON (
pnpm srd:buildregenerates seed from SRD 5.2.1 markdown) - Web import — Paste a URL to pull homebrew-style HTML into the compendium
- PDF & text import — Multi-provider AI extraction (OpenAI, Anthropic, or Google Gemini) or zero-AI deterministic parsing for well-structured class PDFs; subclasses, feat categories, and post-import modifier enrichment supported
- Dump Stat JSON export — Upload compendium export bundles (
.json) via PDF import or paste into text import for fully-linked homebrew content - Multi-file import order — On Import, expand Multi-file import order for spellcasters, Psion disciplines, Martial Exploits, and similar split homebrew; paste a JSON array in dependency order or import files sequentially (libraries before classes/subclasses)
See Import formats and Multi-file homebrew import order below.
- Framework: Next.js 16 (App Router)
- Database: MySQL 8+
- Styling: Tailwind CSS 4
- UI Components: shadcn/ui
- Icons: Lucide React + Game Icons
- Animations: Framer Motion
- Node.js 20+ (recommended for build and production)
- pnpm (via Corepack) or npm
- MySQL 8+ — local install, managed service (RDS, PlanetScale-compatible host, etc.), or MySQL on the same VPS as the app
The browser never connects to MySQL directly. Only the Next.js server uses database credentials from environment variables.
git clone https://github.com/Geph/v0-dump-stat-character-builder.git
cd v0-dump-stat-character-builder
corepack enable
pnpm installIf pnpm is not on your PATH, use corepack pnpm install and corepack pnpm dev.
Create an empty database and a user with full privileges on it. Examples:
Local MySQL (Windows / macOS / Linux)
CREATE DATABASE dump_stat CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- Grant your app user access (adjust user/host as needed)Or use the setup helper (after setting MYSQL_PASSWORD in .env.local):
pnpm db:setupThis creates the dump_stat database and applies mysql/schema.sql.
cp .env.example .env.localEdit .env.local. Use either a connection URL or separate fields:
# Option A — single URL
DATABASE_URL=mysql://DB_USER:DB_PASSWORD@localhost:3306/dump_stat
# Option B — separate fields
# MYSQL_HOST=localhost
# MYSQL_USER=your_db_user
# MYSQL_PASSWORD=your_db_password
# MYSQL_DATABASE=dump_stat
# MYSQL_PORT=3306
NEXT_PUBLIC_SITE_URL=http://localhost:3000
NODE_ENV=development
PORT=3000URL-encode special characters in passwords (e.g. @ → %40).
Restart the dev server after changing .env.local.
Run mysql/schema.sql once against your database:
mysql -h localhost -u YOUR_DB_USER -p dump_stat < mysql/schema.sqlOr import the file through phpMyAdmin, Adminer, or your host’s database UI.
The seed step only inserts data; it does not create tables. After pulling schema updates, run:
pnpm db:migrateThis applies incremental migrations (new columns such as background proficiencies, character weapon/armor proficiencies, feat repeatable, etc.).
If MySQL runs on a remote server and blocks public connections (common on shared/VPS hosts), use one of:
A. SSH tunnel (recommended)
ssh -N -L 3307:127.0.0.1:3306 user@your-server.example.comDATABASE_URL=mysql://DB_USER:DB_PASSWORD@127.0.0.1:3307/dump_statB. Allow your IP in the host’s MySQL/firewall panel, then use the remote hostname in DATABASE_URL.
C. Develop on the server — clone the repo there, use localhost as the DB host, run pnpm dev.
pnpm devOpen http://localhost:3000, go to Import, and click Seed SRD 5.2.1 Content, or:
curl -X POST http://localhost:3000/api/seedSeed data is built from SRD 5.2.1 markdown. To regenerate JSON after parser changes:
pnpm srd:buildSRD seed, web import, and Dump Stat JSON bundles do not use AI. PDF upload and pasted-text import use a server-side AI provider when deterministic parsing is not confident enough.
Supported providers (set one API key; the first configured provider is used unless you set IMPORT_AI_PROVIDER):
| Provider | Environment variable | Default model |
|---|---|---|
| OpenAI | OPENAI_API_KEY |
gpt-4o-mini |
| Anthropic | ANTHROPIC_API_KEY |
claude-sonnet-4-20250514 |
| Google Gemini | GOOGLE_GENERATIVE_AI_API_KEY or GOOGLE_API_KEY |
gemini-2.0-flash-001 |
Add to .env.local (examples):
# Pick ONE provider key:
OPENAI_API_KEY=sk-your-key-here
# ANTHROPIC_API_KEY=sk-ant-your-key-here
# GOOGLE_GENERATIVE_AI_API_KEY=your-gemini-key-here
# Optional: force a provider when multiple keys are set
# IMPORT_AI_PROVIDER=openai|anthropic|google
# Optional: override the default model for that provider
# IMPORT_AI_MODEL=gpt-4o-miniRestart the dev server after changing keys. Without any provider key, seed, web URLs, Dump Stat JSON, and manual compendium edits still work — only AI-powered PDF/text import returns a configuration error.
Import page UI: When server AI is configured, Clipboard and PDF tabs show an optional server AI extraction section (provider/model override, stored in browser localStorage). Keys always stay on the server. When no provider is configured, only BYO JSON import is shown for pasted text.
Zero-AI path: Clean class documents (progression table + feature prose, e.g. homebrew Fighter PDFs) may import with no AI calls when the deterministic confidence gate is high. The import report shows a Zero AI badge when that path is used.
Chunk cache: Successful AI sections are cached in server memory by hash. If a large import fails mid-way (quota/rate limit), retry the same text with the same provider/model — finished sections are reused without extra API cost until the server restarts.
Dump Stat supports four compendium import paths:
| Method | Input | Best for |
|---|---|---|
| SRD seed | Button / POST /api/seed |
Official SRD baseline |
| Dump Stat JSON | .json file or pasted JSON |
Homebrew with full linkedModifiers, repeatable imports |
| Text import | Pasted plain text + optional content hint | UA PDFs, wiki pages, copied stat blocks |
| PDF import | Uploaded PDF (+ optional page range) | Same as text; also accepts JSON export files |
Export bundles use type dump-stat-export with an items array. Each item has type (e.g. dnd-subclass, dnd-feat, dnd-spell) and data (compendium fields without server ids).
Single-item shape:
{
"type": "dnd-subclass",
"version": 1,
"data": {
"name": "Circle of the Titan",
"class_name": "Druid",
"description": "…",
"source": "UA 2026",
"features": [
{ "level": 3, "name": "Circle of the Titan Spells", "description": "…" }
]
}
}Bulk bundle:
{
"type": "dump-stat-export",
"version": 1,
"section": "my-homebrew",
"items": [ … ]
}Import via Import → PDF upload (choose the .json file) or paste the entire JSON into Text Import.
- Subclasses resolve parent classes by
class_name(must exist in compendium — seed SRD first). - Subclass rows run post-import enrichment (always-prepared spell links, limited uses, class-resource bindings) when presets exist.
- Feats should include
"category": "Origin"or"Epic Boon"so they appear in the correct builder pickers.
Example bundle: lib/import/examples/ua-villainous-options-export.json — UA 2026 Villainous Options (three subclasses, Destructive Wave, Origin/Epic Boon feats). Regenerate with:
pnpm dlx tsx scripts/build-ua-villainous-export.tsThe Clipboard tab is the primary import path for pasted text:
- Paste raw source text (from a PDF copy, wiki, or document).
- Copy the extraction prompt and JSON template (matched to your content-type hint).
- Run the prompt in ChatGPT, Claude, Gemini, or any LLM — using your own API key or subscription.
- Paste the model's JSON output back into Dump Stat and click Import JSON.
The prompt includes clean PDF / paste guidelines (keep level tables intact, one content type per run, preserve feature headings, etc.). No server API keys are required for this flow.
Optional server AI: If the host has OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_GENERATIVE_AI_API_KEY configured, an expandable server AI extraction section appears on Clipboard and PDF tabs. It stays hidden when no provider is configured.
Dump Stat JSON export — if the pasted text is a valid dump-stat-export bundle, it imports directly without LLM extraction (same as file upload).
Imported rows use source Text Import or PDF Import and replace same-name rows from that source on re-import.
Same schema and persistence as text import. Optional page range limits extraction to specific pages. Upload a .json export bundle through the PDF file picker for non-AI JSON import.
Requires at least one AI provider key for PDF text extraction (not for JSON bundles or SRD seed).
Many third-party classes ship as several JSON files (spell libraries, discipline powers, class, subclasses). Import supporting libraries before the class and subclass files that reference them so modifier wiring and spell links resolve correctly.
On the app: Import → Multi-file import order (expandable panel at the top of the page) lists workflows for spellcasters, Kibbles Psion, Laserllama-style Martial Exploits, and Inventor.
General rules
- SRD spells — If your compendium is SRD-seeded, standard spells (e.g. Fireball, Burning Hands) do not need a separate import; only import homebrew spell JSON for third-party names.
- One batch or sequential — Either paste a JSON array of import objects in dependency order, or run separate imports in the same order (earlier files persist to the compendium before later ones wire references).
- Set a source label — Use the compendium source label field (e.g.
Kibbles Witch) so you can filter and re-import safely.
Spellcasting classes (Witch, Inventor, full casters)
| Step | Content |
|---|---|
| 1 | Homebrew spell libraries (kibbles-spells-parsed.json, Valda's supplements, etc.) |
| 2 | Class spell list stub (optional) |
| 3 | Class JSON |
| 4 | Subclasses JSON (always-prepared spell tables) |
| 5 | Choice options if separate (grand hexes, invocations, …) |
Kibbles Psion
| Step | Content |
|---|---|
| 1 | psion-disciplines.json (powers in spells[], discipline packages in import_proposals) |
| 2 | psion-class.json |
| 3 | Archetypes / subclasses (e.g. psion-knowing-mind.json) |
Discipline powers with psi-point augments get psionic_augments at import; pick augments on the character sheet when casting those powers.
Martial Exploits (Laserllama Alternate Fighter, etc.)
| Step | Content |
|---|---|
| 1 | Exploit / maneuver library (if separate) |
| 2 | Class with level table (Exploit Dice, Exploits Known) |
| 3 | Subclasses (if separate) |
JSON array example (Clipboard → Step 2):
[
{ "spells": [ … ] },
{ "classes": [ … ] },
{ "subclasses": [ … ] }
]Dump Stat merges the array into one import batch before wiring modifiers.
This app is designed for self-hosted Node + MySQL, not Vercel serverless. If the repo was linked to Vercel from v0, disconnect that integration in the Vercel dashboard (or remove the Git deploy hook) and deploy on your VPS instead.
These steps apply to any Linux VPS or dedicated box where you run Node and MySQL yourself (DreamHost VPS, Linode, DigitalOcean, Hetzner, AWS EC2, a home server, etc.). Adjust paths and panel names for your host.
Internet → reverse proxy (nginx/Caddy/Apache) → Node (Next.js on :3000) → MySQL (localhost or private network)
MySQL and Node on the same machine should use localhost (or a private IP) in DATABASE_URL.
- Node.js 20+
- MySQL 8+
- Git
- A process manager (PM2, systemd) and reverse proxy (nginx recommended)
On the server (or via your host’s DB panel):
-
Create a database (e.g.
dump_stat). -
Create a dedicated MySQL user with privileges only on that database.
-
Import schema once:
mysql -h localhost -u APP_USER -p dump_stat < mysql/schema.sql
git clone https://github.com/Geph/v0-dump-stat-character-builder.git
cd v0-dump-stat-character-builder
pnpm installSet production environment variables (.env.local, PM2 ecosystem file, or systemd Environment=):
DATABASE_URL=mysql://APP_USER:APP_PASSWORD@localhost:3306/dump_stat
NEXT_PUBLIC_SITE_URL=https://yourdomain.com
NODE_ENV=production
PORT=3000
# AI import — one provider key (see "AI import" section above)
OPENAI_API_KEY=sk-your-key-here
# ANTHROPIC_API_KEY=sk-ant-...
# GOOGLE_GENERATIVE_AI_API_KEY=...
# IMPORT_AI_PROVIDER=openai|anthropic|google
# IMPORT_AI_MODEL=gpt-4o-miniBuild and start:
NODE_OPTIONS='--max-old-space-size=4096' pnpm build
pnpm startOr with PM2 (config included in deploy/):
pm2 start deploy/ecosystem.config.cjs
pm2 saveOptional standalone build (copies minimal node_modules into .next/standalone):
NEXT_OUTPUT=standalone pnpm buildserver {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Add TLS with Let’s Encrypt (certbot) or your host’s certificate tooling.
After the app is up and connected to the database:
curl -X POST https://yourdomain.com/api/seed| Host type | Typical approach |
|---|---|
| VPS (DreamHost, DO, Linode, …) | Node + MySQL on same box, nginx in front — steps above |
| Managed MySQL (RDS, Aiven, …) | Point DATABASE_URL at the provider hostname; run Node on a VPS or PaaS |
| PaaS (Railway, Render, Fly.io) | Deploy Next.js build; attach managed MySQL; set env vars in the dashboard |
| Vercel | Not recommended — no persistent MySQL on the same project; use DreamHost VPS + nginx instead |
| Shared PHP/cPanel | Often no long-running Node — use a VPS or PaaS instead unless your plan supports Node apps |
DreamHost-specific: MySQL is created under Goodies → MySQL Databases; remote access may require an SSH tunnel or IP allowlist as described in local dev step 5.
Dump Stat supports two build-time profiles. Choose one when building for production; there is no runtime toggle in the deployed app.
| Profile | Command | Storage | Deploy target |
|---|---|---|---|
| Hosted (default) | pnpm build:hosted |
MySQL via /api/* |
VPS / Node (pnpm start) |
| Static | pnpm build:static |
IndexedDB in browser | GitHub Pages (out/) |
This is the default local development and VPS workflow documented above:
- Configure
DATABASE_URLin.env.local pnpm build:hosted(orpnpm build)- Run with
pnpm startor PM2/nginx as in deploy/
Set NEXT_PUBLIC_DEPLOY_MODE=hosted or leave it unset.
No database server required. Data lives in the visitor's browser.
- Set
NEXT_PUBLIC_BASE_PATHto your repo name for project sites (e.g./dump-stat-character-builder) pnpm build:static— writes static files toout/- Deploy
out/to GitHub Pages (see deploy/github-pages.md)
Static mode includes: builder, characters, compendium, bundled SRD on first visit, JSON pack import/export.
Static mode excludes: PDF/text/web AI import, server seed API. Use JSON exports from a hosted instance to share custom content.
Environment variables for static builds are documented in .env.example.
GitHub Pages: See deploy/github-pages.md. After enabling Pages (Source: GitHub Actions), the app is served at https://geph.github.io/dump-stat-character-builder/.
| Symptom | What to check |
|---|---|
| Dev server hangs / pages never load | Stale next dev on port 3000 after sleep or reboot — kill the Node process, delete .next, run pnpm dev again (see below) |
Database is not configured |
.env.local missing or placeholder values; restart dev server |
fetch failed / ECONNREFUSED |
Wrong host/port, tunnel not running, or firewall blocking MySQL |
Access denied |
Wrong user/password; user not granted access to the database |
Unknown table / doesn't exist |
Run mysql/schema.sql or pnpm db:setup before seeding |
| Seed returns 500 | Server logs; confirm DATABASE_URL points at the DB where schema was applied |
next build OOM |
Set NODE_OPTIONS='--max-old-space-size=4096' |
If http://localhost:3000 spins forever, a zombie Next.js process is often still holding the port:
Get-NetTCPConnection -LocalPort 3000 -ErrorAction SilentlyContinue |
ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }
Remove-Item -Recurse -Force .next
pnpm devRun pnpm db:migrate after pulling if you see unknown column errors for card_image_url.
app/
├── page.tsx # Landing page
├── builder/ # Character builder
├── characters/ # Character list and sheets
├── compendium/ # Content browser and editors
├── import/ # PDF, text, and web import
└── api/ # REST routes (seed, import, data, characters)
lib/
├── db/ # MySQL connection, Drizzle schema, migrations
├── builder/ # Draft storage, ASI allocation, feat selection, equipment utils
├── compendium/ # Background proficiencies, display helpers, editor field styles
├── srd/ # SRD seed data and parsers
├── import/ # Import normalization and dump-stat export format
└── site-images.ts # Marketing image paths
components/
├── compendium/ # Editor header row, card image field, selection cards, detail overlays
├── builder/ # Step nav, multi-select choices, ASI allocator
└── game-icon-picker.tsx # SVG game-icons.net picker for compendium entries
mysql/
└── schema.sql # Database DDL
public/
├── images/ # Hero, feature cards, backgrounds
└── icons/ # Compendium SVG game icons (+ manifest.json from pnpm icons:manifest)
Use the Compendium section to create custom species, classes, backgrounds, feats, spells, equipment, and abilities. Custom entries are marked with source Custom.
Theming lives in app/globals.css (Arcane default plus Parchment, Stone, Moss, and Clay). Use the gear icon in the header to switch styles; choice is stored in localStorage.
- Browser code uses
createClient()from@/lib/db/client→ hosted:/api/charactersand/api/data/*; static: IndexedDB vialib/data/ - Server routes use
lib/db/*(Drizzle +mysql2) — hosted builds only - There is no Supabase dependency. Run
pnpm check:mysqlto verify the repo has no stray Supabase references.
Maintainers only — do not bump version in contributor PRs.
After merging to main, run:
pnpm version:bumpThis increments VERSION and syncs package.json version (e.g. 0.3 → 0.4). Commit the result as part of the release push. Contributors must not run this script or hand-edit those files.
Track bugs and feature ideas in GitHub Issues. There is no published roadmap yet — open an issue to discuss priorities.
Contributions are welcome. Please read CONTRIBUTING.md for setup, branch naming, and PR expectations, and CODE_OF_CONDUCT.md before participating.
The Dump Stat application source code in this repository is licensed under the MIT License (Copyright © Geph).
The MIT license applies to application code only. It does not cover third-party game content or assets bundled with or displayed by the app.
This work includes material from the System Reference Document 5.2.1 ("SRD 5.2.1") by Wizards of the Coast LLC, available at https://www.dndbeyond.com/srd. The SRD 5.2.1 is licensed under the Creative Commons Attribution 4.0 International License, available at https://creativecommons.org/licenses/by/4.0/legalcode.
Compatible with fifth edition.
Section 5 of CC-BY-4.0 includes a Disclaimer of Warranties and Limitation of Liability that limits our liability to you.
Seed data is rebuilt from SRD-derived markdown via pnpm srd:build — see lib/srd/README.md.
Compendium icons are from game-icons.net (thousands of SVGs under public/icons/, manifest from pnpm icons:manifest). The site’s icons are licensed under CC BY 3.0. Attribution appears in the app footer, the landing page, and the compendium icon picker (link to game-icons.net). The site logo uses Spiked Dragon Head by Delapouite (CC BY 3.0).
Solbera’s D&D Fonts by Solbera / Ryrok, CC BY-SA 4.0 — see Solbera D&D Fonts.
