Image Canvas Expand (genkai)
Crop and pad in a panel instead of doing arithmetic in your head
- image
You have a 1024×1024 image and you want 1024×1536 of it to hand to a video model. So you open a calculator, work out that bottom needs to be 512, run the whole queue to find out it looks wrong, and repeat. Image Canvas Expand deletes that loop: load the image, drag the edges, see the result before you queue.
What it is
A loader with a canvas editor bolted on. It reads an image from ComfyUI's input folder, applies four per-edge pixel offsets (negative crops, positive expands), fills anything new with a colour, and returns a normal IMAGE. It's a deterministic pixel operation - no model, no sampler, no seed.
The post-processing doc argues that most of this layer isn't AI at all, and knowing the primitives is how you stop burning a diffusion pass on work a lookup table does perfectly. Padding is the purest case: the answer is fully determined by the numbers you pick.
Where you'll actually use it:
- Getting to a trained aspect ratio without stretching. The resolution section is blunt about the failure mode - generate at ratios the model was trained on (or, for 2026 models, inside the 1–2MP band) and pad rather than squash. Same step gets a first frame into shape for image-to-video; H3, Wan and LTX all want the target aspect.
- Setting up an outpaint. The trick that circulates now is to pad with a solid colour and ask the model to replace it - 200px of red per side, prompt "remove the red padding and show what's behind it" (inpainting doc). Hence the custom hex fill.
How it works
Under the panel it's PIL, numpy and torch: open the file, apply EXIF rotation, convert to RGB, paste the source onto a canvas of (width + left + right) × (height + top + bottom) at offset (left, top), divide by 255, return a 1-image batch. Original pixels are never resized, so nothing gets resampled.
The arithmetic is guarded. Offsets clamp to ±8192, output is capped at 16384px per edge and 64 million pixels, and cropping must leave at least one source pixel. Those run as VALIDATE_INPUTS, so you get a plain-English refusal instead of a traceback - "Cropping must leave at least one source pixel and a positive output size".
The panel is a frontend script that hides the seven real widgets and draws a toolbar, a preview, an original→output size readout and a Crop / expand modal with eight drag handles. It works on a draft: nothing is written back until you press Apply.
The inputs and output that matter
image_file(STRING) - a path relative to ComfyUI'sinputdirectory. You don't type it: Load image…, drag-and-drop onto the panel, or hover and press Ctrl+V all upload it and set the field.left/right/top/bottom(INT, default0, ±8192) - negative crops, positive expands. This is the whole node.fill(black / white / custom) andcustom_color(hex, default#808080) - what the added area gets. Three-digit hex works.image(IMAGE) - the result, a one-image batch.
Install
Part of the pack; nothing else to fetch:
cd ComfyUI/custom_nodes
git clone https://github.com/GENKAIx/Genkai-ComfyUI-Nodes.git
Restart, then Ctrl+F5. It lives in GENKAI/Image, and since Pillow, NumPy and PyTorch ship with ComfyUI, it needs no models and no pip installs of its own.
The pack-level exception breaks all three of these nodes at once: __init__.py pulls in the video modules, and waveform.py imports av and aiohttp at the top level. The pack declares no dependencies anywhere, so without PyAV the import fails and every node in the pack vanishes. pip install av aiohttp and restart.
Where people get burned
It has no IMAGE input. Despite the category, this is a loader-and-edit node: it reads a file, not a tensor, so you can't drop it after your sampler and pad the result - save the image, then load it here. For padding mid-graph, the same pack's Image Expand With Fill takes a real IMAGE input, four non-negative offsets and a stretch_fill toggle (edge-pixel stretch or black bars).
It converts to RGB, so alpha is gone. Transparent PNGs come out flattened against your fill colour. Matters for logo plates; doesn't for photos.
Closing the editor without Apply discards your drags. The panel reads the widget values, so once you Apply, dragging again starts from the numbers you saved.
Custom fill wants a real hex value. custom with garbage in custom_color fails validation; bare 7F8A90 without the # is fine.
One honest caveat about the pack
GENKAI's pack is a solo project with a small footprint - a Telegram channel rather than a subreddit thread, and no community history to search. Since custom nodes run arbitrary Python with no review and no sandbox (the ecosystem's oldest problem), that's when you read the source. Here it's ~120 lines of PIL and validation.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| image_file | STRING | — | |
| left | INT | 0-8192–8192 | — |
| right | INT | 0-8192–8192 | — |
| top | INT | 0-8192–8192 | — |
| bottom | INT | 0-8192–8192 | — |
| fill | COMBO | black | 3 options: black, white, custom |
| custom_color | STRING | #808080 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |