CNS Model Patch
Put the sampler's noise where the picture isn't finished yet
- model
- model
If you've ever noticed that ancestral samplers add a fresh coat of noise to detail they already got right, you've stumbled onto the thing this node fixes. Diffusion has a spectral bias: coarse structure resolves early, fine detail late. A standard stochastic sampler doesn't know that. It injects uniform white noise every step, burning its randomness on frequency bands that are already settled. CNS Model Patch routes that noise toward the bands still forming.
What CNS is, and where it came from
CNS stands for Colored Noise Sampling, from a 2026 paper (Davidson et al., arXiv 2605.30332) that landed on r/StableDiffusion in May 2026 and got ported to ComfyUI within days. The idea is a targeted energy transfer: measure how "built" each frequency band is, then aim the injected noise at the bands with the biggest remaining deficit, while keeping total noise strength constant so the sampler still converges where it always did.
The first community ports handed you a SAMPLER for SamplerCustomAdvanced, which is awkward if your graph is a chain of plain KSamplers. This one goes in the model slot: it clones the model and wraps the sampler-sample call, so a stock KSampler just works.
How it actually works
The model's latent gets split into bands radial rings in the 2D FFT - ring 0 is the coarsest structure, the outer rings are fine detail. For each ring the node works out gamma, the fraction already resolved, from that ring's signal power and the noise-to-signal ratio at the current sigma (read from the model's own parameterisation, so flow-matching and DDPM models both report honestly). The noise scale per ring is then (1 - gamma / divider) ** power, with an exponential tilt exp(alpha * f) laid over the top. The noise draw is FFT'd, scaled per ring, transformed back, and renormalised so its standard deviation equals energy times the white draw's. Same noise budget, different distribution.
Then the honest bit: it learns. The first run at a given size estimates resolution live from the sampler's clean predictions, then measures each band's real progress against the final one and files a running mean under a key of model, latent shape, band count and CFG. The second run at that size reads that profile instead of estimating - which is why it gets closer on run two.
The inputs that matter
Wire model in from your checkpoint loader - downstream of any LoRA or merge, so what runs is what gets patched - and its model output into the sampler's model input. That's the entire wiring.
mode is auto or manual. Leave it on auto: it ignores every widget below it and picks the paper's two published presets, the guided one when the run's CFG is above 1 and the unguided one otherwise. Because auto resolves per-run, it keeps working inside a workflow that changes CFG per stage.
manual exposes the paper's dials: bands (32 rings; 64 for a finer split on a big latent), divider (1.73 keeps at least 42% of a finished band's noise, the guided preset's 25 keeps nearly all of it), power (0.75 follows progress linearly-ish, 0.5 square-roots it), tilt_start / tilt_end (extra lean toward fine detail at the first and last step), sharpness (how late that lean swings across the run) and energy (0.98; think of it as the volume against plain white noise).
Samplers it works with, and the ones it silently ignores
Only samplers that inject noise each step do anything: euler_ancestral, dpmpp_2m_sde, er_sde, hfx_stochastic, and RES4LYF's generators (which get wrapped by name for the run). Plain euler or dpmpp_2m add no noise, so the run is byte-identical to unpatched - the node doesn't error, it just does nothing, which is exactly what you'd want. Everything is auto-detected from the sampler function itself; there's no setting to get right.
What to expect
Set expectations to "better microdetail, occasionally weirder composition". A tester in the port thread put it plainly: it improved microdetail but led to inconsistencies in macro detail. That's the tradeoff of redirecting noise energy, not a bug. Low step counts seem to benefit most, and the paper's FID numbers are averages over thousands of images - not a promise about your render.
Installing it
The pack ships as one unit, so if you already run WAS Node Suite you have this node. Otherwise:
- ComfyUI Manager → search
WAS Node Suite v3→ Install → restart. - Or manually, then restart:
cd ComfyUI/custom_nodes
git clone https://github.com/WASasquatch/was-node-suite-comfyui.git
Needs ComfyUI 0.14.0+ and Python 3.10+, and that's the whole install: v3's requirements.txt is a one-line comment saying there are no default requirements, nothing is pip-installed, and no weights are downloaded unless you opt in with features.network: true in <ComfyUI user dir>/was-node-suite/config.yaml. First start is a second or two slower while that config and the pack's state database are written.
If a 2023-ish tutorial tells you to run install.bat or install opencv to fix WAS Suite, that's v2 advice - that build carried ~20 packages and could get into pin fights with other node packs. v3 installs nothing.
When it doesn't do anything
Check the console. The node logs one line per run:
CNS: euler_ancestral, auto mode, guided preset, coloured 19 noise draw(s); a profile measured on earlier runs, within 0.041 of the share measured at the end
Read it as a diagnosis. "takes no noise sampler, so nothing was coloured" means your sampler is deterministic - switch to an ancestral or SDE one. "drew no noise this run" means the sampler ran but never injected, usually a one-step run. And "a live estimate" instead of "a profile measured on earlier runs" says this is the first run at that size, so the second will be a touch better. Either way, nothing is broken.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | The model to patch; any model loads. | |
| mode | COMBO | auto | `auto` = the published settings, the guided set when the run's CFG is above 1 and the unguided set otherwise, and ignores every widget below; `manual` = the widgets. |
| bands | INT | 324–128 | Frequency rings the noise is split into, as `32`, or `64` for a finer split on a large latent. Read by `manual`. |
| divider | FLOAT | 1.731–100 | How much noise a finished band keeps, as `1.0` for none, `1.73` for at least 42% of it, or `25` for nearly all of it. Read by `manual`. |
| power | FLOAT | 0.750.1–2 | How sharply noise follows each band's progress, as `0.5` for the square root, `0.75` or `1.0` for linear. Read by `manual`. |
| tilt_start | FLOAT | 0.15-2–2 | Extra lean toward fine detail at the first step, as `0.15` to add a little high-frequency noise, `0.0` for none or `-0.3` to take some away. Read by `manual`. |
| tilt_end | FLOAT | -0.50-2–2 | The same lean at the last step, as `-0.5` to quiet fine noise as the image settles or `0.0` for none. Read by `manual`. |
| sharpness | FLOAT | 0.75-8–8 | How the lean moves from start to end, as `0.0` for evenly, `0.75` for a little later, or `4.0` for mostly at the end. Read by `manual`. |
| energy | FLOAT | 0.9800.5–1.5 | Total strength of the noise against white, as `0.98`, `1.0` for the same or `1.05` for a little more. Read by `manual`. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| model | MODEL | The patched model, for any sampler node. |