Nodes/SceneWeaver/Scene Weaver - Sci-Fi
ComfyUI Node

Scene Weaver - Sci-Fi

A prompt randomizer that writes sentences instead of comma piles

By EnragedAntelope·Created 3 days ago·Updated 2 days ago· 2
Scene Weaver - Sci-Fi
  • entity_1_in
  • entity_2_in
  • entity_3_in
  • entity_4_in
  • prompt_text
  • prompt_json
seed0
entity_count1
scene_filterAny
set_all_fieldsOff
environmentRandom
entity1_kindRandom
entity1_subkindRandom
entity1_scaleRandom
entity1_conditionRandom
entity1_formRandom
entity1_materialRandom
entity1_primary_colorRandom
entity1_accent_colorRandom
entity1_markingsRandom
entity1_surface_detailRandom
entity1_appendagesRandom
entity1_appendage_countRandom
entity1_emittersRandom
entity1_emitter_countRandom
entity1_emitter_colorRandom
entity1_armamentRandom
entity1_armament_countRandom
entity1_sensorsRandom
entity1_sensor_countRandom
entity1_apertureRandom
entity1_extrasRandom
entity1_situationRandom
entity2_kindRandom
entity2_subkindRandom
entity2_scaleRandom
entity2_conditionRandom
entity2_situationRandom
entity3_kindRandom
entity3_subkindRandom
entity3_scaleRandom
entity3_conditionRandom
entity3_situationRandom
entity4_kindRandom
entity4_subkindRandom
entity4_scaleRandom
entity4_conditionRandom
entity4_situationRandom
relation_1_2None
relation_1_3None
relation_1_4None
relation_2_3None
relation_2_4None
relation_3_4None
relation_1_2_positionNone
relation_1_3_positionNone
relation_1_4_positionNone
relation_2_3_positionNone
relation_2_4_positionNone
relation_3_4_positionNone
contextRandom

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 to randomize; flip it to fixed when 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 to Off: 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.

Categoryconditioning/sceneweaver

Inputs (59)

NameTypeDefaultDescription
seedINT00–18446744073709550000Seed for reproducible scenes. The control below defaults to 'randomize' so every run differs; set it to 'fixed' to reproduce a scene.
entity_countCOMBO1How 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_filterCOMBOAnyRestrict 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_fieldsCOMBOOffBulk-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.
environmentCOMBORandomWhere the scene is: deep space through orbit and atmosphere to a planetary surface or an interior. Scene | Random=randomize, value=lock, None=omit
entity1_kindCOMBORandomWhat 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_subkindCOMBORandomThe specific type within the kind -- a ship class, a creature type. Identity | Random=randomize, value=lock, None=omit
entity1_scaleCOMBORandomHow big it is relative to a person. Identity | Random=randomize, value=lock, None=omit
entity1_conditionCOMBORandomWhat shape it is in -- pristine, battle-scarred, derelict. Identity | Random=randomize, value=lock, None=omit
entity1_formCOMBORandomThe 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_materialCOMBORandomWhat it is made of, or covered in. Form & Material | Random=randomize, value=lock, None=omit
entity1_primary_colorCOMBORandomThe 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_colorCOMBORandomA 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_markingsCOMBORandomLivery, 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_detailCOMBORandomFinish 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_appendagesCOMBORandomWhat 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_countCOMBORandomHow 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_emittersCOMBORandomWhat glows or thrusts: engines, vents, bioluminescent organs. Components | Random=randomize, value=lock, None=omit
entity1_emitter_countCOMBORandomHow many emitters. Components | Random=randomize, value=lock, None=omit
entity1_emitter_colorCOMBORandomThe 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_armamentCOMBORandomWeapons, 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_countCOMBORandomHow many weapons. Components | Random=randomize, value=lock, None=omit
entity1_sensorsCOMBORandomHow it perceives: arrays and dishes, or eyes and sensory pits. Components | Random=randomize, value=lock, None=omit
entity1_sensor_countCOMBORandomHow many. Four eyes and one eye are different creatures. Components | Random=randomize, value=lock, None=omit
entity1_apertureCOMBORandomThe 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_extrasCOMBORandomOne 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_situationCOMBORandomWhat this entity is doing -- an event in progress, not a pose. Scene | Random=randomize, value=lock, None=omit
entity2_kindCOMBORandomWhat 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_subkindCOMBORandomThe specific type within the kind -- a ship class, a creature type. Identity | Random=randomize, value=lock, None=omit
entity2_scaleCOMBORandomHow big it is relative to a person. Identity | Random=randomize, value=lock, None=omit
entity2_conditionCOMBORandomWhat shape it is in -- pristine, battle-scarred, derelict. Identity | Random=randomize, value=lock, None=omit
entity2_situationCOMBORandomWhat this entity is doing -- an event in progress, not a pose. Scene | Random=randomize, value=lock, None=omit
entity3_kindCOMBORandomWhat 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_subkindCOMBORandomThe specific type within the kind -- a ship class, a creature type. Identity | Random=randomize, value=lock, None=omit
entity3_scaleCOMBORandomHow big it is relative to a person. Identity | Random=randomize, value=lock, None=omit
entity3_conditionCOMBORandomWhat shape it is in -- pristine, battle-scarred, derelict. Identity | Random=randomize, value=lock, None=omit
entity3_situationCOMBORandomWhat this entity is doing -- an event in progress, not a pose. Scene | Random=randomize, value=lock, None=omit
entity4_kindCOMBORandomWhat 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_subkindCOMBORandomThe specific type within the kind -- a ship class, a creature type. Identity | Random=randomize, value=lock, None=omit
entity4_scaleCOMBORandomHow big it is relative to a person. Identity | Random=randomize, value=lock, None=omit
entity4_conditionCOMBORandomWhat shape it is in -- pristine, battle-scarred, derelict. Identity | Random=randomize, value=lock, None=omit
entity4_situationCOMBORandomWhat this entity is doing -- an event in progress, not a pose. Scene | Random=randomize, value=lock, None=omit
relation_1_2COMBONoneHow 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_3COMBONoneHow 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_4COMBONoneHow 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_3COMBONoneHow 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_4COMBONoneHow 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_4COMBONoneHow 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_positionCOMBONoneWhere 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_positionCOMBONoneWhere 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_positionCOMBONoneWhere 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_positionCOMBONoneWhere 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_positionCOMBONoneWhere 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_positionCOMBONoneWhere 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
contextCOMBORandomWhat 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_inoptSCENE_ENTITYOptional. 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_inoptSCENE_ENTITYOptional. 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_inoptSCENE_ENTITYOptional. 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_inoptSCENE_ENTITYOptional. 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)

NameTypeDescription
prompt_textSTRING
prompt_jsonSTRING