π¨ Outpaint Controller
The outpaint canvas ComfyUI should have shipped with
- left
- right
- top
- bottom
- mask
- padded_image
- original_image
Outpainting - uncropping an image beyond its edges - is mostly math. Where do I place the source in the bigger frame? How many pixels of padding on each side? What does the mask look like? Do it by hand and you're juggling four INT widgets and hoping they add up to a multiple of 8. OutpaintController turns that arithmetic into dragging a picture around a canvas, and it's the pack's flagship node.
It's the star of elliefox-ai/comfyui-scene-generator for good reason: instead of a separate LoadImage node plus hand-computed padding, you load an image directly in the node, position it inside a target frame with your mouse, and get the four padding values, a ready-to-use mask, and the padded image for any inpaint/outpaint workflow. It's a composition tool, not a model - you still bring your own outpaint model or workflow.
How it works
The UI half is a real interactive canvas: drag the source image around the frame, resize it with corner handles, and the padding readout (L/R/T/B in pixels) updates live. Aspect ratio presets (16:9, 3:2, 4:3, 1:1 plus vertical variants) set the target frame for you. You can drag-and-drop an image file from your OS, or use the upload button.
The compute half is where the engineering shows. The node:
- Snaps every dimension - target size, padding, even the edge-crop remainder - to an 8px grid. This is deliberate: latent space is 8Γ downsampled, and misaligned dimensions are a classic source of outpaint seams.
- Builds a mask that's 1.0 in the padding region (generate) and 0.0 in the source region (preserve) - exactly the convention diffusion models expect.
- Applies a feathered transition band at the source boundary, so the model blends instead of pasting.
- Pads with black, which pairs naturally with the "fill" mask content and a high denoise (0.8β1.0) approach that outpainting needs.
If your source ends up bigger than the target frame, it auto-scales to fit rather than erroring out.
The inputs that matter
Don't touch most of these on the first pass. The ones you'll actually set:
- image - load or drop your source.
- aspect_ratio - pick a preset and forget
target_width/target_height, which then only set the long side. - source_resize - scale the source's longest edge down to leave room for outpainting.
0= no resize. Smaller = more padding room. - feather - width of the mask transition band (default 16). When seams appear, this is the first dial.
- edge_crop - strips N pixels from each edge of the source before padding. The tooltip says it plainly: strips JPEG/compression artifacts that cause seams. If your source is a screenshot or a heavy JPEG, this is your friend.
- center_x / center_y - where the source sits in the frame (0β1). Usually you'll set these by dragging on the canvas instead.
Outputs: left, right, top, bottom (INT) for any padding node, mask (MASK), padded_image (IMAGE), and original_image (IMAGE) for chaining.
Installing it
The pack installs as one unit:
cd ComfyUI/custom_nodes/
git clone https://github.com/elliefox-ai/comfyui-scene-generator.git ComfyUI-EllieFoxAI-scene-gen
Restart, then find π¨ Outpaint Controller under EllieFoxAI. ComfyUI Manager works (search "comfyui-scene-generator"). No extra Python dependencies and no model downloads - this is a pure front-end + padding node.
Common issues
- Seams at the source boundary - raise
feather, bumpedge_cropif the source is compressed, and make sure your workflow covers the padding with a healthy overlap of the original. Outpainting's classic failure is the "pasted on" look, and most of it is controllable here. - Source looks wrong after run - check
source_resize. Scaling the source down is the intended way to leave room, but at0the node only auto-shrinks when the source is bigger than the target. - Odd-looking dimensions - everything snaps to the 8px grid. That's the feature, not a bug; it's protecting you from latent misalignment.
One fair caveat: masked outpainting has been losing ground to instruction models that just "extend the canvas" (a red padding trick that needs two nodes). But when the original image must survive byte-for-byte, or you're extending in several passes, a proper padded+masked workflow still wins - and this is the least fiddly way I've seen to build one.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| image | COMBO | Select an uploaded image, or use the upload button in the grid panel. | |
| aspect_ratio | COMBO | custom | Preset target aspect ratio. Sets target_width/height from long side. |
| source_resize | INT | 00β8192 | Scale source longest edge to this. 0 = no resize. Smaller = more padding room. |
| target_width | INT | 102464β8192 | β |
| target_height | INT | 102464β8192 | β |
| center_x | FLOAT | 0.500β1 | β |
| center_y | FLOAT | 0.500β1 | β |
| feather | INT | 160β512 | β |
| edge_crop | INT | 00β64 | Crop this many pixels from each edge of the source before padding. Strips JPEG/compression artifacts that cause seams. |
Outputs (7)
| Name | Type | Description |
|---|---|---|
| left | INT | β |
| right | INT | β |
| top | INT | β |
| bottom | INT | β |
| mask | MASK | β |
| padded_image | IMAGE | β |
| original_image | IMAGE | β |