⭐ Star Horror Villains Builder
From Masked Slasher to Cenobite Without Typing a Word
- description
- villain_sheet
- system_prompt
Horror is the genre where being vague gets you a guy in a hoodie. Ask for "a scary killer" and you get the same stock silhouette: no mask logic, no weapon, no wrongness in the skin. The specificity is the whole job, and nobody wants to type it at 1am.
⭐ Star Horror Villains Builder is the StarNodes answer. Seventeen dropdowns, three string outputs, no model, no VRAM. Pick an archetype, then pick what's wrong with it - the mask, the skin, the eyes, what it's holding - and the node assembles the prompt.
The mechanism, which is dumber and more useful than it sounds
Options live in json/star_horror_villains_builder.json: 17 lists (51 villain types in one of them), plus settings and a system_prompt. On load each list becomes a dropdown with none, random and exclude prepended.
none skips the category, random picks an entry, and exclude skips it and keeps it out when random_all is on - which is the point of three states instead of a checkbox. Set face and weapon to exclude with your choices locked in, flip random_all on, and you'll roll endless villains who all wear the same mask and carry the same blade. That's how you build a cast instead of a grab bag.
seed controls every pick: 0 re-rolls each queue, a pinned number reproduces the villain. The node reports itself changed on every run (its IS_CHANGED returns a timestamp), so nothing is cached and seed: 0 genuinely re-rolls.
One bit of actual language handling: entries from the face list get an article with the right vowel rule, so it reads "…with a burlap sack mask", not "…with burlap sack mask". Tiny, and why the sheet output reads like prose instead of tag soup.
Three outputs, three jobs
description is the plain comma-separated prompt. villain_sheet specifies a reference layout - two tall full-body panels left (front and back), a 3×2 detail grid right (face/mask front, profile, 45°, chest, pelvis, rear), expanding to 4×2 with close-ups of mask and weapon.
Then the style clause: "presented in a clinical and scientific way like a forensic case file, with flat, even studio lighting… and enforces absolute consistency in the villain's… appearance across every panel." A deliberate tonal collision - your style, shot like evidence. For a sheet it's the right call: even lighting is what stops a grid drifting into three different monsters, the failure mode anyone who's chased a turnaround knows (character-consistency.md).
system_prompt is the instruction text you'd feed an LLM to write these sheets itself, think-step-by-step preamble and all. Steal it if you're wiring an Ollama or API node upstream - LLM-assisted prompting went from novelty to default practice over 2025–26, and this saves you writing the system message (prompt-engineering.md).
What to actually set
villain_type- 51 entries, doing more work than the rest combined: killer doll, ventriloquist dummy, cenobite, masked slasher, chainsaw butcher.face- 33 options, because the mask is the character. Burlap sack, scratched hockey mask, mask made of human skin.weapon,injuries,aura- what stops it reading as generic;aurais the one people forget.style- defaults tocinematic horror film stilland lands in both outputs; the only dropdown whose default isn'tnone.age- free text; a number becomes43 year old,random_agerolls 25–90.
Install
Manager → search Starnodes → Install → restart, or:
cd ComfyUI/custom_nodes
git clone https://github.com/Starnodes2024/ComfyUI_StarNodes
cd ComfyUI_StarNodes
pip install -r requirements.txt
The builder imports nothing beyond the standard library - the pip line is for the pack's other hundred-odd nodes, and install.py runs no installs of its own, so a bare clone still leaves you a working text node.
On the pack generally: it's a solo-run toolbox that updates constantly. Upside, things get fixed fast. Downside, re-check a workflow built on a StarNodes node after an update.
Where people get burned
Editing the JSON does nothing until you restart. Dropdown lists are read at module load. random picks re-read the file every run, so a new mask gets used immediately - you just won't see it in the dropdown yet.
Broken JSON fails quietly. The loader swallows the exception, prints StarNodes: Could not load star_horror_villains_builder.json, and hands back empty lists. The node still runs; the dropdowns shrink to three options and the prompt comes out nearly blank. Validate first:
python3 -m json.tool ComfyUI/custom_nodes/ComfyUI_StarNodes/json/star_horror_villains_builder.json > /dev/null
The sheet came out unclothed. There's a single outfit field here, not a wear rule, and if it's none the sheet prompt says plainly that the villain is unclothed in the full-body panels. Set an outfit, or set the field to exclude - the tooltip is honest about which way that cuts.
random_all overwrote your choices. Intended - that's what exclude is for.
Inputs (22)
| Name | Type | Default | Description |
|---|---|---|---|
| sex | COMBO | none | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| villain_type | COMBO | none | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| size | COMBO | none | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| face | COMBO | none | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| hair | COMBO | none | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| skin | COMBO | none | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| eyes | COMBO | none | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| body_build | COMBO | none | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| outfit | COMBO | none | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| weapon | COMBO | none | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| injuries | COMBO | none | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| accessories | COMBO | none | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| aura | COMBO | none | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| expression | COMBO | none | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| pose | COMBO | none | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| custom | COMBO | none | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| style | COMBO | cinematic horror film still | 'none' skips this trait, 'random' picks a random entry, 'exclude' keeps it out of the prompt even when random_all is on. Add your own entries in json/star_horror_villains_builder.json (restart ComfyUI to see them in this list, 'random' uses them right away). |
| age | STRING | Free text. Empty = skipped. Type 'random' or enable the random_age toggle for a random age (25-90 by default, change age_random_min/max in the json settings). A plain number becomes 'X year old'. | |
| random_age | BOOLEAN | false | If enabled each run, picks a random age between age_random_min and age_random_max from the json settings and ignores the age text field. |
| additional | STRING | Free text that is appended to the end of the prompt. Empty = skipped. | |
| seed | INT | 00–18446744073709550000 | Seed for all 'random' picks. 0 = new random picks every run. |
| random_all | BOOLEAN | false | If enabled, every category gets a random pick regardless of its widget setting. Categories set to 'exclude' stay out of the prompt. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| description | STRING | The plain generated comma-separated description prompt. |
| villain_sheet | STRING | The ready-to-use strict horror villain sheet image prompt for the same villain (multi-panel grid, face/mask and weapon close-up panels). Note: the villain is unclothed unless an outfit is selected or randomized. |
| system_prompt | STRING | The system prompt to feed into your LLM so it can write horror villain sheet prompts itself. Editable as 'system_prompt' in json/star_horror_villains_builder.json. |