Video Extension (Pusa 2.2 — temporal flowmatching)
Extend / continue a video temporally with Pusa 2.2 in ComfyUI.
How to install
- Setup differs for this server — follow the Installation part of the README below.
- Claude Code:
claude mcp add <name> -- <command>. - Claude Desktop / Cursor: add it under
mcpServersin the MCP config file.
npx degit artokun/comfyui-mcp/plugin/skills/video-extend#main ~/.claude/skills/video-extendFor one project only, change the path to .claude/skills/video-extend. This skill also uses wanvideo_2_2_14B_Pusa_extension_example_01.json — copying SKILL.md alone won't be enough. See the folder on GitHub.
This one runs on your machine and can reach your files. Read the README below before you connect it.
Not working?
- Check which app you pasted it into — the steps above name the right one.
- Some skills need the paid tier of Claude or ChatGPT.
Paste into Claude, ChatGPT or Cursor.
Show the full text488 lines
Video Extension (Pusa 2.2 — temporal flowmatching)
Overview
Pusa extends a video temporally. It continues and lengthens an existing clip
rather than regenerating it from scratch. It does this on the
ComfyUI-WanVideoWrapper stack (kijai) using the WAN 2.2 T2V A14B dual
HIGH/LOW models you already have for wan-t2v-video, plus the small Pusa V1
LoRAs and a Pusa-specific sampling path: the flowmatch_pusa scheduler and
the WanVideoAddPusaNoise node. The input clip is encoded with
WanVideoEncode and injected as the first latents of the generation, which
is what carries the existing motion and content into the continuation.
The official reference graph is kijai's
wanvideo_2_2_14B_Pusa_extension_example_01.json (in
ComfyUI-WanVideoWrapper/example_workflows/). This skill is built directly from
that workflow plus the live node schemas.
Relationship to
wan-t2v-video: Pusa rides on the exact same WanVideoWrapper stack. Same T2V A14B HIGH/LOW fp8 models, same UMT5 text encoder, same WAN VAE, same block-swap/torch-compile machinery. The only new downloads are the two Pusa V1 LoRAs (~1.9 GB total). Readwan-t2v-videofirst for the base stack; this skill is the temporal-extension delta on top of it.
Verification note: every node, model, LoRA filename and setting below was confirmed against the live ComfyUI
/object_info(WanVideoWrapper installed) and against kijai's example workflow JSON + HF repo (June 2026). Where a value is a starting recommendation rather than a hard requirement it's flagged. Don't substitute a node you can't confirm withinstall_custom_node(action: "list") /create_workflow (action:"node_info").
What "temporal flowmatching" means here (why it extends, not regenerates)
WAN is a flow-matching video model: sampling integrates a velocity field from noise to a clean latent, and every frame normally shares the same denoising timestep. Pusa's contribution (Vectorized Timestep Adaptation) is to make the timestep per-frame. The frames you already have can be held at (or near) t = 0 (clean) while the new frames start from t = 1 (noise), and the model flow-matches the noisy tail conditioned on the clean head.
Concretely in the graph:
WanVideoEncodeturns the tail of your loaded clip into a clean latent.- That latent is placed at the front of an otherwise-empty embed
(
WanVideoEmptyEmbeds+WanVideoAddExtraLatent), so the generation's first latents ARE your real footage. WanVideoAddPusaNoiseassigns small, ramping per-latent noise multipliers to those conditioning latents (so they stay mostly clean) and full noise to the new latents. This per-frame noise schedule is the "vectorized timestep."flowmatch_pusaonWanVideoSamplerintegrates that mixed-timestep field.
Because the conditioning latents are real (not a single start image like I2V), the continuation inherits the existing motion, subject, camera and color, then keeps going. That's the difference from plain T2V (no memory of any clip) and from I2V (conditions on one still frame only).
⭐ Recommended pipeline (the kijai extension graph)
VHS_LoadVideo (your clip)
│ IMAGE (all frames)
▼
ImageResizeKJv2 ◄── resize to 832×480 (divisible by 16), get W/H
│
├─► GetImageRangeFromBatch (tail N frames) ─► WanVideoEncode (vae, image)
│ │ LATENT = clean
│ ▼ conditioning latents
│ GetLatentSizeAndCount ─► count
│ │
WanVideoEmptyEmbeds (W,H, total_frames=81) ▼
│ WANVIDIMAGE_EMBEDS CreateScheduleFloatList
└────────► WanVideoAddExtraLatent ◄────────┘ (per-latent noise multipliers,
│ (encoded clip latent at front) ramp e.g. 0→0.2)
▼ WANVIDIMAGE_EMBEDS
WanVideoAddPusaNoise ◄── noise_multipliers (list), noisy_steps
│
┌──────────────┴───────────────┐
▼ (pass 1, HIGH) ▼ (pass 2, LOW)
WanVideoSampler (HIGH model WanVideoSampler (LOW model
+ Pusa HIGH LoRA + distill, + Pusa LOW LoRA + distill,
flowmatch_pusa, steps 6, cfg 1, flowmatch_pusa, steps 6, cfg 1,
shift 5, start 0 / end 3) shift 5, start 3 / end -1)
└──────────────┬───────────────┘
▼ LATENT
WanVideoDecode (WAN VAE)
│ IMAGE
▼
VHS_VideoCombine ─► MP4 (16 fps)
VHS_LoadVideo/VHS_VideoCombinecome from ComfyUI-VideoHelperSuite (installed).VHS_VideoCombineis preferred for the encode (audio passthrough).- Everything
WanVideo*is ComfyUI-WanVideoWrapper (installed). ImageResizeKJv2,GetImageRangeFromBatch,GetLatentSizeAndCount,CreateScheduleFloatListare ComfyUI-KJNodes (installed alongside the wrapper). They're convenience nodes; see "Minimal wiring" if you want fewer.
The two load-bearing nodes (confirmed schemas)
WanVideoAddPusaNoise: "Adds latent and timestep noise multipliers when
using flowmatch_pusa."
| Input | Type | Meaning |
|---|---|---|
embeds |
WANVIDIMAGE_EMBEDS |
the embeds carrying your encoded clip latents |
noise_multipliers |
FLOAT (list) |
per-input-latent noise; 0 = keep that latent fully clean, higher = let the model change it. In the example this is a ramp [0.0, 0.07, 0.13, 0.17, 0.19, 0.2] fed from CreateScheduleFloatList (one value per conditioning latent), so the oldest conditioning frame stays cleanest and the seam frame gets a touch of noise for smooth blending. |
noisy_steps |
INT (default −1) |
how many sampling steps the extra noise is applied for; the example uses 0 on the HIGH pass and 2 on the LOW pass. −1 = all steps. |
It outputs WANVIDIMAGE_EMBEDS straight into WanVideoSampler's image_embeds.
flowmatch_pusa is a value in WanVideoSampler.scheduler (confirmed present
in the dropdown: ...flowmatch_distill, flowmatch_pusa, multitalk...). It must
be selected on the sampler(s) for the Pusa noise schedule to be interpreted
correctly. The example also wires explicit WanVideoScheduler nodes set to
flowmatch_pusa, steps 6, shift 5 (one per pass, split 0 to 3 and 3 to end).
How the input clip conditions the extension (the key wire)
WanVideoEncode(vae, image=<tail frames of clip>) → LATENT →
WanVideoAddExtraLatent (or WanVideoEmptyEmbeds.extra_latents, tooltip:
"First latent to use for the Pusa -model"). This places the real clip's
latents at the head of the embed window. The sampler then only has to generate
the tail, flow-matched onto that clean head. That is the entire trick. No
CLIPVision, no WanFirstLastFrameToVideo.
In practice: load → strip → re-point (DON'T hand-build) ⭐ preferred
The kijai wanvideo_2_2_14B_Pusa_extension_example_01.json is a 56-node graph
thick with GetNode/SetNode buses, Reroutes, and an alternate (dead) text
branch. Hand-wiring the Pusa noise / extra-latent / frame-stitch path is slow and
error-prone. The reliable flow is to load the real graph, then adapt ~7 widgets:
- Stage the example anywhere on disk (e.g. copy into the ComfyUI workflows folder).
panel_load_workflow(path: …)drops it on the canvas server-side (no 150KB JSON through chat).panel_strip_workflow(path: …)returns the resolved API graph (Get/Set/Reroute/bypass collapsed to real links). This is how you SEE what is actually wired. It exposes both the dead text branch and the silently-reset dropdowns below. (Raw UI JSON hides them.)
⚠️ TRAP 1 — the example's model paths reset to the WRONG file on load
The example references models by subfolder (WanVideo\2_2\…,
WanVideo\Lightx2v\…, wanvideo\Wan2_1_VAE_bf16…). On a flat local models/
layout those don't resolve, so ComfyUI silently falls each dropdown back to the
first entry in the list. E.g. both WanVideoModelLoaders land on
Qwen_Image_Edit-Q8_0.gguf and the WanVideoVAELoader on LTX23_audio_vae_bf16.
It looks wired but errors (wrong arch) or renders garbage. After loading, set
each explicitly:
| Node | Set to (local) |
|---|---|
WanVideoModelLoader HIGH |
Wan2_2-T2V-A14B_HIGH_fp8_e4m3fn_scaled_KJ.safetensors — note underscore before HIGH |
WanVideoModelLoader LOW |
Wan2_2-T2V-A14B-LOW_fp8_e4m3fn_scaled_KJ.safetensors — note dash before LOW |
WanVideoVAELoader |
wan_2.1_vae.safetensors |
WanVideoLoraSelectMulti ×2, slot lora_0 |
Pusa HIGH/LOW — these DO resolve if you downloaded to loras/WanVideo/Pusa/ |
WanVideoLoraSelectMulti ×2, slot lora_1 |
lightx2v_T2V_14B_cfg_step_distill_v2_lora_rank128_bf16.safetensors @ 1.0 |
VHS_LoadVideo |
your clip |
WanVideoTextEncodeCached positive_prompt |
your continuation prompt |
The official HIGH-underscore / LOW-dash filename inconsistency is a real trap. Verify each one rather than copy-pasting.
⚠️ TRAP 2 — the distill LoRA silently drops to none
The example's lightx2v path is WanVideo\Lightx2v\…rank64_bf16_.safetensors (note
the trailing _). Locally you usually have rank128 (…rank128_bf16), so the
slot resets to none on load, which removes the speed LoRA, and 6-step /
cfg-1 sampling then produces mush. Re-add it to lora_1 (strength 1.0) on BOTH
WanVideoLoraSelectMulti nodes. Keep merge_loras=false on both (fp8 gotcha
above).
⚠️ TRAP 3 — the active prompt is on WanVideoTextEncodeCached, not CLIPTextEncode
The example also contains a CLIPLoader → CLIPTextEncode → WanVideoTextEmbedBridge
branch (the "red panda" prompt). It is NOT wired to the samplers. Both
WanVideoSampler.text_embeds come from WanVideoTextEncodeCached
(umt5-xxl-enc-bf16). Edit the prompt THERE; the CLIPTextEncode pair is a decoy
that get_workflow (action:"strip") will show dangling.
⚠️ TRAP 4 — match the conditioning fps to WAN-native (16)
If your source clip was frame-interpolated (e.g. RIFE'd to 32/50 fps), set
VHS_LoadVideo.force_rate = 16 so the conditioning frames carry motion at
WAN's native cadence. Otherwise the encoded "past" runs at 2 to 3× the model's
pace and you get a velocity jump at the seam, the exact artifact Pusa exists to
avoid. Best practice: extend the pre-interpolation 16fps master, then
interpolate/upscale the combined result afterwards, not before.
⚠️ TRAP 5 — the example assumes SageAttention + torch.compile (triton)
WanVideoModelLoader in the example sets attention_mode: sageattn and wires
a WanVideoTorchCompileSettings (inductor) into compile_args. Both are
optional accelerators with extra deps that a stock Windows ComfyUI usually lacks:
sageattnneeds thesageattentionpackage. Missing means the model loader hard-fails withValueError: Can't import SageAttention: No module named 'sageattention'before any sampling. Fix: setattention_modetosdpaon BOTHWanVideoModelLoaders (always available; a bit slower).- inductor
torch.compileneeds triton (no official Windows build). Missing means compile errors later. Fix: disconnectWanVideoTorchCompileSettingsfrom each model loader'scompile_args(or don't load it). Only re-enable these two if you've actually installed sageattention / triton-windows.
Check first with the ComfyUI startup log (it prints Could not load sageattention… and triton: unavailable) or install_custom_node (action: "list").
Preferred end-to-end order
Generate (or Krea2→WAN/LTX i2v), then Pusa-extend at 832×480/16fps, THEN
upscale+interpolate (hand the extended clip to the video-upscale block / a
saved Upscale4x-RIFE-1080p subgraph). Upscaling or interpolating before
extending wastes the work and feeds Pusa an off-cadence, harder-to-match
conditioning clip.
Models, LoRAs & where to get them
UNET — WAN 2.2 T2V A14B (already installed for wan-t2v-video)
| Model | Loader | Notes |
|---|---|---|
Wan2_2-T2V-A14B-HIGH_fp8_e4m3fn_scaled_KJ.safetensors |
WanVideoModelLoader |
HighNoise expert, fp8. Quantization fp8_e4m3fn_scaled. |
Wan2_2-T2V-A14B-LOW_fp8_e4m3fn_scaled_KJ.safetensors |
WanVideoModelLoader |
LowNoise expert, fp8. |
Text encoder + VAE: same as wan-t2v-video. UMT5
(umt5_xxl_fp8_e4m3fn_scaled / umt5_xxl_fp16) via the wrapper's text-embed
path, and the WAN VAE (wan_2.1_vae) via WanVideoVAELoader. The example uses
WanVideoTinyVAELoader + taew2_1.safetensors for fast preview decode; use
the full WAN VAE for final-quality decode.
Pusa V1 LoRAs — the ONLY new download (~1.9 GB)
From kijai's HF repo Kijai/WanVideo_comfy, folder Pusa/. Place in
models/loras/ (the example expects them under loras/WanVideo/Pusa/):
| LoRA file | ~Size | Applies to | Strength (example) |
|---|---|---|---|
Wan22_PusaV1_lora_HIGH_resized_dynamic_avg_rank_98_bf16.safetensors |
~956 MB | HIGH T2V model | 1.5 |
Wan22_PusaV1_lora_LOW_resized_dynamic_avg_rank_98_bf16.safetensors |
~968 MB | LOW T2V model | 1.4 |
There is also a single-file
Wan21_PusaV1_LoRA_14B_rank512_bf16.safetensors(~4.9 GB) in the same folder. That's the Wan 2.1 single-model Pusa LoRA. For the 2.2 dual HIGH/LOW extension graph, use the twoWan22_...rank_98files above, matched to the correct expert. Upstream weights / paper:RaphaelLiu/PusaV1on HF.
Speed LoRA (paired with Pusa in the example)
The example also stacks the lightx2v T2V distill LoRA on each model via
WanVideoLoraSelectMulti, so 6-step low-CFG sampling works:
| LoRA | Strength | From |
|---|---|---|
lightx2v_T2V_14B_cfg_step_distill_v2_lora_rank64_bf16_.safetensors |
1.0 | Kijai/WanVideo_comfy/Lightx2v/ |
LoRAs are selected with WanVideoLoraSelectMulti (multi-slot) and fed into
each WanVideoModelLoader's lora input. One select feeds HIGH (Pusa HIGH +
distill), one feeds LOW (Pusa LOW + distill).
⚠️ CRITICAL — merge_loras=false on fp8 models (same gotcha as wan-t2v-video)
Pusa loads LoRAs onto the fp8-quantized T2V A14B models
(quantization=fp8_e4m3fn_scaled). As documented in wan-t2v-video: when a LoRA
is applied to an fp8 model via the wrapper's LoRA select, set merge_loras to
false. The default merge_loras=true tries to bake the LoRA into the
already-quantized fp8 weights and hard-crashes ComfyUI during LoRA loading with
no Python traceback (looks like an unexplained restart/OOM). false applies
the LoRA as a runtime patch, which is fp8-safe. This applies to BOTH the Pusa
LoRAs and the lightx2v distill LoRA. Use merge_loras=true only on
non-quantized bf16/fp16 models.
Settings
Sampler (from the example — distilled 6-step, two-pass HIGH→LOW)
| Param | HIGH pass | LOW pass | Notes |
|---|---|---|---|
| model | HIGH + Pusa HIGH (1.5) + distill (1.0) | LOW + Pusa LOW (1.4) + distill (1.0) | |
| scheduler | flowmatch_pusa |
flowmatch_pusa |
required for Pusa |
| steps | 6 | 6 | distilled; raise to ~20–30 for the non-distill path |
| cfg | 1.0 | 1.0 | distilled low-CFG; ~5–6 without distill |
| shift | 5.0 | 5.0 | flow-matching shift |
| start_step / end_step | 0 / 3 | 3 / −1 | HIGH does early steps, LOW finishes |
noisy_steps (on AddPusaNoise) |
0 | 2 | extra-noise duration per pass |
If you drop the distill LoRA: use steps ~20 to 30, cfg ~5 to 6, keep
flowmatch_pusa and shift 5, single-pass unipc-style splitting still works
HIGH→LOW.
Pusa noise (WanVideoAddPusaNoise.noise_multipliers)
This is the dial that controls how strictly the continuation honors the input clip vs. how free it is to diverge:
- Lower multipliers (toward 0) = conditioning latents stay clean = the continuation clings tightly to the source frames (less drift, but can look "stuck" or repeat).
- Higher multipliers = more noise on the conditioning latents = the model is freer to evolve the scene (more new motion, more drift risk).
- The example ramps them
[0.0 … 0.2]across the conditioning latents (one per encoded latent, viaCreateScheduleFloatListdriven byGetLatentSizeAndCount) so the oldest frame is locked and the seam frame gets a little noise for a smooth blend. Start there; nudge the top of the ramp up (~0.3) if continuations feel frozen, down if they drift.
Seam color/saturation drift → ColorMatch the generated frames ⭐
The most common quality complaint with a Pusa extension: the moment you cross
the seam, the color saturates or shifts. The conditioning frames are your real
footage (near-clean latents), but the generated tail comes purely from the
model's prior, which biases toward higher contrast and saturation (worse with the
distill LoRA and fp16_fast). Motion carries fine; the palette pops.
Two fixes, best applied together:
base_precision: bf16on bothWanVideoModelLoaders instead offp16_fast. fp16_fast's reduced precision drifts over the generated tail and compounds the saturation; bf16 is more color-stable (small speed cost).Re-grade the generated frames to the source palette with a
ColorMatchV2(KJNodes) betweenWanVideoDecodeand the final stitch/save:image_target←WanVideoDecode(the generated window)image_ref← the resized original clip (ImageResizeKJv2output, your real footage)method:hm-mkl-hm(histogram→MKL→histogram; strongest at removing a palette jump while keeping per-frame variation),strength1.0.- Re-route the downstream consumers (
ImageBatchMulti/ImageConcatMulti'simage_1) to take the ColorMatch output instead of the raw decode.
Tune: if under-corrected, raise
strength; if washed or over-corrected, drop to ~0.6; for an even tighter temporal lock use a single clean reference frame (the last conditioning frame) instead of the whole clip. UseColorMatchV2(not the deprecatedColorMatch).
This also matters for chaining. Color-match every new segment to the previous one before concat or the drift compounds hop-to-hop.
Length, frame counts & fps
WanVideoEmptyEmbeds.num_framesis the total window (conditioning frames + new frames). The example uses 81 total (the WAN-native4n+1length, ~5 s @16 fps).- The number of new frames added = total − conditioning frames. With ~13 tail frames conditioned and 81 total, you add ~68 new frames (~4 s) per pass.
num_framesstep is 4 in the node; keep total on the WAN4n+1grid (49 / 81 / 121 …).frame_ratefor output is 16 fps (WAN 2.2 native).- Resolution: 832×480 default (divisible by 16).
ImageResizeKJv2withcrop/centerand divisor 16 keeps the loaded clip on-grid.
Chaining multiple extensions
Making a long video by repeating the extension is in references/chaining.md.
VRAM tiers
Same envelope as wan-t2v-video (dual A14B fp8 + UMT5); Pusa adds only ~1.9 GB
of LoRA. Use the wrapper's offload tooling.
| VRAM | Setup |
|---|---|
| 24 GB+ | Dual fp8 A14B + Pusa LoRAs + distill. WanVideoBlockSwap (offload some blocks) for headroom; WanVideoTorchCompileSettings (inductor) for speed; sageattn. 81 frames @832×480 fits. |
| 12–16 GB | More aggressive WanVideoBlockSwap; enable VAE tiling on WanVideoEncode (enable_vae_tiling=true, 272/144 tiles) and on WanVideoDecode; drop total frames to 49; consider single-pass. |
| 8 GB | Tight — heavy block swap + tiled VAE + 49 frames + tiny VAE preview decode. Expect slow. |
WanVideoModelLoaderquantfp8_e4m3fn_scaled, base precisionfp16_fast,offload_device,sageattn(the example's settings).- Always
clear_vrambefore switching to this from another model family. - Encoder VAE tiling (
WanVideoEncode) matters here because you're VAE-encoding real footage in addition to decoding output.
Gotchas
- Loading the example silently resets model/VAE/distill-LoRA dropdowns to the
wrong first entry (subfolder paths don't resolve on a flat layout). This is the
#1 cause of a Pusa run that errors or generates wrong content. See "In practice:
load → strip → re-point" and re-point ALL of them. Use
get_workflow (action:"strip")to spot it. - The prompt lives on
WanVideoTextEncodeCached, not the CLIPTextEncode "decoy" branch (which isn't wired to the samplers). - Interpolated source → seam speed jump. Set
VHS_LoadVideo.force_rate = 16, or condition on the pre-interpolation 16 fps master. sageattn/ torch.compile errors. The example assumes SageAttention + triton. On a box without them, setattention_mode=sdpaand disconnectWanVideoTorchCompileSettingsfrom both model loaders (TRAP 5).- Saturation/color pop after the seam. Re-grade the generated frames with a
ColorMatchV2(hm-mkl-hm) referencing the source clip, and usebf16notfp16_fast(see "Seam color/saturation drift"). - Scheduler must be
flowmatch_pusa. Leaving it onunipc/eulerignores the Pusa per-latent noise schedule, so the conditioning latents don't behave as clean anchors and you get a hard cut / regeneration instead of a smooth continuation. merge_loras=falseon fp8 (see CRITICAL above) applies to the Pusa AND distill LoRAs; defaulttruekills the process with no traceback.- Match Pusa LoRA to expert:
...HIGH...→ HIGH model,...LOW...→ LOW model. Crossing them degrades quality. Don't substitute the Wan 2.1 single-filerank512LoRA into the 2.2 dual graph. - Frame-count grid: keep
num_frameson4n+1(49/81/121). Off-grid totals can error or pad oddly.num_framesUI step is 4. - Motion drift / "frozen" continuation: tune
noise_multipliers. Too low = stuck/looping; too high = subject/scene wanders. The0→0.2ramp is the safe middle. - Color/exposure drift across chained hops is the most common long-video artifact. Mitigate: modest noise, restate the prompt, and optionally color-match each new segment to the previous before concat.
- Audio: WAN/Pusa generate silent video. The original clip's audio is
not extended. Re-attach/curate audio at the end with
VHS_VideoCombine(pass the sourceaudiothrough) or in an editor, and note the new section has no native sound. - ffmpeg required for the final mux (same as the other video skills): if
VHS_VideoCombineerrorsffmpeg ... could not be found, run<comfy-venv>/python -m pip install imageio-ffmpegand reboot. - Preview vs final VAE:
taew2_1(TinyVAE) is for fast preview decode; decode the final with the full WAN VAE for quality.
Minimal wiring (if you want fewer KJNodes)
The KJNodes (GetImageRangeFromBatch, GetLatentSizeAndCount,
CreateScheduleFloatList, ImageResizeKJv2) are conveniences. The irreducible
chain is:
load clip → (resize to 16-grid) → WanVideoEncode(vae, tail frames) → LATENT
WanVideoEmptyEmbeds(W,H,total) [extra_latents = that LATENT] → embeds
embeds → WanVideoAddPusaNoise(noise_multipliers, noisy_steps) → embeds
WanVideoSampler(model+Pusa LoRA, embeds, scheduler=flowmatch_pusa, shift 5) → LATENT
WanVideoDecode(WAN VAE) → VHS_VideoCombine
You can hand a constant list to noise_multipliers instead of building a ramp;
the ramp smooths the seam. Two-pass HIGH→LOW is recommended (matches WAN
2.2's MoE) but a single LOW-model pass works for quick tests.
See also
wan-t2v-video. The base WAN 2.2 T2V stack this builds on (model/encoder/ VAE loading, themerge_loras=falsefp8 gotcha in full, block-swap/VRAM). Read it first.video-upscale. The natural next step: extend, then upscale. Generate / extend at 832×480, then run the result through the downscale → SeedVR2 (temporal restore+upscale) → RIFE → VHS encode pipeline for a clean, higher-res, higher-fps final. Do the extension first, upscale last (upscaling then extending wastes the restorer's work and risks re-drift).ltxv2-video. An alternative video family with its own extender variant; Pusa/WAN is the path when you want to continue an existing WAN-style clip.
Packs
No dedicated video-extend installer pack ships yet. Since Pusa reuses the
installed WanVideoWrapper + KJNodes + VideoHelperSuite stack, a pack only needs to
list those custom_nodes[] (kijai/ComfyUI-WanVideoWrapper,
Kijai/ComfyUI-KJNodes, Kosinkadink/ComfyUI-VideoHelperSuite) plus the two Pusa V1
LoRAs in models[] (from Kijai/WanVideo_comfy/Pusa/). The big T2V A14B models
are shared with wan-t2v-video; don't re-download. Install nodes ad-hoc with
panel_install_node or apply a manifest with apply_manifest. Contribute a
finished pack upstream (github.com/artokun/comfyui-mcp).
Sources
- Official: none found.
- Empirical: sampler values, wiring, and prompt notes from working graphs in
packs/and observed renders; not a vendor prompting guide.
| 1 | |
| 2 | name video-extend |
| 3 | description Extend / continue a video temporally with Pusa 2.2 in ComfyUI. Temporal flowmatching (the flowmatch_pusa scheduler + WanVideoAddPusaNoise) on the WanVideoWrapper stack with WAN 2.2 T2V A14B (HIGH/LOW) models and the Pusa V1 LoRAs, conditioning on the loaded clip via WanVideoEncode so the existing motion carries into the continuation. Covers the kijai wanvideo_2_2_14B_Pusa_extension graph, model/LoRA slots + downloads, noise/length/scheduler settings, chaining multiple extensions, VRAM tiers, gotchas, and the extend→upscale handoff. |
| 4 | globs |
| 5 | - "**/*.json" |
| 6 | - "**/packs/**" |
| 7 | |
| 8 | |
| 9 | # Video Extension (Pusa 2.2 — temporal flowmatching) |
| 10 | |
| 11 | ## Overview |
| 12 | |
| 13 | Pusa extends a video temporally. It continues and lengthens an existing clip |
| 14 | rather than regenerating it from scratch. It does this on the |
| 15 | ComfyUI-WanVideoWrapper stack (kijai) using the WAN 2.2 T2V A14B dual |
| 16 | HIGH/LOW models you already have for `wan-t2v-video`, plus the small Pusa V1 |
| 17 | LoRAs and a Pusa-specific sampling path: the `flowmatch_pusa` scheduler and |
| 18 | the `WanVideoAddPusaNoise` node. The input clip is encoded with |
| 19 | `WanVideoEncode` and injected as the first latents of the generation, which |
| 20 | is what carries the existing motion and content into the continuation. |
| 21 | |
| 22 | The official reference graph is kijai's |
| 23 | `wanvideo_2_2_14B_Pusa_extension_example_01.json` (in |
| 24 | `ComfyUI-WanVideoWrapper/example_workflows/`). This skill is built directly from |
| 25 | that workflow plus the live node schemas. |
| 26 | |
| 27 | > Relationship to `wan-t2v-video`: Pusa rides on the exact same WanVideoWrapper |
| 28 | > stack. Same T2V A14B HIGH/LOW fp8 models, same UMT5 text encoder, same WAN |
| 29 | > VAE, same block-swap/torch-compile machinery. The only new downloads are the |
| 30 | > two Pusa V1 LoRAs (~1.9 GB total). Read `wan-t2v-video` first for the base |
| 31 | > stack; this skill is the temporal-extension delta on top of it. |
| 32 | |
| 33 | > Verification note: every node, model, LoRA filename and setting below was |
| 34 | > confirmed against the live ComfyUI `/object_info` (WanVideoWrapper installed) |
| 35 | > and against kijai's example workflow JSON + HF repo (June 2026). Where a value |
| 36 | > is a starting recommendation rather than a hard requirement it's flagged. Don't |
| 37 | > substitute a node you can't confirm with `install_custom_node` (`action: "list"`) / |
| 38 | > `create_workflow (action:"node_info")`. |
| 39 | |
| 40 | |
| 41 | |
| 42 | ## What "temporal flowmatching" means here (why it extends, not regenerates) |
| 43 | |
| 44 | WAN is a flow-matching video model: sampling integrates a velocity field from |
| 45 | noise to a clean latent, and every frame normally shares the same denoising |
| 46 | timestep. Pusa's contribution (Vectorized Timestep Adaptation) is to make the |
| 47 | timestep per-frame. The frames you already have can be held at (or near) |
| 48 | *t = 0 (clean)* while the new frames start from *t = 1 (noise)*, and the model |
| 49 | flow-matches the noisy tail conditioned on the clean head. |
| 50 | |
| 51 | Concretely in the graph: |
| 52 | |
| 53 | `WanVideoEncode` turns the tail of your loaded clip into a clean latent. |
| 54 | That latent is placed at the front of an otherwise-empty embed |
| 55 | (`WanVideoEmptyEmbeds` + `WanVideoAddExtraLatent`), so the generation's first |
| 56 | latents ARE your real footage. |
| 57 | `WanVideoAddPusaNoise` assigns small, ramping per-latent noise |
| 58 | multipliers to those conditioning latents (so they stay mostly clean) and |
| 59 | full noise to the new latents. This per-frame noise schedule is the |
| 60 | "vectorized timestep." |
| 61 | `flowmatch_pusa` on `WanVideoSampler` integrates that mixed-timestep field. |
| 62 | |
| 63 | Because the conditioning latents are real (not a single start image like |
| 64 | I2V), the continuation inherits the existing motion, subject, camera and |
| 65 | color, then keeps going. That's the difference from plain T2V (no memory of any |
| 66 | clip) and from I2V (conditions on one still frame only). |
| 67 | |
| 68 | |
| 69 | |
| 70 | ## ⭐ Recommended pipeline (the kijai extension graph) |
| 71 | |
| 72 | |
| 73 | VHS_LoadVideo (your clip) |
| 74 | │ IMAGE (all frames) |
| 75 | ▼ |
| 76 | ImageResizeKJv2 ◄── resize to 832×480 (divisible by 16), get W/H |
| 77 | │ |
| 78 | ├─► GetImageRangeFromBatch (tail N frames) ─► WanVideoEncode (vae, image) |
| 79 | │ │ LATENT = clean |
| 80 | │ ▼ conditioning latents |
| 81 | │ GetLatentSizeAndCount ─► count |
| 82 | │ │ |
| 83 | WanVideoEmptyEmbeds (W,H, total_frames=81) ▼ |
| 84 | │ WANVIDIMAGE_EMBEDS CreateScheduleFloatList |
| 85 | └────────► WanVideoAddExtraLatent ◄────────┘ (per-latent noise multipliers, |
| 86 | │ (encoded clip latent at front) ramp e.g. 0→0.2) |
| 87 | ▼ WANVIDIMAGE_EMBEDS |
| 88 | WanVideoAddPusaNoise ◄── noise_multipliers (list), noisy_steps |
| 89 | │ |
| 90 | ┌──────────────┴───────────────┐ |
| 91 | ▼ (pass 1, HIGH) ▼ (pass 2, LOW) |
| 92 | WanVideoSampler (HIGH model WanVideoSampler (LOW model |
| 93 | + Pusa HIGH LoRA + distill, + Pusa LOW LoRA + distill, |
| 94 | flowmatch_pusa, steps 6, cfg 1, flowmatch_pusa, steps 6, cfg 1, |
| 95 | shift 5, start 0 / end 3) shift 5, start 3 / end -1) |
| 96 | └──────────────┬───────────────┘ |
| 97 | ▼ LATENT |
| 98 | WanVideoDecode (WAN VAE) |
| 99 | │ IMAGE |
| 100 | ▼ |
| 101 | VHS_VideoCombine ─► MP4 (16 fps) |
| 102 | |
| 103 | |
| 104 | `VHS_LoadVideo` / `VHS_VideoCombine` come from ComfyUI-VideoHelperSuite |
| 105 | (installed). `VHS_VideoCombine` is preferred for the encode (audio passthrough). |
| 106 | Everything `WanVideo*` is ComfyUI-WanVideoWrapper (installed). |
| 107 | `ImageResizeKJv2`, `GetImageRangeFromBatch`, `GetLatentSizeAndCount`, |
| 108 | `CreateScheduleFloatList` are ComfyUI-KJNodes (installed alongside the |
| 109 | wrapper). They're convenience nodes; see "Minimal wiring" if you want fewer. |
| 110 | |
| 111 | ### The two load-bearing nodes (confirmed schemas) |
| 112 | |
| 113 | `WanVideoAddPusaNoise`: *"Adds latent and timestep noise multipliers when |
| 114 | using flowmatch_pusa."* |
| 115 | |
| 116 | | Input | Type | Meaning | |
| 117 | |---|---|---| |
| 118 | | `embeds` | `WANVIDIMAGE_EMBEDS` | the embeds carrying your encoded clip latents | |
| 119 | | `noise_multipliers` | `FLOAT` (list) | per-input-latent noise; **0 = keep that latent fully clean**, higher = let the model change it. In the example this is a **ramp** `[0.0, 0.07, 0.13, 0.17, 0.19, 0.2]` fed from `CreateScheduleFloatList` (one value per conditioning latent), so the oldest conditioning frame stays cleanest and the seam frame gets a touch of noise for smooth blending. | |
| 120 | | `noisy_steps` | `INT` (default −1) | how many sampling steps the extra noise is applied for; the example uses **0 on the HIGH pass and 2 on the LOW pass**. −1 = all steps. | |
| 121 | |
| 122 | It outputs `WANVIDIMAGE_EMBEDS` straight into `WanVideoSampler`'s `image_embeds`. |
| 123 | |
| 124 | `flowmatch_pusa` is a value in `WanVideoSampler.scheduler` (confirmed present |
| 125 | in the dropdown: `...flowmatch_distill, flowmatch_pusa, multitalk...`). It must |
| 126 | be selected on the sampler(s) for the Pusa noise schedule to be interpreted |
| 127 | correctly. The example also wires explicit `WanVideoScheduler` nodes set to |
| 128 | `flowmatch_pusa`, steps 6, shift 5 (one per pass, split 0 to 3 and 3 to end). |
| 129 | |
| 130 | ### How the input clip conditions the extension (the key wire) |
| 131 | |
| 132 | `WanVideoEncode(vae, image=<tail frames of clip>) → LATENT` → |
| 133 | `WanVideoAddExtraLatent` (or `WanVideoEmptyEmbeds.extra_latents`, tooltip: |
| 134 | "First latent to use for the Pusa -model"). This places the real clip's |
| 135 | latents at the head of the embed window. The sampler then only has to generate |
| 136 | the tail, flow-matched onto that clean head. That is the entire trick. No |
| 137 | `CLIPVision`, no `WanFirstLastFrameToVideo`. |
| 138 | |
| 139 | |
| 140 | |
| 141 | ## In practice: load → strip → re-point (DON'T hand-build) ⭐ preferred |
| 142 | |
| 143 | The kijai `wanvideo_2_2_14B_Pusa_extension_example_01.json` is a 56-node graph |
| 144 | thick with `GetNode`/`SetNode` buses, `Reroute`s, and an alternate (dead) text |
| 145 | branch. Hand-wiring the Pusa noise / extra-latent / frame-stitch path is slow and |
| 146 | error-prone. The reliable flow is to load the real graph, then adapt ~7 widgets: |
| 147 | |
| 148 | Stage the example anywhere on disk (e.g. copy into the ComfyUI workflows |
| 149 | folder). |
| 150 | `panel_load_workflow(path: …)` drops it on the canvas server-side (no |
| 151 | 150KB JSON through chat). |
| 152 | `panel_strip_workflow(path: …)` returns the resolved API graph |
| 153 | (Get/Set/Reroute/bypass collapsed to real links). This is how you SEE what is |
| 154 | actually wired. It exposes both the dead text branch and the silently-reset |
| 155 | dropdowns below. (Raw UI JSON hides them.) |
| 156 | |
| 157 | ### ⚠️ TRAP 1 — the example's model paths reset to the WRONG file on load |
| 158 | |
| 159 | The example references models by subfolder (`WanVideo\2_2\…`, |
| 160 | `WanVideo\Lightx2v\…`, `wanvideo\Wan2_1_VAE_bf16…`). On a flat local `models/` |
| 161 | layout those don't resolve, so ComfyUI silently falls each dropdown back to the |
| 162 | first entry in the list. E.g. both `WanVideoModelLoader`s land on |
| 163 | `Qwen_Image_Edit-Q8_0.gguf` and the `WanVideoVAELoader` on `LTX23_audio_vae_bf16`. |
| 164 | It looks wired but errors (wrong arch) or renders garbage. After loading, set |
| 165 | each explicitly: |
| 166 | |
| 167 | | Node | Set to (local) | |
| 168 | |---|---| |
| 169 | | `WanVideoModelLoader` **HIGH** | `Wan2_2-T2V-A14B_HIGH_fp8_e4m3fn_scaled_KJ.safetensors` — note **underscore** before HIGH | |
| 170 | | `WanVideoModelLoader` **LOW** | `Wan2_2-T2V-A14B-LOW_fp8_e4m3fn_scaled_KJ.safetensors` — note **dash** before LOW | |
| 171 | | `WanVideoVAELoader` | `wan_2.1_vae.safetensors` | |
| 172 | | `WanVideoLoraSelectMulti` ×2, slot `lora_0` | Pusa HIGH/LOW — these DO resolve if you downloaded to `loras/WanVideo/Pusa/` | |
| 173 | | `WanVideoLoraSelectMulti` ×2, slot `lora_1` | `lightx2v_T2V_14B_cfg_step_distill_v2_lora_rank128_bf16.safetensors` @ 1.0 | |
| 174 | | `VHS_LoadVideo` | your clip | |
| 175 | | `WanVideoTextEncodeCached` `positive_prompt` | your continuation prompt | |
| 176 | |
| 177 | > The official HIGH-underscore / LOW-dash filename inconsistency is a real |
| 178 | > trap. Verify each one rather than copy-pasting. |
| 179 | |
| 180 | ### ⚠️ TRAP 2 — the distill LoRA silently drops to `none` |
| 181 | |
| 182 | The example's lightx2v path is `WanVideo\Lightx2v\…rank64_bf16_.safetensors` (note |
| 183 | the trailing `_`). Locally you usually have rank128 (`…rank128_bf16`), so the |
| 184 | slot resets to `none` on load, which removes the speed LoRA, and 6-step / |
| 185 | cfg-1 sampling then produces mush. Re-add it to `lora_1` (strength 1.0) on BOTH |
| 186 | `WanVideoLoraSelectMulti` nodes. Keep `merge_loras=false` on both (fp8 gotcha |
| 187 | above). |
| 188 | |
| 189 | ### ⚠️ TRAP 3 — the active prompt is on `WanVideoTextEncodeCached`, not CLIPTextEncode |
| 190 | |
| 191 | The example also contains a `CLIPLoader → CLIPTextEncode → WanVideoTextEmbedBridge` |
| 192 | branch (the "red panda" prompt). It is NOT wired to the samplers. Both |
| 193 | `WanVideoSampler.text_embeds` come from `WanVideoTextEncodeCached` |
| 194 | (`umt5-xxl-enc-bf16`). Edit the prompt THERE; the CLIPTextEncode pair is a decoy |
| 195 | that get_workflow (action:"strip") will show dangling. |
| 196 | |
| 197 | ### ⚠️ TRAP 4 — match the conditioning fps to WAN-native (16) |
| 198 | |
| 199 | If your source clip was frame-interpolated (e.g. RIFE'd to 32/50 fps), set |
| 200 | `VHS_LoadVideo.force_rate = 16` so the conditioning frames carry motion at |
| 201 | WAN's native cadence. Otherwise the encoded "past" runs at 2 to 3× the model's |
| 202 | pace and you get a velocity jump at the seam, the exact artifact Pusa exists to |
| 203 | avoid. Best practice: extend the pre-interpolation 16fps master, then |
| 204 | interpolate/upscale the combined result afterwards, not before. |
| 205 | |
| 206 | ### ⚠️ TRAP 5 — the example assumes SageAttention + torch.compile (triton) |
| 207 | |
| 208 | `WanVideoModelLoader` in the example sets `attention_mode: sageattn` and wires |
| 209 | a `WanVideoTorchCompileSettings` (inductor) into `compile_args`. Both are |
| 210 | optional accelerators with extra deps that a stock Windows ComfyUI usually lacks: |
| 211 | |
| 212 | `sageattn` needs the `sageattention` package. Missing means the model loader |
| 213 | hard-fails with `ValueError: Can't import SageAttention: No module named |
| 214 | 'sageattention'` before any sampling. Fix: set `attention_mode` to `sdpa` |
| 215 | on BOTH `WanVideoModelLoader`s (always available; a bit slower). |
| 216 | inductor `torch.compile` needs triton (no official Windows build). |
| 217 | Missing means compile errors later. Fix: disconnect `WanVideoTorchCompileSettings` |
| 218 | from each model loader's `compile_args` (or don't load it). Only re-enable |
| 219 | these two if you've actually installed sageattention / triton-windows. |
| 220 | |
| 221 | Check first with the ComfyUI startup log (it prints `Could not load |
| 222 | sageattention…` and `triton: unavailable`) or `install_custom_node` (`action: "list"`). |
| 223 | |
| 224 | ### Preferred end-to-end order |
| 225 | |
| 226 | Generate (or Krea2→WAN/LTX i2v), then Pusa-extend at 832×480/16fps, THEN |
| 227 | upscale+interpolate (hand the extended clip to the `video-upscale` block / a |
| 228 | saved `Upscale4x-RIFE-1080p` subgraph). Upscaling or interpolating before |
| 229 | extending wastes the work and feeds Pusa an off-cadence, harder-to-match |
| 230 | conditioning clip. |
| 231 | |
| 232 | |
| 233 | |
| 234 | ## Models, LoRAs & where to get them |
| 235 | |
| 236 | ### UNET — WAN 2.2 T2V A14B (already installed for `wan-t2v-video`) |
| 237 | |
| 238 | | Model | Loader | Notes | |
| 239 | |---|---|---| |
| 240 | | `Wan2_2-T2V-A14B-HIGH_fp8_e4m3fn_scaled_KJ.safetensors` | `WanVideoModelLoader` | HighNoise expert, fp8. Quantization `fp8_e4m3fn_scaled`. | |
| 241 | | `Wan2_2-T2V-A14B-LOW_fp8_e4m3fn_scaled_KJ.safetensors` | `WanVideoModelLoader` | LowNoise expert, fp8. | |
| 242 | |
| 243 | Text encoder + VAE: same as `wan-t2v-video`. UMT5 |
| 244 | (`umt5_xxl_fp8_e4m3fn_scaled` / `umt5_xxl_fp16`) via the wrapper's text-embed |
| 245 | path, and the WAN VAE (`wan_2.1_vae`) via `WanVideoVAELoader`. The example uses |
| 246 | `WanVideoTinyVAELoader` + `taew2_1.safetensors` for fast preview decode; use |
| 247 | the full WAN VAE for final-quality decode. |
| 248 | |
| 249 | ### Pusa V1 LoRAs — the ONLY new download (~1.9 GB) |
| 250 | |
| 251 | From kijai's HF repo `Kijai/WanVideo_comfy`, folder `Pusa/`. Place in |
| 252 | `models/loras/` (the example expects them under `loras/WanVideo/Pusa/`): |
| 253 | |
| 254 | | LoRA file | ~Size | Applies to | Strength (example) | |
| 255 | |---|---|---|---| |
| 256 | | `Wan22_PusaV1_lora_HIGH_resized_dynamic_avg_rank_98_bf16.safetensors` | ~956 MB | **HIGH** T2V model | **1.5** | |
| 257 | | `Wan22_PusaV1_lora_LOW_resized_dynamic_avg_rank_98_bf16.safetensors` | ~968 MB | **LOW** T2V model | **1.4** | |
| 258 | |
| 259 | > There is also a single-file `Wan21_PusaV1_LoRA_14B_rank512_bf16.safetensors` |
| 260 | > (~4.9 GB) in the same folder. That's the Wan 2.1 single-model Pusa LoRA. |
| 261 | > For the 2.2 dual HIGH/LOW extension graph, use the two `Wan22_...rank_98` |
| 262 | > files above, matched to the correct expert. Upstream weights / paper: |
| 263 | > `RaphaelLiu/PusaV1` on HF. |
| 264 | |
| 265 | ### Speed LoRA (paired with Pusa in the example) |
| 266 | |
| 267 | The example also stacks the lightx2v T2V distill LoRA on each model via |
| 268 | `WanVideoLoraSelectMulti`, so 6-step low-CFG sampling works: |
| 269 | |
| 270 | | LoRA | Strength | From | |
| 271 | |---|---|---| |
| 272 | | `lightx2v_T2V_14B_cfg_step_distill_v2_lora_rank64_bf16_.safetensors` | 1.0 | `Kijai/WanVideo_comfy/Lightx2v/` | |
| 273 | |
| 274 | LoRAs are selected with `WanVideoLoraSelectMulti` (multi-slot) and fed into |
| 275 | each `WanVideoModelLoader`'s `lora` input. One select feeds HIGH (Pusa HIGH + |
| 276 | distill), one feeds LOW (Pusa LOW + distill). |
| 277 | |
| 278 | ### ⚠️ CRITICAL — `merge_loras=false` on fp8 models (same gotcha as `wan-t2v-video`) |
| 279 | |
| 280 | Pusa loads LoRAs onto the fp8-quantized T2V A14B models |
| 281 | (`quantization=fp8_e4m3fn_scaled`). As documented in `wan-t2v-video`: when a LoRA |
| 282 | is applied to an fp8 model via the wrapper's LoRA select, set `merge_loras` to |
| 283 | `false`. The default `merge_loras=true` tries to bake the LoRA into the |
| 284 | already-quantized fp8 weights and hard-crashes ComfyUI during LoRA loading with |
| 285 | no Python traceback (looks like an unexplained restart/OOM). `false` applies |
| 286 | the LoRA as a runtime patch, which is fp8-safe. This applies to BOTH the Pusa |
| 287 | LoRAs and the lightx2v distill LoRA. Use `merge_loras=true` only on |
| 288 | non-quantized bf16/fp16 models. |
| 289 | |
| 290 | |
| 291 | |
| 292 | ## Settings |
| 293 | |
| 294 | ### Sampler (from the example — distilled 6-step, two-pass HIGH→LOW) |
| 295 | |
| 296 | | Param | HIGH pass | LOW pass | Notes | |
| 297 | |---|---|---|---| |
| 298 | | model | HIGH + Pusa HIGH (1.5) + distill (1.0) | LOW + Pusa LOW (1.4) + distill (1.0) | | |
| 299 | | scheduler | `flowmatch_pusa` | `flowmatch_pusa` | **required** for Pusa | |
| 300 | | steps | 6 | 6 | distilled; raise to ~20–30 for the non-distill path | |
| 301 | | cfg | 1.0 | 1.0 | distilled low-CFG; ~5–6 without distill | |
| 302 | | shift | 5.0 | 5.0 | flow-matching shift | |
| 303 | | start_step / end_step | 0 / 3 | 3 / −1 | HIGH does early steps, LOW finishes | |
| 304 | | `noisy_steps` (on AddPusaNoise) | 0 | 2 | extra-noise duration per pass | |
| 305 | |
| 306 | If you drop the distill LoRA: use `steps` ~20 to 30, `cfg` ~5 to 6, keep |
| 307 | `flowmatch_pusa` and `shift` 5, single-pass `unipc`-style splitting still works |
| 308 | HIGH→LOW. |
| 309 | |
| 310 | ### Pusa noise (`WanVideoAddPusaNoise.noise_multipliers`) |
| 311 | |
| 312 | This is the dial that controls how strictly the continuation honors the input |
| 313 | clip vs. how free it is to diverge: |
| 314 | |
| 315 | Lower multipliers (toward 0) = conditioning latents stay clean = the |
| 316 | continuation clings tightly to the source frames (less drift, but can look |
| 317 | "stuck" or repeat). |
| 318 | Higher multipliers = more noise on the conditioning latents = the model is |
| 319 | freer to evolve the scene (more new motion, more drift risk). |
| 320 | The example ramps them `[0.0 … 0.2]` across the conditioning latents (one |
| 321 | per encoded latent, via `CreateScheduleFloatList` driven by |
| 322 | `GetLatentSizeAndCount`) so the oldest frame is locked and the seam frame |
| 323 | gets a little noise for a smooth blend. Start there; nudge the top of the ramp |
| 324 | up (~0.3) if continuations feel frozen, down if they drift. |
| 325 | |
| 326 | ### Seam color/saturation drift → ColorMatch the generated frames ⭐ |
| 327 | |
| 328 | The most common quality complaint with a Pusa extension: the moment you cross |
| 329 | the seam, the color saturates or shifts. The conditioning frames are your real |
| 330 | footage (near-clean latents), but the generated tail comes purely from the |
| 331 | model's prior, which biases toward higher contrast and saturation (worse with the |
| 332 | distill LoRA and `fp16_fast`). Motion carries fine; the palette pops. |
| 333 | |
| 334 | Two fixes, best applied together: |
| 335 | |
| 336 | `base_precision: bf16` on both `WanVideoModelLoader`s instead of |
| 337 | `fp16_fast`. fp16_fast's reduced precision drifts over the generated tail |
| 338 | and compounds the saturation; bf16 is more color-stable (small speed cost). |
| 339 | Re-grade the generated frames to the source palette with a `ColorMatchV2` |
| 340 | (KJNodes) between `WanVideoDecode` and the final stitch/save: |
| 341 | `image_target` ← `WanVideoDecode` (the generated window) |
| 342 | `image_ref` ← the resized original clip (`ImageResizeKJv2` output, your |
| 343 | real footage) |
| 344 | `method`: `hm-mkl-hm` (histogram→MKL→histogram; strongest at removing a |
| 345 | palette jump while keeping per-frame variation), `strength` 1.0. |
| 346 | Re-route the downstream consumers (`ImageBatchMulti` / `ImageConcatMulti`'s |
| 347 | `image_1`) to take the ColorMatch output instead of the raw decode. |
| 348 | |
| 349 | Tune: if under-corrected, raise `strength`; if washed or over-corrected, drop to |
| 350 | ~0.6; for an even tighter temporal lock use a single clean reference frame |
| 351 | (the last conditioning frame) instead of the whole clip. Use `ColorMatchV2` |
| 352 | (not the deprecated `ColorMatch`). |
| 353 | |
| 354 | This also matters for chaining. Color-match every new segment to the |
| 355 | previous one before concat or the drift compounds hop-to-hop. |
| 356 | |
| 357 | ### Length, frame counts & fps |
| 358 | |
| 359 | `WanVideoEmptyEmbeds.num_frames` is the total window (conditioning frames + |
| 360 | new frames). The example uses 81 total (the WAN-native `4n+1` length, ~5 s |
| 361 | @16 fps). |
| 362 | The number of new frames added = total − conditioning frames. With ~13 |
| 363 | tail frames conditioned and 81 total, you add ~68 new frames (~4 s) per pass. |
| 364 | `num_frames` step is 4 in the node; keep total on the WAN `4n+1` grid |
| 365 | (49 / 81 / 121 …). `frame_rate` for output is 16 fps (WAN 2.2 native). |
| 366 | Resolution: 832×480 default (divisible by 16). `ImageResizeKJv2` with |
| 367 | `crop`/`center` and divisor 16 keeps the loaded clip on-grid. |
| 368 | |
| 369 | |
| 370 | |
| 371 | ## Chaining multiple extensions |
| 372 | |
| 373 | Making a long video by repeating the extension is in [`references/chaining.md`]. |
| 374 | |
| 375 | ## VRAM tiers |
| 376 | |
| 377 | Same envelope as `wan-t2v-video` (dual A14B fp8 + UMT5); Pusa adds only ~1.9 GB |
| 378 | of LoRA. Use the wrapper's offload tooling. |
| 379 | |
| 380 | | VRAM | Setup | |
| 381 | |---|---| |
| 382 | | **24 GB+** | Dual fp8 A14B + Pusa LoRAs + distill. `WanVideoBlockSwap` (offload some blocks) for headroom; `WanVideoTorchCompileSettings` (inductor) for speed; `sageattn`. 81 frames @832×480 fits. | |
| 383 | | **12–16 GB** | More aggressive `WanVideoBlockSwap`; enable **VAE tiling** on `WanVideoEncode` (`enable_vae_tiling=true`, 272/144 tiles) and on `WanVideoDecode`; drop total frames to 49; consider single-pass. | |
| 384 | | **8 GB** | Tight — heavy block swap + tiled VAE + 49 frames + tiny VAE preview decode. Expect slow. | |
| 385 | |
| 386 | `WanVideoModelLoader` quant `fp8_e4m3fn_scaled`, base precision `fp16_fast`, |
| 387 | `offload_device`, `sageattn` (the example's settings). |
| 388 | Always `clear_vram` before switching to this from another model family. |
| 389 | Encoder VAE tiling (`WanVideoEncode`) matters here because you're VAE-encoding |
| 390 | real footage in addition to decoding output. |
| 391 | |
| 392 | |
| 393 | |
| 394 | ## Gotchas |
| 395 | |
| 396 | **Loading the example silently resets model/VAE/distill-LoRA dropdowns** to the |
| 397 | wrong first entry (subfolder paths don't resolve on a flat layout). This is the |
| 398 | #1 cause of a Pusa run that errors or generates wrong content. See "In practice: |
| 399 | load → strip → re-point" and re-point ALL of them. Use `get_workflow (action:"strip")` to spot |
| 400 | it. |
| 401 | **The prompt lives on `WanVideoTextEncodeCached`**, not the CLIPTextEncode |
| 402 | "decoy" branch (which isn't wired to the samplers). |
| 403 | **Interpolated source → seam speed jump.** Set `VHS_LoadVideo.force_rate = 16`, |
| 404 | or condition on the pre-interpolation 16 fps master. |
| 405 | **`sageattn` / torch.compile errors.** The example assumes SageAttention + |
| 406 | triton. On a box without them, set `attention_mode=sdpa` and disconnect |
| 407 | `WanVideoTorchCompileSettings` from both model loaders (TRAP 5). |
| 408 | **Saturation/color pop after the seam.** Re-grade the generated frames with a |
| 409 | `ColorMatchV2` (`hm-mkl-hm`) referencing the source clip, and use `bf16` not |
| 410 | `fp16_fast` (see "Seam color/saturation drift"). |
| 411 | **Scheduler must be `flowmatch_pusa`.** Leaving it on `unipc`/`euler` ignores |
| 412 | the Pusa per-latent noise schedule, so the conditioning latents don't behave as |
| 413 | clean anchors and you get a hard cut / regeneration instead of a smooth |
| 414 | continuation. |
| 415 | **`merge_loras=false` on fp8** (see CRITICAL above) applies to the Pusa |
| 416 | AND distill LoRAs; default `true` kills the process with no traceback. |
| 417 | **Match Pusa LoRA to expert**: `...HIGH...` → HIGH model, `...LOW...` → LOW |
| 418 | model. Crossing them degrades quality. Don't substitute the Wan 2.1 |
| 419 | single-file `rank512` LoRA into the 2.2 dual graph. |
| 420 | **Frame-count grid**: keep `num_frames` on `4n+1` (49/81/121). Off-grid |
| 421 | totals can error or pad oddly. `num_frames` UI step is 4. |
| 422 | **Motion drift / "frozen" continuation**: tune `noise_multipliers`. Too low = |
| 423 | stuck/looping; too high = subject/scene wanders. The `0→0.2` ramp is the safe |
| 424 | middle. |
| 425 | **Color/exposure drift** across chained hops is the most common long-video |
| 426 | artifact. Mitigate: modest noise, restate the prompt, and optionally |
| 427 | color-match each new segment to the previous before concat. |
| 428 | **Audio**: WAN/Pusa generate silent video. The original clip's audio is |
| 429 | not extended. Re-attach/curate audio at the end with `VHS_VideoCombine` |
| 430 | (pass the source `audio` through) or in an editor, and note the new section |
| 431 | has no native sound. |
| 432 | **ffmpeg required** for the final mux (same as the other video skills): if |
| 433 | `VHS_VideoCombine` errors `ffmpeg ... could not be found`, run |
| 434 | `<comfy-venv>/python -m pip install imageio-ffmpeg` and reboot. |
| 435 | **Preview vs final VAE**: `taew2_1` (TinyVAE) is for fast preview decode; decode |
| 436 | the final with the full WAN VAE for quality. |
| 437 | |
| 438 | |
| 439 | |
| 440 | ## Minimal wiring (if you want fewer KJNodes) |
| 441 | |
| 442 | The KJNodes (`GetImageRangeFromBatch`, `GetLatentSizeAndCount`, |
| 443 | `CreateScheduleFloatList`, `ImageResizeKJv2`) are conveniences. The irreducible |
| 444 | chain is: |
| 445 | |
| 446 | |
| 447 | load clip → (resize to 16-grid) → WanVideoEncode(vae, tail frames) → LATENT |
| 448 | WanVideoEmptyEmbeds(W,H,total) [extra_latents = that LATENT] → embeds |
| 449 | embeds → WanVideoAddPusaNoise(noise_multipliers, noisy_steps) → embeds |
| 450 | WanVideoSampler(model+Pusa LoRA, embeds, scheduler=flowmatch_pusa, shift 5) → LATENT |
| 451 | WanVideoDecode(WAN VAE) → VHS_VideoCombine |
| 452 | |
| 453 | |
| 454 | You can hand a constant list to `noise_multipliers` instead of building a ramp; |
| 455 | the ramp smooths the seam. Two-pass HIGH→LOW is recommended (matches WAN |
| 456 | 2.2's MoE) but a single LOW-model pass works for quick tests. |
| 457 | |
| 458 | |
| 459 | |
| 460 | ## See also |
| 461 | |
| 462 | **`wan-t2v-video`.** The base WAN 2.2 T2V stack this builds on (model/encoder/ |
| 463 | VAE loading, the `merge_loras=false` fp8 gotcha in full, block-swap/VRAM). |
| 464 | Read it first. |
| 465 | **`video-upscale`.** The natural next step: extend, then upscale. Generate |
| 466 | / extend at 832×480, then run the result through the |
| 467 | *downscale → SeedVR2 (temporal restore+upscale) → RIFE → VHS encode* pipeline |
| 468 | for a clean, higher-res, higher-fps final. Do the extension first, upscale |
| 469 | last (upscaling then extending wastes the restorer's work and risks re-drift). |
| 470 | **`ltxv2-video`.** An alternative video family with its own extender variant; |
| 471 | Pusa/WAN is the path when you want to continue an existing WAN-style clip. |
| 472 | |
| 473 | ## Packs |
| 474 | |
| 475 | No dedicated `video-extend` installer pack ships yet. Since Pusa reuses the |
| 476 | installed WanVideoWrapper + KJNodes + VideoHelperSuite stack, a pack only needs to |
| 477 | list those `custom_nodes[]` (kijai/ComfyUI-WanVideoWrapper, |
| 478 | Kijai/ComfyUI-KJNodes, Kosinkadink/ComfyUI-VideoHelperSuite) plus the two Pusa V1 |
| 479 | LoRAs in `models[]` (from `Kijai/WanVideo_comfy/Pusa/`). The big T2V A14B models |
| 480 | are shared with `wan-t2v-video`; don't re-download. Install nodes ad-hoc with |
| 481 | `panel_install_node` or apply a manifest with `apply_manifest`. Contribute a |
| 482 | finished pack upstream (`github.com/artokun/comfyui-mcp`). |
| 483 | |
| 484 | ## Sources |
| 485 | |
| 486 | **Official:** none found. |
| 487 | **Empirical:** sampler values, wiring, and prompt notes from working graphs in `packs/` and observed renders; not a vendor prompting guide. |
| 488 |