ComfyUI Node

StepByStepSampler

Watch Every Step of Diffusion — and Stop When It's Done

By TakkunRed·Created 5 months ago·Updated 5 months ago· 2
StepByStepSampler
  • model
  • positive
  • negative
  • latent_image
  • vae
  • LATENT
  • STEP_IMAGES
  • LAST_IMAGE
  • STOPPED_AT_STEP
seed0
steps20
cfg8.0
sampler_name
scheduler
denoise1.00
save_interval1
show_overlaytrue
diff_methodMSE
auto_stopfalse
stop_threshold0.0001

You fire off KSampler with 20 steps and wait, staring at a progress bar, with zero idea whether step 12 was already good enough. That's the black box this node is built to open. StepByStepSampler is a drop-in replacement for KSampler that VAE-decodes the image at every step, numbers the frames, measures how much each step actually changed, and - if you ask it to - stops the moment the image stops changing. It's the centerpiece of TakkunRed's small ComfyUI-StepByStep-Sampler pack, which also ships a Player and a Comparer for looking at the frames it captures.

If you've absorbed the sampler lore in the KB, this clicks immediately: converging samplers like Euler and DPM++ 2M reach a stable image and stop changing beyond a certain step count, while ancestral samplers never settle. StepByStepSampler makes "when did it stop changing?" an answerable, visible question instead of a hunch.

What it actually is

The required inputs are KSampler's, unchanged: model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, denoise, plus a vae. Then come the five that make it interesting: save_interval, show_overlay, diff_method, auto_stop, and stop_threshold.

How it works

Under the hood it calls the same comfy.sample.sample loop as KSampler, but passes a callback. On every saved step it takes the model's current prediction of the clean latent (the x0, after scaling), VAE-decodes it to pixels, stamps a Step 3/20 MSE: 0.0041 label on it, and appends it to the STEP_IMAGES batch. The change metric is computed in latent space between consecutive x0 predictions, not on pixels - which is the right call, because that's literally what "is the image still changing" means mid-sampling.

The metrics all tell the same story but read backwards from each other. MSE, RMSE, and L1 shrink toward 0 as steps settle; PSNR climbs (40 dB+ is the usual convergence target); SSIM climbs toward 1.0 (0.99+). The node knows this, and auto_stop evaluates accordingly: lower-is-better metrics stop when the diff drops below the threshold, higher-is-better metrics stop when it rises above. When it decides you've converged, it breaks out of the sampling loop with an internal exception, and the LATENT it hands you is the last predicted clean latent - a complete image, just sampled in fewer steps than you asked for.

The inputs that matter

You mostly touch three or four of these:

  • save_interval - 1 captures every step. Raise it to 2–4 on long runs; each capture is a full VAE decode, and that's the expensive part.
  • show_overlay - stamps the step number and diff value on each frame. Turn it off if you want clean frames for the Comparer.
  • diff_method, auto_stop, stop_threshold - the convergence brain. The threshold defaults to 0.0001, which makes sense for MSE; if you switch to PSNR you're comparing against dB values like 40 instead.
  • denoise - same as KSampler, for img2img passes.

Outputs

Four sockets. LATENT is a standard latent and feeds VAE Decode just like KSampler's. STEP_IMAGES is the labeled batch - wire it into the pack's Player or Comparer, or into Preview Image / Save Image. LAST_IMAGE is the final frame without the overlay. STOPPED_AT_STEP tells you where it stopped, and equals steps when you didn't early-stop.

Install

ComfyUI Manager, search ComfyUI-StepByStep-Sampler, or:

cd ComfyUI/custom_nodes
git clone https://github.com/TakkunRed/ComfyUI-StepByStep-Sampler.git

Restart ComfyUI. The pack declares zero Python dependencies - it runs on torch, numpy, and Pillow, all of which ship with ComfyUI - and downloads no models. The repo also includes a sample workflow you can drag straight onto the canvas.

Where people get burned

  • auto_stop on an ancestral sampler. Euler A, DPM++ SDE and friends inject noise every step, so the diff never settles and the node either runs the full step count or stops on noise. Use a converging sampler (euler, dpmpp_2m) if you want early stopping to mean anything.
  • Threshold tuning. Stops at step 2? Your threshold is too loose. Never stops? Too tight. Start with the defaults on MSE and watch the overlay numbers before trusting it.
  • PSNR's scale is SD1.5/SDXL-flavored. The node's PSNR assumes a max pixel value of 6.0, which matches the usual latent range. On other architectures (Flux and friends) the absolute dB number is still fine as a relative "is it still changing" signal - just don't treat 40 dB as a universal law.
  • The overlay fonts. On a headless Linux box it falls back to a default bitmap font. Ugly, harmless.

For a niche pack with almost no search traffic, this is remarkably well made - the mechanism is clean and the early-stop is genuinely clever. If you're hunting your minimum viable step count, or you just want to see the process instead of trusting the progress bar, it earns its spot next to KSampler.

Categorycustom_nodes/sampling

Inputs (16)

NameTypeDefaultDescription
modelMODEL
seedINT00–18446744073709550000
stepsINT201–10000
cfgFLOAT8.00–100
sampler_nameCOMBO44 options: euler, euler_cfg_pp, euler_ancestral, euler_ancestral_cfg_pp, heun, heunpp2, +38
schedulerCOMBO9 options: simple, sgm_uniform, karras, exponential, ddim_uniform, beta, +3
positiveCONDITIONING
negativeCONDITIONING
latent_imageLATENT
denoiseFLOAT1.000–1
vaeVAE
save_intervalINT11–100
show_overlayBOOLEANtrue
diff_methodCOMBOMSE5 options: MSE, RMSE, L1, PSNR, SSIM
auto_stopBOOLEANfalse
stop_thresholdFLOAT0.00010–100

Outputs (4)

NameTypeDescription
LATENTLATENT
STEP_IMAGESIMAGE
LAST_IMAGEIMAGE
STOPPED_AT_STEPINT