# ComfyUI Prompt Builder

Custom ComfyUI node for building prompts from the existing `generate_prompt_batches.py`
generator without writing image batches.

The node is registered as:

- `prompt_builder / SxCP Prompt Builder`
- `prompt_builder / SxCP Global Seed`
- `prompt_builder / SxCP Seed Control`
- `prompt_builder / SxCP Seed Locker`
- `prompt_builder / util / SxCP SDXL Bucket Size`
- `prompt_builder / SxCP Camera Control`
- `prompt_builder / SxCP Camera Orbit Control`
- `prompt_builder / SxCP Qwen Camera Translator`
- `prompt_builder / SxCP For Loop Start`
- `prompt_builder / SxCP For Loop End`
- `prompt_builder / SxCP Loop Append`
- `prompt_builder / SxCP Accumulator`
- `prompt_builder / util / SxCP Preview Any As Text`
- `prompt_builder / SxCP Category Preset`
- `prompt_builder / SxCP Cast Control`
- `prompt_builder / SxCP Cast Bias`
- `prompt_builder / SxCP Generation Profile`
- `prompt_builder / SxCP Style Pool`
- `prompt_builder / SxCP Ethnicity List`
- `prompt_builder / SxCP Hair Length`
- `prompt_builder / SxCP Hair Color`
- `prompt_builder / SxCP Hair Style/Cut`
- `prompt_builder / SxCP Character Manual Details`
- `prompt_builder / SxCP Advanced Filters`
- `prompt_builder / SxCP Prompt Builder From Configs`
- `prompt_builder / SxCP Woman Slot`
- `prompt_builder / SxCP Man Slot`
- `prompt_builder / SxCP Character Slot`
- `prompt_builder / SxCP Character Profile Save`
- `prompt_builder / SxCP Character Profile Load`
- `prompt_builder / SxCP Caption Naturalizer`
- `prompt_builder / SxCP Krea2 Formatter`
- `prompt_builder / SxCP Insta/OF Options`
- `prompt_builder / SxCP Insta/OF Prompt Pair`
- `prompt_builder / v2_scene / SxCP Scene Start`
- `prompt_builder / v2_scene / SxCP Scene Cast`
- `prompt_builder / v2_scene / SxCP Scene Character`
- `prompt_builder / v2_scene / SxCP Scene Wardrobe`
- `prompt_builder / v2_scene / SxCP Scene Location`
- `prompt_builder / v2_scene / SxCP Scene Set Dressing`
- `prompt_builder / v2_scene / SxCP Scene Blocking`
- `prompt_builder / v2_scene / SxCP Scene Action`
- `prompt_builder / v2_scene / SxCP Scene Performance`
- `prompt_builder / v2_scene / SxCP Scene Camera`
- `prompt_builder / v2_scene / SxCP Scene Composition`
- `prompt_builder / v2_scene / SxCP Scene Lighting`
- `prompt_builder / v2_scene / SxCP Scene Branch Pair`
- `prompt_builder / v2_scene / SxCP Softcore Branch Options`
- `prompt_builder / v2_scene / SxCP Hardcore Branch Options`
- `prompt_builder / v2_scene / SxCP Scene Output`
- `prompt_builder / v2_scene / SxCP Scene Pair Output`

It outputs:

- `prompt`
- `negative_prompt`
- `caption`
- `metadata_json`
- `category`
- `subcategory`

## Split Workflow Nodes

The original `SxCP Prompt Builder` remains available as the full all-in-one
node. For cleaner workflows, use the split nodes:

- `SxCP Category Preset` outputs `category_config` for broad intent such as
  legacy random, full random, women casual, men casual, couple casual,
  provocative erotic, or hardcore pose.
- `SxCP Cast Control` outputs `cast_config` plus raw `women_count` and
  `men_count`, so couple/two-women/two-men/group setups can be reused.
- `SxCP Cast Bias` outputs the same cast sockets, but chooses counts from
  weighted lists. With `women_start_count=1`, `women_weights=0.6,0.2` means
  60% one woman and 20% two women; with `men_start_count=0`,
  `men_weights=0.5,0.3` means 50% no man and 30% one man.
- `SxCP Location Pool` outputs `location_config`. `replace` uses only the
  selected/custom location pool; `add` keeps the category's own locations and
  adds yours. Custom lines can be plain location text, `slug: location text`, or
  one-line JSON objects/arrays. JSON location entries preserve metadata such as
  inline `camera_profile` / `scene_camera_profile`.
- `SxCP Composition Pool` outputs `composition_config` to control framing
  separately from location. Use it when category framing mentions unrelated
  outfit-check details such as shoes, bags, or mirror poses. Custom composition
  lines can also be one-line JSON objects/arrays when metadata needs to travel
  with the selected composition.
- `SxCP Location Theme` outputs matched `location_config` and
  `composition_config`. Themes such as `classical_library`,
  `semi_public_affair`, `hotel_corridor`, `parking_garage`, and
  `theater_backstage` keep scene and framing compatible.
- `SxCP Style Pool` outputs `style_config` for visual rendering style only.
  It can force realistic/photo/cinematic/comic output independently from
  category, action, pose, location, and camera. The previous colored-pencil
  comic wording is available as the `comic_pinup_colored_pencil` preset instead
  of being baked into hardcore pose prompts.
- `SxCP Generation Profile` outputs `generation_profile` for common behavior
  presets such as casual-clean, evocative-softcore, hardcore-intense,
  Krea2-friendly, or Flux-original. Its clothing and pose overrides can be
  `random`, and `expression_intensity_mode=random` makes expression strength
  vary by seeded row.
- `SxCP Ethnicity List` outputs a reusable ethnicity filter from checkboxes.
  It includes broad groups and narrower European/Mediterranean groups such as
  `french_european`, `germanic_european`, `nordic_european`,
  `slavic_european`, `italian_mediterranean`, and `iberian_mediterranean`.
  Connect `ethnicity` to a prompt or slot `ethnicity_list` input, or connect
  `filter_config` to generator-level `filter_config`.
- `SxCP Advanced Filters` outputs `filter_config` for appearance include
  checkboxes, figure, and plus-size inclusion.
