Nodes/SuperScaler/Pipeline SuperScaler
ComfyUI Node

Pipeline SuperScaler

The node that swallows your 12-node upscale chain

By tritant·Created 11 months ago·Updated 10 months ago· 168
Pipeline SuperScaler
  • image_in
  • mask_in
  • model_pass_1
  • vae_pass_1
  • positive_pass_1
  • negative_pass_1
  • model_pass_2
  • vae_pass_2
  • positive_pass_2
  • negative_pass_2
  • model_pass_3
  • vae_pass_3
  • positive_pass_3
  • negative_pass_3
  • image_out
◄enable_latent_passfalse►
◄latent_upscale_by1.1►
◄latent_denoise0.20►
◄latent_sampler_name▾►
◄latent_scheduler▾►
◄latent_steps6►
◄latent_cfg1.00►
◄enable_tiled_pass_2true►
◄tiled_upscale_by_22.0►
◄tiled_denoise_20.25►
◄tile_size_2960►
◄tile_overlap_264►
◄tiled_sampler_name_2▾►
◄tiled_scheduler_2▾►
◄tiled_steps_28►
◄tiled_cfg_21.00►
◄enable_tiled_pass_3false►
◄tiled_upscale_by_32.0►
◄tiled_denoise_30.25►
◄tile_size_3960►
◄tile_overlap_364►
◄tiled_sampler_name_3▾►
◄tiled_scheduler_3▾►
◄tiled_steps_38►
◄tiled_cfg_31.00►
◄enable_sharpenfalse►
◄sharpen_amount1.00►
◄sharpen_radius1►
◄enable_grainfalse►
◄grain_typepoisson►
◄grain_intensity0.014►
◄grain_size1.3►
◄saturation_mix0.20►
◄adaptive_grain0.15►
◄mask_blend_weight1.00►
◄seed0►

Generative upscaling in ComfyUI is normally a pile of nodes: an upscale, a VAE encode, a sampler, a decode, then a second pass on top, then a sharpen and a grain node bolted on the end. SuperScaler_Pipeline exists to collapse that pile into one collapsible box. You feed it an image, wire in a model, VAE and conditioning, and it runs a latent refine, up to two tiled generative upscales, frequency-split sharpening and film grain, then hands you a finished image_out - no Ultimate SD Upscale + tile-ControlNet + post-fx sprawl in between.

Where it sits in the upscaling picture matters. "Upscale" covers two different jobs: adding pixels (ESRGAN, Lanczos) and inventing detail (SUPIR, SeedVR2, tile-ControlNet). SuperScaler is firmly the second kind - it's not a model, it's a workflow, and it ships no weights. It downloads nothing and reuses whatever checkpoint, VAE and conditioning you already have loaded.

How it works

The node is four passes in sequence, each one toggled on or off (the JS drops each disabled section's widgets out of the UI, which is why the node doesn't look as terrifying as the parameter list suggests):

  • Pass 1, latent refine. VAE-encode, nearest-neighbor upscale the latent, sample, decode. A gentle img2img pass for refining before the real upscale. Off by default.
  • Pass 2 & 3, tiled generative upscales. This is the meat. It first does a plain bicubic upscale in pixel space, then chops that into overlapping tiles. Each tile is VAE-encoded, sampled through nodes.common_ksampler, decoded, multiplied by a feather mask, and blended over its neighbors - overlapping regions are averaged so you don't get visible seams. Tiling is what keeps big 2x+2x upscales on low-VRAM cards: peak memory is roughly one tile, not the whole image. The same seed runs through every tile, which is what stops adjacent tiles from going their own way and producing seams that even the feather mask can't hide.
  • Pass 4, post-FX. Frequency-split sharpen (scipy.ndimage.gaussian_filter) - blur a copy, subtract it to isolate the high-frequency detail, boost that, recombine. That's the trick that sharpens without halos. Then optional grain: poisson, gaussian or perlin noise, with saturation_mix to desaturate it and adaptive_grain to push more grain into shadows, because perfectly smooth skin reads as plastic.

There's also a masked final blend: feed a mask_in and the protected areas get swapped for a clean bicubic upscale of your original, so skies and smooth surfaces don't collect generative noise. Trap for newcomers: the mask logic is inverted from ComfyUI's usual convention. Black protects (clean upscale), white gets the full treatment. mask_blend_weight at 1.0 fully replaces the protected area, 0.5 gives a 50/50 mix, 0.0 does nothing.

The inputs that matter

