Nodes/Krea2 Two Character Composer/Krea2 Two Character Composer
ComfyUI Node

Krea2 Two Character Composer

Two Characters, Two LoRAs, One Krea 2 Image — Without the Face Merge

By cyberdeliaAI·Created 26 days ago·Updated 26 days ago· 0
Krea2 Two Character Composer
  • model
  • clip
  • latent
  • character_1_mask
  • character_2_mask
  • model
  • positive
  • latent
  • region_masks
  • negative
  • prompts
◄base_prompt►
◄negative_prompt►
◄character_1_trigger►
◄character_1_prompt►
◄character_1_loraNone►
◄character_1_strength1.00►
◄character_2_trigger►
◄character_2_prompt►
◄character_2_loraNone►
◄character_2_strength1.00►
◄directionLeft / Right►
◄character_1_percent50►
◄separationBalanced►
◄boxes5, 10, 42, 85; 53, 10, 42, 85►
◄lora_edge_softness0►
◄base_strength1.00►
◄preview_ratioAuto►
◄width—►
◄height—►

Load two character LoRAs into one prompt and you get the classic result: one face, two hairstyles, and a jacket that can't decide whose it is. Regional prompting is the standing fix, and the one technique that never went away - 20 to 40 threads a month since 2023, because no model really solves attribute bleeding. The tooling churns instead: every architecture shift breaks it and someone rebuilds the pack. Krea 2 is a 12B single-stream DiT with a Qwen3-VL encoder, so the SD-era tricks don't port. This is that rebuild - one node, two regions, two LoRAs, one pass. Krea 2 handles plain placement fine; you need this when identity comes from two LoRAs that each have to own a body.

How it works

All three prompts - shared scene, character 1, character 2 - are encoded separately, then concatenated into one Krea 2 text sequence. Because Krea 2 is single-stream, text and image tokens pass through every attention op together, so the engine injects a joint attention mask: image tokens inside a region read that region's prompt plus the shared base prompt, and nothing else. The LoRAs are never merged into the weights - each eligible Linear gets a class swap that adds mask * scale * (B A x) on top of the base projection, so the LoRA delta is exactly zero outside its region. That's a hard spatial guarantee, not an attention nudge the model can ignore.

Two details matter when prompting. The node writes the placement sentence itself (On the left side of the image: …, On the right side: …) from where the masks actually sit, so a 75/25 split still reads "left" and "right". And triggers stay out of that sentence, pushed to the front of each character prompt, because Krea 2 renders stray trigger tokens as literal text on the canvas.

Inputs that matter