- `SxCP Prompt Builder From Configs` consumes those config outputs and produces
  the same prompt, negative, caption, metadata, category, and subcategory
  outputs as the full builder.

The practical compact workflow is:

`Category Preset` + `Cast Control` + `Generation Profile` + optional
`Advanced Filters`, `Seed Locker` or `Seed Control`, `Camera Control` or
`Camera Orbit Control`, `Location Theme` or `Location Pool` + `Composition Pool`,
`Style Pool`, `Woman Slot` / `Man Slot`, and `Character Profile`
into `Prompt Builder From Configs`.

## Scene-Chain v2 Nodes

The v2 scene nodes are an additive workflow surface. They pass one structured
`SXCP_SCENE` object through cast, character, wardrobe, location, set dressing,
blocking, action, performance, camera, composition, and lighting layers. Use
`SxCP Scene Output` for a single prompt, or split a shared scene with
`SxCP Scene Branch Pair`, refine it with `SxCP Softcore Branch Options` and
`SxCP Hardcore Branch Options`, then render both sides through
`SxCP Scene Pair Output`.

Each layer can stay light on the main chain and take optional side-node inputs:
`SxCP Scene Layer Seed Options`, `SxCP Scene Cast Options`,
`SxCP Scene Character Options`, `SxCP Scene Wardrobe Options`,
`SxCP Scene Location Layout Options`, `SxCP Scene Set Dressing Options`,
`SxCP Scene Blocking Options`, `SxCP Scene Action Options`,
`SxCP Scene Performance Options`, `SxCP Scene Camera Options`,
`SxCP Scene Composition Options`, `SxCP Scene Lighting Options`, and
`SxCP Scene Branch Options`. These side nodes are chainable and only override
the layer they are connected to.

The current v2 output nodes intentionally reuse the existing builder,
Insta/OF pair, and formatter metadata routes. This keeps old workflows working
while giving new workflows a cleaner movie-scene structure.

An importable default workflow is included at
`examples/default_task_lanes_workflow.json`. It is laid out by task instead of
as one long chain:

1. A regular generation lane: config nodes into `Prompt Builder From Configs`,
   then optional `Caption Naturalizer` and `Krea2 Formatter`.
2. An Insta/OF dual-generation lane: `Insta/OF Options`, seed/camera controls,
   `Insta/OF Prompt Pair`, then optional formatter/naturalizer nodes.
3. Profile save/load tools parked as an adjacent side branch. They can be wired
   manually into either generation lane, but they are not part of the default
   main path.

A dedicated v2 scene-chain Insta/OF branching demo is available at
`examples/scene_chain_insta_of_branching_workflow.json`.

A proposed adapter-style v2 layout is available at
`examples/scene_chain_adapter_layout_workflow.json`. It keeps the main scene
chain in one center lane and parks layer-specific option nodes beside the layer
they override.

A no-freeform workspace lounge Insta/OF branch demo is available at
`examples/scene_chain_workspace_lounge_insta_of_workflow.json`. It uses the
`workspace_lounge` location theme with camera orbit control, so the coworking
layout text adapts to camera position while the softcore and hardcore prompts
stay split.

## Loop Nodes

`SxCP For Loop Start` and `SxCP For Loop End` provide a lightweight replacement
for the easy-use for-loop dependency. They use the same recursive ComfyUI loop
pattern, but add a dedicated collector output for building a result sequence.

Basic loop wiring:

1. Connect `For Loop Start.flow` to `For Loop End.flow`.
2. Use `For Loop Start.index` inside the loop for row/seed/index changes.
3. Connect the per-iteration output you want to keep, such as an image, latent,
   prompt, or metadata string, to `For Loop End.collect_value`.
4. Optionally connect `For Loop Start.collected` to `For Loop End.collected`.
   If omitted, the end node uses the start collector internally.
5. After the loop finishes, use `For Loop End.collected` as the combined output.

`For Loop Start.index` is 1-based so it can be wired directly into prompt-builder
`row_number` inputs. `For Loop Start.schedule` is an optional input for choosing
which indexes run while keeping row numbers stable. Omit it to run `1..total`,
connect a list such as `[2, 5, 8]`, or connect text such as `2,5,8` or `2-8`.
Indexes outside `1..total` are ignored.

`collection_mode` controls how values are stored:

- `auto_batch`: concatenates image tensors or latent samples when possible,
  otherwise falls back to a Python list.
- `image_batch`: prefers image tensor batching.
- `latent_batch`: prefers latent `samples` batching.
- `list`: always appends each iteration result to a list.
- `string_lines`: joins each collected value with newlines.

`value1`, `value2`, and later slots are normal carry-through channels for state
you want to update each iteration. They are separate from the collector and grow
dynamically in the UI as you connect them.

`SxCP Accumulator` stores outputs across executions under a `store_key` or the
node id. Put it after an image-producing step inside or after a loop and connect
the generated `image`.

For camera/pose tuning, leave `action=append_variant`. Every rerun is kept as a
new variant, so you can regenerate row 1 several times without managing ids. If
you connect `For Loop Start.index` to `entry_id`, variants are labelled by row
internally; they still append instead of replacing.

For deterministic loop resume/dedupe, set `action=replace_by_entry_id` and
connect `For Loop Start.index` to `entry_id`. Optional `entry_tag` lets multiple
branches share the same row index without overwriting each other, such as
`soft`, `hard`, or `upscale`.

Its outputs are:

- `collection`: all stored values, or images when no explicit `value` is wired.
- `image_batch`: all stored images as one ComfyUI image batch when they share
  the same height and width. Set `image_batch_mode=resize_to_first` if you want
  mixed sizes resized into one batch.
- `image_list`: a ComfyUI list output containing each stored image separately.
- `image_batch_1..4`: same-size grouped batches for mixed-format runs, so a
  square group and a portrait group can be saved or processed separately.

ComfyUI image batches require matching dimensions. For mixed image formats, use
`image_list` or the grouped `image_batch_1..4` outputs instead of `image_batch`.

`SxCP Accumulator Preview` can show images as a wrapped grid or as a carousel.
Set `view_mode=carousel` to inspect one image at a time with the `Prev` and
`Next` buttons. `zoom_level` controls thumbnail size in grid mode and the image
area in carousel mode; `carousel_index` stores the selected carousel position.

