Foveated KSampler (FovDiff)
The KSampler that refuses to render pixels nobody's looking at
- model
- positive
- negative
- latent_image
- foveation_mask
- latent
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, thepe_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 theValueErrorthe 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 LoadFoveatedLoRA → FoveationMask → this sampler.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| positive | CONDITIONING | — | |
| negative | CONDITIONING | — | |
| latent_image | LATENT | — | |
| foveation_mask | FOVEATION_MASK | — | |
| seed | INT | 00–18446744073709550000 | — |
| steps | INT | 501–200 | — |
| cfg | FLOAT | 4.01–20 | — |
| sampler_name | COMBO | 44 options: euler, euler_cfg_pp, euler_ancestral, euler_ancestral_cfg_pp, heun, heunpp2, +38 | |
| scheduler | COMBO | 9 options: simple, sgm_uniform, karras, exponential, ddim_uniform, beta, +3 | |
| denoise | FLOAT | 1.000–1 | — |
| decode_mode | COMBO | direct | 2 options: direct, merge |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| latent | LATENT | — |