Image Composer /noEmbryo
Pack a sheet without leaving ComfyUI
- image1
- image2
- image3
- image4
- image5
- image6
- image7
- image8
- image9
- image10
- image11
- image12
- image13
- image14
- image15
- image16
- IMAGE
ComfyUI's image path is stubbornly one-tensor-at-a-time. You generate eight variations, and to actually look at them together you're opening a folder in your file browser, or dragging them into Photoshop, or squinting at a montage node that wants you to specify the row and column count by hand first.
Image Composer is the usefully lazy version of that: throw images at it, get one packed sheet back.
Why you'd reach for it
Three real uses. A contact sheet so you can judge a seed sweep or a LoRA strength run in one glance instead of eight tabs. A single reference image for a downstream node that only takes one - feeding a composed sheet to a single-image conditioning or vision model is a rough multi-reference, and rough is often good enough. And asset sheets: pose sheets, outfit lineups, turnaround sheets for a visual-novel character, which is a thing people build by hand in image editors far more often than they should.
It's a utility node, and utility nodes are the majority of any ComfyUI workflow - the layer that doesn't touch pixels but decides whether a graph is readable. This one is the pack's tidiest example of that.
How the packing actually works
Connect a second Load Image and the node grows a new input slot; it starts at one image1 and will keep minting slots up to image16. Order is preserved - image1 goes down first - and nothing is ever rotated.
Underneath, it packs a skyline sheet at natural pixel sizes. It sweeps 48 candidate strip widths across four different placement orders (input order, tallest-first, widest-first, biggest-area-first), genuinely tries the packing for each, and keeps the tightest one whose overall aspect ratio lands between 0.45 and 2.2. That aspect clamp is the answer to the question everyone asks the first time they use it - "why did it pick those rows?" - because a configuration that reads perfectly in your head may get rejected as too long and thin. Canvas dimensions snap to a multiple of 16, and with a non-zero gap you get a half-gap frame around the outside, so the border visually matches the spacing between tiles.
Two behaviours matter more than the algorithm. First, tiles are never enlarged - a 512px image stays 512px, so a sheet of mixed sizes stays mixed. Second, if the result exceeds max_megapixels, the whole layout is scaled down together rather than rearranged; the shape you liked is preserved, the pixels are interpolated. Blur is the price of a cap set too low.
The three widgets you'll touch
gap is the pixels of background between layers (defaults to 0, steps of 2). background is black, grey or white - grey is a true mid-grey, which is the neutral choice if the sheet is going somewhere that composites on a light or dark surface. max_megapixels caps the sheet size (1.0 ≈ a 1024×1024 sheet) and 0 means no cap at all, which is the right default until a downstream model complains.
The single output is IMAGE - one sheet, not a batch. Wire it to Save Image, an upscaler, or whatever eats it next. A nice touch while you're iterating: the node draws a live preview that refreshes when an upstream crop, rotation or megapixel setting changes, so you can nudge gap and background without queueing a run.
Install
ComfyUI Manager → search noEmbryo → install, restart. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/noembryo/ComfyUI-noEmbryo
Restart ComfyUI and the node shows up under Add Node → noEmbryo → Image. The pack declares no Python dependencies - no requirements.txt, nothing for pip to resolve - so there's no dependency-conflict roulette here, which in a 500-pack custom-node ecosystem is genuinely worth calling out.
Where it bites
- "ImageComposer: no images connected." At least one input has to be live, and a configured-but-unwired slot doesn't count.
- Huge
gapmakes a lot of sheet. It's capped at 256 and steps by 2, so a typo of 200 on a grid of a dozen images will build you a mostly-black canvas. Keep it small - 4 to 16 covers most sheets. - Mixed sizes stay mixed. Because nothing is enlarged, one low-res tile in a sheet of high-res ones is visibly the runt. Resize upstream if that bothers you - the pack's own Load Image (from path) has a width/height force that will do it, and pixel-art sheets should be scaled with integer nearest-neighbour, not a smooth filter.
- Routing images in through Set/Get nodes is the fragile path. The composer tries to coerce values that aren't tensors, but after a workflow JSON round-trip that's exactly the case where you'll get nothing useful. Direct links are the reliable wiring.
- It is not a batch node. Whatever's downstream sees one image where it might have expected eight. If you wanted a batch, this isn't the tool - this is the tool for when one packed image is the point.
Inputs (19)
| Name | Type | Default | Description |
|---|---|---|---|
| gap | INT | 00–256 | Pixels of background between layers. |
| background | COMBO | black | Colour behind the layers. |
| max_megapixels | FLOAT | 0.000–128 | Cap the sheet size (1.0 = 1024x1024 px). 0 = no cap. |
| image1opt | IMAGE | Image layer — connect another Load Image node to reveal the next input slot. | |
| image2opt | IMAGE | Image layer — connect another Load Image node to reveal the next input slot. | |
| image3opt | IMAGE | Image layer — connect another Load Image node to reveal the next input slot. | |
| image4opt | IMAGE | Image layer — connect another Load Image node to reveal the next input slot. | |
| image5opt | IMAGE | Image layer — connect another Load Image node to reveal the next input slot. | |
| image6opt | IMAGE | Image layer — connect another Load Image node to reveal the next input slot. | |
| image7opt | IMAGE | Image layer — connect another Load Image node to reveal the next input slot. | |
| image8opt | IMAGE | Image layer — connect another Load Image node to reveal the next input slot. | |
| image9opt | IMAGE | Image layer — connect another Load Image node to reveal the next input slot. | |
| image10opt | IMAGE | Image layer — connect another Load Image node to reveal the next input slot. | |
| image11opt | IMAGE | Image layer — connect another Load Image node to reveal the next input slot. | |
| image12opt | IMAGE | Image layer — connect another Load Image node to reveal the next input slot. | |
| image13opt | IMAGE | Image layer — connect another Load Image node to reveal the next input slot. | |
| image14opt | IMAGE | Image layer — connect another Load Image node to reveal the next input slot. | |
| image15opt | IMAGE | Image layer — connect another Load Image node to reveal the next input slot. | |
| image16opt | IMAGE | Image layer — connect another Load Image node to reveal the next input slot. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |