Nodes/GPU LayerStyles/GPU LayerStyles ImageScaleDown
ComfyUI Node

GPU LayerStyles ImageScaleDown

Crisp downscaling, and the pre-pass that makes upscalers behave

By lany42·Created 23 days ago·Updated 15 days ago· 0
GPU LayerStyles ImageScaleDown
  • image
  • image
◄width512►
◄height512►
◄methodbicubic►
◄output_devicecpu►
◄batch_size0►

Everyone has an upscale node. What you actually need more often is the opposite, done properly: get a 4K batch down to exact dimensions without the aliasing shimmer that a lazy F.interpolate leaves behind. GPULayerStyles_ImageScaleDown downscales to exact width/height with antialiased bicubic or Lanczos-3, in float32, across a whole batch.

Why downscaling is the underrated node

Two jobs. The obvious one is thumbnails and previews - you don't want to save a 1200-frame 1080p sequence just to look at it, and a downscale pass is a few milliseconds of arithmetic.

The less obvious one is upstream of an upscaler. When a source is soft or noisy, feeding a restoration model like SeedVR2 at full resolution gives it nothing to work with; the community-standard move is to downscale to roughly a third of a megapixel first and let the model rebuild from a sharper-relative base. This node is exactly that pre-pass, and it's batch-capable so you're not looping one frame at a time.

It's also the honest answer to "make this fit 1024×1024 for an img2img pass" - deterministic, exact, one node.

How it works

Both methods resize in float32 on ComfyUI's compute device. Bicubic is Torch's interpolate with antialiasing enabled, so the filter kernel scales with the downscale factor - that's what stops fine texture turning into a moiré mess when you drop 4K to 512. Lanczos-3 is Pillow's algorithm reimplemented for tensors: a windowed-sinc filter with a scale-aware tap count (up to ceil(3 × scale) × 2 + 1 taps per axis), per-axis coefficients computed once per invocation and reused across every chunk.

If the image carries alpha, filtering happens on premultiplied RGB. That matters more than it sounds: filtering colour and alpha separately makes semi-transparent edges leak their (often black or undefined) RGB into the result, which is the classic dark fringe around a cut-out after a resize. Output is clamped to [0, 1], and requesting unchanged dimensions copies the input without filtering at all.

Inputs and output

image in. Then width and height, integers from 1 to 16384, both defaulting to 512 - so a node you drop in and queue without touching the widgets will hand you a 512×512 batch, which is the first thing to check if your output looks suspiciously small.

method is bicubic (default) or lanczos. Lanczos is the sharper of the two on fine detail and the one to reach for when the result is going straight into another model; bicubic is slightly softer and cheaper. Neither will save a bad source, and for text or UI screenshots Lanczos is very obviously the better pick.

Then the pack's execution controls: output_device (cpu by default, gpu to keep the result on the compute device for a downstream on-device node) and batch_size (frames per chunk, 0 = start at 64, auto-halved with a log warning if allocation fails). The node sizes its own working memory from the chunk size, so big downscales are the case where lowering batch_size genuinely helps.

One output, image.

What it will not do

It will not upscale. If either requested dimension is larger than the source, it raises. That's deliberate - silently switching to an upscale path would be a worse surprise than an error - but it means a tidy adaptive "fit within N" graph needs a check upstream.

It is not the pixel-art node. If you're recovering pixel art, you don't want bicubic or Lanczos: you want to detect the block grid and downscale by an integer factor with dominant-colour voting, then quantise the palette. Filtering is precisely the thing that ruins that pipeline.

Install

ComfyUI Manager → search GPU LayerStyles, or:

cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/lany42/gpu-layerstyles
# restart ComfyUI

Nothing else installs. Empty runtime dependency list, no models, no API key, no requirements.txt to fight - the Lanczos implementation and Pillow's notices ship in-tree (Pillow itself is a dev dependency, not a runtime one). Python 3.13+ and a ComfyUI build with comfy_api are the real requirements. AGPL-3.0-only.

Getting burned

The defaults. 512×512 is a fine default and a terrible surprise, and it's the most common "why is my image tiny" report this node will generate.

Second, downscaling is lossy and there's no undo inside ComfyUI - if a workflow branches, downscale on the branch that needs it rather than in the trunk. Third, doing this to a batch means keeping the full input and output alive at once; the batch_size chunking only limits the transient buffers.

CategoryGPU LayerStyles/Image

Inputs (6)

NameTypeDefaultDescription
imageIMAGE—
widthINT5121–16384—
heightINT5121–16384—
methodCOMBObicubic2 options: bicubic, lanczos
output_deviceCOMBOcpuReturn output on CPU (default) after float32 processing on ComfyUI's selected compute device. GPU keeps output on that device to avoid transfers between compatible nodes. ComfyUI CPU mode returns CPU output.
batch_sizeINT00–2147483647Frames per processing chunk. 0 starts at up to 64 frames. Positive values can exceed 64, capped by the output length. Allocation failures halve the chunk size for this run; each run starts fresh. Complete inputs and outputs still need memory. Pass smaller batches through the entire workflow to reduce complete-batch memory.

Outputs (1)

NameTypeDescription
imageIMAGE—