base_prompt is the scene - background, lighting, style; both regions read it, so keep characters out of it. Each character gets a character_N_trigger (its LoRA's trigger, usually required), a character_N_prompt (appearance, outfit, pose - only that region reads it), and character_N_lora / character_N_strength. Strength 0 also switches a LoRA off.

direction picks Left / Right, Top / Bottom or Free boxes, and character_1_percent sets the area split - 60 gives Character 1 the left 60%. That's image area, not how big the person looks; describe size in the prompts. With Free boxes, drag them in the preview or type x, y, width, height; x, y, width, height into boxes, in percent. On overlap, Character 1 owns the prompt region - priority, not a promise it draws them in front.

separation I'd leave at Balanced. Strong blocks cross-region image attention for the first half of sampling; Soft is experimental and releases prompt isolation for the last 40% so the figures can knit together. lora_edge_softness fades the two LoRAs into each other at their boundary - 0 gives the strongest separation, so try 32 only when a seam looks ugly. base_strength nudges how much each area follows the base prompt.

Wiring

model goes straight into the sampler - no other model patch in between, that's where the regional wrappers live. positive and negative take the usual sampler slots; negative is a matching empty negative, a formality at CFG 1 but the safe default. latent is your latent passed through, region_masks is both masks as a batch for previewing, and prompts is the exact encoded text - wire it to Preview Any while tuning.

Krea 2 MODEL ──┐
Krea 2 CLIP ───┼── Two Character Composer ─ model ──── KSampler.model
Krea 2 LATENT ─┘                          ├ positive ─ KSampler.positive
                                          ├ negative ─ KSampler.negative
                                          └ latent ─── KSampler.latent_image

Feed it from EmptySD3LatentImage (despite the name, it makes the 16-channel latent Krea 2 wants) and CLIPLoader at type krea2.

Install

Search "Krea2 Two Character Composer" in ComfyUI Manager, or:

cd ComfyUI/custom_nodes
git clone https://github.com/cyberdeliaAI/comfyui-krea2-two-character-composer.git

Then restart. The pleasant surprise: pyproject.toml declares zero dependencies and nothing downloads at runtime, so this isn't one of those installs that quietly wrecks your Python environment. You do need a ComfyUI with native Krea 2 support - the node refuses with "This ComfyUI version has no native Krea 2 support" instead of failing deep in the sampler. Optional: comfyui-negpip-zimage, without which negative_prompt does nothing.

Where people get burned

The big one is a LoRA that isn't a Krea 2 LoRA. It loads, the menu lists it, and nothing happens - the engine looks for Krea 2 layer names and logs "keys didn't match any layer… is this a Krea 2 LoRA?" to console. If your character doesn't change, read the log before rewriting the prompt.

A negative prompt with no effect is also correct at CFG 1 - no unconditional pass to steer, which is exactly why NegPiP exists. Worth knowing: people running it on Krea 2 report it lands on simple prompts and gets ignored as prompts get long, so positive framing usually wins on a dense two-character prompt.

Bleeding identities: keep every distinguishing feature in its own field, use edge softness 0 and try Strong, and check you haven't loaded a character LoRA before the composer - those stay global and leak over both figures. The node also errors clearly when a mask is too thin for a 16px token block, or when Character 1's box swallows Character 2 whole; widen the mask rather than wondering what broke. Krea 2-only, stills only, batches included.

Categoryconditioning/krea2_regional

Inputs (24)

NameTypeDefaultDescription
modelMODEL—
clipCLIP—
latentLATENT—
base_promptSTRINGRead by the whole image. A placement sentence for both characters is added automatically.
negative_promptSTRINGCompiled to negative weights by the installed NegPiP Prompt pack (comfyui-negpip-zimage). Works at CFG 1. Leave empty if you don't use NegPiP.
character_1_triggerSTRINGGoes first in this character's prompt and is kept out of the shared scene prompt, so Krea 2 doesn't render it as text.
character_1_promptSTRINGOnly this region reads this prompt. With NegPiP installed, negative weights like (beard:-0.6) apply to this character only.
character_1_loraCOMBONone1 options: None
character_1_strengthFLOAT1.00-10–10—
character_2_triggerSTRINGGoes first in this character's prompt and is kept out of the shared scene prompt, so Krea 2 doesn't render it as text.
character_2_promptSTRINGOnly this region reads this prompt. With NegPiP installed, negative weights like (beard:-0.6) apply to this character only.
character_2_loraCOMBONone1 options: None
character_2_strengthFLOAT1.00-10–10—
directionCOMBOLeft / RightSplit left/right or top/bottom, or place two Free boxes. A connected character mask replaces that character's area.
character_1_percentINT501–99Image area for Character 1 (left or top). Character 2 gets 100 minus this value.
separationCOMBOBalancedBalanced preserves scene interaction. Strong also restricts cross-region image attention for the first half of sampling. Soft (experimental) lets the whole image read both character prompts for the last 40% of sampling.
boxesoptSTRING5, 10, 42, 85; 53, 10, 42, 85Free boxes: 'x, y, width, height; x, y, width, height' in percent of the image, Character 1 first. Drag them in the preview. Character 1 is in front where boxes overlap.
lora_edge_softnessoptINT00–256Pixels over which the two character LoRAs fade into each other at their boundary. The prompts stay separated. 0 = hard edge.
base_strengthoptFLOAT1.000.05–2Experimental. How strongly the characters' areas follow the base prompt compared to their own prompt. 1.0 = unchanged.
character_1_maskoptMASKOptional. Replaces Character 1's area. Of a batch, the first mask is used.
character_2_maskoptMASKOptional. Replaces Character 2's area. Of a batch, the first mask is used.
preview_ratiooptCOMBOAutoPreview only: choose a fixed ratio before sampling, or Auto to read connected width/height or a recognizable latent resolution. Generation dimensions come from the LATENT.
widthoptINTOptional preview width. Connect together with height. Does not resize the latent.
heightoptINTOptional preview height. Connect together with width. Does not resize the latent.

Outputs (6)

NameTypeDescription
modelMODELPatched Krea 2 model. Connect directly to the sampler.
positiveCONDITIONINGRegional positive conditioning.
latentLATENTThe input latent, passed through.
region_masksMASKThe two region masks as a batch, for previewing.
negativeCONDITIONINGMatching empty negative for the sampler (only used at CFG > 1).
promptsSTRINGThe scene and character prompts exactly as encoded.