Y7 Flux.2 Klein Edit Multi-Ref
Multi-reference Flux.2 Klein edits without the conditioning spaghetti
- vae
- ref_images
- external_mask
- positive
- negative
- reference_latent
- positive
- negative
- preview_image
- preview_mask
- debug
You've got a Klein checkpoint, a photo you want to change, and a couple more images holding the answer - a character sheet, the shirt, the room you're copying. ComfyUI's official multi-reference templates make you hand-patch reference_latents lists and babysit resize nodes. Y7 Flux.2 Klein Edit Multi-Ref collapses that into one node: pick the image being edited on the node, paint a mask if you want the change confined, wire the extras in, and it hands your sampler conditioning that's ready to run. It works with every FLUX.2 Klein variant, base or distilled, 4B or 9B.
How Klein actually uses references
Klein's text encoder is a Qwen3-VL model running with its visual tower switched off - it never sees your images. The references only reach the transformer as VAE latents appended to its token sequence, which explains two things:
- There is no reference syntax.
<image1>,[Image 1],@ref2- none of it is a real token. Refer to images in plain English by position: the on-nodeimageis 1,ref_image_2is 2, and so on. Always pair the number with a noun - "the man in Figure 1", not "the man in 1" - because a bare index has nothing to latch onto. - Every reference costs tokens and VRAM. That's the whole reason
ref_megapixelsexists, and why thedebugoutput reports a per-image token cost.
The inputs that matter
vae(required) andimage: wire in your Klein VAE and pick the picture from the input folder. Click into it to open the mask editor - paint there to confine the edit. The loaded image is reference 1.target_megapixelsvsref_megapixels: two separate budgets, both defaulting to 1.0. The first sets your output size - Klein is trained around 1 MP and degrades badly above it, so leave it. The second sizes each reference and never touches output resolution. It's your main VRAM lever - drop it to 0.5 when things get slow or OOM; a face or a style still comes through at half size.ref_image_2…ref_image_8(growable sockets): connect the empty slot and another appears, up to eight references total. They're IMAGE-only, unmaskable, and any alpha they carry is dropped. A socket holding a batch counts as several references.external_mask: an alternative to painting - a mask fromLoad Image (as Mask)or a segmentation node. It's stretched to fit the loaded image, so match the aspect ratio or the edge drifts. Watch the channel:Load Image (as Mask)starts on alpha, which returns an empty mask for a plain black-and-white file - switch it to red.
The masking gotcha that isn't a bug
Klein has no mask input of its own, and this is where people get burned. It's not an inpaint model - its in/out channels match, so ComfyUI drops the concat mask this node sets. The mask survives as the latent's noise_mask; the sampler restores everything outside it after each step, at 1/16 resolution. The settings follow from that:
expand_maskdefaults to 16 - one full latent cell - because a tightly painted edge otherwise loses a sliver to the downsample. Try 24–32 for hair and soft edges.feather_maskstays at 0 for a reason: a feather under ~16px is erased by the latent downsample, and a wider one smears a seam instead of blending. Do soft joins in pixel space after decode instead.binary_mask(on) hard-thresholds the finished mask so half-strength brush strokes don't turn into muddy blends.- Set the sampler's
denoiseto 1.0. The unmasked area is restored every step regardless, so lowering denoise to "protect" it just weakens the edit.
Outputs and wiring
reference_latent is the edited image's latent - feed it to the sampler's latent input. positive and negative come back with the references and mask baked in. preview_image / preview_mask show the resize and mask before you commit, and debug is a text report of every image's size, token cost and mask state - wire it into any text viewer when a result surprises you.
Install
ComfyUI Manager → Custom Nodes Manager → search Y7 or Y7Nodes, or:
cd ComfyUI/custom_nodes
git clone https://github.com/yushan777/ComfyUI-Y7Nodes
Then restart ComfyUI. Two caveats: this node runs on ComfyUI's newer V3 node API (Autogrow sockets, the mask editor), so update ComfyUI if it doesn't show up; and the pack's README predates this node entirely, so treat the tooltips as the manual. Nothing downloads here - you supply the Klein checkpoint, its Qwen3 text encoder, and a VAE as usual. And since Klein is guidance-distilled, keep CFG at 1 (1.2–1.5 if anatomy wanders) with 4–8 Euler steps.
Recurring causes of confusion: the alpha-channel empty mask, expecting a mask where you painted none, and feathering in latent space. Wire debug into a text viewer and the node tells you exactly what happened.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| vae | VAE | — | |
| image | COMBO | 1 options: example.png | |
| target_megapixels | FLOAT | 1.000–16 | How big to make the picture you are editing. A megapixel is a million pixels, so 1.0 resizes it to about a million pixels while keeping its shape - nothing gets stretched or squashed. This decides how big your finished picture comes out. 0 = leave it at its original size. Klein is built for around 1.0; going much higher usually makes things worse rather than better. |
| ref_megapixels | FLOAT | 1.000–16 | How big to make each reference picture. Works exactly like target_megapixels, but these pictures are only there to be looked at, so this does not change how big your finished picture comes out. 0 = leave them at their original size. It applies to each picture separately, so they add up: one picture plus six references, all at 1.0, is seven megapixels of work rather than one, and that is what uses your video memory and your time. If you run slow or run out of memory, turn this down first - try 0.5. A face or a style still comes through fine at half the size, and your result stays just as big. |
| crop_2_nearest_16px | BOOLEAN | true | Trims a few pixels off the edges so the width and height divide by 16, which Flux.2 works best with. Applies to the picture you are editing, its mask, and every reference picture. Does nothing if the sizes already divide by 16. |
| expand_mask | INT | 160–256 | Grows the area you painted outwards by this many pixels, so the change covers a little more than you painted. 16 is a good starting point: the mask is used at a much smaller size than your picture, so a tight edge tends to lose a sliver of what you painted. Try 24-32 for hair, fur or other soft edges, or when the new thing needs more room than the old one. 0 turns it off. Like every mask setting here, it only affects the picture being edited. |
| feather_mask | INT | 80–256 | Softens the edge of the area you painted. It makes almost no difference to what the model generates - the mask is used at a much smaller size there, so a small softening vanishes into it. It matters if you paste the result back over your original afterwards (ImageCompositeMasked, or Y7 Paste Cropped Image Back), where the default 8 gives a soft join instead of a hard cut. Needs `binary_mask` off, which is how it ships. Above about 32 this acts as a loose, fuzzy edit area rather than a tidy edge. |
| binary_mask | BOOLEAN | false | Makes the mask edge hard, so every part you painted is either fully changed or left alone - nothing in between. Off by default, so that any softening from `feather_mask` survives to blend the edit back into the picture afterwards. Turn it on if the mask brush has left soft, half-strength areas that come out muddy, or if something further down the workflow needs a strictly hard mask. |
| ref_images | COMFY_AUTOGROW_V3 | Extra pictures for the model to look at, on top of the one you are editing. Connect one and another empty socket appears, up to seven in total. Refer to them in your prompt by number - the one on the node is 1, ref_image_2 is 2, and so on. | |
| external_maskopt | MASK | Use a mask from somewhere else instead of painting one on this node - a black-and-white mask file loaded with Load Image (as Mask), or a mask from any other node. White marks the part to change. If you use Load Image (as Mask) with a plain black-and-white file, set its channel to red: on alpha (the setting it starts on) that node hands back an empty mask and nothing here gets masked. It is stretched to match the picture being edited, so give it the same shape to avoid a blurred or shifted edge. When this is connected and has anything in it, it replaces whatever was painted on the node; leave it unconnected to paint instead. Like a painted mask, it only ever applies to the picture being edited on this node - the extra reference pictures are never masked. | |
| positiveopt | CONDITIONING | — | |
| negativeopt | CONDITIONING | — |
Outputs (6)
| Name | Type | Description |
|---|---|---|
| reference_latent | LATENT | — |
| positive | CONDITIONING | — |
| negative | CONDITIONING | — |
| preview_image | IMAGE | — |
| preview_mask | MASK | — |
| debug | STRING | — |