Reference Concat
Feed several identity references to a video model without losing the subject
- reference_images
- main_image
- image
- mask
Video models love a first frame that tells them what the subject should look like. That's the entire trick behind image-to-video and the reference-to-video family - anchor frame one to a known image and a lot of identity drift problems vanish. But what do you do when one image isn't enough? Front view and back view, the outfit, a prop - the more angles the model sees, the better the identity survives. And you can't feed it five separate frames.
Reference Concat is the node that glues your reference images onto a main image as a strip, so you can hand a video model a single composite first frame that contains several views of the subject at once. It's a pixel-level tool: no training, no adapter, no API - just concatenation done carefully.
How it works
The strip goes on the main image's longest side, sized to exactly match that side's length. The main image is never shrunk or cropped - that's a hard rule. ref_scale sets the strip's thickness as a fraction of the main image's cross-side, and the long side is a cap: if the natural strip would overflow, it scales down; it never stretches to fill.
The fun part is offset. Positive values slide the strip into the main image, overlapping it by that many pixels; negative values pull it away, leaving a gap filled with the fill color. Everything is clamped so the main image stays intact. fill supports edge_average, which samples the main image's edge nearest the strip - useful when black or white would scream.
There's a hidden mode too: leave main_image unconnected and it outputs a contact-sheet grid of just the references, sized to the largest one. Fine in a pinch, but it's an auto-layout - if you want control, that's what Reference Grid is for.
The inputs that matter
reference_images- connect a MoonPack Image List to keep each reference's native resolution (recommended for mixed-res refs), or a plain stacked batch.main_image- the first-frame image. If a batch lands here, only the first frame is used. Unconnected = contact-sheet mode.side(top/bottom/left/right) andref_scale- where the strip goes and how thick it is.offset,fill,interpolation,max_ref_side- the knobs above.max_ref_sidecaps each reference's longest side before sizing (downscale only,0= no cap), so one oversized photo can't blow up the whole composite.invert_mask- flips the output mask.
Outputs: the composite image, plus a mask marking the reference area. Some video pipelines use that mask to tell the model which region is reference versus first frame; if yours doesn't, ignore it.
Installing it
Reference Concat ships in MoonPack (comfyui-moonpack). Via ComfyUI Manager, search MoonPack; or:
cd ComfyUI/custom_nodes
git clone https://github.com/moonwhaler/comfyui-moonpack.git
Restart ComfyUI. No models, no extra dependencies.
Where people get burned
Two things. First, if you pass references as a stacked batch, they were already resized to match each other upstream - the Image List input exists precisely to avoid that, so use it when your refs have different resolutions. Second, that hidden contact-sheet mode: if you unplug main_image to test something and forget, your "strip" output suddenly becomes a grid. The node warns you in its own docs, but it's the kind of silent mode switch that's easy to miss.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| reference_images | IMAGE | Reference images to concatenate. Connect a MoonPack Image List to keep each image's native resolution (recommended for mixed-resolution references), or a plain stacked IMAGE batch. | |
| side | COMBO | top | Which side of the main image the strip goes on. By default the strip's row-vs-column layout is auto-picked from the main image's longest side, and 'top'/'left' place the strip before the main image along that axis while 'bottom'/'right' place it after. Enable force_side to make this side always win instead. Ignored if main_image is unconnected. |
| force_side | BOOLEAN | false | Force the strip onto the exact side chosen above, overriding the automatic row-vs-column pick: 'left'/'right' always stack the strip as a full-height column, 'top'/'bottom' always stack it as a full-width row. The resulting strip may end up thin if this fights the main image's orientation. Ignored if main_image is unconnected. |
| ref_scale | FLOAT | 0.500.01–4 | Strip thickness as a fraction of the main image's cross-side. The main image's long side is a cap: the strip is only scaled down (never stretched) if it would overflow. Ignored if main_image is unconnected. |
| offset | INT | 0-8192–8192 | Positive: slide the strip into the main image, covering that many pixels of it. Negative: pull the strip away from the main image, leaving a filled gap. Clamped so the main image is never cropped or shrunk. Ignored if main_image is unconnected. |
| fill | COMBO | black | Background color for any gap/leftover space. 'edge_average' samples the main image's edge nearest the strip, or the average of all references when main_image is unconnected. |
| interpolation | COMBO | bicubic | Resampling filter used whenever a reference image is resized, including upscaling references smaller than the target size. |
| max_ref_side | INT | 00–8192 | Caps each reference's longest side to this many pixels before any other processing (downscale only, never upscales). Keeps one oversized reference from blowing up the contact-sheet or strip size. 0 = no cap. |
| invert_mask | BOOLEAN | false | Flip the output mask (main-image/empty-cell area becomes 1.0 instead of the reference area). |
| main_imageopt | IMAGE | Main / first-frame image. If a batch is passed, only the first frame is used. Leave unconnected to instead output a contact-sheet grid of the reference images. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| mask | MASK | — |