Canvas Merger
Composite after inpainting, built into a node
- canvas_selection
- canvas_context
- merged_image
The community's oldest inpainting rule, delivered with force back in 2024, is "PLEASE composite your image after inpainting." Run the whole canvas through VAE encode and decode a few times and it degrades to mush, even when the model itself did nothing wrong. Canvas Merger is that rule as a node: it takes the piece you generated and pastes it back onto the untouched original canvas at exactly the spot Canvas Selector cut it out of.
How it works
Two inputs: canvas_selection (the IMAGE you generated) and canvas_context (the CONTEXT object Selector gave you). It unpacks the context, which carries the original canvas plus the bounding box and the scale factor that was applied when the region was cut. If you scaled the crop with the selector's scale_factor, the merger resizes your generated piece back to the bounding box's dimensions with bilinear interpolation, then writes it straight into canvas_image[bbox] = selection.
The result (merged_image) is the full canvas with only your region replaced. Everything outside the box is bit-identical to what you started with - the same guarantee the KB credits crop-and-stitch tools with, and the reason masked workflows still beat whole-frame edit models when the unmasked pixels must survive byte-for-byte. Edit models like Kontext or Qwen-Image-Edit shift details they weren't asked to touch; a hard paste never does.
The things that bite
- The context must come from a Canvas Selector. This is the pack's own CONTEXT format - a canvas plus a bounding-box dict - not a generic pipe. Feed it an rgthree context or a different image and the paste will land wrong or crash. Wire Selector → Merger and don't improvise.
- It's a hard paste. No mask feathering, no alpha blend. If you want soft edges you feather the selection mask before generating, or blur the boundary afterward. Selector gives you
selection_maskprecisely so you can. - You're still responsible for the generation. The merger pastes what you give it. If you sampled at a wildly different style or lighting, the seam is on you - the node won't harmonize anything.
Where it sits in the loop
The canonical pack flow is Loader → Selector → encode/sample/decode → Merger → preview, with Canvas Cache Updater on the merged output if you want the loader to treat it as the new canvas next run. You could also feed merged_image to a Save Image node and call it a day.
Installing
It's one of the five nodes in leocbehe/canvas. ComfyUI Manager → search "Canvas", or:
cd ComfyUI/custom_nodes
git clone https://github.com/leocbehe/canvas
Restart ComfyUI. No dependencies beyond the bundled torch/PIL, no model downloads, no keys. Like the rest of the pack it's a fresh personal project (version 0.0.1, GPL-3.0) with zero community track record yet - if the paste misbehaves, that's a bug to report, not a setting you're missing.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| canvas_selection | IMAGE | — | |
| canvas_context | CONTEXT | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| merged_image | IMAGE | — |