Krea2 Two Character Composer
Two Characters, Two LoRAs, One Krea 2 Image — Without the Face Merge
- model
- clip
- latent
- character_1_mask
- character_2_mask
- model
- positive
- latent
- region_masks
- negative
- prompts
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.
Inputs (24)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| clip | CLIP | — | |
| latent | LATENT | — | |
| base_prompt | STRING | Read by the whole image. A placement sentence for both characters is added automatically. | |
| negative_prompt | STRING | Compiled 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_trigger | STRING | Goes 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_prompt | STRING | Only this region reads this prompt. With NegPiP installed, negative weights like (beard:-0.6) apply to this character only. | |
| character_1_lora | COMBO | None | 1 options: None |
| character_1_strength | FLOAT | 1.00-10–10 | — |
| character_2_trigger | STRING | Goes 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_prompt | STRING | Only this region reads this prompt. With NegPiP installed, negative weights like (beard:-0.6) apply to this character only. | |
| character_2_lora | COMBO | None | 1 options: None |
| character_2_strength | FLOAT | 1.00-10–10 | — |
| direction | COMBO | Left / Right | Split left/right or top/bottom, or place two Free boxes. A connected character mask replaces that character's area. |
| character_1_percent | INT | 501–99 | Image area for Character 1 (left or top). Character 2 gets 100 minus this value. |
| separation | COMBO | Balanced | Balanced 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. |
| boxesopt | STRING | 5, 10, 42, 85; 53, 10, 42, 85 | Free 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_softnessopt | INT | 00–256 | Pixels over which the two character LoRAs fade into each other at their boundary. The prompts stay separated. 0 = hard edge. |
| base_strengthopt | FLOAT | 1.000.05–2 | Experimental. How strongly the characters' areas follow the base prompt compared to their own prompt. 1.0 = unchanged. |
| character_1_maskopt | MASK | Optional. Replaces Character 1's area. Of a batch, the first mask is used. | |
| character_2_maskopt | MASK | Optional. Replaces Character 2's area. Of a batch, the first mask is used. | |
| preview_ratioopt | COMBO | Auto | Preview 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. |
| widthopt | INT | Optional preview width. Connect together with height. Does not resize the latent. | |
| heightopt | INT | Optional preview height. Connect together with width. Does not resize the latent. |
Outputs (6)
| Name | Type | Description |
|---|---|---|
| model | MODEL | Patched Krea 2 model. Connect directly to the sampler. |
| positive | CONDITIONING | Regional positive conditioning. |
| latent | LATENT | The input latent, passed through. |
| region_masks | MASK | The two region masks as a batch, for previewing. |
| negative | CONDITIONING | Matching empty negative for the sampler (only used at CFG > 1). |
| prompts | STRING | The scene and character prompts exactly as encoded. |