Video XY Plot Sampler
Compare Wan CFG and shift without babysitting a dozen renders
- model
- vae
- positive
- negative
- latent_image
- IMAGE
Wan video has two knobs everybody ends up tuning blind: CFG and the sigma shift. Set them wrong and you get mush or burn, and because each run costs you a full video render, dialing them in one at a time eats an afternoon. VideoXYPlotSampler is the fix: it renders a whole CFG-by-shift grid in one go and tiles the results into a single image or video, straight out of markuryy/ComfyUI-Simple-Video-XY-Plot.
This is the video descendant of the XY plot script from the A1111 era - the same idea people have asked about for Wan on r/comfyui since 2025 ("how do I build an XY plot for video?"). The author's own description is the honest one: the node is "very basic," basically a ModelSamplingSD3 patch, a KSampler, and a VAE decode wrapped in a loop. It does one thing, but it does the thing you'll otherwise hand-roll.
How it works
Pick a CFG range and a shift range. The node builds cfg_steps CFG values and shift_steps shift values with np.linspace (endpoints included), then, for each combination: patches the model with the SD3-style shift via ModelSamplingSD3, samples your latent with comfy.sample.sample (same guts as a KSampler), and decodes through the VAE. Columns are CFG, rows are shift. If add_labels is on, it stamps "CFG: 5.00" across the top and "Shift: 6.00" down the left so you don't have to guess which cell is which.
The shift here is the SD3/Flux-family sigma shift - the same parameter Wan 2.x workflows set to ~8 in ComfyUI templates. The defaults (shift_min 1, shift_max 5) run low for Wan, so expect to raise shift_max toward 8 before the sweep tells you anything useful.
The inputs that matter
Five wires and a few numbers. The wires are the boring part: model, vae, positive, negative, and latent_image - feed it what you'd feed any KSampler, where the latent is an empty image or video latent. The numbers:
cfg_min/cfg_max/cfg_stepsandshift_min/shift_max/shift_steps- the sweep.cfg_stepsandshift_stepsare capped at 10 and are counts, not step sizes: 4 means four values including both endpoints.steps,sampler_name,scheduler,seed,denoise- the KSampler settings every cell shares.add_labels- keep it enabled; the labels are the whole point.
Output is one IMAGE: the full grid. For video latents each frame of that tensor is the entire grid, so wire it into SaveWEBM or Video Helper Suite to get an actual movie out - the demo workflow that ships with the pack ends in SaveWEBM.
Installing it
Search "Video XY Plot" in ComfyUI Manager, or clone it manually:
cd ComfyUI/custom_nodes
git clone https://github.com/markuryy/ComfyUI-Simple-Video-XY-Plot
Then restart ComfyUI. No model downloads, no runtime pip dependencies - pyproject.toml lists an empty dependency set and it's MIT licensed, so it's about as light a pack as exists.
Where people get burned
The trap is compute. Every cell is a full sampling run, so a 4×4 grid is 16 renders - and a multi-frame video latent multiplies that again. A 3×3 sweep of an 81-frame Wan clip is nine video generations. Keep cfg_steps and shift_steps low while you're testing; there's no VRAM saving here, each cell runs sequentially, and there's no way to cancel a single cell.
The second gotcha is video handling. The node decides "video" by the latent's batch dimension (shape[0] > 1), and it only outputs the tiled grid - individual per-setting videos are never produced. The still-image path is solid; the video path is where the node gets rough, so if your grid comes out scrambled, check what shape your video latent actually is before debugging anything else.
None of that makes it less useful - this is the node you reach for when you want to know what CFG and shift your Wan checkpoint actually wants, in one run, and then delete from the graph.
Inputs (17)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| vae | VAE | — | |
| positive | CONDITIONING | — | |
| negative | CONDITIONING | — | |
| latent_image | LATENT | — | |
| cfg_min | FLOAT | 1.00–100 | — |
| cfg_max | FLOAT | 8.00–100 | — |
| cfg_steps | INT | 42–10 | — |
| shift_min | FLOAT | 1.00–100 | — |
| shift_max | FLOAT | 5.00–100 | — |
| shift_steps | INT | 42–10 | — |
| steps | INT | 201–10000 | — |
| sampler_name | COMBO | 34 options: euler, euler_cfg_pp, euler_ancestral, euler_ancestral_cfg_pp, heun, heunpp2, +28 | |
| scheduler | COMBO | 9 options: normal, karras, exponential, sgm_uniform, simple, ddim_uniform, +3 | |
| seed | INT | 00–18446744073709550000 | — |
| denoise | FLOAT | 1.000–1 | — |
| add_labels | COMBO | enable | 2 options: enable, disable |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |