Nodes/ComfyUI-Foveated_Diffusion/Foveated KSampler (FovDiff)
ComfyUI Node

Foveated KSampler (FovDiff)

The KSampler that refuses to render pixels nobody's looking at

By ruwwww·Created 2 months ago·Updated 2 months ago· 1
Foveated KSampler (FovDiff)
  • model
  • positive
  • negative
  • latent_image
  • foveation_mask
  • latent
seed0
steps50
cfg4.0
sampler_name
scheduler
denoise1.00
decode_modedirect

If you've ever stared at a FLUX.2 Klein render and wished it were 2–4× faster without switching to fewer steps, this is the node for you. FoveatedKSampler is the engine of the Foveated Diffusion pack: it runs the sampling loop at mixed resolution, spending full tokens where your foveation mask says the viewer looks and a fraction of a token block everywhere else. The README quotes ~10s instead of ~17s for Klein at a 0.60 radius, ~6.5s at 0.30 - and those are real token-count savings, not a quant trick.

It replaces your normal KSampler in a Klein workflow, and it earns the swap by doing the sampling entirely in token space. Before the loop it tokenizes the full latent once, downsamples each peripheral block to a single top-left token (at lr_factor 2 that's 4× fewer peripheral tokens, at 4 it's 16×), injects a cross-resolution phase-aligned (CRPA) attention patch so the low-res tokens still talk to the high-res ones coherently, and reconstructs the full grid exactly once at the end. Same interface you already know - seed, steps, cfg, sampler_name, scheduler, denoise - so nothing about your prompting changes.

The one input that's not a standard KSampler input

decode_mode (direct default, or merge) decides how the mixed-resolution latent becomes pixels:

  • direct - reconstruct and blend inside latent space, then hand the result to a normal VAEDecode. Default, fewer moving parts, use this first.
  • merge - decode the high-res and low-res regions separately and blend them in pixel space via FoveatedVAEDecode. Slightly better edge handling at the fovea boundary, at the cost of an extra decode.

Everything else - model, positive, negative, latent_image, foveation_mask - is the same shape as stock ComfyUI, and foveation_mask is what this pack's FoveationMask node produces. The output is a normal LATENT that goes into whatever decode path you chose.

The real setup requirements

  • It's FLUX.2 Klein-only. The wrapper reaches into Flux2-specific APIs (process_img, forward_orig, the pe_embedder), so feeding it any other model will either throw or silently pass through - and the pack doesn't ship a guard for it.
  • The latent must be divisible by your lr_factor. 1024×1024 at factor 2 is fine; an oddball custom resolution can trip the ValueError the node throws on purpose.
  • The mask must come from the same latent that feeds the sampler - shape mismatch is the most common first-run error.

Settings and expectations

The default steps=50, cfg=4.0 is the safe pair for the base Klein checkpoints (which is what the README's benchmark uses at 20 steps). If you're running the distilled 4B/9B instead, you can ride its usual fast settings - the foveation math doesn't care about distillation - but the speedup story matters most on the slower base model. And be honest about the trade: peripheral regions are intentionally lower-res. A small radius on the mask with your subject off-center is how you get a fast image of a blurry face. The community has barely adopted this technique yet, so there's no crowd-sourced lore - start conservative (radius ~0.4–0.6, lr_factor 2) and watch what actually degrades.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/ruwwww/ComfyUI-Foveated_Diffusion

or ComfyUI Manager → search "FovDiff" → install → restart. Needs a current ComfyUI with FLUX.2 Klein support, PyTorch 2.0+, and einops importable (pip install einops if your venv is missing it). The rest of the chain is LoadFoveatedLoRAFoveationMask → this sampler.

Categoryfoveated_diffusion

Inputs (12)

NameTypeDefaultDescription
modelMODEL
positiveCONDITIONING
negativeCONDITIONING
latent_imageLATENT
foveation_maskFOVEATION_MASK
seedINT00–18446744073709550000
stepsINT501–200
cfgFLOAT4.01–20
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
denoiseFLOAT1.000–1
decode_modeCOMBOdirect2 options: direct, merge

Outputs (1)

NameTypeDescription
latentLATENT