Load Image Layers
Edit a layered character without flattening it
- manifest
- base
- base_regions
- images
- image_list
- layout
- editor_config
- source_layers
If you're building a character, a UI sheet, or any art that ships as separate components, your source of truth is a folder of PNGs - sleeve, torso, hair, cape - with positions. ComfyUI's native loaders flattens all that into one image, and the moment you flatten, you've thrown away the thing you actually wanted to edit.
LF_LoadImageLayers is the bridge. It reads a manifest of component files plus their target rectangles, loads each one at native size, and puts them onto square RGBA editing canvases that the pack's image editor can work with - while keeping the ordered native sources intact so the result can be composed back without resampling your artwork into mush.
How it works
The manifest is a JSON object with a root and an ordered layers array:
{
"root": "D:/project/components",
"layers": [
{"id": "sleeve", "label": "Sleeve", "file": "arms/sleeve.png", "rect": [0, 0, 256, 128]}
]
}
file is always relative to root, and resolution through .. or a symlink can't escape it - a sensible guard when a workflow file might come from someone else. There's an optional sha256 per layer that's checked before the file is admitted, which is the difference between "my edit moved" and "the source changed under me".
Each layer decodes once at native RGBA, and its aspect ratio must match its target rectangle - so nothing gets stretched. Array order is composition order, later entries render above earlier ones, and overlapping rectangles are fine here (unlike the regions path, which forbids them). That's the whole reason both exist.
There's a second mode: give it optional base plus base_regions and you can edit an underlying base image in the same session, with the base regions coming first in the batch under base:<id> IDs. Turn on include_masks and each fabric layer gains a cut-mask canvas after it - white keeps original coverage, black cuts it, grey partially keeps. That's how you do "shorten the sleeve, let the arm show through" without destroying the layer.
The inputs and outputs that matter
manifest- the JSON above. The one input you author.canvas_size- default 512, the square editing canvas every layer fits onto without stretching.include_masks(optional, default off) - adds the cut-mask canvases.baseandbase_regions(optional) - supply both or neither; one alone is an error.
Outputs, in order: images (all canvases as one compatible batch), image_list (the same individually), layout (order, target rects, native sizes, content rects), editor_config (the named image_entries config for the editing breakpoint), and source_layers - the authoritative ordered native RGBA sources.
Wire images + editor_config into the editor, and source_layers straight into LF_ComposeImageLayers. Keep that last wire out of the editor: the composer needs native geometry, and routing it through a canvas would quietly make the resized copy the source of truth. Everything shares one square geometry top to bottom, which is what makes the round trip possible.
How to install it
ComfyUI Manager → search LF Nodes → install → restart, or:
cd ComfyUI/custom_nodes
git clone https://github.com/lucafoscili/lf-nodes
python -m pip install -r lf-nodes/requirements.txt
Pip with the interpreter that runs ComfyUI. Two pack-wide notes: it's 154 nodes with a heavy requirements list (Pillow and numpy are what this node needs, but torch, rembg and friends come along), and lucafoscili/comfyui-lf is the archived old repo - you want lf-nodes. No models to download.
Common issues & troubleshooting
Path escapes / file not found. Every file must be relative to root; absolute paths and .. traversal are refused. This is the node being careful with an external manifest, not a bug.
Aspect-ratio complaints. A layer's source dimensions must match its rect's aspect ratio, because the workflow promises no stretching. If you want to scale a component, change the rect or resize the PNG - the loader won't half-fit it for you.
"IDs must be unique." Duplicated id values across layers. The IDs are what make ordered editing and composition unambiguous, so they're enforced.
The composed result is soft. Check your source_layers wiring before blaming anything else. If the native sources went through the editor, you're compositing resized copies, and no amount of settings recovers the original detail.
Cut masks did nothing. They start white with the source alpha, so an untouched mask preserves the layer exactly - that's the baseline, not a failure. Paint them black where you want a cut. And note mask edits can't expand coverage; they only reduce it.
Base and regions disagree. Supply exactly one mask the size of the original base, and remember base_mask lives on the composer, not here.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| manifest | JSON | Object with root and ordered layers: [{id,label,file,sha256?,rect:[x,y,width,height]}]. | |
| canvas_size | INT | 5121–4096 | Square RGBA editing canvas; each native layer fits without stretching. |
| include_masksopt | BOOLEAN | false | Add cut-mask canvases: white keeps coverage, black cuts, gray partially keeps. |
| baseopt | IMAGE | One base image; supply together with base_regions to edit it in the same session. | |
| base_regionsopt | JSON | Non-overlapping base regions: {regions:[{id,label,rect:[x,y,width,height]}]}. |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | All ordered RGBA editing canvases in one compatible batch. |
| image_list | IMAGE | Ordered individual RGBA editing canvases. |
| layout | JSON | Layer order, target rectangles, native sizes and content rectangles for composition. |
| editor_config | JSON | Named image_entries configuration for the image editing breakpoint. |
| source_layers | IMAGE | Authoritative ordered native-size RGBA sources; connect directly to Compose Image Layers. |