ComfyUI Node

Scene Framer

Cut a panorama into shots without building a wall of crop nodes

By ankushgawande·Created 6 months ago·Updated 6 months ago· 5
Scene Framer
  • background_image
  • shot_1
  • shot_2
  • shot_3
  • shot_4
  • shot_5
  • shot_6
  • shot_7
  • shot_8
shot_data[]

Why this node exists

Every time you want several different views out of one generated image - a wide establishing shot plus the two close-ups it frames - ComfyUI's default answer is "stack crop nodes," and the graph turns into a fire hazard of typed-in x/y/w/h coordinates. Scene Framer is the small fix for that: one node where you drag the shots out visually, right on the image, and every shot walks out through its own IMAGE socket.

It's aimed at a specific, very real workflow: you've got one background or panorama (think "one environment, several camera views," which is how people keep multi-shot scenes consistent) and you want to frame each view by eye, then send the crops off to different parts of the pipeline. Each shot goes to a different KSampler img2img pass, a ControlNet node, or an upscaler, and the graph stays readable instead of dissolving into crop-node spaghetti.

How it works

Scene Framer is two halves. The front half is a JavaScript extension that draws a canvas inside the node: you click to place a box, drag the corner handles to resize it, and switch between shots with tabs. Each shot gets its own color and a name, and you can add or remove shots up to eight. When you stop dragging, your boxes get serialized to JSON and written into a hidden shot_data widget.

The back half is a plain Python node. process() takes your background_image, grabs the first frame, and crops with ordinary numpy slicing. Coordinates are clamped to the image bounds so a stray box can't crash you. If a shot's crop size doesn't match its output resolution, it's resized with PIL LANCZOS - PIL is what ComfyUI already uses everywhere, so this adds nothing to install. Inactive shots get a 1×1 black blank rather than nothing, so all eight sockets always have a tensor on them.

That split matters because the interactive canvas lives in the browser, not the graph. If you only see two plain widgets, your frontend hasn't picked up the node's JS yet.

The inputs and outputs that matter

Honestly, there are only two inputs and you mostly touch one of them:

  • background_image (IMAGE) - the panorama or background you're framing. Only the first frame is used, so feed it a single image, not a batch.
  • shot_data (STRING) - a hidden widget holding your crop boxes as JSON. Don't hand-edit it; the canvas writes it for you.

The outputs are shot_1 through shot_8, each an IMAGE. Disabled shots still output - a black 1×1 blank - which is why you should switch off boxes you're not using rather than leaving them empty. Wire the active ones into any image input downstream: img2img, ControlNet, a preview, whatever you'd feed a regular CropImage result into.

The panel also lets you set each shot's output resolution via presets (1024×576, 1216×832, 1024×1024, 832×1216, 512×512, 1920×1080) or custom W×H. That resize is a pure stretch, so treat it like any upscale: small deltas are fine, but pulling a 400px crop up to 1080p just blurs it. Send those shots through a real upscaler or a low-denoise img2img pass instead.

Installing it

Two routes, same result. In ComfyUI Manager, search for "Scene Framer" and install. Or by hand:

cd ComfyUI/custom_nodes
git clone https://github.com/ankushgawande/comfyui-scene-framer

That's the whole install - there's no requirements.txt, no model files to download, no GPU-heavy dependencies. The pack only needs torch, numpy, and PIL, all of which ship with ComfyUI. Restart ComfyUI, then hard-refresh your browser tab (Ctrl+Shift+R) so the frontend loads the canvas code.

Gotchas

The biggest one is the frontend: the canvas needs a fresh JS bundle, and it needs the image actually in memory. Connect a LoadImage node and run the graph once, or click Refresh image in the panel, and the canvas loads the source on its own.

The second gotcha is coordinate space. Shot boxes are stored in the source image's pixel coordinates, so if you swap a 1024-wide background for a 1536-wide one, every box lands somewhere it wasn't meant to be. Reframe after changing source resolution.

Third: disabled shots output that 1×1 black image, and some downstream nodes - resamplers, anything that assumes a meaningful image - will choke on a 1-pixel tensor. Toggle boxes off, don't just leave them dead.

Verdict

For a one-off crop, the built-in CropImage is free and does the job. Scene Framer earns its slot when the task is "frame several views from one environment and keep the graph legible" - the author is a VFX compositor, and it shows in the framing-first design. It's a small, well-made utility, not a miracle: it extracts shots, it doesn't generate new content. Frame here, do the heavy lifting downstream.

CategoryScene Framer

Inputs (2)

NameTypeDefaultDescription
background_imageIMAGE
shot_dataSTRING[]

Outputs (8)

NameTypeDescription
shot_1IMAGE
shot_2IMAGE
shot_3IMAGE
shot_4IMAGE
shot_5IMAGE
shot_6IMAGE
shot_7IMAGE
shot_8IMAGE