CFG-Ctrl / SMC-CFG Guider
Prompt adherence without the burn
- model
- positive
- negative
- GUIDER
Every CFG has a ceiling. You push the scale up to make the model obey your prompt harder, and somewhere past the sweet spot the image stops getting more obedient and just starts burning - oversaturated, contrast-cranked, faces going feral. That's the wall this node attacks.
CFGCtrlSMCGuider (sold as "CFG-Ctrl / SMC-CFG Guider") is a ComfyUI port of the CFG-Ctrl paper, a March 2026 method that reframes classifier-free guidance as a control problem and stabilizes it with sliding mode control. It's one of the cleaner research-to-ComfyUI ports I've seen: a single GUIDER output that drops into your existing custom-sampling workflow, no model downloads, no extra dependencies. If you've been running CFG 6–9 on SDXL-lineage models and hitting the burn wall, this is the "more prompt adherence without cranking the dial" play.
Why sliding mode control, anyway
Here's the failure mode. Vanilla CFG computes uncond + cfg * (cond − uncond) at every step. The (cond − uncond) difference is noisy, and it oscillates step to step - so cranking the scale amplifies both the signal and the wobble, and the wobble is what reads as burn and overshoot. SMC-CFG instead treats that difference as a sliding surface and actively drives it toward a stable target instead of just multiplying it.
The node's source spells the whole controller out in a docstring:
e_t = cond - uncond
s_t = (e_t - e_{t-1}) + lambda * e_{t-1}
u_sw = -K * sign(s_t)
e'_t = e_t + u_sw
guided = uncond + cfg_scale * e'_t
So it looks at how the guidance changed since the previous step (e_t − e_{t-1}, the derivative term) plus a proportional term scaled by smc_lambda, then applies a switching correction u_sw tuned by smc_k. The result is you can get prompt adherence you'd normally need a higher CFG for, without the deep-fry. It works across architectures - the paper authors demoed it on a spread of models including Wan video, and the r/StableDiffusion thread got a warm reception, with the usual "run 20 gens before you believe the comparison" skepticism attached.
The inputs that actually matter
The node takes model, positive, negative and the rest of the defaults are sane. You'll set:
cfg- same dial as always. Default 4, and on SDXL-family models you'll often find yourself lowering it from 7 once SMC is doing the adherence work.smc_lambda(default 6) - the paper's reported sweet spot. Sliding-surface shaping; leave it near 6 unless you're exploring.smc_k(default 0.3) - switching gain. The README is refreshingly honest that 0.3 is a generic starter, not a paper-optimal default, and that FLUX-like flow models often tolerate higherk.switch_mode-signis the paper behavior. If you see chattering (grainy flickering between steps), flip totanhorsatwithboundary_epsilonaround1e-3–1e-2to smooth the switching.no_cfg_warmup_steps- runs the first N steps on the conditional prediction alone, no CFG mixing. Note the README's emphasis: this is conditional-only warmup, not "unconditional."active_start_percent/active_end_percent- gate when the controller is active by denoising progress. The README recommends these over the raw step windows for portability, and the node converts them viapercent_to_sigma, so the gating holds across samplers.
The single output is a GUIDER, and that's the whole gotcha in one word: this is not a KSampler drop-in. Feed the guider into SamplerCustom or SamplerCustomAdvanced, not a regular KSampler.
Install
Via ComfyUI Manager (search ComfyUI-CFG-Ctrl), or the manual route:
cd ComfyUI/custom_nodes
git clone https://github.com/xmarre/ComfyUI-CFG-Ctrl
# restart ComfyUI
That's it. The pyproject.toml lists no external dependencies beyond PyTorch/ComfyUI itself - no model files, no weights to fetch. It's pure math on the tensors your sampler already produces.
Traps and troubleshooting
- Wrong sampler node. Wiring the
GUIDERinto a plain KSampler fails immediately. Use custom sampling. - Distilled / flow models at CFG 1. Guidance-distilled checkpoints bake CFG in and run at scale 1; SMC is fixing a problem you don't have there. The
apply_only_if_cfg_gt_1toggle exists exactly because the author refused to silently self-disable atcfg ≤ 1- but on a distilled model you should just turnenable_smcoff. - Chattering or overshoot. Drop to
tanh/satand raiseboundary_epsilon. That's the intended stability knob, not a hack. - Nothing changes. If
enable_smcis off, this is just a plain CFG guider with extra steps - check you actually toggled it on.
It's one of those rare nodes where the defaults are worth trusting first: lambda=6, k=0.3, sign, adjust only if the output tells you to.
Inputs (20)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| positive | CONDITIONING | — | |
| negative | CONDITIONING | — | |
| cfg | FLOAT | 4.00–100 | — |
| enable_smc | BOOLEAN | true | — |
| smc_lambda | FLOAT | 6.00–50 | — |
| smc_k | FLOAT | 0.300–5 | — |
| no_cfg_warmup_steps | INT | 00–50 | — |
| active_start_step | INT | 00–10000 | — |
| active_end_step | INT | 10000000000–1000000000 | — |
| active_start_percent | FLOAT | 0.000–1 | — |
| active_end_percent | FLOAT | 1.000–1 | — |
| switch_mode | COMBO | sign | 4 options: sign, sat, tanh, vector_norm |
| boundary_epsilon | FLOAT | 0.0000–10 | — |
| epsilon_mode | COMBO | absolute | 2 options: absolute, relative |
| math_dtype | COMBO | float32 | 4 options: float32, match, float16, bfloat16 |
| state_dtype | COMBO | match | 4 options: match, float16, bfloat16, float32 |
| apply_only_if_cfg_gt_1 | BOOLEAN | false | — |
| reset_on_shape_change | BOOLEAN | true | — |
| detach_prev | BOOLEAN | true | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| GUIDER | GUIDER | — |