ComfyUI Extension: ComfyUI-ResizeToCanvasSize

Authored by Carasibana

Created

Updated

3 stars

Run ComfyUI workflows without the setup

No installs, no CUDA version roulette, no GPU sitting idle on your bill. Bring a workflow and run it in the browser.

A ComfyUI custom node pack that resizes images to an exact target canvas size with full control over scaling, cropping, and placement.

Looking for a different extension?

Custom Nodes (0)

    README

    ComfyUI-ResizeToCanvasSize

    A ComfyUI custom node pack that resizes images to an exact target canvas size with full control over scaling, cropping, and placement.


    Version License

    Nodes

    | Node | Description | |---|---| | Resize To Canvas Size | Resize an image to a canvas using a 9-point anchor grid (flush edge/corner placement) | | Resize To Canvas Size (COI) | Resize with mask-driven Centre of Interest placement — a 7×7 target grid controls where the detected subject lands on the canvas | | Load Image To Canvas | Load an image from disk and interactively composite it onto a canvas — live preview, rotary zoom dials, drag-to-reposition, flip controls, and a context-aware mask |


    Resize To Canvas Size

    Resize To Canvas Size node

    Features

    • Photoshop-style anchor grid — 3×3 button grid at the top of the node; click any cell to set the anchor point. The selected cell shows a dot, directly adjacent cells show arrows pointing away from the anchor, and non-adjacent cells are blank
    • 5 scale methods — choose how the image is scaled before the final fit step
    • 2 fill methods — crop (trim overflow, optionally pad gaps) or stretch (distort to fill)
    • 6 padding fill options — black, white, 50% gray, transparent, custom colour (native colour picker), or random noise with a seed
    • Outputs RGBA image + padding mask — the padding_mask output is 1 where padding was added and 0 where original image pixels are, making it easy to pipe into an inpainting workflow

    Inputs

    | Input | Type | Description | |---|---|---| | image | IMAGE | Source image (batch supported) | | width | INT | Target canvas width in pixels | | height | INT | Target canvas height in pixels | | anchor | Anchor Grid | 3×3 click grid — sets the anchor point for crop/pad positioning | | scale_method | Dropdown | How to scale the image before filling (see below) | | fill_method | Dropdown | crop or stretch | | padding_fill | Dropdown | How to fill any gaps (see below) | | custom_color_hex | Colour picker | Only shown when padding_fill is custom | | custom_color_hex_input | STRING (optional connector) | Only visible when padding_fill is custom. Connect any node that outputs a #RRGGBB / #RRGGBBAA string to override the colour picker. Invalid values fall back to the picker value. | | noise_seed | INT | Seed for noise generation — only shown when padding_fill is noise |

    Custom colour fill with input connector

    Outputs

    | Output | Type | Description | |---|---|---| | IMAGE | IMAGE (RGBA) | The resized image | | padding_mask | MASK | 1.0 where padding was added, 0.0 where original image pixels are. All zeros when no padding occurred. |

    Basic resize workflow — noise-padded portrait crop result


    Resize To Canvas Size (COI)

    | Manual mode | Mask mode (mask_weighted_centre) | |---|---| | COI node — manual mode | COI node — mask weighted centre mode |

    Places the image on the canvas by first computing a Centre of Interest (COI) from an optional mask, then using a 7×7 target grid to control exactly where that COI lands on the canvas.

    Core concept

    The anchor grid in this node always means: "place the COI at this point on the canvas."

    | Mode | COI used for placement | |---|---| | manual | Image centre (src_w/2, src_h/2) | | mask_bbox_centre | Centre of the bounding box of non-zero mask pixels | | mask_weighted_centre | Weighted centroid (centre of mass) — large contiguous masked areas dominate; stray pixels have negligible pull |

    If no mask is connected, or anchor_mode is manual, the image centre is used regardless of mode.

    The 7×7 grid

    A 7×7 lattice of 49 selectable target points is drawn over a 3×3 nonant background. The 7 positions per axis sit at multiples of 1/6 of the canvas dimension:

    • Even indices (0, 2, 4, 6) — nonant boundaries (0, W/3, 2W/3, W)
    • Odd indices (1, 3, 5) — nonant midpoints and centres (W/6, W/2, 5W/6)

    Click any dot to set the canvas target for the COI. The default (3,3) places the COI at canvas centre. Boundary dots are displayed slightly larger; the selected dot is highlighted in blue.

    Inputs

    | Input | Type | Description | |---|---|---| | image | IMAGE | Source image (batch supported) | | width | INT | Target canvas width in pixels | | height | INT | Target canvas height in pixels | | anchor_grid | 7×7 COI Grid | Click grid — selects where on the canvas the Centre of Interest is placed | | anchor_mode | Dropdown | manual, mask_bbox_centre, or mask_weighted_centre | | scale_method | Dropdown | How to scale the image before filling | | fill_method | Dropdown | crop or stretch | | padding_fill | Dropdown | How to fill any gaps | | custom_color_hex | Colour picker | Only shown when padding_fill is custom | | custom_color_hex_input | STRING (optional connector) | Overrides the colour picker when connected | | noise_seed | INT | Only shown when padding_fill is noise | | mask | MASK (optional) | Mask used to compute the COI when anchor_mode is not manual. If the mask size differs from the image, it is resized automatically. |

    Outputs

    | Output | Type | Description | |---|---|---| | IMAGE | IMAGE (RGBA) | The resized image | | padding_mask | MASK | 1.0 where padding was added, 0.0 where original image pixels are |


    Scale Methods

    | Method | Behaviour | |---|---| | None – use original size | No scaling — use the image at its original resolution | | Fit to Canvas short edge | Scale so the image's shortest edge matches the canvas's shortest edge | | Fit to Canvas long edge | Scale so the image's longest edge matches the canvas's longest edge | | Fit to Canvas height | Scale to match the canvas height exactly | | Fit to Canvas width | Scale to match the canvas width exactly |

    After scaling, the image may be larger than the canvas (overflow → crop) or smaller (gap → filled with padding_fill).


    Padding Fill Options

    | Option | Behaviour | |---|---| | black | Fill gaps with solid black | | white | Fill gaps with solid white | | gray_50 | Fill gaps with 50% gray | | transparent | Fill gaps with transparency (alpha = 0) | | custom | Fill gaps with a colour chosen via the colour picker | | noise | Fill gaps with random per-pixel noise — a noise_seed field appears to control reproducibility |


    Fill Methods

    | Method | Behaviour | |---|---| | crop | Place the scaled image using the anchor position, trim any overflow, fill any gaps with the padding colour. Anchor determines which part of the image is kept. | | stretch | Distort/resize the scaled image to exactly fill the canvas. Anchor is not used. |


    Common Recipes

    Crop to square, keep the centre:

    • scale_method: Fit to Canvas short edge · fill_method: crop · anchor: center

    Letterbox / pillarbox (fit within canvas, pad the rest):

    • scale_method: Fit to Canvas long edge · fill_method: crop · padding_fill: black

    Distort to fill (ignore aspect ratio):

    • scale_method: None – use original size · fill_method: stretch

    Cookie-cutter crop at original resolution:

    • scale_method: None – use original size · fill_method: crop — canvas dimensions act as a crop window at the anchor position

    Inpaint the padded region:

    • Connect padding_mask to a KSampler inpainting setup to fill padding areas with generated content

    Face-centred portrait crop (COI node):

    • Feed a face detection mask (e.g. UltralyticsDetectorProviderBboxDetectorSEGSSAMDetectorCombined) into the mask input
    • Set anchor_mode: mask_bbox_centre · grid: (3,3) (canvas centre)
    • Set scale_method: Fit to Canvas width — the detected face will be centred on the canvas regardless of where it appears in the source image

    Face-centred portrait crop — single subject, mask_bbox_centre

    With multiple subjects, mask_weighted_centre finds the centre of gravity across all detected regions, automatically framing everyone together:

    Two-subject crop — both faces detected, mask_weighted_centre centres between them



    Load Image To Canvas

    Load Image To Canvas — demo

    | Locked aspect ratio | Unlocked aspect ratio | |---|---| | Load Image To Canvas — locked aspect ratio mode | Load Image To Canvas — unlocked aspect ratio mode |

    Loads an image from disk and composites it onto a user-defined canvas with full interactive control over scaling, zoom, positioning, and padding. Designed as a manual, intentional starting node — user interaction is the point.

    Load Image To Canvas — full workflow

    Features

    • Live preview — canvas-aspect-ratio preview embedded in the node, updates reactively as every parameter changes. A resolution label (W × H) sits just below the preview and stays accurate when canvas dimensions change
    • Drag to reposition — click and drag anywhere on the preview to move the image; the offset_x / offset_y values update live
    • Scroll to zoom — scroll the mouse wheel over the preview or the dials; velocity-sensitive steps (rapid scroll = coarser, slow = finer). Shift+scroll for large jumps
    • Shift+drag crop box — draw an aspect-ratio-locked region on the preview; releasing snaps zoom and offset so that region fills the canvas exactly
    • Rotary zoom dials — canvas-based knobs for zoom. Drag diagonally to change value; click the numeric readout to type an exact value
    • Aspect ratio lock (🔒 Aspect Ratio Locked / 🔓 Aspect Ratio Unlocked) — single unified dial when locked; independent Width and Height dials when unlocked. Locking preserves the x:y ratio you set while unlocked
    • Fit Width / Fit Height buttons — one-shot snap buttons flanking the dial(s); scale the image to fill the chosen canvas axis and centre the offset on that axis
    • = (Equalize) button — appears between the Width and Height dials in unlocked mode; copies the Width zoom value to Height, restoring the image's natural (undistorted) aspect ratio
    • ⇄ Swap W↔H button — swaps canvas_width and canvas_height with each other; useful for quickly toggling between portrait and landscape canvas orientations
    • Flip controls — horizontal and vertical flip
    • 6 padding fill options — black, white, 50% gray, transparent, custom colour, or noise (same options as the rest of the pack)
    • Context-aware mask output — canvas-space (white = image, black = padding) when padding is present; source-image-space (white = sampled pixels) when fully covered
    • Original image passthroughORIGINAL IMAGE output carries the raw unmodified loaded image with no transforms applied

    Inputs

    | Input | Type | Description | |---|---|---| | image | File picker | Source image loaded from ComfyUI's input/ folder | | canvas_width | INT | Target canvas width in pixels | | canvas_height | INT | Target canvas height in pixels | | padding_fill | Dropdown | How to fill any uncovered canvas area | | custom_color_hex | Colour picker | Only shown when padding_fill is custom | | custom_color_hex_input | STRING (optional connector) | Overrides the picker when connected | | noise_seed | INT | Only shown when padding_fill is noise | | lock_aspect_ratio | BOOLEAN | Controlled via the lock toggle button in the Zoom section | | zoom_x | FLOAT | Horizontal zoom multiplier (1.0 = 100%) — set via dial or readout | | zoom_y | FLOAT | Vertical zoom multiplier — independent when unlocked | | offset_x | FLOAT | Horizontal position (0.5 = centred) — set via drag or manual input | | offset_y | FLOAT | Vertical position (0.5 = centred) | | flip_horizontal | BOOLEAN | Mirror left/right | | flip_vertical | BOOLEAN | Mirror top/bottom | | invert_mask | BOOLEAN | Flip white/black on the mask output |

    Outputs

    | Output | Type | Description | |---|---|---| | IMAGE | IMAGE (RGBA) | Composited canvas result | | MASK | MASK | Context-aware mask — canvas-space when padding present; source-image-space when no padding | | ORIGINAL IMAGE | IMAGE (RGBA) | Raw unmodified loaded image, no transforms applied |

    Mask detail

    | State | Mask space | Mask content | |---|---|---| | Image has padding | Canvas space (canvas W × H) | White = where image sits, black = padding | | No padding (fully covered/cropped) | Source image space (original W × H) | White = which source pixels were sampled |

    invert_mask flips this in all cases.

    Transform order

    1. Load image
    2. Apply flip_horizontal / flip_vertical
    3. Apply zoom_x / zoom_y
    4. Apply offset_x / offset_y (image centre placed at offset × canvas)
    5. Composite onto canvas with padding_fill
    6. Compute mask

    Installation

    Clone this repository into your ComfyUI custom_nodes folder:

    cd ComfyUI/custom_nodes
    git clone https://github.com/Carasibana/ComfyUI-ResizeToCanvasSize.git
    

    Then restart ComfyUI.

    No additional Python packages are required beyond those already included with ComfyUI (Pillow, NumPy, PyTorch).


    File Structure

    ComfyUI-ResizeToCanvasSize/
    ├── __init__.py               # Registers nodes and web directory
    ├── nodes.py                  # Python node logic
    ├── js/
    │   └── anchor_widget.js      # Custom anchor grid and COI grid UI widgets
    └── examples/
        └── ResizeToCanvasSize_Example_Workflow.json
                                  # Example workflow: basic resize (top) + face-COI resize (bottom)
    

    Run ComfyUI workflows without the setup

    No installs, no CUDA version roulette, no GPU sitting idle on your bill. Bring a workflow and run it in the browser.

    Learn more