Neo 高清修复放大 (Forge Neo)
The second pass that forgot to be a second pass
- latent
- vae
- upscale_model
- LATENT
Hires fix is the oldest trick in the book and still the best one: generate small, then run a second pass at a larger size with a low denoise so the model fills in detail instead of hallucinating a new image. Skip it and anything above the model's native resolution turns into repeated tiles - you know the look.
The annoying part is that the "upscale" step in the middle isn't standardized. A1111 and Forge Neo do it their way; ComfyUI gives you a grab bag of Upscale Latent variants and a separate Upscale Image (using Model) node. Swap them and your second pass doesn't match Neo's, even though the idea is the same. This node replicates Neo's version of that middle step so a two-pass workflow in ComfyUI behaves like the one in Neo.
What it does
You give it a LATENT from your first pass, it works out the target size Neo would, resizes the latent there, and hands you a LATENT for a second Neo KSampler.
Two paths, chosen by the upscaler widget. In the latent paths it interpolates the latent samples directly - cheap, memory-light, slightly soft. In the image paths it decodes to pixels, resizes, then re-encodes to latent: a destructive round trip through the VAE, which is exactly why picking a real upscale model there gets you crisp detail pure latent interpolation can't. Neo's tiling is reproduced for the upscale model, and the pixel resize lands with Lanczos after it.
The target size is computed with Neo's resolution rounding: scale multiplies your current size, then the result gets snapped to the res_step grid (64 by default). That's the same rounding Neo applies, and it's why 1.5× on 1024 doesn't give you 1536 - it gives you the nearest sensible multiple. If you want an exact size, set resize_width / resize_height instead; setting only one of them computes the other from the current aspect ratio.
The inputs
latent- first-pass output. Use a Neo KSampler, not something else, if you care about the seed carrying over.vae- required even in pure latent mode, because the pixel paths need it.upscaler-Latent,Latent (antialiased),Latent (bicubic),Latent (bicubic antialiased),Latent (nearest),Latent (nearest-exact),放大模型(接 upscale_model),None(Lanczos). Default is the upscale-model option. If you pick that and don't wireupscale_model, it raises rather than silently doing something else.scale- default 1.5. The tooltip says Anima wants ≤1.5 per pass; if you need more, stack passes rather than jump.resize_width/resize_height- 0 means "use the scale factor".upscale_model(optional) - the UPSCALE_MODEL loader output, e.g. a 4x ESRGAN-style model.res_step- Neo's Resolution Step, 64 by default. Match Neo's settings page.tile_size/tile_overlap- Neo's Tile Size for Upscalers (256) and overlap (16). The tile size halves automatically on an out-of-memory error and retries, down to a floor of 64 - Neo itself would just fail here, so this is a ComfyUI-side courtesy.
Output: one LATENT, into a second Neo KSampler set to Hires.fix.
Wiring
Neo KSampler (pass 1) ─→ Neo Hires Upscale ─→ Neo KSampler (pass 2)
denoise_mode = Hires.fix(精确步数)
denoise = your redraw strength (0.3–0.5)
The RNG state rides inside the LATENT dictionary, so the second sampler picks up the same seeds and regenerates noise at the new size - Neo's behaviour, and the reason the graph stays reproducible. The first pass also records its step count in the latent so the second pass can run prompt schedules with Neo's offset rules.
Install
ComfyUI Manager → ComfyUI Neo Sampler, or:
cd ComfyUI/custom_nodes
git clone https://github.com/wangjue520/ComfyUI-Neo-Sampler
Restart. lark, torchsde and scipy are installed automatically into the Python that runs ComfyUI; the console prints the manual pip command if the auto-install fails. You need an upscale model file of your own if you want the upscale-model path - nothing is bundled.
Things that will annoy you
Choosing an upscale model but leaving upscale_model unwired is a hard error ("selected an upscale model but no upscale_model is connected"), which is the right call but reads as cryptic if you forgot.
The pixel paths drop any noise_mask from the latent. Combined with the sampler ignoring masks too, that means this whole pipeline is not an inpainting pipeline - do your hires pass on a clean latent.
VRAM is the real constraint. The pixel path decodes, holds the frames, tiles the upscale model, then re-encodes - a 4x model on a big latent will push you into the tiling fallback, and heavy tiling at a small tile_size shows up as seams rather than a crash. If your output has visible grid lines, raise tile_size. And if you want resolution doubling without the model pass, Latent (bicubic antialiased) at 2.0 is the boring, fast, surprisingly-fine choice.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| latent | LATENT | — | |
| vae | VAE | — | |
| upscaler | COMBO | 放大模型(接 upscale_model) | 8 options: Latent, Latent (antialiased), Latent (bicubic), Latent (bicubic antialiased), Latent (nearest), Latent (nearest-exact), +2 |
| scale | FLOAT | 1.501–8 | Hires upscale by(Anima 单次建议 ≤1.5) |
| resize_width | INT | 00–16384 | Resize width to(0 = 按倍数) |
| resize_height | INT | 00–16384 | Resize height to(0 = 按倍数) |
| upscale_modelopt | UPSCALE_MODEL | — | |
| res_stepopt | INT | 648–256 | Neo 设置 → Resolution Step(默认 64) |
| tile_sizeopt | INT | 2560–2048 | Neo 设置 → Tile Size for Upscalers;显存不足时会自动减半 |
| tile_overlapopt | INT | 160–256 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| LATENT | LATENT | — |