Nodes/ComfyUI-RMBG/Canvas (RMBG)
ComfyUI Node Runs on cloud

Canvas (RMBG)

The compositor hiding inside a background-removal pack

By 1038lab·Created 2 years ago·Updated 22 days ago· 2,226
Canvas (RMBG)
  • background_color
  • Background
  • layer_1
  • IMAGE
  • MASK
◄canvas_width640►
◄canvas_height360►
◄backgroundAlpha►
◄fit_background_sizefalse►
◄canvas_state{}►

Every cutout workflow ends with the same embarrassing step. You've got a beautiful transparent PNG, and now you need the subject at a decent size, off-centre, on a coloured background, next to a logo - and you're doing it with Image Composite, three ImageScale nodes, and a Transform whose rotation eats your edges. Six nodes to do what Photoshop does in a drag.

This is the pack's answer: a multi-layer, drag-it-around compositing canvas inside ComfyUI. It landed in v3.3.0 in October 2026, it's the only node here that isn't about removing a background, and in a cutout pipeline it's the natural last stop.

How it works

The compositor runs in torch, on the GPU, on a 4-channel RGBA canvas. Each layer goes through the same sequence: optional flips, scale, rotation with expand=True, then a paste at an arbitrary position - clipped to the canvas bounds so a layer hanging off the edge just hangs off the edge. Layers are blended with correct straight-alpha "over" math, which is the part most hand-rolled compositing graphs get wrong; that's how you get a subject with a grey fringe against a dark background. There isn't one here.

Ordering matters and it comes from canvas_state, a plain string widget holding serialized JSON. That JSON is what the front-end writes when you drag things around: each layer's position, scale, rotation, flip, opacity and stacking order. The node then sorts layers by that z-index and draws them in order, scaling coordinates relative to the canvas size recorded in the JSON so a layout survives you changing resolution. You are not meant to hand-edit it - the visual editor is the editor, and the JSON is its save file.

Two behaviours from the source worth knowing up front. Layers bigger than the canvas are auto-fit down to 85% of it so nothing lands half off-screen on first connect; layers that are already small are left at native scale. And canvas dimensions are rounded down to a multiple of 8, which is ComfyUI doing you a favour.

Inputs

Six required widgets, and honestly you'll touch three of them:

  • fit_background_size - when true, canvas dimensions come from the Background image and canvas_width / canvas_height are ignored. When false, you get the explicit size. Default is false, so if you wire in a background and wonder why it's cropped, that's the switch.
  • background - Alpha for a transparent output, Color to fill with background_color (hex, default #222222).
  • canvas_width / canvas_height - 640×360 by default, 8px steps.

Then the two image inputs: Background (optional) and layer_1 (optional). The node is dynamic - it grows additional layer sockets as you start using them, which is why the schema only lists the first. The one quirk: layer sockets only appear once connected, so a fresh node looks like it has fewer inputs than it does.

Outputs

IMAGE is the composite. MASK is the accumulated coverage of your layers - the union of every layer's alpha as composited, not the background's. That's genuinely useful: wire MASK into an inpainting or regional-conditioning path and you're targeting everything you laid down, in the exact place you laid it down.

The node is also an OUTPUT_NODE, so it saves previews to the temp directory and always executes. Whatever it composited is visible whether or not you've got a Preview Image downstream.

Install

Nothing extra. No models, no new dependencies - it ships with the pack and works the moment the pack loads:

cd ComfyUI/custom_nodes
git clone https://github.com/1038lab/ComfyUI-RMBG
./ComfyUI/python_embeded/python -m pip install -r ComfyUI-RMBG/requirements.txt

Or ComfyUI Manager → search ComfyUI-RMBG. The pack is GPL-3.0, which is worth a thought if you're redistributing anything built around it.

Where it bites

Everything is tiny or in the wrong place. Layers over 85% of the canvas get scaled on first connect; layers under it don't. So a 300px logo stays 300px while your 2000px subject shrinks. Set scale per layer in the editor rather than trusting the defaults.

Output has no alpha even though background is Alpha. If you fed a 3-channel image into Background, the node composites in RGB and hands back RGB - there's nothing transparent left to keep. Feed it an RGBA background, or leave Background empty.

Layout jumps after you change resolution. canvas_state positions are stored relative to the canvas size they were authored at and rescaled on load. Large size changes will visibly move things. Re-drag after a big change.

Copy-pasting the node between workflows scrambles it. The JSON travels with the copy but the layers don't unless you copy them too. If a pasted node has one layer floating in a corner, that's what happened.

Rotation crops the corners. It shouldn't, given expand=True, but it will if the layer is large enough that the rotated bounding box exceeds the canvas - then the paste is clipped by the canvas edge. Scale down first, rotate second.

Category🧪AILab/🖼️IMAGE

Inputs (8)

NameTypeDefaultDescription
canvas_widthINT64064–8192Canvas width in pixels when not fitting background image size.
canvas_heightINT36064–8192Canvas height in pixels when not fitting background image size.
backgroundCOMBOAlphaChoose background type: Alpha (transparent) or Color (custom background color).
background_colorCOLORCODE#222222Pick background color (use color picker).
fit_background_sizeBOOLEANfalseWhen True, canvas dimensions match the background image. When False, canvas dimensions use canvas_width and canvas_height.
canvas_stateSTRING{}Serialized JSON layout containing layer coordinates, scales, rotations, and order.
BackgroundoptIMAGEOptional background image input.
layer_1optIMAGELayer 1 image input.

Outputs (2)

NameTypeDescription
IMAGEIMAGE—
MASKMASK—