FlashVSR_SM_KSampler
One step, four times the pixels, and the five knobs people get wrong
- model
- image
- latent
This is the node that does the upscaling. Everything else in the pack exists to feed it or to turn its output into pictures. It takes your frames as one IMAGE batch, hands them to a one-step DMD model, and gives you back a latent - not images. That trips people up: there's no preview here, and if you stop at this node you'll think it's broken.
Wire it as FlashVSR_SM_Model → model, frames → image, and then latent into FlashVSR_SM_Decoder alongside a FlashVSR_SM_Vae. Then CreateVideo and SaveVideo.
Why a KSampler for an upscaler
Because FlashVSR is a diffusion model, just a distilled one. The streaming/DMD release was trained to land its result in a single denoising step, which is the whole reason it beat SeedVR2 to adoption in late 2025: it was fast enough to sit inside a normal evening of work. It's a restorer, not a generator - it resolves detail the low-res source already implies, which is why it's the wrong tool for genuine 256px mush.
Mechanically, the sampler resamples your frames to width × height, pads the frame count up to the nearest 8n+1 (and trims the padding back off later), runs the single denoise pass with block-sparse attention over a temporal window, and hands back the latent plus the "LQ" conditioning frames it used. Those extra bits ride inside the LATENT dict - that's why the decoder can colour-match against your original footage.
The inputs you actually set
image takes a batch of frames - LoadVideo → GetVideoComponents → image. Feed it a single image and the pipeline quietly repeats it to 25 frames, so a still becomes a clip whether you asked for one or not.
width and height are the working canvas - your frames get resampled to exactly this before the model sees them. They are not the output size. Output lands at roughly 4× that canvas (the model's 4× reconstruction), which is why the bundled workflows happily put 512 or 832 in here and still get multi-megapixel frames. Keep both divisible by 128; the pipeline centre-crops down to a 128 multiple, so 832 quietly becomes 768 and you lose a strip off each side.
scale (1–4) is the pre-magnification of that canvas before the model's own 4× pass. The README says a scale of 1 isn't the intended setting because the training data is 4× pairs, though the shipped examples use 1 or 2 with small canvases and look fine. Practically: keep the canvas small, let scale do less work than you think, and check the dimensions of your first output rather than trusting arithmetic.
The tuning knobs, and what they honestly do:
stepsdefaults to 1 and should stay there. This is a distilled model; extra steps buy you time, not detail.cfgdefaults to 1.0 for the same reason - the negative-prompt machinery isn't really in play here.local_rangeis the one people argue about. The pack's own source notes 9 for sharper detail and 11 for more stable, less flickery results. If your footage shimmers, go up.kv_ratio(3.5) andsparse_ratio(2.0) are the sparse-attention knobs - how much temporal context the model keeps and how sparse the attention budget is.sparse_ratiois normalised against your resolution in code, so it behaves consistently if you change canvas size. Leave both alone until everything else works.seedmatters less than you'd hope and more than you'd think: the sampler is stochastic enough that rerolling can rescue a slightly crunchy face.
If you're on FlashVSR+ (plus), half of this node is decorative. The plus pipeline is a fixed one-step path and the pack prints a note telling you that steps, cfg, sparse_ratio, kv_ratio, local_range and scale are ignored. What still matters there is width, height and seed.
Output
A latent. Not a preview, not an image. It carries the samples plus the LQ frames and pad count the decoder needs, so send it to FlashVSR_SM_Decoder - don't route it into a plain VAEDecode and expect the colour to match the rest of FlashVSR's behaviour.
Install
Manager, or:
cd ComfyUI/custom_nodes
git clone https://github.com/smthemex/ComfyUI_FlashVSR
cd ComfyUI_FlashVSR && pip install -r requirements.txt
Weights go in ComfyUI/models/FlashVSR (DiT, LQ_proj_in, posi_prompt) - see the Model page, since this node won't load without them.
Troubleshooting
CUDA OOM on the sample, card at 100% for a minute, then a crash. Lower width/height first - it's the cheapest lever by far - then turn on offload in the Model node. Tiling lives in the decoder; this stage is the DiT and it just needs the memory.
Result looks like a soft upscale with no new detail. You're probably feeding it a source that's already been upscaled, or a canvas so large that the model's 4× is barely a stretch. Feed FlashVSR the real low-res frames.
Timing seems absurd for "one step". Frame count, not steps, is your cost. A 300-frame clip is 300 frames of attention no matter how many steps you set. Cut a long video into pieces with FlashVSR_SM_VideoTrimLoop, or switch the Model node to tiny_long.
Texture crawls between frames. Raise local_range to 11 before blaming the model - temporal consistency is the whole reason you're here rather than running an image upscaler on every frame.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| model | FlashVSR_SM_Model | — | |
| image | IMAGE | — | |
| width | INT | 1280128–16384 | — |
| height | INT | 768128–16384 | — |
| seed | INT | 00–2147483647 | — |
| scale | INT | 41–4 | — |
| kv_ratio | FLOAT | 3.50–10 | — |
| local_range | INT | 111–50 | — |
| steps | INT | 11–10000 | — |
| cfg | FLOAT | 1.00–100 | — |
| sparse_ratio | FLOAT | 2.00–10 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| latent | LATENT | — |