NoisyLatentPerlin
The initial noise that survives an upscale
- LATENT
Every txt2img run starts the same way: a sampler eats a tensor of random Gaussian noise and slowly carves an image out of it. That noise is usually the most disposable thing in your workflow - nobody ever cares which random noise, just that there is one. NoisyLatentPerlin is the node for when you do care, because it generates that initial latent filled with seed-deterministic Perlin noise instead of white noise. Same seed, same aspect ratio, and you get the exact same pattern at any resolution. That one property is the whole trick, and it's what makes this node worth installing.
Why you'd reach for it
The classic problem this solves is mid-generation upscaling. You generate at native resolution (say 1024), upscale the latent, and run a second pass for detail. But the fresh random noise you feed that second pass has no relationship to the first pass's structure, so the sampler invents detail that doesn't fit - blobby, incoherent, "fake detail" is the usual complaint. Reinject the same perlin pattern at the bigger size instead, and the sampler has something coherent to refine. It's the hi-res fix pattern from the KB - generate at native res, second pass at low denoising for detail - except now the detail pass knows where the detail is supposed to go.
The author's demonstration is an SDXL run at 15 steps, VAE decode, lanczos ×1.5, VAE re-encode, then the same seed re-injected at the bigger size for a 5-step refiner pass. The README also points you at city96's SD-Latent-Upscaler to do the latent upscale in the middle - that's the natural pairing.
How it works
Under the hood it's a compact perlin implementation (ported from perlin-numpy), seeded via torch.manual_seed(seed). The clever bit is this line, straight from the source:
noise = torch.remainder(torch.abs(noise)*1000000,11)/11
Raw perlin noise is smooth and blobby - if you fed that to a sampler you'd get cloudy smears. This %11 scramble "destroys the smooth pattern while keeping it predictable at all scales," as the README puts it. The result is then passed through an inverse-error-function transform (erfinv(2*noise-1)*sqrt(2)) so it lands in a roughly Gaussian distribution samplers expect, scaled by detail_level and clamped to [-5, 5]. Output is a standard LATENT at the usual 8× downsampled size, 4 channels for SD1.5/SDXL.
Inputs and outputs that matter
Three of the five inputs are what you'll actually touch:
- seed - pick one you like and reuse it. This is the whole point: the pattern is reproducible, so a seed becomes something you can carry across an upscale.
- width / height - set your image size. The critical rule: keep the same aspect ratio if you're going to reinject after upscaling, or the patterns won't match.
- detail_level (default 0, range -1 to 1) - scales noise amplitude. Negative gets quieter, smoother results; positive pushes more energy into the start, which reads as more contrast/detail but tips into noise fast. Default is a fine starting point.
batch_size makes batches. The single output, LATENT, wires straight into a KSampler's latent_image input at denoise 1.0, or into your second-pass upscale sampler.
Installation
Both nodes ship in the same tiny pack:
cd ComfyUI/custom_nodes
git clone https://github.com/Extraltodeus/noise_latent_perlinpinpin
Restart ComfyUI (or refresh from the Manager, which finds it as "noise_latent_perlinpinpin"). The README's "just drop the .py file in" is the old-school route and still works. There are no model files and no heavy dependencies - it's torch and math, both already in ComfyUI. The README mentions needing BlenderNeko's noise nodes and city96's latent upscaler, but that's for the bundled workflow, not for this node to function.
Common gotchas
- Aspect ratio changes break the match. The author's own warning, all caps and everything: keep the same aspect ratio when you upscale or the pattern won't line up. This is the #1 way to get disappointing results from this node.
- 8192 is a hard cap. The source defines
MAX_RESOLUTION=8192, so you can't push width or height past it - confirmed by people hitting the same wall in the wild. - It's still just initial noise. If your sampler is ancestral (Euler A, DPM++ SDE) it adds its own noise every step and will partly bury the pattern. The author suggests Euler with plain noise or DPM++ 2M Karras, which is a good starting point.
Niche? Sure - "perlin noise" only bubbles up a few times a month in the community. But if you've ever watched an upscale second pass invent garbage detail, this is the cheap, dependency-free fix for it.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| seed | INT | 00–18446744073709550000 | — |
| width | INT | 10248–8192 | — |
| height | INT | 10248–8192 | — |
| batch_size | INT | 11–64 | — |
| detail_level | FLOAT | 0.0-1–1 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| LATENT | LATENT | — |