Adaptive Noise Scale Sampler
It's a sampler that watches its own noise scale and fixes it mid-generation
- sampler
- sampler
Ancestral samplers like Euler a and DPM++ SDE add fresh noise at every step - that's what makes them never quite settle, and it's also what makes them sprinkle in noise that's out of proportion to where you are in the denoise. Turn the CFG up and the excess gets worse: blotchy texture, over-baked detail, that slightly fried look. Adaptive Noise Scale Sampler is a wrapper that measures the problem while it's happening and rescales the per-step noise accordingly. If you're fighting noise-induced artifacts on an ancestral sampler and are tired of hand-tuning, this is the niche tool for you.
It's a port of the author's own sd-webui-adaptive-noise-scale extension, and a single node: you drop it between KSamplerSelect and KSampler (Advanced).
[KSamplerSelect] → [Adaptive Noise Scale Sampler] → [KSampler (Advanced)]
How it actually works
Two passes, which is the whole personality of the node. Pass 1 is a short calibration run: it samples with a special callback that compares how fast the denoised output is changing against how fast the sigma schedule is dropping - that ratio is the "excess." If the image is changing faster than the schedule says it should, the injected noise is too hot; if it's changing slower, you could stand a little more. It collects these measurements during the texture phase (σ between 0.5 and 5.0), aborts early once it has enough, and computes:
correction = clamp(1 / median_excess^power, floor, ceiling)
Then Pass 2 restarts from the exact same latent and noise and redoes the whole generation with the correction applied to every intra-step noise injection. So the final image benefits from the calibration, and the calibration doesn't pollute the result.
The implementation detail worth knowing: for non-SDE samplers it monkeypatches ComfyUI's default_noise_sampler for the duration of the run, and for SDE samplers it injects a scaled BrownianTreeNoiseSampler via the sampler's extra_options. Both are restored afterward, so it's safe to run alongside other custom samplers.
The inputs that matter
Only one of these deserves your attention on day one:
- correction_power (default 0.5) - the exponent on the correction. 0.5 is gentle (square root), 1.0 is linear, above 1 is aggressive. Start at the default.
- warmup_steps (default 5) - how many texture-phase measurements to collect before calibrating. More = more robust, but the calibration pass runs longer.
- dampen_floor (default 0.80) - the lowest multiplier allowed. This is your safety rail: the node won't damp noise below 0.8× no matter what the measurements say.
- boost_ceiling (default 1.15) - the highest multiplier. The asymmetry is intentional; the tool's main job is damping.
- phase_binned (default on) - computes separate corrections for the structural (σ > 5), texture, and cleanup phases instead of one global number. Leave it on; bins with too few samples fall back to the global value anyway.
Output is a single sampler, which wires straight into the sampler port of KSampler (Advanced). Same seed in, same seed out - the noise path is the only thing that changes.
Installing it
Via ComfyUI Manager, search ComfyUI-Adaptive-Noise-Scale. Or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/nawka12/comfyui-adaptive-noise-scale
Restart ComfyUI. Good news on the dependency front: the only Python dependency is torch, which you already have. No model files, no API keys, nothing to download. This is about as light an install as custom nodes get.
Where people get burned
It doubles your sampling time. Every generation is a calibration pass plus a production pass. That's the price of the design, and the one thing you'll notice immediately.
Don't use it with deterministic samplers. DPM++ 2M, LMS, and friends produce a nearly-constant change between steps, so the calibration never finds a texture phase. The node prints a warning and runs the whole thing again - you pay double for zero benefit. This tool only makes sense on ancestral and SDE samplers.
SDE detection is by name. The node identifies SDE samplers by matching against a hardcoded list (sample_dpmpp_sde, sample_dpmpp_2m_sde, etc.). A third-party or renamed SDE sampler that's not on that list gets treated as non-SDE, and its Brownian noise won't actually be scaled - it'll silently do the wrong thing. If you're running a custom sampler pack, keep that in mind.
The honest caveat: there's essentially no community chatter around this node - it's a niche, author-ported tool, not an ecosystem staple. But the mechanism is straightforward and the defaults are sensible, so the worst case is you lose a bit of render time, not an evening.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| sampler | SAMPLER | — | |
| warmup_steps | INT | 51–20 | Texture-phase steps (0.5 < sigma < 5.0) to collect before calibrating |
| correction_power | FLOAT | 0.500.1–2 | Exponent: 0.5=gentle (sqrt), 1.0=linear, >1=aggressive |
| dampen_floor | FLOAT | 0.800.5–1 | Minimum correction multiplier |
| boost_ceiling | FLOAT | 1.151–2 | Maximum correction multiplier |
| phase_binned | BOOLEAN | true | Separate correction factors for structural / texture / cleanup phases |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| sampler | SAMPLER | — |