Runtime Prompt Random Choice
Runtime Prompt Random Choice
- selected_text
- selected_text_safe
You've got 100 generations queued for overnight and you want every run to roll a different background, time of day, or weather. ComfyUI's built-in {town|park|lake} syntax handles the rolling fine - but once that queue is submitted, you can't touch the pool. This node fixes exactly that: it's a "prompt gacha" (the author's own framing, README and all) that re-rolls on every execution and lets you edit the candidate list while the queue is still running. The next job picks up your latest edit without you re-submitting anything.
It's the runtime version of the pack's plain Prompt Random Choice, part of the small ruminar/ComfyUI-PromptRandomChoice pack. It sits before your CLIP encoder and outputs a plain STRING, so only the chosen candidate flows downstream.
How the "runtime" part actually works
The trick is a server-side state store. As you type in the node's text box, the frontend pushes the text to ComfyUI's backend (/prompt_random_choice/runtime_candidate_state), which validates it and keeps the newest accepted revision, keyed per node. When a job executes, the Python side reads that accepted revision - not what's sitting in the widget - and picks from it. The node's title shows where you are: EDITING, SYNCING, then LIVE once the server has accepted your latest text.
And it re-rolls every single run because its IS_CHANGED method returns NaN, the standard "always re-execute" idiom that opts a node out of ComfyUI's caching (the same trick every random picker and clock uses under the hood). That's also the one real cost: a node like this stays dirty forever, so don't put it behind anything expensive in a huge graph.
The inputs that matter
- options_text - your candidate list, one per line or
|-separated (it accepts both, mixed). A candidate starting with#is commented out and never picked; one starting with!is force-selected - but that's runtime-only, so don't expect it to work on the classic nodes. - change_every - default
1re-rolls every run. Set it to3and the same pick is held for three consecutive runs, which is how you compare backgrounds without the noise of a fresh roll each time. Note it's not runtime-synced - the value from queue time wins. - state_key - leave it blank. The node builds its own key per instance; you'd only touch it to deliberately share a pool across nodes.
Outputs
- selected_text - the chosen string. If the literal
()candidate wins, this is an empty string. - selected_text_safe - the same text scrubbed for filenames (
/, spaces, commas, parens all replaced), oremptywhen nothing was chosen. Wire this into your Save Image filename and stop fightinginvalid character in patherrors.
Why you'd reach for it over {a|b|c}
Two reasons. First, the queue-editing thing - you genuinely cannot do that with ComfyUI's native syntax, which is resolved at CLIP-encode time and is invisible to you until the image comes out. Second, hygiene: because this node resolves the choice before the encoder and outputs one plain string, candidates that carry LoRA trigger words don't leak into every generation. (That's a string-level guarantee, not a "LoRA loads only sometimes" - the model still loads; only the text is kept clean.)
The title shows the current pick - Rt: lake or Rt: (empty) (2/3) - so you can glance at the node and know which candidate produced the image in front of you.
Installing
ComfyUI Manager (search "Prompt Random Choice"), or:
cd ComfyUI/custom_nodes
git clone https://github.com/ruminar/ComfyUI-PromptRandomChoice
then restart ComfyUI. There's no requirements.txt, no model downloads, nothing heavy - it's pure Python plus a small frontend extension. GPL-3.0, same license family as ComfyUI itself.
Where people get burned
- Status stuck at
EDITINGorSYNC ERROR- the sync route didn't accept your latest text (say, you edited on a different browser tab). It falls back to the last accepted revision, not the raw widget contents, so your edit silently doesn't apply until the sync clears. change_everyignores your edits mid-hold - a held pick keeps its old text until the counter runs out; edits only take effect at the next re-selection. If you want edits applied to the very next job, setchange_every = 1.!is a literal character in the non-runtime nodes - the README is explicit about this, and it's the most common way people confuse the two variants.
Wire selected_text into a string-join node (the README suggests kjnodes' Join String Multi) feeding your positive prompt, and it slots straight into an overnight batch.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| options_text | STRING | town, girl's room, park, lake, flower garden, castle, fortress, forest, grasslands, sea, snowy landscape, mountain, flower field, starry sky, coffee shop, | — |
| change_every | INT | 11–999999 | — |
| state_key | STRING | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| selected_text | STRING | — |
| selected_text_safe | STRING | — |