You mostly live on these few, per pass:

  • enable_tiled_pass_2 - the workhorse. On by default.
  • tiled_upscale_by_2 / tiled_denoise_2 - how much you scale and how much the sampler is allowed to invent. 0.25–0.35 is the sweet spot for adding detail; above that and it starts rewriting your image.
  • tile_size_2 / tile_overlap_2 - larger tiles = fewer passes but more VRAM; more overlap = fewer seams but more redundant work.
  • seed - one global seed drives all three generative passes, with the standard fixed/increment/randomize control.

Note the defaults: pass 2 uses CFG 1.0 and 8 steps, which is a guidance-distilled (Flux-style) model's territory. On SDXL you'll want to raise tiled_cfg_2 toward 6–7 or you'll get mush.

Installing it

ComfyUI Manager → search "SuperScaler", or:

cd ComfyUI/custom_nodes
git clone https://github.com/tritant/ComfyUI_SuperScaler

Restart ComfyUI. No model downloads - it uses your existing checkpoints. One real gotcha: the node imports scipy at module load, but the pack's pyproject.toml declares no dependencies, so Manager won't always pull it in. If the node refuses to appear, install it yourself:

pip install scipy

Where people get burned

The most common failure is leaving Pass 1 on without wiring its model inputs - the node skips any pass whose model/VAE/conditioning aren't connected, so your "refine" silently never runs. Watch the console, too: it logs every tile (PASS 2: Inférence de la tuile 12/45... - the author's French leaks through, harmless). A 4K upscale is a lot of sequential tiles, so this is not a fast node; it's a patient, VRAM-bounded one. That's the honest trade: convenience and memory fit, at the cost of speed you'd only notice on a big image. For one-off "make it 4K and film-like" jobs it's the node I'd actually reach for over rebuilding the chain from scratch.

Categoryupscaling

Inputs (50)

NameTypeDefaultDescription
image_inIMAGE—
mask_inoptMASK—
enable_latent_passoptBOOLEANfalse—
model_pass_1optMODEL—
vae_pass_1optVAE—
positive_pass_1optCONDITIONING—
negative_pass_1optCONDITIONING—
latent_upscale_byoptFLOAT1.11–4—
latent_denoiseoptFLOAT0.200–1—
latent_sampler_nameoptCOMBO44 options: euler, euler_cfg_pp, euler_ancestral, euler_ancestral_cfg_pp, heun, heunpp2, +38
latent_scheduleroptCOMBO9 options: simple, sgm_uniform, karras, exponential, ddim_uniform, beta, +3
latent_stepsoptINT61–1000—
latent_cfgoptFLOAT1.000–100—
enable_tiled_pass_2optBOOLEANtrue—
model_pass_2optMODEL—
vae_pass_2optVAE—
positive_pass_2optCONDITIONING—
negative_pass_2optCONDITIONING—
tiled_upscale_by_2optFLOAT2.01–8—
tiled_denoise_2optFLOAT0.250–1—
tile_size_2optINT960256–2048—
tile_overlap_2optINT6432–512—
tiled_sampler_name_2optCOMBO44 options: euler, euler_cfg_pp, euler_ancestral, euler_ancestral_cfg_pp, heun, heunpp2, +38
tiled_scheduler_2optCOMBO9 options: simple, sgm_uniform, karras, exponential, ddim_uniform, beta, +3
tiled_steps_2optINT81–1000—
tiled_cfg_2optFLOAT1.000–100—
enable_tiled_pass_3optBOOLEANfalse—
model_pass_3optMODEL—
vae_pass_3optVAE—
positive_pass_3optCONDITIONING—
negative_pass_3optCONDITIONING—
tiled_upscale_by_3optFLOAT2.01–8—
tiled_denoise_3optFLOAT0.250–1—
tile_size_3optINT960256–2048—
tile_overlap_3optINT6432–512—
tiled_sampler_name_3optCOMBO44 options: euler, euler_cfg_pp, euler_ancestral, euler_ancestral_cfg_pp, heun, heunpp2, +38
tiled_scheduler_3optCOMBO9 options: simple, sgm_uniform, karras, exponential, ddim_uniform, beta, +3
tiled_steps_3optINT81–1000—
tiled_cfg_3optFLOAT1.000–100—
enable_sharpenoptBOOLEANfalse—
sharpen_amountoptFLOAT1.000–5—
sharpen_radiusoptINT11–20—
enable_grainoptBOOLEANfalse—
grain_typeoptCOMBOpoisson3 options: poisson, gaussian, perlin
grain_intensityoptFLOAT0.0140.001–1—
grain_sizeoptFLOAT1.31–16—
saturation_mixoptFLOAT0.200–1—
adaptive_grainoptFLOAT0.150–2—
mask_blend_weightoptFLOAT1.000–1—
seedoptINT00–18446744073709550000—

Outputs (1)

NameTypeDescription
image_outIMAGE—