D&D5E NPC Sheet Generator
Print-ready D&D NPC cards straight out of ComfyUI
- image
- card_image
If you're a DM who also runs ComfyUI, this is the node that makes the two hobbies finally talk to each other. You've generated a thousand portraits you'll never use; the D&D5E NPC Sheet Generator is the end of a three-node pipeline that turns one of them into a proper, printable two-sided NPC card - front has the portrait, name, race, CR, AC and HP; the back has the full stat block. It won't run a session for you, but it turns "I have a cool picture" into "I have a handout I can actually hand someone."
The card is sized for real print: 63.5 × 88.9 mm, which is the standard poker-card / mini-card dimension, at a default 300 DPI. Feed it an image and a JSON stat block and you get back a single card_image tensor - front on the left, back on the right, divided by a thin black line. Save it with any Save Image node and you've got a spread you can cut in half or send to a print shop.
How it works. This is the rare ComfyUI node that does no ML on the input. It's a PIL layout engine wearing a node costume. The image tensor gets converted to a PIL image, then text and stats are drawn onto the card at coordinates defined by a layout JSON file. Positions are parts-per-thousand (ppt) of the card size, so the same layout scales cleanly to any DPI or dimension. The bundled font is Cormorant Garamond Bold Italic - the fantasy-serif look is the whole vibe - and the front/back mask PNGs are composited on top as visual overlays (front at 60% opacity, back full) so you can skin the card without touching the layout. There's a small Python class called St_image carrying mm/px/DPI metadata around, which is the part you never see and the part that keeps 300 DPI output from turning into a mushy mess.
The inputs that matter. Out of the eight, a beginner really touches four:
image- your NPC portrait, from any image generation workflow. If the tensor has a batch dimension, the first frame is used.i_s_npc_json- the JSON string from the D&D5E NPC STAT BLOCK node (and its chained ability nodes). This is a required field and it'sjson.loads-ed at runtime, so malformed JSON isn't a warning - it's a hard error.i_ls_layout_file_pathand the two mask paths - defaults point at the English layout and the bundled masks, all relative to the node's own folder.layout/npc_layout_it.jsongets you an Italian card.i_w_card_width_mm/i_h_card_height_mm/i_n_dots_per_inch- leave them at 63.5 / 88.9 / 300 until you have a reason not to.
The layout file looks up text fields by key - STRENGTH, RESISTENCE, ACTIONS - so if you hand-edit the JSON instead of using the STAT BLOCK node, the keys have to match what the layout expects. Note the spelling on that second one: the pack writes RESISTENCE (typo included) everywhere, consistently, so it renders fine. Fix it in the JSON and the field goes silent.
Installing it. This is a rare pleasant install. No model downloads, no requirements.txt pip dance - the only real dependencies (Pillow, torch/torchvision) are already inside ComfyUI. ComfyUI Manager: search comfyui-orso-character-sheet-generator and install. Or the manual way:
cd ComfyUI/custom_nodes
git clone https://github.com/OrsoEric/comfyui-orso-character-sheet-generator
Then restart ComfyUI. The nodes appear under a dnd5e-orso category.
Where people get burned. The crash-on-bad-JSON is the big one - if you connect a plain text node to i_s_npc_json instead of the STAT BLOCK output, it fails loudly. Keep the wiring as designed: STAT BLOCK → (ABILITY ×N) → this node. Second, remember the output is a combined front/back spread, not two images - if you want separate files, crop it. And know what this node isn't: it renders what the JSON says; it's not a character-consistency solution by itself, so if you're making a whole roster that should look like the same party, the portrait generation is still your problem. This is the node that makes the cards; the two data nodes before it are where the real work happens.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| i_w_card_width_mm | FLOAT | 63.50 | — |
| i_h_card_height_mm | FLOAT | 88.90 | — |
| i_n_dots_per_inch | INT | 300 | — |
| image | IMAGE | — | |
| i_s_npc_json | STRING | — | |
| i_ls_layout_file_path | STRING | layout/npc_layout_en.json | — |
| i_ls_mask_front_path | STRING | mask/front_mask.png | — |
| i_ls_mask_back_path | STRING | mask/back_mask.png | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| card_image | IMAGE | — |