Nodes/Skkut Utils/Skutils Scale Image to Total Pixels
ComfyUI Node

Skutils Scale Image to Total Pixels

The Resize Node That Won't Kill Your Workflow When the Image Is Missing

By skkut·Created 29 days ago·Updated a day ago· 0
Skutils Scale Image to Total Pixels
  • image
  • IMAGE
◄upscale_methodnearest-exact►
◄megapixels1.00►
◄resolution_steps1►

You wanted a workflow with an optional second reference image: plug it in if you have one, leave the slot empty if you don't. You dropped the core Scale Image to Total Pixels node inline to normalize that image's pixel budget, and the whole thing stopped queueing. Not on the run where the image was missing - on every run, including the ones where the slot was filled.

That's not a bug in your graph. Skutils Scale Image to Total Pixels exists to fix that one situation, and it changes exactly one thing about the built-in node.

Why the built-in node can't be left empty

ComfyUI validates every node in a prompt before it executes a single one. A required input with nothing wired into it fails the whole submission with Prompt outputs failed validation - and that check is not lazy-aware. It doesn't matter that the node sits on a branch a switch never selects, or behind a boolean you set to false. "That code path never runs" is not a defence.

The core node's image input is required, and its body opens with samples = image.movedim(-1, 1), so there's no None guard either. The usual workarounds are all worse than the problem:

  • Feed it a dummy image. Now the graph resizes and carries an image nobody asked for, and anything downstream consumes it silently.
  • Put a switch after the resize. The switch is never evaluated, because validation happens first. Still rejected.
  • Swap in LayerUtility: ImageScaleByAspectRatio V2 (comfyui_layerstyle). Its image really is optional and it really does return None - but it's a different node: eleven widgets, budget in kilo-pixels, rounding up instead of to nearest - different dimensions. A workaround, not a drop-in.

This node removes the constraint instead of routing around it. Widgets, ranges, defaults and arithmetic are copied from comfy_extras/nodes_post_processing.py; the only edits are moving image into the optional section and adding a None return.

The inputs that matter

Three widgets, all identical to the core node:

  • upscale_method - nearest-exact / bilinear / area / bicubic / lanczos, the core node's list in its order, default nearest-exact. That default isn't a recommendation, it's just the first entry of the list. For shrink-to-budget work, lanczos and area are what people actually reach for.
  • megapixels - float, default 1.0, range 0.01–16.0. Per the author's tooltip, 1 MP here means 1024×1024 = 1,048,576 px, not a decimal megapixel. The image is scaled by sqrt(target / actual): a budget, not a dimension.
  • resolution_steps - int, default 1 (no rounding), up to 256, hidden behind the advanced flag. Round width and height to the nearest multiple of this: try 32/64 if the result feeds something that likes latent-friendly grids.

And image, which is the whole point: the one optional input. Leave it unconnected and the node returns nothing instead of failing validation.

The output is a single IMAGE. Where you wire it is the part people get wrong. None propagates like any other value, so whatever consumes it has to tolerate it - an autogrow image container such as TextEncodeQwenImage21's images does, and so do the optional on_true / on_false inputs on a switch node. A plain required IMAGE input does not, and will fail validation in its turn. Put this node at the end of an optional branch, never in the middle of one that must always produce an image.

Install

The pack is on the ComfyUI Registry - Manager → search skkut-utils - or clone it the old way:

cd ComfyUI/custom_nodes
git clone https://github.com/skkut/ComfyUI-skkut-utils.git

Restart ComfyUI. No build step, no models. The repo's requirements.txt holds one optional entry, piexif, used only by the pack's Save Compressed Weppy node - skip it if all you want is this resize.

Know what else you're getting, though: the repo bundles six utilities and two self-activate on startup - an OS-synced theme and a floating execution timer bottom-right. If your theme changes after installing, that's this, not a corrupted frontend.

Practical notes and traps

A number worth stealing: the Comfy Org handbook's SeedVR2 recipe downscales the input to 0.35 megapixels here before upscaling - a soft source has no detail at full size to recover, so you match resolution to actual sharpness and let the model rebuild. Counterintuitive, and it works.

Rounding to nearest, not up. If you're replacing a LayerUtility aspect-ratio node with this one (or the reverse), expect dimensions up to one step apart. Different rule, not a bug.

Tiny megapixels + big resolution_steps can round a dimension to 0 - inherited from the core node, which keeps the arithmetic identical on purpose.

The image port renders below the widgets, since ComfyUI puts optional inputs after required ones. Cosmetic only; the settings are otherwise the core node's.

Categoryutils/resolution

Inputs (4)

NameTypeDefaultDescription
upscale_methodCOMBOnearest-exactResampling filter — the same list, in the same order, as the core node.
megapixelsFLOAT1.000.01–16Target total pixel count in megapixels (1 MP = 1024×1024 px). The image is scaled by sqrt(target / actual), exactly as the core node does.
resolution_stepsINT11–256Round width and height to a multiple of this. The core node's default is 1 — no rounding.
imageoptIMAGEOptional. Leave unconnected to skip the resize and output nothing.

Outputs (1)

NameTypeDescription
IMAGEIMAGE—