Nodes/Klinter_nodes/Zoom Out Composer - klinter
ComfyUI Node

Zoom Out Composer - klinter

Fake a camera move between images, no video model required

By klinter007·Created 3 years ago·Updated 9 months ago· 19
Zoom Out Composer - klinter
  • images
  • zoomed_frames
zoom1.50
frames_per_transition24
modezoom-out
output_width1024
output_height1024
keep_aspecttrue

You've got a folder of outpainted stills, each one a little wider than the last, and you want that smooth push-in or pull-back between them that turns a slideshow into a camera move. That's the whole job of Zoom Out Composer. Feed it two or more images and it hands back a stack of zooming frames you can encode straight to video - no diffusion model in the loop, no VRAM fireworks, just math.

It lives inside Klinter_nodes, the grab-bag utility pack by klinter (Gili Ben-Shahar) that also ships Outpaint Padding, Folder Loader, and a pile of string/video helpers. It's the sort of node you don't think about until you're stitching an outpainting zoom sequence and realize stock ComfyUI has no built-in "animate a zoom" step. That's exactly where a Ken Burns-style clip belongs: generate the stills, compose the motion here, then encode.

How it actually works

Open custom_nodes/klinter_nodes/zoom_out_composer.py and the mechanism is refreshingly plain. Give it M images and it generates frames_per_transition × (M − 1) frames. Each frame gets mapped through an easeInOutSine curve, so the zoom eases in and out instead of lurching - that's what makes it look like footage rather than a stretched thumbnail. Per frame it picks the current image, builds a 2×3 affine matrix, and samples through grid_sample to produce the zoomed result.

Two things follow from that code. First, the images don't need to match in size - the final resize normalizes everything to your output_width/output_height. Second, it's a CPU loop that runs as one blocking node: the tqdm progress bar lands in your terminal, not on the ComfyUI queue bar, so don't assume it hung.

The twist you'll hit first

The default mode is a lie, and I verified the math so I'm not guessing. The node computes local_zoom = zoom ^ (1 + frac) where zoom is always ≥ 1.1, then scales each frame by 1 / local_zoom. A scale below 1 means grid_sample produces a magnified center crop. So "zoom-out" mode actually plays as a slow push-in, hard-cutting between images at each boundary, and "zoom-in" mode (which simply reverses the frame order) is the one that gives you a pull-back. Black bars never appear - it's crops all the way down. Pick your mode by the direction you want on screen, not by the label.

Inputs that matter

  • images - an IMAGE batch of 2+. One image raises a ValueError; the whole node refuses to run.
  • zoom (1.1–3.0, default 1.5) - the magnification each frame applies; higher feels faster and punchier.
  • frames_per_transition (1–120, default 24) - how long each hop between images lasts. 4 images at 24 is 72 frames; the round-trip modes double that, and all of it lands in one tensor in RAM.
  • mode - zoom-out (default), zoom-in, zoom-out-in, zoom-in-out. The round-trip ones loop back to the first image for a seamless bounce.
  • output_width/height + keep_aspect - heads-up: with keep_aspect on, the output is resized to fit inside that box, not padded to it. If you need an exact canvas, turn it off.

Output and wiring

It emits one thing, zoomed_frames (IMAGE, [N, H, W, C]) - plain ComfyUI image batches, so it drops straight into a core SaveAnimatedWEBP node or anything in VideoHelperSuite/VHS for MP4 or GIF. That's the whole pipeline: stills → this node → encoder → clip.

Installing it

Same as any custom node: ComfyUI Manager → search "Klinter_nodes", or

cd ComfyUI/custom_nodes
git clone https://github.com/klinter007/klinter_nodes

then restart ComfyUI. There's no requirements.txt - the code only needs torch and tqdm, both of which ComfyUI already ships. The README is thin (no real install or dependency docs), so don't go looking for setup steps that aren't there.

Troubleshooting

The one real footgun: recent versions of the pack were migrated to the newer ComfyUI Nodes 2.0 SDK and import from comfy_api.latest import io at module top level. Stock ComfyUI doesn't ship that module, so if the node fails to load with No module named 'comfy_api', that's why - you'll need a build that bundles the new SDK or the comfyapi pip package. Beyond that, keep an eye on total frame count (round-trip modes double it), and remember the mode-direction inversion above. Small pack, zero documentation, but the node itself is genuinely handy once you know its two quirks.

Categoryklinter

Inputs (7)

NameTypeDefaultDescription
imagesIMAGE
zoomFLOAT1.501.1–3
frames_per_transitionINT241–120
modeCOMBOzoom-out4 options: zoom-out, zoom-in, zoom-out-in, zoom-in-out
output_widthINT102416–8192
output_heightINT102416–8192
keep_aspectBOOLEANtrue

Outputs (1)

NameTypeDescription
zoomed_framesIMAGE