Nodes/comfyui-outpaintdirectional/πŸ“ Outpaint Directional Canvas Pad
ComfyUI Node

πŸ“ Outpaint Directional Canvas Pad

Decide which side of the frame gets invented

By hyukudanΒ·Created 2 months agoΒ·Updated 2 months agoΒ· 1
πŸ“ Outpaint Directional Canvas Pad
  • image
  • mask
  • image
  • mask
β—„target_width736β–Ί
β—„target_height1280β–Ί
β—„feathering48β–Ί
β—„directioncenter (50/50 both sides)β–Ί
β—„upscale_methodbicubicβ–Ί
β—„custom_bias0.50β–Ί

You have a 16:9 clip and you need 9:16, or a square photo that has to become a widescreen plate. Cropping throws away half the frame, so you're doing the generative version: build a bigger canvas, park the original on it, let the model invent the rest. OutpaintDirectionalPad is the first half of that job, and it's the half people fumble. It generates nothing. It decides where the empty canvas lives.

That decision matters more than it sounds. Extension quality collapses when the model is guessing in an ambiguous direction: ask for headroom and floor space at the same time and you get a poster frame. Anchor the source low, so 100% of the new pixels are sky, and the same model does much better work. The author's dropdown even names the cases in Spanish (cielo sky, suelo floor) - this was built by someone converting footage to vertical by hand.

What it actually does to your pixels

It's about thirty lines of arithmetic, so you can predict it exactly. It computes scaling_factor = min(target_width / W, target_height / H) and only ever scales down: if your source is bigger than the target it's resized with the method you picked so the whole frame fits inside the target box, aspect intact. Smaller in both axes, and it's pasted at native size. There's no upscale path, so this node never invents detail by resampling.

Then it places that source on a black canvas of exactly target_width Γ— target_height, distributing the leftover space according to direction, and hands you a mask that's 1 on the new canvas and 0 over the source. After the sampler runs, that mask is what tells the model which part of the frame it may touch.

Do the math once before wiring it up. A 1920Γ—1080 clip into the defaults (736Γ—1280) scales to 736Γ—414 - the original occupies 32% of the canvas height and the model invents the other 866 pixels. That's a lot of invention from a small anchor. Extend in two passes with a composite between them, or skip the problem entirely: LTX 2.3+ generates native portrait up to 1080Γ—1920 (thread 1rlm21a, +685).

The inputs that matter

  • target_width / target_height - the canvas you pad up to (64–16384, step 8). Defaults 736Γ—1280; for true 9:16 use 720Γ—1280, and check what your video model's VAE wants before trusting the step-8 grid.
  • direction - six choices. center splits the leftover evenly; bottom (... cielo) anchors the source low so everything new appears above it; top (... suelo) does the reverse; left / right expand sideways; custom_vertical_bias hands the split to custom_bias.
  • feathering - pixels of transition gradient at the seam. Default 48, max 512. This is the setting that can eat your original; see below.
  • upscale_method - bicubic (default), bilinear, nearest-exact, area, lanczos. Only consulted when downscaling, where area and lanczos beat bicubic on big reductions.
  • custom_bias (optional) - for custom_vertical_bias, the fraction of leftover height placed above the source. The label says "0.0=bottom, 1.0=top" without clarifying which end that means; the code puts 0.0 at the top. Watch the preview on the first run.
  • mask (optional) - resized into the source rectangle of the output mask. It can't mark padded areas as "keep"; those are forced to 1.

Outputs

image is the padded batch (resampled source plus black fill) and mask is the 1/0 map above. From there: an inpaint-aware encode (VAE Encode (for Inpainting), or InpaintModelConditioning for Fill-style models) β†’ KSampler at high denoise β†’ composite the result rather than shipping the whole frame through another VAE pass.

Install

ComfyUI Manager β†’ search ComfyUI-OutpaintDirectional β†’ Install β†’ restart. Or:

cd ComfyUI/custom_nodes
git clone https://github.com/hyukudan/ComfyUI-OutpaintDirectional.git

That's the whole install: one declared dependency (torch, already present), no requirements.txt, no compiled extension, no model download. MIT, v1.0.0, one 200-line Python file. If Manager can't find it in the registry yet, clone it and move on.

Where it bites

Feathering cuts into your original. The gradient is written inside the source rectangle - the row at the seam starts near mask 1.0 and fades to 0 over feathering pixels of your footage. At the default 48 the model may repaint 48px of the original per touched edge. Raise it for stubborn seams; set 0 if the original must stay untouched, and accept a hard seam.

It's a horizontal gradient, not an outline. Bands are written along the top and bottom edge rows only, so left and right seams come out harder than the word "feathering" implies.

Black fill isn't neutral. Some models read a black band as content - if yours does, pad with a loud solid colour and let an instruction model remove it.

Masking only works if the sampler honours the mask. The README's "batched video tensors, LTX / Wan / Hunyuan" line just means this is a plain torch op, so it takes a 200-frame IMAGE batch happily. Feed those frames to an I2V sampler that never receives the mask and you've built a black-bar generator. The feather loop is also a Python for over batch Γ— feathering, so 300 frames at feathering 256 is a lot of tiny ops for cosmetic work.

Byte-exact preservation? Wrong tool. The source is resampled and re-encoded. Inpaint Crop and Stitch is what people reach for when unmasked pixels must not change, and it handles outpainting natively.

Summary: deterministic, dependency-free, and about as obscure as a ComfyUI node gets - zero mentions of this pack or its author in the reddit corpus, so assume you're the first to hit whatever you hit. Survivable, because the whole behaviour is one readable file.

Categoryimage/outpainting

Inputs (8)

NameTypeDefaultDescription
imageIMAGEβ€”
target_widthINT73664–16384β€”
target_heightINT128064–16384β€”
featheringINT480–512β€”
directionCOMBOcenter (50/50 both sides)6 options: center (50/50 both sides), bottom (outpaint TOP only / cielo), top (outpaint BOTTOM only / suelo), left (outpaint RIGHT only), right (outpaint LEFT only), custom_vertical_bias (0.0=bottom, 1.0=top)
upscale_methodCOMBObicubic5 options: bicubic, bilinear, nearest-exact, area, lanczos
maskoptMASKβ€”
custom_biasoptFLOAT0.500–1β€”

Outputs (2)

NameTypeDescription
imageIMAGEβ€”
maskMASKβ€”