`SxCP Preview Any As Text` is a persistent text preview for arbitrary values.
Connect any output to `value`; the node renders strings directly and formats
dict/list/tensor-like values as readable text. After execution, its
`preview_text` widget is updated by the frontend and is serialized in the
workflow. Save the workflow after a run and the preview text will still be
there after reload.

## Character Profiles

`SxCP Woman Slot` and `SxCP Man Slot` are the scalable per-participant control
nodes. `Cast Control` still decides how many women and men are generated; slot
nodes decide who those people are. Each slot defines one participant with age,
ethnicity, body, expression, and config inputs. Leave fields on `random` to let
the generator fill that part from the normal pools.

Use `SxCP Character Manual Details` when you need exact manual text for a slot:
manual age, manual body, body phrase, skin, hair, eyes, softcore outfit, or
hardcore clothing. Connect `Character Manual Details.manual` to the slot's
`manual` input. Manual detail nodes are chainable through their top `manual`
input/output.

Each slot has `slot_seed`. Leave it at `-1` to follow the generator's normal
person seed. Set any fixed value when the slot's `random` fields should resolve
as one stable character across scene, pose, outfit, or row rerolls. This seed is
shared by that slot's random age/body/appearance choices, so you can keep the
same participant while changing other generation axes.

Connect `SxCP Ethnicity List.ethnicity` to a slot's `ethnicity_list` input when
that character should randomize inside a selected heritage list. This is useful
for narrowing broad groups, for example choosing French/Germanic/Nordic/Slavic
European entries instead of the entire `european` pool.

Hair can be controlled the same way with chainable characteristic nodes. Connect
the final `hair_config` output to a slot's `hair_config` input:

`Hair Length -> Hair Color -> Hair Style/Cut -> Woman Slot.hair_config`

Each hair node only changes its own axis and passes the rest through. For
example, select `long` in `Hair Length`, select the blonde variants you allow in
`Hair Color`, then select `waves` and `ponytail` in `Hair Style/Cut`. If the slot's
manual details include `hair`, that exact text overrides the hair config.

Use `Woman Slot` for women because it exposes woman-focused body choices and a
`figure_bias` selector. Use `Man Slot` for men because it exposes man-focused
body choices and omits figure bias. The older generic `SxCP Character Slot`
remains available for compatibility and manual mixed use, but the gendered
slots are the cleaner default.

Each slot also has `descriptor_detail`, which controls how much appearance text
is emitted into named-cast descriptors:

- `auto`: women use `full`; men use `compact`.
- `full`: age, body, skin, hair, and eyes.
- `medium`: age, body, skin, and hair.
- `compact`: age, body, and skin.
- `minimal`: age and body only.

`SxCP Man Slot` defaults to `compact`, which keeps men readable in Krea-style
couple/group prompts without turning every partner into a fully detailed primary
character. Set a man slot to `full` when the partner needs exact hair/eye detail.

`SxCP Man Slot` also has `presence_mode`. Use `visible` for a normal rendered
partner. Use `pov` when that man is the first-person camera participant: he
remains part of the cast and role graph, but his appearance descriptor and
per-character expression text are omitted, and pose wording is rendered from
the POV participant's viewpoint. The generic `SxCP Character Slot` exposes the
same field, but it only has an effect when `subject_type=man`.

Slots also expose `expression_enabled` and `expression_intensity`. Disable
`expression_enabled` when that character should not receive a face/expression
directive. Leave `expression_intensity` at `-1` to use the generator or Insta/OF
option fallback. Set it from `0.0` to `1.0` to make expression selection
character-driven. For configured casts, matching enabled slots emit
per-character expression text such as `Woman A has ...; Man A has ...`; Krea
formatting naturalizes those labels in pair prompts.

For Insta/OF pairs, slots also expose character-level overrides:

- `softcore_expression_intensity` and `hardcore_expression_intensity`: override
  the option-node expression fallback for that character and that output half.
- `softcore_outfit` and `hardcore_clothing` are provided by
  `SxCP Character Manual Details` and connected through the slot's `manual`
  input. For `Woman A`, a hardcore clothing override replaces the global
  `hardcore_clothing_continuity` text to avoid contradictory clothing prompts.

Slots are chainable through the `character_cast` input/output. In automatic
label mode, the slot closest to the final generator becomes `A` for its gender,
the next upstream slot becomes `B`, then `C`, and so on. Example:

`Woman slot 3 -> Woman slot 2 -> Woman slot 1 -> Insta/OF Prompt Pair`

In that chain, `Woman slot 1` resolves as `Woman A`, `Woman slot 2` resolves as
`Woman B`, and `Woman slot 3` resolves as `Woman C`. Men resolve separately the
same way, so the closest man slot becomes `Man A`.

Connect the final `character_cast` output to `SxCP Prompt Builder`,
`SxCP Prompt Builder From Configs`, or `SxCP Insta/OF Prompt Pair`. It applies
to JSON/custom single woman/man rows, JSON/custom configured-cast rows such as
`Hardcore sexual poses`, and Insta/OF named casts. The older profile save/load
nodes remain useful for one reusable primary character, but slots are better
when you need different settings for each participant.

`SxCP Character Profile Save` extracts a reusable woman/man profile from
`metadata_json`, a `character_slot`, or from manual fields. The profile stores
age, body/body phrase, skin, hair, eyes, figure, and subject type. To save a
Woman Slot, connect `Woman Slot.character_slot` to
`Character Profile Save.character_slot`, set `source=character_slot`, enter a
name, run the workflow once so the save node caches that exact profile, then
click `Save Profile Now`. The button writes the cached profile directly and does
not queue or regenerate the workflow, so it saves the character you just liked.
Otherwise the node just outputs profile JSON for direct wiring. Saved files are
written under `profiles/<profile_name>.json`; saved profile files are ignored by
git. The hidden `save_now` trigger remains for legacy/API use, but the visible
button does not use it.

`SxCP Character Profile Load` has an `enabled` switch. When disabled, it returns
an empty profile so connected prompt builders ignore it. When enabled, it loads
a saved profile by selector or passes through a connected fallback profile JSON.
It also has load-time override fields. Leave them blank or on `keep_profile` to
use the saved value; fill only the attributes you want to change for this
workflow, such as `override_age`, `override_body`, `override_skin`,
`override_hair`, `override_eyes`, `override_figure`, or
`override_descriptor_detail`. Overrides affect both the `character_profile` and
`character_cast` outputs, but they do not rewrite the saved profile file.

The load node also has explicit file-operation triggers:

- `Delete Selected Profile`: deletes the selected saved profile.
- `Rename Selected Profile` + `rename_to`: renames the selected saved profile.

Delete and rename are conservative: if both triggers are enabled together,
nothing happens; rename does not overwrite an existing target profile. The
buttons are backed by hidden `delete_now` and `rename_now` triggers and queue
the workflow once.

Connect the loader's `character_profile` output to `SxCP Prompt Builder`,
`SxCP Prompt Builder From Configs`, or `SxCP Insta/OF Prompt Pair` for the
older single-primary-character path. Connect `character_cast` instead when you
want the loaded profile to act like a slot in a cast chain.

When loaded through `character_profile`, profile reuse applies to structured
JSON-category single woman/man rows and to the primary creator in Insta/OF pair
mode. When loaded through `character_cast`, the same saved appearance behaves
like an auto-labeled slot and can participate in couple/group casts. The outfit,
scene, pose, expression, and composition can still change while the saved
character appearance remains stable.

`SxCP Global Seed` is the simplest reproducibility node. Set `global_seed`,
connect its `seed` output to the prompt node's `seed` input, and connect its
`seed_config` output to the prompt node's `seed_config` input. With the same
row number, settings, category files, and sampler seed, this recreates the same
prompt result. Manual fields and explicitly fixed per-axis or character-slot
seeds still override the global seed for those parts.

`SxCP Seed Control` outputs `seed_config`, which can be connected to the prompt
builder's optional `seed_config` input. It also outputs a `summary` string with
the resolved value for every axis. When an axis is set to `random`, the widget
can stay at `-1`, but the emitted `seed_config` and `summary` contain the
concrete seed used for that queued prompt. Use `Lock Random Seeds Now` on the
node when you want to convert the current random axes into fixed reusable seeds.

`SxCP Seed Locker` is the fast version for iteration. Set `base_seed` to a seed
you like, choose one `reroll_axis`, and connect its `seed_config`. All other
axes stay frozen to `base_seed`; the rerolled axis follows `reroll_seed`, or the
main prompt seed when `reroll_seed=-1`.

`SxCP SDXL Bucket Size` randomly selects one of the built-in SDXL bucket
resolutions and outputs `width`, `height`, and a `resolution` string. Use
`orientation` to restrict the pool to portrait, square, or landscape. Leave
`bucket_index=0` for random selection, or set `bucket_index` to pick a specific
position inside the filtered pool. Connect `SxCP Global Seed` or Seed Locker's
`seed_config` when the bucket choice must be reproducible.

`SxCP Krea2 Resolution Selector` is a simple size picker: choose `megapixels`
and `aspect_ratio`, then use the output `width` and `height`. It assumes local
Krea2 Turbo 2K limits, searches for the best multiple-of-16 size, and clamps the
selected megapixel target when that aspect ratio cannot fit it. `max_for_aspect`
returns the largest valid size for the chosen ratio. The official Krea API
aspect ratios are listed first, with a few local helper ratios such as `8:9`
after them.

`SxCP Camera Control` and `SxCP Camera Orbit Control` output `camera_config`,
which can be connected to the prompt builder or the Insta/OF pair node. They
make camera/framing first-class instead of relying on a weak phrase inside the
prompt.

Camera controls:

- `camera_mode`: `disabled`, `standard`, `handheld_selfie`, `mirror_selfie`,
  `phone_tripod`, `creator_pov`, `bed_selfie`, `bathroom_mirror`,
  `phone_flash`, or `action_cam`.
- `shot_size`: `auto`, `full_body`, `three_quarter`, `waist_up`, `close_up`,
  or `extreme_close_up`.
- `angle`: `auto`, `eye_level`, `high_angle`, `low_angle`, `overhead`,
  `side_profile`, `rear_view`, or `mirror_reflection`.
- `lens`: `auto`, `smartphone_wide`, `ultra_wide`, `portrait_lens`,
  `telephoto`, or `macro_detail`.
- `distance`: `auto`, `arm_length`, `near_body`, `bedside`, or `room_corner`.
- `orientation`: `auto`, `vertical_story`, `square_feed`, or `horizontal`.
- `phone_visibility`: `auto`, `phone_visible`, `phone_hidden`,
  `screen_reflection`, or `ring_light_visible`.
- `priority`: `soft_hint`, `strong`, or `locked`.
- `camera_detail`: `off` emits no camera sentence, `compact` emits one short
  camera sentence, and `full` emits the full detailed camera constraint.

`SxCP Camera Orbit Control` is the numeric/directable version inspired by
multi-angle camera nodes. It maps `horizontal_angle`, `vertical_angle`, and
`zoom` into a stable prompt such as `135-degree back-right quarter view,
elevated shot, medium shot`. Use it when the model needs an exact front, side,
back-quarter, low, high, or overhead style camera anchor. Its first output is
the same `camera_config` type, so it can replace `SxCP Camera Control` anywhere.

Orbit controls:

- `horizontal_angle`: `0` front, `90` right side, `180` back, `270` left side,
  with quarter views between them.
- `vertical_angle`: negative values are low-angle, `0` is eye-level, positive
  values move toward elevated/high-angle.
- `zoom`: `0-2` wide, `2-6` medium, `6-10` close unless `framing` overrides it.
- `subject_focus`: optionally centers face, torso, hips, full body, main action,
  contact points, or the environment.
- `include_degrees`: keeps the numeric angle in the emitted camera phrase.

If you use `ComfyUI-qwenmultiangle`, keep its nicer Three.js camera viewer and
add `SxCP Qwen Camera Translator` after it:

1. Connect Qwen Multiangle Camera `prompt` to translator `qwen_prompt`.
2. Optionally connect Qwen Multiangle Camera `camera_info` to translator
   `camera_info`; this keeps exact numeric angle/zoom from the viewer.
3. Connect translator `camera_config` to `Prompt Builder`, `Prompt Builder From
   Configs`, or `Insta/OF Prompt Pair`.

The translator accepts the Qwen labels such as `front-right quarter view`,
`eye-level shot`, and `medium shot`, then emits the same `camera_config` format
as the native camera nodes. `suppress_phone_visibility` is enabled by default so
generic Qwen camera views do not add `phone hidden` or other phone wording.

For camera-aware locations, the prompt builder also uses the translated camera
geometry to add a location-aware framing sentence. It currently has scene
profiles for coworking/business-office spaces, classical library/book-stack
spaces, and semi-public repeating-structure locations such as hotel corridors,
parking garages, archives, laundromats, station lockers, backstage halls, wine
cellars, nightclub back halls, and restaurant booths. Front/side/back views,
zoom, and elevation change which desks, windows, partitions, bookshelves,
corridors, pillars, shelves, tables, lamps, or aisles are kept visible. In
male-POV setups this becomes a first-person spatial description and the
external camera sentence is suppressed.
Rows keep the selected `scene_entry`, `location_theme`, `scene_theme`,
`composition_entry`, `composition_theme`, and `scene_camera_profile_key` in
`metadata_json` so location/camera behavior can be debugged without guessing
from prompt text alone.
When camera-aware profile routing runs, explicit `scene_camera_profile_key` and
theme metadata are used before fallback text matching.
Advanced scene entries may also include an inline `camera_profile` /
`scene_camera_profile` object with `layout_label`, `foreground`, `midground`,
`background`, and optional composition text, so custom location packs can define
their own camera behavior.

`SxCP SDXL Formatter` rewrites prompt builder output or `metadata_json` into
comma-tag SDXL/Pony-style prompts. Connect `metadata_json` when possible so
character, camera, outfit, and action metadata stay available to the tag route.

SDXL formatter controls:

- `formatter_profile`: `manual_controls` keeps `style_preset` and
  `quality_preset` authoritative. `pony_flat_vector`, `sdxl_photo`, and
  `flat_vector` apply coherent formatter defaults.
- `style_preset`: positive style anchor such as `flat_vector_pony`,
  `flat_vector`, or `photographic`.
- `quality_preset`: quality/score tail such as `pony_high` or `sdxl_high`.
- `trigger` and `prepend_trigger_to_prompt`: explicit model/LoRA trigger
  placement for SDXL-style workflows.
- `custom_style` and `custom_quality`: override the selected preset text.

`SxCP Caption Naturalizer` rewrites tag-like captions or labeled prompts into
more natural language. Connect the prompt builder's `metadata_json` output to
`source_text` for the cleanest result. You can also connect `caption` or
`prompt`; in that case the node falls back to prompt-label parsing or comma-tag
cleanup.

When connected to `SxCP Insta/OF Prompt Pair` metadata, the naturalizer emits a
single combined natural caption with the shared descriptor plus separate
softcore and hardcore side descriptions. It uses the final selected
expression and composition from the generated rows, including any expression
pool and intensity settings.
Set `target=softcore` or `target=hardcore` to emit only one side of the pair for
training captions or formatter chains.

Naturalizer controls:

- `input_hint`: `auto`, `metadata_json`, or `caption_or_prompt`.
- `target`: `auto` keeps the combined pair caption; `single`, `softcore`, and
  `hardcore` mirror the formatter target controls.
- `caption_profile`: `manual_controls` keeps the detail/style/trigger widgets
  authoritative; `training_concise`, `training_dense`, and `browsing` apply
  preset caption behavior.
- `detail_level`: `concise`, `balanced`, or `dense`.
- `style_policy`: `drop_style_tail` removes old fixed style tails; `keep_style_terms`
  keeps style descriptions in the rewritten text.
- `trigger`: defaults to `sxcppnl7`.
- `include_trigger`: prepends the trigger as its own sentence.

It outputs:

- `natural_caption`
- `method`

`SxCP Krea2 Formatter` rewrites an existing prompt or `metadata_json` into a
Krea2-oriented natural-language paragraph. It is a formatter, not a safety or
content downgrade pass: hardcore items, role graphs, sexual pose wording, and
camera controls are preserved. Negative prompts stay separate.

Important behavior:

- Concrete age wording is preserved deliberately. Phrases like
  `25-year-old adult woman` and `late 60s adult man` are kept because they help
  avoid unwanted young-looking outputs, while generic adult boilerplate is
  omitted from the Krea2 rewrite.
- Trigger words are removed by default because Krea2 prompting generally reads
  better as natural language. Enable `preserve_trigger` if you still need a LoRA
  trigger in the positive prompt.
- `style_mode`: `preserve` keeps the current generated style text,
  `photographic` converts the style tail toward creator-photo language, and
  `minimal` omits most style text.
- For Insta/OF paired metadata, the node returns both `krea_softcore_prompt` and
  `krea_hardcore_prompt`, with separate softcore and hardcore negatives.
- Insta/OF cast metadata is rewritten as direct named-character prose such as
  `Woman A is ...` and `Man A is ...`, so Krea2 does not have to interpret a
  `Cast descriptors:` label.
- Man slots set to `presence_mode=pov` are not emitted as visible cast prose.
  The formatter keeps them in the role graph, rewrites the action from the
  first-person viewer position, and adds a POV camera sentence.

It outputs:

- `krea_prompt`
- `negative_prompt`
- `krea_softcore_prompt`
- `krea_hardcore_prompt`
- `softcore_negative_prompt`
- `hardcore_negative_prompt`
- `method`

`SxCP Insta/OF Prompt Pair` is a special paired-output mode. It creates one
primary creator descriptor internally, then returns both a softcore prompt and a
hardcore prompt from that descriptor. This is useful when you want matching
person/look/scene continuity but need two different prompt strengths.

