Unblend Dynamic Sampler (Bounded Attention)
The Unblend Dynamic Sampler
- model
- clip
- latent_image
- vae
- latent
- char_masks
- mask_preview
- info
- model
- positive
- negative
Prompt two characters, hit generate, and one of three things happens: the hair colors mix, the eyepatch migrates to the wrong face, or you get twins - two people who have clearly spent their lives sharing one face. The closer they're posed, the worse it gets. UnblendDynamicSampler is this pack's answer to that, and it's the node you'll build your whole workflow around.
Its display name, Bounded Attention, is the honest one. The sampler watches where each character's tokens are attending in the model's own cross-attention maps, rebuilds a live mask for every [CHARn] every few steps, and keeps those tokens locked inside their own zone. No hand-drawn masks, no regional-prompting spaghetti. The README demos it on a deliberately brutal case - Ningguang × Shenhe, two white-haired Genshin characters, exactly where a plain sampler blurs identities into mush.
How it works
For the first chunk of steps (warmup_at, default 0.15 of the schedule) the characters form anywhere. After that, every refresh_every steps the node reads the cross-attention maps, thresholds them (map_threshold), and rebuilds each character's mask from where its tokens actually are. Then it constrains sampling so CHAR1's tokens only influence CHAR1's pixels. Two knobs tune the strictness:
identity_lock- bonus attention to a character's own distinctive tokens inside its zone. 0 = off, 1 = moderate, up to 4 if you're feeling strict. Crank it when signatures keep leaking to the wrong face.self_attn_jail/jail_strength- cuts self-attention between characters so they stop peeking at each other. Sounds great, but here's the trap: hugs and touches form through cross-body attention. For[INTERACT]scenes, leaveearly_jailoff.
The real input, though, is the prompt. Unblend uses tagged prompts - [QUALITY], [STYLE], [SCENE], [CHAR1]/[CHAR2], [INTERACT] - and the author's own bug list (in PROMPTING.md) gives you three rules that save most of the pain:
- Keep each
[CHARn]compact, ~35–45 tokens. A long one breaks across the 77-token CLIP boundary and you getCHARn tokens not foundin the log - a square instead of a silhouette. - Put the other character's name only in
[INTERACT], never inside a[CHARn], or it binds to the wrong mask and you're back to identity mixups. - Don't write
1girlyourself - the node sets the count. Mixed cast? Putgirl, boyinsubject_wordand each character keeps its own gender.
What comes out
This node is a pipeline in one box. latent is your generated image - decode and look at it. char_masks and mask_preview are the masks it built along the way. The interesting pair is model and positive: a couple+jail-patched model and the conditioning that goes with it, intended for the detail pass. That's the hand-off to the second stage - either a manual hi-res KSampler or, per the intended pipeline, Sequential Refine. There's also a solo fallback: give it zero or one [CHARn] and it plain-samples without bounded attention, no crash, outputs still compatible with the graph.
Install
The pack installs through ComfyUI Manager - search "Unblend", Install, restart, dependencies handled. Manual:
cd ComfyUI/custom_nodes
git clone https://github.com/LatentDesireAI/ComfyUI-Unblend
pip install -r ComfyUI-Unblend/requirements.txt
Then restart. This is an SDXL / Illustrious node: the code runs an SDXL check on every execution, so a Flux checkpoint will just fail. The dghs-imgutils / dghs-realutils deps do the silhouette and head detection and pull their ONNX weights automatically on first run - expect a short download the first time.
Troubleshooting
Read the info string every run. A healthy one says characters: N with no CHARn tokens not found. If your masks come back as a clean 50/50 vertical split, that's the column fallback (init_ratios) kicking in because char_points is empty - set anchors like 0.3,0.45; 0.7,0.45 (normalized, in CHARn order). If identities still swap, the characters need more contrast - different hair, eyes, clothes, the things the model tells people apart by. If the style drifts between scenes, it's almost always [STYLE], not the sampler. And if it all just works, enjoy it - this is the node that makes the rest of the pack worth having.
Inputs (24)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| clip | CLIP | — | |
| latent_image | LATENT | — | |
| prompt | STRING | [QUALITY] masterpiece, best quality [STYLE] anime style [SCENE] simple background [CHAR1] columbina (genshin impact), black hair, white dress [CHAR2] fischl (genshin impact), blonde hair, eyepatch [INTERACT] embracing, face to face | — |
| negative_prompt | STRING | lowres, worst quality, low quality, bad anatomy, bad hands | — |
| seed | INT | 00–18446744073709550000 | — |
| steps | INT | 328–200 | — |
| cfg | FLOAT | 5.50.5–20 | — |
| sampler_name | COMBO | 44 options: euler, euler_cfg_pp, euler_ancestral, euler_ancestral_cfg_pp, heun, heunpp2, +38 | |
| scheduler | COMBO | 9 options: simple, sgm_uniform, karras, exponential, ddim_uniform, beta, +3 | |
| warmup_at | FLOAT | 0.150.03–0.6 | — |
| refresh_every | INT | 41–16 | — |
| mask_source | COMBO | 2 options: attn_maps+silhouette, attn_maps | |
| map_threshold | FLOAT | 0.350.05–0.95 | — |
| bound_strength | FLOAT | 1.000–1 | — |
| self_attn_jail | BOOLEAN | true | — |
| jail_strength | FLOAT | 1.000–1 | — |
| identity_lock | FLOAT | 1.000–4 | — |
| subject_word | STRING | girl | Booru count word. One word = all characters (e.g. 'girl'). Comma list for a mixed cast: 'girl, boy' -> CHAR1 girl, CHAR2 boy (padded with the last). |
| divider | COMBO | 2 options: watershed, nearest_center | |
| init_ratios | STRING | 1,1 | — |
| char_points | STRING | — | |
| early_jail | BOOLEAN | false | — |
| vaeopt | VAE | — |
Outputs (7)
| Name | Type | Description |
|---|---|---|
| latent | LATENT | result latent |
| char_masks | MASK | character masks |
| mask_preview | IMAGE | mask preview |
| info | STRING | log |
| model | MODEL | patched model (couple+jail with the final masks) — for the detail/hires KSampler; masks recompute themselves for the new size |
| positive | CONDITIONING | draw conditioning (REQUIRED as the second KSampler's positive — the bias is tied to its token positions) |
| negative | CONDITIONING | negative (for convenience) |