Scene Weaver - Sci-Fi
A prompt randomizer that writes sentences instead of comma piles
- entity_1_in
- entity_2_in
- entity_3_in
- entity_4_in
- prompt_text
- prompt_json
Most prompt randomizers hand you a comma pile: starship, red, damaged, space, detailed, 8k. Words with no grammar, so the model guesses which adjective sticks to what - and when the pieces disagree, like a derelict with its engines firing, the picture becomes a negotiation. Scene Weaver - Sci-Fi draws from pools the same way but writes English: a place, up to four things in it, what each is doing, and how they relate.
Why you'd reach for it
You want variety without writing a prompt per image. The usual answer is the bracket trick - {brown|blonde|black} hair, batch it, cherry-pick - which buys variation at the cost of coherence. Coherence is the point here: a contradictory prompt averages to nothing, and attention is finite, so four fully-described entities never render whole.
How it works
The seed drives one random.Random, and everything downstream is that draw. Fields resolve in widget order from the pool their scope chain selects - which is why kind: gas giant narrows form live to sphere shapes instead of offering a spiral coil. Constraint rules then run to a fixed point before anything is written: what a place affords, how a shape holds itself up, which traits can't coexist on one body. A ring can't stand on its own ring because a stance rule excludes it, not because someone wrote that sentence.
Last comes the detail budget. Each occupied slot keeps a fixed allowance of optional fields and the whole scene is capped at 320 tokens: slot 1 speaks in full, later slots get their silhouette unless you promote them, and anything cut is written back to None in prompt_json too - no "resolved but unspoken" state, so the JSON never promises a detail the image wasn't asked for. A locked field is never cut.
The controls that matter
Every descriptive dropdown - fifty-plus once all four slots are revealed - defaults to Random, so an untouched node already generates complete scenes. The ones you'll touch:
seed- the scene is a pure function of it. The control below defaults torandomize; flip it tofixedwhen you like something.entity_count-"1"to"4", and raising it reveals that slot's widgets. One has the highest hit rate, two is the sweet spot; three and four read as a crowd and models blend subjects. A wired Scene Entity occupies its slot regardless.scene_filter-Any,Peaceful,Conflict. Peaceful masks weapons and hostile actions out of the pools before the draw, rather than drawing a weapon and talking around it.set_all_fields-Randomize all/Clear all. It never touches a locked value and snaps back toOff: a one-shot action, not a mode.
The morphology fields - form, material, primary_color, markings, emitters, emitter_color, sensors - are where the depth lives, and form is what makes two ships genuinely different. Slots 2–4 expose only kind, subkind, scale, condition and situation; the relation_* pairs default to None on purpose.
Outputs
Two strings. prompt_text goes into any text encoder, and into a Show Text node so you can read what you got. prompt_json is the same scene as structured data. The node's face also shows a readout: scene contents, filter, counts, wired slots, and an approximate token count.
Install
ComfyUI Manager, search SceneWeaver, install, restart. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/EnragedAntelope/comfyui-sceneweaver
Keep the folder named comfyui-sceneweaver. There are no dependencies - standard library only, so no requirements.txt step and nothing to build. It does need ComfyUI 0.25.0+ and Python 3.10+, since it's written against the newer node API (from comfy_api.latest import io) rather than the old NODE_CLASS_MAPPINGS style.
Where people get burned
There's no style in the output, on purpose. SceneWeaver never mentions lighting, lens, medium or mood, so a bare prompt is drawn as bare content. The maintainer's own graphs prefix every prompt with cinematic film-quality slightly-grainy analog film still. - one line, and the difference between flat and the README's gallery.
Locking beats rules, quietly. A locked field wins over a coherence rule, which is what you asked for - unless you locked condition: derelict and emitters: reactor flare yourself. Those decisions land in prompt_json._meta.warnings.
The seed trap is the usual ComfyUI one. control_after_generate fires after the run, so the seed left in the box isn't the one that made your image. Set "widget control mode" to Before, or set seed's control to fixed first.
Generic labels mean the JavaScript didn't load. The frontend relabels counts per kind (emitter_count is "Engine count" on a vessel, "Eye count" on a creature) and narrows pools live; plain labels mean a stale cache. Hard-refresh.
Custom vocabulary is a file, not a fork. A gitignored user_options.json beside the pack adds creature types, environments, situations. Write ion thruster, not an ion thruster - the engine composes articles and plurals.
Inputs (59)
| Name | Type | Default | Description |
|---|---|---|---|
| seed | INT | 00–18446744073709550000 | Seed for reproducible scenes. The control below defaults to 'randomize' so every run differs; set it to 'fixed' to reproduce a scene. |
| entity_count | COMBO | 1 | How many subjects the scene has. Slot 1 is described most fully and later slots a little less, because an image has one subject's worth of attention to share out however many things are in it. Raising this reveals that slot's widgets on the node; every slot it reveals is described, never just named. Two is the sweet spot -- three and four work, and read as a crowd. A Scene Entity wired into a slot occupies it whatever this says. How much a slot actually speaks is also capped by what kind of thing it is: a creature or a person carries its whole component list, a station or a wreck only its silhouette, so a slot full of locked widgets can still read shorter than you expect. |
| scene_filter | COMBO | Any | Restrict what random draws may pick. 'Peaceful' leaves weapons and hostile actions out of the description entirely; 'Conflict' favours them. A value you lock yourself is always kept. |
| set_all_fields | COMBO | Off | Bulk-edit every descriptive widget below. 'Randomize all' turns each one that says 'None' into 'Random'; 'Clear all' does the reverse. A field you locked to a value is never touched, and the control snaps back to 'Off' as soon as it has run, so it stays a one-shot action rather than a mode. |
| environment | COMBO | Random | Where the scene is: deep space through orbit and atmosphere to a planetary surface or an interior. Scene | Random=randomize, value=lock, None=omit |
| entity1_kind | COMBO | Random | What this entity fundamentally is. Scopes every other field on this slot -- its options and its labels. Set to None to leave the slot empty. Identity | Random=randomize, value=lock, None=omit |
| entity1_subkind | COMBO | Random | The specific type within the kind -- a ship class, a creature type. Identity | Random=randomize, value=lock, None=omit |
| entity1_scale | COMBO | Random | How big it is relative to a person. Identity | Random=randomize, value=lock, None=omit |
| entity1_condition | COMBO | Random | What shape it is in -- pristine, battle-scarred, derelict. Identity | Random=randomize, value=lock, None=omit |
| entity1_form | COMBO | Random | The silhouette: a hull shape, or a body plan. This is the field that makes two ships actually look different. Form & Material | Random=randomize, value=lock, None=omit |
| entity1_material | COMBO | Random | What it is made of, or covered in. Form & Material | Random=randomize, value=lock, None=omit |
| entity1_primary_color | COMBO | Random | The colour of the thing itself -- hull, skin, integument. This describes the subject, not the lighting or the grade. Surface & Colour | Random=randomize, value=lock, None=omit |
| entity1_accent_color | COMBO | Random | A secondary colour: trim, panelling, banding. On a built subject this competes for a rotating detail slot; lock it to always speak it. Surface & Colour | Random=randomize, value=lock, None=omit |
| entity1_markings | COMBO | Random | Livery, insignia, patterning. Carries no readable lettering or numerals -- models garble text. On a built subject this competes for a rotating detail slot; lock it to always speak it. Surface & Colour | Random=randomize, value=lock, None=omit |
| entity1_surface_detail | COMBO | Random | Finish and wear at close range -- pitting, seams, scaling. On a built subject this competes for a rotating detail slot; lock it to always speak it. Surface & Colour | Random=randomize, value=lock, None=omit |
| entity1_appendages | COMBO | Random | What sticks out: pylons and fins, or limbs and tentacles. On a built subject this competes for a rotating detail slot; lock it to always speak it. Components | Random=randomize, value=lock, None=omit |
| entity1_appendage_count | COMBO | Random | How many. Composed into the appendage phrase, so it is only voiced when there are appendages to count. Components | Random=randomize, value=lock, None=omit |
| entity1_emitters | COMBO | Random | What glows or thrusts: engines, vents, bioluminescent organs. Components | Random=randomize, value=lock, None=omit |
| entity1_emitter_count | COMBO | Random | How many emitters. Components | Random=randomize, value=lock, None=omit |
| entity1_emitter_color | COMBO | Random | The colour of the glow itself. Emissive colour belongs to the subject; it is not a lighting instruction. Components | Random=randomize, value=lock, None=omit |
| entity1_armament | COMBO | Random | Weapons, mounted or grown. A Peaceful scene simply leaves this out rather than saying the thing is unarmed. Components | Random=randomize, value=lock, None=omit |
| entity1_armament_count | COMBO | Random | How many weapons. Components | Random=randomize, value=lock, None=omit |
| entity1_sensors | COMBO | Random | How it perceives: arrays and dishes, or eyes and sensory pits. Components | Random=randomize, value=lock, None=omit |
| entity1_sensor_count | COMBO | Random | How many. Four eyes and one eye are different creatures. Components | Random=randomize, value=lock, None=omit |
| entity1_aperture | COMBO | Random | The opening: a launch bay or viewport, or a mouth and its teeth. On a built subject this competes for a rotating detail slot; lock it to always speak it. Components | Random=randomize, value=lock, None=omit |
| entity1_extras | COMBO | Random | One more distinguishing component -- a cargo pod, a dorsal crest. On a built subject this competes for a rotating detail slot; lock it to always speak it. Components | Random=randomize, value=lock, None=omit |
| entity1_situation | COMBO | Random | What this entity is doing -- an event in progress, not a pose. Scene | Random=randomize, value=lock, None=omit |
| entity2_kind | COMBO | Random | What this entity fundamentally is. Scopes every other field on this slot -- its options and its labels. Set to None to leave the slot empty. Identity | Random=randomize, value=lock, None=omit |
| entity2_subkind | COMBO | Random | The specific type within the kind -- a ship class, a creature type. Identity | Random=randomize, value=lock, None=omit |
| entity2_scale | COMBO | Random | How big it is relative to a person. Identity | Random=randomize, value=lock, None=omit |
| entity2_condition | COMBO | Random | What shape it is in -- pristine, battle-scarred, derelict. Identity | Random=randomize, value=lock, None=omit |
| entity2_situation | COMBO | Random | What this entity is doing -- an event in progress, not a pose. Scene | Random=randomize, value=lock, None=omit |
| entity3_kind | COMBO | Random | What this entity fundamentally is. Scopes every other field on this slot -- its options and its labels. Set to None to leave the slot empty. Identity | Random=randomize, value=lock, None=omit |
| entity3_subkind | COMBO | Random | The specific type within the kind -- a ship class, a creature type. Identity | Random=randomize, value=lock, None=omit |
| entity3_scale | COMBO | Random | How big it is relative to a person. Identity | Random=randomize, value=lock, None=omit |
| entity3_condition | COMBO | Random | What shape it is in -- pristine, battle-scarred, derelict. Identity | Random=randomize, value=lock, None=omit |
| entity3_situation | COMBO | Random | What this entity is doing -- an event in progress, not a pose. Scene | Random=randomize, value=lock, None=omit |
| entity4_kind | COMBO | Random | What this entity fundamentally is. Scopes every other field on this slot -- its options and its labels. Set to None to leave the slot empty. Identity | Random=randomize, value=lock, None=omit |
| entity4_subkind | COMBO | Random | The specific type within the kind -- a ship class, a creature type. Identity | Random=randomize, value=lock, None=omit |
| entity4_scale | COMBO | Random | How big it is relative to a person. Identity | Random=randomize, value=lock, None=omit |
| entity4_condition | COMBO | Random | What shape it is in -- pristine, battle-scarred, derelict. Identity | Random=randomize, value=lock, None=omit |
| entity4_situation | COMBO | Random | What this entity is doing -- an event in progress, not a pose. Scene | Random=randomize, value=lock, None=omit |
| relation_1_2 | COMBO | None | How two slots relate. Rendered only when both of its entities exist. Defaults to None: six random relations between four random entities reads as noise, so this is opt-in. Relations | Random=randomize, value=lock, None=omit |
| relation_1_3 | COMBO | None | How two slots relate. Rendered only when both of its entities exist. Defaults to None: six random relations between four random entities reads as noise, so this is opt-in. Relations | Random=randomize, value=lock, None=omit |
| relation_1_4 | COMBO | None | How two slots relate. Rendered only when both of its entities exist. Defaults to None: six random relations between four random entities reads as noise, so this is opt-in. Relations | Random=randomize, value=lock, None=omit |
| relation_2_3 | COMBO | None | How two slots relate. Rendered only when both of its entities exist. Defaults to None: six random relations between four random entities reads as noise, so this is opt-in. Relations | Random=randomize, value=lock, None=omit |
| relation_2_4 | COMBO | None | How two slots relate. Rendered only when both of its entities exist. Defaults to None: six random relations between four random entities reads as noise, so this is opt-in. Relations | Random=randomize, value=lock, None=omit |
| relation_3_4 | COMBO | None | How two slots relate. Rendered only when both of its entities exist. Defaults to None: six random relations between four random entities reads as noise, so this is opt-in. Relations | Random=randomize, value=lock, None=omit |
| relation_1_2_position | COMBO | None | Where the first entity sits relative to the second: the spatial framing of the relation. Rendered only when the relation itself exists. Not tag-scoped: position is neutral, the action carries the peace/conflict meaning. Relations | Random=randomize, value=lock, None=omit |
| relation_1_3_position | COMBO | None | Where the first entity sits relative to the second: the spatial framing of the relation. Rendered only when the relation itself exists. Not tag-scoped: position is neutral, the action carries the peace/conflict meaning. Relations | Random=randomize, value=lock, None=omit |
| relation_1_4_position | COMBO | None | Where the first entity sits relative to the second: the spatial framing of the relation. Rendered only when the relation itself exists. Not tag-scoped: position is neutral, the action carries the peace/conflict meaning. Relations | Random=randomize, value=lock, None=omit |
| relation_2_3_position | COMBO | None | Where the first entity sits relative to the second: the spatial framing of the relation. Rendered only when the relation itself exists. Not tag-scoped: position is neutral, the action carries the peace/conflict meaning. Relations | Random=randomize, value=lock, None=omit |
| relation_2_4_position | COMBO | None | Where the first entity sits relative to the second: the spatial framing of the relation. Rendered only when the relation itself exists. Not tag-scoped: position is neutral, the action carries the peace/conflict meaning. Relations | Random=randomize, value=lock, None=omit |
| relation_3_4_position | COMBO | None | Where the first entity sits relative to the second: the spatial framing of the relation. Rendered only when the relation itself exists. Not tag-scoped: position is neutral, the action carries the peace/conflict meaning. Relations | Random=randomize, value=lock, None=omit |
| context | COMBO | Random | What else is in the shot besides the subject -- a secondary, architectural detail for scale and story. Drawn once per scene and spoken after the entities. Always a thing in the world, never a lighting or framing instruction. Scene | Random=randomize, value=lock, None=omit |
| entity_1_inopt | SCENE_ENTITY | Optional. A Scene Entity wired here describes slot 1, replacing that slot's own descriptive widgets and giving it the top allowance for the scene. It is a promotion, not an exemption: how much the slot speaks is still capped by what kind of thing it is, so a station reads shorter than a creature. A field you locked on the Scene Entity is a choice: it wins over these widgets and over a coherence rule, and the rule's reason is reported in prompt_json._meta. A field the Scene Entity drew at random is a draw: this scene first picks a place that can hold the whole wired subject, and re-draws a drawn field only when no place can -- the node face then names what was re-drawn. What the entity is *doing* stays this node's: situation and relations are never taken from a wired entity. | |
| entity_2_inopt | SCENE_ENTITY | Optional. A Scene Entity wired here describes slot 2, replacing that slot's own descriptive widgets and giving it the top allowance for the scene. It is a promotion, not an exemption: how much the slot speaks is still capped by what kind of thing it is, so a station reads shorter than a creature. A field you locked on the Scene Entity is a choice: it wins over these widgets and over a coherence rule, and the rule's reason is reported in prompt_json._meta. A field the Scene Entity drew at random is a draw: this scene first picks a place that can hold the whole wired subject, and re-draws a drawn field only when no place can -- the node face then names what was re-drawn. What the entity is *doing* stays this node's: situation and relations are never taken from a wired entity. | |
| entity_3_inopt | SCENE_ENTITY | Optional. A Scene Entity wired here describes slot 3, replacing that slot's own descriptive widgets and giving it the top allowance for the scene. It is a promotion, not an exemption: how much the slot speaks is still capped by what kind of thing it is, so a station reads shorter than a creature. A field you locked on the Scene Entity is a choice: it wins over these widgets and over a coherence rule, and the rule's reason is reported in prompt_json._meta. A field the Scene Entity drew at random is a draw: this scene first picks a place that can hold the whole wired subject, and re-draws a drawn field only when no place can -- the node face then names what was re-drawn. What the entity is *doing* stays this node's: situation and relations are never taken from a wired entity. | |
| entity_4_inopt | SCENE_ENTITY | Optional. A Scene Entity wired here describes slot 4, replacing that slot's own descriptive widgets and giving it the top allowance for the scene. It is a promotion, not an exemption: how much the slot speaks is still capped by what kind of thing it is, so a station reads shorter than a creature. A field you locked on the Scene Entity is a choice: it wins over these widgets and over a coherence rule, and the rule's reason is reported in prompt_json._meta. A field the Scene Entity drew at random is a draw: this scene first picks a place that can hold the whole wired subject, and re-draws a drawn field only when no place can -- the node face then names what was re-drawn. What the entity is *doing* stays this node's: situation and relations are never taken from a wired entity. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| prompt_text | STRING | — |
| prompt_json | STRING | — |