When the hardcore cast includes partners, pair mode also creates deterministic
cast descriptors such as `Woman A` and `Man A`. Use
`softcore_cast=same_as_hardcore`, `hardcore_cast=couple`, and
`continuity=same_creator_same_room` when you want both outputs to reuse the cast
and location. The generated positive prompts are still standalone: each output
lists the relevant cast descriptors directly and does not depend on the image
model carrying context from another prompt.

For per-character control, chain `SxCP Woman Slot` and `SxCP Man Slot` nodes
into the pair node's `character_cast` input. The nearest woman slot controls
the shared primary creator (`Woman A`) in both softcore and hardcore outputs;
additional woman/man slots fill partner descriptors before random fallback
descriptors are used.

For POV pair prompts, set the relevant `SxCP Man Slot` to
`presence_mode=pov`. The softcore output frames the primary creator from that
participant's camera, while the hardcore output keeps the same cast and scene
but converts the role graph into first-person positioning.

It outputs:

- `softcore_prompt`
- `hardcore_prompt`
- `softcore_negative_prompt`
- `hardcore_negative_prompt`
- `softcore_caption`
- `hardcore_caption`
- `shared_descriptor`
- `metadata_json`

The positive Insta/OF outputs do not embed an `Avoid:` section; use the matching
negative prompt outputs when wiring the sampler.

`SxCP Insta/OF Options` outputs `options_json`, which can be connected to the
pair node. Defaults are set so the softcore prompt is solo while the hardcore
prompt can include partners. Softcore defaults to handheld selfie framing;
hardcore defaults to `from_camera_config`, which emits no camera sentence unless
a camera config is connected or you select an explicit hardcore camera mode.
For stronger camera control, connect `SxCP Camera Control` or
`SxCP Camera Orbit Control` to the pair node's optional `camera_config` input.
For different softcore and hardcore camera views, connect separate camera
configs to `softcore_camera_config` and `hardcore_camera_config`; those override
the shared `camera_config` for their side.

Options:

- `softcore_cast`: `solo` or `same_as_hardcore`.
- `hardcore_cast`: `use_counts`, `couple`, `threesome`, or `group`.
- `hardcore_women_count` and `hardcore_men_count`: used when `hardcore_cast` is
  `use_counts`. The pair mode always keeps at least one adult woman as the
  primary creator so the shared descriptor remains valid.
- `softcore_level`: `social_tease`, `lingerie_tease`, `implied_nude`,
  `explicit_tease`, or `explicit_nude`. Insta/OF softcore uses dedicated outfit
  pools so teaser prompts do not randomly pull hardcore-adjacent harness,
  microwear, or shirtless partner styling. `explicit_nude` is available when
  you want visible nude creator-shot framing without a sex act.
- `hardcore_level`: `explicit` or `hardcore`.
- `softcore_expression_intensity`: fallback when no connected character slot
  sets `softcore_expression_intensity`. `0.0` is mild/controlled, `0.5` is sensual,
  `1.0` strongly favors more heated softcore faces.
- `hardcore_expression_intensity`: fallback when no connected character slot
  sets `hardcore_expression_intensity`. `0.0` is controlled, `0.5` is balanced
  hardcore, `1.0` strongly favors stronger hardcore expressions.
- `softcore_expression_enabled` and `hardcore_expression_enabled`: disable the
  expression sentence for that half of the Insta/OF pair. The intensity values
  are fallbacks; `SxCP Woman Slot` / `SxCP Man Slot` `expression_intensity`
  overrides them when character slots are connected, and a disabled slot omits
  that character's expression.
- `platform_style`: `hybrid`, `instagram`, or `onlyfans`.
- `continuity`: `same_creator_same_room` keeps the scene aligned while each
  output keeps its own pose/composition; `same_creator_new_scene` keeps the same
  creator descriptor but lets the hardcore scene use its own setting.
- `hardcore_clothing_continuity`: `none`, `same_outfit`, `partially_removed`,
  `implied_nude`, or `explicit_nude`. This controls whether the hardcore prompt
  references the softcore outfit, uses it displaced/removed, or makes Woman A
  explicitly nude. It is a fallback for Woman A; `hardcore_clothing` on
  `SxCP Woman Slot` or `SxCP Man Slot` takes priority for that character.
- `softcore_camera_mode`: `from_camera_config` or a base camera mode for the
  softcore output. The default is still `handheld_selfie`.
- `hardcore_camera_mode`: `from_camera_config`, `same_as_softcore`, or a
  separate base camera mode for the hardcore output. `from_camera_config` is
  neutral with no connected camera config, and uses `SxCP Camera Control` or
  `SxCP Camera Orbit Control` when one is connected.
- Pair camera inputs: use shared `camera_config` for one camera on both sides,
  or use `softcore_camera_config` and `hardcore_camera_config` for two separate
  Qwen/Orbit camera controls.
- `camera_detail`: `from_camera_config`, `off`, `compact`, or `full` for the
  pair prompt camera text. Use `from_camera_config` with `SxCP Qwen Camera
  Translator` so the translator's own detail setting is preserved.
- `hardcore_detail_density`: `compact` keeps the Krea hardcore rewrite mostly
  to the position/action sentence, `balanced` keeps one useful non-duplicated
  motion or aftermath detail, and `dense` keeps more detail after dedupe. This
  is separate from expression intensity.

## Built-In Categories

The node keeps the original generator controls:

- `category`: `auto_weighted`, `auto_full`, `woman`, `man`, `couple`,
  `group_or_layout`, `custom_random`, or a custom JSON category.
- `clothing`: `random`, `full`, or `minimal`.
- `minimal` is the local adult wardrobe pool. It can roll sheer mesh,
  see-through lace, transparent layers, body tape, micro pieces, and exposed
  nipple wording; it is not limited to older softcore-safe euphemisms.
- `minimal_clothing_ratio`: `-1` disables ratio mixing; `0.0` to `1.0` mixes minimal/full clothing when `clothing` is fixed.
- `ethnicity`: `any`, `european`, `mediterranean_mena`, `latina`,
  `east_asian`, `southeast_asian`, `south_asian`, `black_african`,
  `indigenous`, `mixed`, `asian`, or `white_asian`. Combined filter strings
  such as `latina+south_asian` are also accepted in config JSON.
