Dark Prompt
The prompt randomizer that re-reads your wildcard files without a restart
- string
DarkPrompt is the flagship of the DarkPrompts pack, and it does one thing that a surprising number of prompt-randomizer nodes get wrong: it re-reads its file on every generation. Edit your wildcard file, hit queue, and the new lines are live. No restart, no "clear cache" ritual. That single behavior is why people stick with it.
The idea is older than ComfyUI. Prompt variety - batch 30 images where the clothing, location or camera changes each time - goes back to A1111's {a|b|c} syntax and Impact Pack's __wildcard__ files. DarkPrompt is the standalone version: it reads lines from a text file, a text box, or both, and picks one at random per generation, seeded so you can reproduce exactly what you got.
How it works
Give it a filename (a plain path string, more on that below), text, or both. It merges the lines, optionally strips # and // comments and blank lines, then seeds Python's random with the seed input and does a random.choice. The output is a STRING you wire into a CLIP Text Encode or into another DarkPrompt.
Two behaviors are worth understanding before you use it:
randomly_disabledefaults to ON. This is the trap. With it on, each generation has a coin flip on whether the node returns nothing at all. That's the intended use - an "optional section" of prompting you only sometimes want, like shoes on a character - but if you just want a reliable wildcard, flip it off or every other image silently lacks that part of the prompt. The source deliberately doesn't use your seed for this flip, so all your DarkPrompts don't disable in lockstep.prefixandsuffixwrap the chosen line. The README's example is the good one: a prefix of(and a suffix of:1.2)turns the file linewalking a doginto(walking a dog:1.2), which is exactly the weight syntax the CLIP encoder expects.
Chaining them
You don't need a separate combiner node. Feed one DarkPrompt's output into the next one's combine_with input, set combine_with_delimiter to \n, and each node adds its line to the growing prompt. Because each is seeded independently, you get a different combination on every queue - same architecture as the {a|b|c} trick, but each section lives in its own editable file. If a file ends up empty, the node falls back to returning whatever combine_with was holding, so one dead list doesn't nuke the whole chain.
The inputs that matter
seed(INT, wireable) - reproducibility. Same seed, same file, same pick.filename(STRING) - a plain text path, not a file picker. Give it an absolute path or a path relative to your ComfyUI working directory; relative paths silently fail to find the file otherwise.text- inline lines as an alternative to (or in addition to) a file.strip_comments,strip_blank_lines- keep both on; comment lines starting with#or//are your way to annotate a list without polluting the prompt.
Install & troubleshooting
ComfyUI Manager → search "DarkPrompts" → install, restart. Or clone it straight in:
cd ComfyUI/custom_nodes
git clone https://github.com/darkpixel/darkprompts.git
Then restart ComfyUI. No models, no extra pip dependencies - it's pure Python on the ComfyUI API.
The classic failure modes: "DarkPrompt text and filename can not both be blank" means you wired nothing in; a file that won't load is almost always a path problem, and it raises a clear FileNotFoundError telling you exactly what it tried to open. And if an output looks "wrong," check randomly_disable first - it's the node lying to you by design.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| strip_blank_lines | BOOLEAN | true | — |
| strip_comments | BOOLEAN | true | — |
| randomly_disable | BOOLEAN | true | — |
| seed | INT | 00–18446744073709550000 | — |
| filenameopt | STRING | — | |
| prefixopt | STRING | — | |
| suffixopt | STRING | — | |
| textopt | STRING | — | |
| combine_withopt | STRING | — | |
| combine_with_delimiteropt | STRING | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| string | STRING | — |