ComfyUI Node

ND Load Images

Twenty reference images in one node, each with its own mask and crop

By neural-dreamer·Created 22 days ago·Updated 2 days ago· 5
ND Load Images
  • params
  • params
◄state{"version": 1, "hideBlockSettings": false, "blocks": [{"id": "b0", "enabled": true, "image": "example.png", "masked": null, "edit": {"rotate": 0, "flipH": false, "flipV": false, "crop": null}, "keep": "crop", "slider": 1.0, "settings": {"maskButton": true, "keepEnabled": true, "keepApply": true, "scaleMethod": "lanczos", "sliderEnabled": true, "sliderName": "Strength", "sliderMin": 0.0, "sliderMax": 1.0, "sliderStep": 0.1, "lockOrder": false}}]}►

Load Image is fine until you need four of them. Then you're placing four nodes, uploading into each, dumping four resizes downstream, and dragging wires across the canvas every time you swap the reference set. ND Load Images puts up to twenty image blocks in one node and gives each one a mask, an edit, a resize policy and a slider.

What it is

A grid of image blocks. Block count (1–20) is set in the node's own settings - there are no add/remove buttons on the canvas, which is a choice you'll either love or find maddening. Each block has an upload button (drag-and-drop works too), a Mask button that opens ComfyUI's mask editor, a Keep Proportion selector, an Edit button for crop/rotate/flip, and a slider. The circle top-left disables the block; drag the grip to reorder, and lock a block's order position if you've got a layout you don't want disturbed.

The "why" is multi-image jobs: character sheets, three-angle references for an edit model, a batch of style references, before/after pairs. Doing that with stock nodes is four Load Image shuffles plus four resize chains you keep in sync by hand. This node has one target size for the whole grid, so the sync problem disappears.

Order of operations

Per block, in this order: mask (drawn on the original) → rotate → flip → crop → Keep Proportion to the target size. Nothing ambiguous there, and it matters for crop: you're cropping the oriented image, so a crop drawn before a rotate would have landed somewhere else.

Keep Proportion has six modes, and the last two are the ones worth knowing: pad edge repeats edge pixels outward (nice for outpainting-adjacent work, garbage for transparent art), and pad edge color fills each side with the average colour of the edge next to it, which is by far the least ugly pad for photos. stretch is the default-on-paper alternative that nobody should use on faces. The block's own scaling method defaults to lanczos, and you can pick from nearest-exact, bilinear, area, bicubic.

Inputs and outputs

  • params (ND_PARAMS, optional) - carries the fields width and height (INT). Connect a Pack Parameters and it creates both pins for you. Without them, the target size is the size of the first enabled block's image after its edits, and everything else is fitted to it. With no enabled block at all, nothing is resized.
  • state (STRING, required) - the hidden JSON blob holding every block's file, mask, edits, options and slider range. That's why all of it survives a save; it's also why hand-editing the node in a text editor is a bad idea.

The output is one params wire holding Image N Parameters per block. That's a bundle inside a bundle, and it needs two Unpacks: one ND Unpack Parameters to get the per-block fields, then another Unpack on each field to reach image, mask, keep_proportion, the slider value and enabled. Rename a block in its settings and the matching field shows up as <name> Parameters, which is the difference between a workflow you can read in a month and one you can't.

Each block's slider is a real output - name it, set range and step, and you get a per-image float. Set 0.0–1.0 and call it Strength and you have per-reference weighting for an img2img or ControlNet chain, which is a genuinely nice thing to have on a grid of reference images.

Install

Ships in Neural Dreamer Nodes (v1.1.0, MIT, no Python dependencies - dependencies = [], no requirements file, no model downloads). Images come from ComfyUI's normal input/ folder.

cd ComfyUI/custom_nodes
git clone https://github.com/neural-dreamer/ComfyUI-NeuralDreamer-Nodes.git

Restart, then refresh the tab. ComfyUI Manager users: search "Neural Dreamer Nodes".

Common issues

Every block loads the same picture. A new block starts on example.png from the input folder, so an untouched grid is twenty copies of the same test image. That's expected, not a caching bug.

I can't add or remove a block. Node settings (gear, top right of the node), number of blocks. There are deliberately no per-block buttons on the canvas.

The wire menu doesn't show image. You unpacked once and stopped. The per-block field is itself a parameters bundle; unpack it again.

A block's mask vanished. Masks are stored per block in the workflow state. Blocks with no mask output an empty mask rather than nothing, so a mask wire that's "all white" usually means the mask was never drawn.

Hide the gears before sharing. Hide block settings locks the per-block gears so a recipient can't accidentally detune a finished setup. Small feature, saves a support thread.

Everything came out the same size as one image. That's the no-width/height fallback: the first enabled block defines the target. Wire a Pack Parameters with both fields if you want the size fixed by the workflow rather than by whichever image happens to be first.

CategoryNeural Dreamer/Images

Inputs (2)

NameTypeDefaultDescription
stateSTRING{"version": 1, "hideBlockSettings": false, "blocks": [{"id": "b0", "enabled": true, "image": "example.png", "masked": null, "edit": {"rotate": 0, "flipH": false, "flipV": false, "crop": null}, "keep": "crop", "slider": 1.0, "settings": {"maskButton": true, "keepEnabled": true, "keepApply": true, "scaleMethod": "lanczos", "sliderEnabled": true, "sliderName": "Strength", "sliderMin": 0.0, "sliderMax": 1.0, "sliderStep": 0.1, "lockOrder": false}}]}—
paramsoptND_PARAMS—

Outputs (1)

NameTypeDescription
paramsND_PARAMS—