- `poses`: `random`, `standard`, or `evocative`.
- `expression_enabled`: disable facial-expression text entirely for this row.
- `expression_intensity`: `-1` randomizes per row; `0.0` favors mild, neutral, controlled expressions;
  `0.5` favors balanced category expressions; `1.0` strongly favors the most
  intense expressions available in the selected category. This affects custom
  JSON categories such as `Provocative erotic clothes` and `Hardcore sexual
  poses`.
- `standard_pose_ratio`: `-1` disables ratio mixing; `0.0` to `1.0` mixes standard/evocative poses when `poses` is fixed.
- `backside_bias`: `0.0` to `1.0`, applies to evocative single-subject poses.
- `figure`: `random`, `curvy`, `balanced`, `bombshell`.
- In split workflows, use `SxCP Advanced Filters` checkboxes instead of negative
  toggles. Black/African and plus-size are positive include choices there.
- Optional `camera_config`: connect `SxCP Camera Control` or
  `SxCP Camera Orbit Control` to force selfie, phone, lens, angle, numeric
  orbit, crop, and camera-priority behavior. This applies to custom categories
  too, including `Hardcore sexual poses`.

`auto_weighted` uses the original batch mix: mostly women, then men, couples, and
group/layout rows. `auto_full` keeps that original random source in the pool but
also rolls the JSON categories, so it can land on casual, erotic, or hardcore
categories from the same easy all-in-one node. `custom_random` rolls only JSON
categories. Direct categories generate only that selected category.

## Custom Categories

Add or edit JSON files in `categories/*.json`. Each file can define new main
categories, subcategories, and optional extensions to the built-in pools. Restart
or reload ComfyUI after changing JSON so dropdown choices are rebuilt.

Included JSON categories:

- `Casual clothes`
- `Men casual clothes`
- `Couple casual clothes`
- `Provocative erotic clothes`
- `Hardcore sexual poses`

Example:

```json
{
  "version": 1,
  "categories": [
    {
      "name": "Casual clothes",
      "slug": "casual_clothes",
      "subject_type": "woman",
      "item_label": "Clothing",
      "style": "realistic casual social-feed photo with everyday styling",
      "subcategories": [
        {
          "name": "Streetwear",
          "slug": "streetwear",
          "items": [
            "oversized hoodie with slim jeans and clean sneakers",
            "cropped bomber jacket with cargo pants and chunky trainers"
          ],
          "scenes": [
            {
              "slug": "city_crosswalk",
              "prompt": "sunlit city crosswalk with storefront reflections"
            }
          ],
          "poses": [
            "standing with one hand in a pocket",
            "walking forward with a casual runway stride"
          ]
        }
      ]
    }
  ]
}
```

Custom categories do not need a Python generator. If no `prompt_template` is
provided, the node uses a generic composer that selects subject appearance,
scene, pose, expression, composition, and a random item from the selected
subcategory.

Reusable banks can be defined with top-level `scene_pools`,
`expression_pools`, and `composition_pools` in any `categories/*.json` file.
Categories, subcategories, and items can reference them with `scene_pools`,
`expression_pools`, and `composition_pools`; referenced pools are merged with
any local `scenes`, `expressions`, or `compositions`. This keeps expansion
scalable without duplicating the same bedroom, selfie, mirror, creator,
expression, camera, or group-sex framing across every subcategory.

Set `"inherit_scenes": false`, `"inherit_expressions": false`, or
`"inherit_compositions": false` on a subcategory or item when it should use only
its own pools instead of also inheriting parent category values. This is useful
for narrow subcategories such as group scenes, fetish sets, outdoor-only sets,
or any category where generic parent wording would be a bad match.

Example:

```json
{
  "expression_pools": {
    "creator_tease_faces": [
      "direct creator-shot eye contact",
      "heavy-lidded bedroom gaze"
    ]
  },
  "composition_pools": {
    "creator_selfie_frames": [
      "handheld selfie crop from face to hips",
      "mirror selfie with phone visible and body framed clearly"
    ]
  },
  "scene_pools": {
    "creator_selfie_rooms": [
      {
        "slug": "bedroom_phone_tripod",
        "prompt": "private creator bedroom with a phone tripod, rumpled bedding, and warm lamps"
      }
    ]
  },
  "categories": [
    {
      "name": "Example",
      "subcategories": [
        {
          "name": "Selfie set",
          "inherit_scenes": false,
          "inherit_expressions": false,
          "inherit_compositions": false,
          "scene_pools": ["creator_selfie_rooms"],
          "expression_pools": ["creator_tease_faces"],
          "composition_pools": ["creator_selfie_frames"],
          "items": ["simple outfit prompt"]
        }
      ]
    }
  ]
}
```

For large categories, prefer `item_templates` plus `item_axes` instead of writing
every final item by hand:

```json
{
  "name": "Example clothes",
  "subject_type": "woman",
  "subcategories": [
    {
      "name": "Lingerie",
      "item_templates": [
        "{color} {fabric} {top} with {bottom} and {stockings}"
      ],
      "item_axes": {
        "color": ["black", "red", "ivory"],
        "fabric": ["lace", "satin", "transparent mesh"],
        "top": ["balconette bra", "open-cup bra"],
        "bottom": ["matching thong", "high-cut g-string"],
        "stockings": ["thigh-high stockings", "fishnet stockings"]
      }
    }
  ]
}
```

The node chooses one template, fills each placeholder from the matching axis,
then records the selected axis values in `metadata_json`.

Supported `subject_type` values:

- `single_any`
- `woman`
- `man`
- `couple`
- `group`
- `layout`
- `scene`
- `configured_cast`

`configured_cast` uses the node's `women_count` and `men_count` inputs. It adds
template fields for `{women_count}`, `{men_count}`, `{person_count}`,
`{cast_summary}`, `{scene_kind}`, and `{role_graph}`. This is intended for
categories where the same prompt generator should support couples, threesomes,
and larger groups.

`{role_graph}` is a generated choreography sentence that assigns roles to the
cast, such as who penetrates, who receives oral, and who joins from the side.
It is currently most useful for `Hardcore sexual poses`.

