Add Margins 311
The one-node square pad that collapses a four-node chain
- image
- mask
- IMAGE
- MASK
Almost every outpainting workflow starts with the same boring move: stick your image on a bigger canvas, centered, with empty space around it to generate into. ComfyUI's stock way to do that is a four-node chain - ImageSize, ScaleByAspectRatio, Upscale By, Image Align - which is exactly as fiddly as it sounds. Add Margins 311 is the whole chain compressed into one node, with one crucial bonus: it pads the mask too, and keeps it registered with the image.
That bonus is the point. Inpaint nodes only work if the mask lines up pixel-for-pixel with the image you feed the sampler. Stretch the image one way and the mask another - easy to do by accident when you're stitching four padding nodes together - and your inpaint quietly generates in the wrong place. Add Margins 311 moves the image and its mask onto the same canvas in one tensor operation, so they can't drift apart.
What it actually does
The default square_longest mode matches that four-node chain exactly: take the longest side of your image, build a black 1:1 canvas at longest_side * scale_by (1.25 by default), and center the content on it without resampling. A 500×712 image becomes an 890×890 canvas. That's padding, not stretching - exactly what you want when the canvas space is the region you're going to mask and regenerate. The 1.25 default is a sensible bit of headroom; you get a wide border to outpaint into instead of a sliver.
The other two modes cover the cases where square doesn't fit:
add_margins- symmetric (extra_width/extra_height, in pixels or percent) or fully custom per-sideleft/right/top/bottom, each with its own unit. Use this when you only want to extend one edge, like extending a portrait upward for more headroom.target_size- contain-fit the image onto an absolutetarget_width×target_heightcanvas. This one does resample (lanczos for the image, bilinear for the mask) to fit, so it's the mode for forcing a non-square image into a model's native resolution box.
Everything else is what you'd expect and mostly ignorable: fill_color for the canvas background (the default #000000 - and note it accepts #RGB or #RRGGBB only), plus halign / valign for where the content sits if you don't want it dead center.
Outputs are IMAGE and MASK, both on the same canvas. Wire them into your sampler (or the now-common pattern of padding with a solid color and prompting the model to "remove the red/black padding" - the 2026 outpainting trick - where fill_color is doing real work).
Installing it
The pack is tiny and dependency-free - no model downloads, just torch and ComfyUI's own comfy.utils:
cd ComfyUI/custom_nodes && git clone https://github.com/Latentnaut/ComfyUI-311-Inpaint
then restart ComfyUI. ComfyUI Manager users can search "comfyui-311-inpaint" instead.
The gotchas
Two things will bite you, and neither is the node's fault. First, it's written against ComfyUI's newer backend API (comfy_api.latest, io.ComfyNode, no NODE_CLASS_MAPPINGS). If it doesn't load, that's not a broken pack - it's an old ComfyUI. Update first; this style requires a recent build.
Second, the node is strict about input shapes. Feed it a mask that doesn't match the image's size or batch count and it raises a ValueError instead of silently doing something wrong - annoying, but it's the check that keeps your inpaint aligned. If you hit it, resize your mask upstream or skip the mask input entirely and add the pad to the mask after.
Reality check: this is a one-node pack ("311 series" currently ships exactly this node), a single initial commit, zero community footprint - the author is a Reddit regular, not an established node dev. But it's a clean, readable, MIT-licensed ~50-line pad utility, and for the core job - square-pad an image plus its mask without resampling - it does it in one node where the stock alternative needs four.
Inputs (21)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| size_mode | COMBO | square_longest | square_longest: square canvas = longest_side * scale_by, center content. add_margins: expand with symmetric or custom side margins. target_size: fit onto target_width x target_height. |
| scale_by | FLOAT | 1.250.01–8 | Multiplier for longest side when size_mode=square_longest (default 1.25). |
| margin_layout | COMBO | symmetric | symmetric: extra_width/height split equally L/R and T/B. custom: independent left/right/top/bottom (each with own unit). |
| unit | COMBO | pixels | Unit for extra_width / extra_height when margin_layout=symmetric. |
| extra_width | INT | 00–8192 | Total added width (split L/R). Used when size_mode=add_margins + symmetric. |
| extra_height | INT | 00–8192 | Total added height (split T/B). Used when size_mode=add_margins + symmetric. |
| left | FLOAT | 00–8192 | Left margin (custom layout). |
| left_unit | COMBO | pixels | 2 options: pixels, percent |
| right | FLOAT | 00–8192 | Right margin (custom layout). |
| right_unit | COMBO | pixels | 2 options: pixels, percent |
| top | FLOAT | 00–8192 | Top margin (custom layout). |
| top_unit | COMBO | pixels | 2 options: pixels, percent |
| bottom | FLOAT | 00–8192 | Bottom margin (custom layout). |
| bottom_unit | COMBO | pixels | 2 options: pixels, percent |
| target_width | INT | 5121–8192 | Canvas width when size_mode=target_size. |
| target_height | INT | 5121–8192 | Canvas height when size_mode=target_size. |
| fill_color | STRING | #000000 | Canvas fill color as #RGB or #RRGGBB. |
| halign | COMBO | center | 3 options: center, left, right |
| valign | COMBO | center | 3 options: center, top, bottom |
| maskopt | MASK | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |
| MASK | MASK | — |