Load Images & Compose (obvpm)
Four reference images, one wire, and no invented pixels
- image
Multi-reference work in ComfyUI usually looks like this: four Load Image nodes, four crop nodes, four resize nodes set to match, an Image Stitch to glue them together, and a permanent question about whether the sheet is the size the model expects. Reference-to-video and identity-transfer workflows have made this worse, not better - modern models take a sheet of references, and a sheet is exactly what you don't want to assemble by hand every run.
Load Images & Compose (obvpm) is that whole stack in one node. It loads several images as layers, gives each one its own crop, and composes them into a single image within a megapixel budget. The layer strip is on the left of the node, the main view on the right, and the composition itself is the first entry in the strip.
Building the sheet
Add images with the + add image button, the add dropdown (a live listing of your input folder, subfolders included), by dropping files onto the node, or by pasting from the clipboard with the node selected. Each becomes a layer; a file already in the input folder gets referenced in place rather than copied. Cards dragged in from the Artius browser work too.
Select a layer by clicking it, delete it with the ✕ badge, drag to reorder. Each layer keeps its own crop and its own aspect lock - the same editor as Load Image & Crop: drag to draw, drag inside to move, drag a corner to resize, click outside to clear. Per-layer ratios are independent, free by default. There's a duplicate button that inserts a copy of the selected layer right after it.
The info line under the view is where the node earns its keep. It reads something like 4 layers → 1104 × 928 · 0.98 MP · 92% filled · 57% of source - the composed size, how much of the sheet is actually image, the single scale factor applied to everything, and how much of the source resolution you kept. You know what you're about to feed the model before you queue.
Settings
max_megapixels- the largest the composed image may be; 1.0 means 1024×1024. Default 1.0. Sides are rounded to a multiple of 16.gap- pixels of background between layers.0puts them flush; a few pixels genuinely helps a model tell one reference from the next.background-black,greyorwhite, seen in the gaps and in the small margin left by rounding.
One output: image, the composed sheet.
The design decision that matters: it's a cap, not a target
This is the part to understand, because it's where this node differs from a generic image-stitcher, and it's why the results look right.
Every layer is scaled by the same factor, and none is ever enlarged. A 300×200 crop next to a 3000×2000 one comes out nine times smaller in area - because it is nine times smaller. A small crop cannot take space from a large one by being stretched to fill a slot. So four 256×256 images compose to a 512×512 sheet no matter how big max_megapixels is, since enlarging them would be inventing pixels that were never in your references. Set max_megapixels to 0 and there's no cap at all: every layer at its own size.
Nothing is ever rotated. A texture-atlas packer would happily take a quarter-turn to fill a gap; a rotated reference is a wrong reference, so this doesn't.
Your order is the answer. The packer sweeps 48 candidate sheet widths and four placement orders - your layer order, then tallest-first, widest-first and largest-first. Your order wins by default and is only displaced by an ordering that packs at least three points tighter, so dragging layers around still means something. The info line tells you which order was actually used.
That combination is a stronger guarantee than it looks: order preserved, aspect ratios exact, relative sizes honest. If you're feeding a reference-conditioned model, that's the difference between a sheet that encodes "subject is small in frame" and one that silently says the opposite.
Install
ComfyUI Manager → search comfyui-obvpm, or:
cd ComfyUI/custom_nodes
git clone https://github.com/chanon/comfyui-obvpm
Restart ComfyUI. It's under obvpm/image, and a node-menu search for obvpm lists the pack. No Python dependencies, no models. The pack's author has a walkthrough video for this node (Load Images & Compose on YouTube) if you'd rather watch it once than read it.
Gotchas
layers is managed by the editor - don't hand-edit it. Changing any crop, or swapping the file behind a layer, re-executes the node on the next run, so a re-crop does cost a downstream re-run. And if your sheet comes out smaller than you expected, that's the cap-not-target rule doing its job: a budget of 2 MP doesn't mean you should expect 2 MP out of small crops.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| layers | STRING | [] | Managed by the layer editor on the node — no need to edit by hand. |
| max_megapixels | FLOAT | 1.000–128 | Largest the composed image may be (1.0 = 1024x1024 pixels). A CAP, not a target: every layer is scaled by one shared factor and none is ever enlarged, so a sheet of small images comes out small rather than being blown up to fill this. 0 = no cap: every layer at its own size (in fill sizing, a sheet of the sources' total area). Sides are rounded to a multiple of 16. |
| gap | INT | 00–256 | Pixels of background between layers. 0 puts them flush against each other; a few pixels helps a model tell one reference from the next. |
| background | COMBO | black | Colour behind the layers — seen in the gaps, and in the up-to-16-pixel margin left by rounding. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | The composed sheet, one image containing every layer. |