For `Hardcore sexual poses`, `SxCP Hardcore Position Pool` also includes an
`outercourse` family and position checkboxes for `boobjob`, `testicle_sucking`,
`penis_licking`, and `footjob`. `SxCP Hardcore Action Filter` exposes
`outercourse_only` and `allow_outercourse` so these can be selected separately
from oral or penetration. If a man slot is set to `presence_mode=pov`, these
positions emit first-person wording from that participant's viewpoint.

Subcategories, templates, and axis values can declare cast constraints:

```json
{
  "name": "Threesomes",
  "min_people": 3,
  "item_axes": {
    "act": [
      {
        "text": "strap-on penetration and cunnilingus",
        "cast": "women_only"
      },
      {
        "text": "male/male oral and anal contact",
        "cast": "men_only"
      },
      {
        "text": "front-and-back penetration",
        "cast": "mixed"
      }
    ]
  }
}
```

Supported constraints:

- `min_women`, `max_women`
- `min_men`, `max_men`
- `min_people`, `max_people`
- `cast` or `requires`: `women_only`, `men_only`, `mixed`, `has_women`,
  `has_men`, `solo`, `couple`, `threesome`, `group`

If an exact subcategory has a larger minimum cast size than the current
`women_count` and `men_count`, the node raises the effective cast count to that
minimum instead of failing. The original and effective counts are recorded in
`metadata_json.cast_count_adjustment`. Other impossible cast constraints still
raise a clear error instead of generating an impossible prompt.
When both cast counts are `0`, custom category selection treats the effective
configured cast as one adult woman so random filtering still has a valid cast.

Use the `subcategory` dropdown to select either `random` or an exact
`Main category / Subcategory` path. Exact paths override the `category` dropdown,
which is useful because ComfyUI does not provide dependent dropdowns from Python
alone.

## Seed Control

The main `seed` input is still the default master seed. Connect `SxCP Seed
Control` to `seed_config` when you want to lock or vary specific axes. Each
axis has its own mode plus seed value:

- `auto`: legacy behavior; `-1` follows the main seed, `0` or higher fixes that
  axis to the entered value.
- `follow_main`: always follows the final generator's main `seed` input and
  ignores the entered axis seed.
- `fixed`: always uses the entered axis seed.
- `random`: generates a fresh resolved axis seed when the workflow queues.

The `summary` output lists the resolved value for every axis, including random
axes whose visible widget value remains `-1`. The `Lock Random Seeds Now` button
turns every current `random` axis into a visible concrete seed and switches
those axes to `fixed`.

For exact prompt reproduction, `SxCP Global Seed` is the shortest path:

- Connect `seed` to the generator `seed` input.
- Connect `seed_config` to the generator `seed_config` input.
- Keep character `slot_seed=-1` when that character should follow the global
  person seed; set `slot_seed` only when that character should be independently
  pinned.

For normal prompt iteration, `SxCP Seed Locker` is usually simpler:

- `base_seed`: the seed whose character/location/etc. you want to keep.
- `reroll_axis`: `none`, `content`, `person`, `scene`, `pose`, `role`,
  `expression`, `composition`, `content_pose`, or `scene_pose`.
- `reroll_seed`: `-1` makes the selected axis follow the main prompt seed;
  `0` or higher pins that selected axis to a specific seed.

Seed values in `auto` mode:

- `-1`: follow the main seed.
- `0` or higher: override only that axis.

Axes:

- `category_seed`: category selection when `auto_full` or `custom_random` is
  used; also drives `SxCP Cast Bias` when its `seed_config` input is connected.
- `subcategory_seed`: random subcategory selection.
- `content_seed`: generated item content, such as outfit wording.
- `person_seed`: appearance/person selection.
- `scene_seed`: scene/environment selection.
- `pose_seed`: body pose selection. For `Hardcore sexual poses`, this also drives the generated sexual pose content.
- `role_seed`: participant choreography for `{role_graph}`. If left at `-1`, it follows `pose_seed`.
- `expression_seed`: facial expression selection.
- `composition_seed`: camera/composition selection.

Example workflow: if the person and scene are right but the pose is wrong, keep
`person_seed` and `scene_seed` fixed, then change `pose_seed`. If the cast roles
are wrong but the act wording is good, change `role_seed`. If the clothing or
sexual act wording is wrong, change `content_seed`; for pose-driven categories,
change `pose_seed`.

## Pool Extensions

Use `pool_extensions` to add new entries to built-in pools without editing
Python:

```json
{
  "pool_extensions": {
    "women_clothes": ["relaxed high-waist jeans with a fitted ribbed tank top"],
    "men_clothes": ["clean white T-shirt with relaxed jeans and canvas sneakers"],
    "poses": ["standing with a relaxed hand-on-hip pose"],
    "expressions": ["easygoing half-smile"],
    "scenes": [
      {
        "slug": "city_cafe",
        "prompt": "quiet city cafe terrace with morning light and small round tables"
      }
    ]
  }
}
```

Known extension pools:

`women_clothes`, `women_clothes_minimal`, `men_clothes`, `men_clothes_minimal`,
`couple_outfits`, `couple_outfits_minimal`, `poses`, `evocative_poses`,
`backside_poses`, `expressions`, `compositions`, `props`, `figure_curvy`,
`figure_athletic`, `figure_bombshell`, `scenes`, `group_scenes`,
`layouts_full`, `layouts_minimal`, `group_compositions`, `group_ages`.

## Templates

A category, subcategory, or individual item can provide `prompt_template` and
`caption_template`. Templates can use these fields:

`{trigger}`, `{main_category}`, `{subcategory}`, `{item}`, `{item_label}`,
`{subject}`, `{subject_phrase}`, `{age}`, `{body}`, `{body_phrase}`, `{skin}`,
`{hair}`, `{eyes}`, `{figure}`, `{scene}`, `{pose}`, `{expression}`,
`{composition}`, `{style}`, `{positive_suffix}`, `{negative_prompt}`,
`{women_count}`, `{men_count}`, `{person_count}`, `{cast_summary}`,
`{scene_kind}`, `{role_graph}`.

If `prepend_trigger_to_prompt` is enabled, the node prepends the trigger to the
positive prompt. Disable it for output closer to the original script's `prompt`
field.
