Wan VAE Decode
Decoding Wan latents without melting your VRAM
- vae
- latents
- image
WASWANVAEDecode is the node that turns Wan video latents back into pixels without the OOM that kills every first Wan workflow. If you've generated with Wan 2.1 or 2.2 and watched ComfyUI's plain VAE Decode die on a 5D [B,C,F,H,W] latent, you already know why this exists. It's the decode half of the WAS Affine pack's Wan support, and for long clips or high-res video it's genuinely the difference between a completed render and a softlock.
Wan's VAE is a big deal compression-wise: roughly 8x spatial and 4x temporal. A 81-frame clip at 1280x720 decodes into a lot of pixels, and a single non-tiled decode pass is exactly how you spike VRAM past your card's ceiling. This node gives you four independent ways to chew the decode down to size.
How it works
It takes the standard Wan latent - a dict with samples shaped [B,C,F,H,W], where F=1 means a single image - and decodes it through the VAE you provide. The required vae must match the model that produced the latents (Wan 2.1 and 2.2 share the wan_2.1_vae in most workflows, but mismatched VAEs give you garbage, so don't improvise).
Three mechanisms matter:
Latent-space tiling. horizontal_tiles and vertical_tiles (each 1–12) split the latent across width/height, with overlap (default 1, in latent pixels) controlling how much the tiles bleed into each other for seamless blending. Output overlap scales up by the VAE's compression factor, so a little latent overlap goes a long way. Default 1/1 is a single pass; raise them for big decodes.
Pass limits. frames_per_pass and batch_per_pass (0 = all) slice the job into chunks so peak VRAM stays flat. This is the reliable lever for long videos - decode 8 frames, write, decode 8 more.
A real VRAM budget. max_decode_megapixels (default 0 = off) estimates the working set as decH * decW * out_frames * batch / 1e6 and paces the decode to stay under it. It's a budget in megapixels, not a hard guarantee, but it's the honest knob for "how big a chunk can this card chew."
last_frame_fix deserves a mention because it's a Wan-specific wart. Wan's video VAE has a tendency to render a glitched final frame in some decode paths; this toggle appends a duplicate of the last latent frame before decoding and trims it after, which sidesteps the artifact. If your clip's last frame looks wrong, this is the switch.
You can also nudge the internals: accumulate_dtype (fp32 default) is the accumulator for tile blending - leave it unless you're chasing speed on a modern card - and output_dtype (auto = fp16 on CUDA, else fp32) sets the final image dtype.
The output
A single IMAGE in normal [B,F,H,W,C] form, ready to feed a PreviewImage, SaveImage, or the rest of your pipeline. This is the node the pack's 720p→2k workflow hangs off - decode the latent, then run the Ultimate Affine KSampler pass on the pixels.
Installing it
It's part of WAS Affine (WASasquatch/was_affine). Via Manager, search "WAS Affine" and install; or:
cd ComfyUI/custom_nodes
git clone https://github.com/WASasquatch/was_affine.git
Restart ComfyUI after. No model downloads - you already have the Wan VAE. Dependencies are just torch/numpy (matplotlib in requirements.txt, used for schedule plots).
The practical workflow
Start with defaults and only add what your card forces you to. If a 720p decode OOMs, raise horizontal_tiles/vertical_tiles to 2 first (cheapest fix), then start dropping frames_per_pass to 16 or 8. Set max_decode_megapixels to roughly your card's VRAM in GB once you know your ceiling. Where people get burned: forgetting that overlap is in latent pixels and cranking it to 16, which multiplies the blended region for no visible gain - 1–4 is plenty for most Wan VAEs.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| vae | VAE | VAE used to decode the latents. Must correspond to the model that produced the latents. Respects the VAE's device/dtype and internal time/space scaling. | |
| latents | LATENT | Latent dict with key 'samples' shaped [B,C,F,H,W]. F=frames (F=1 for images). H/W are latent-space, not pixel-space. | |
| horizontal_tiles | INT | 11–12 | Tiles across WIDTH in latent space. |
| vertical_tiles | INT | 11–12 | Tiles across HEIGHT in latent space. |
| overlap | INT | 10–16 | Overlap in LATENT pixels. Output overlap = overlap * VAE scale. |
| last_frame_fix | BOOLEAN | false | Append last latent frame before decode, trim after. For video VAEs. |
| frames_per_passopt | INT | 00–64 | Limit frames per pass (0 = all). |
| batch_per_passopt | INT | 00–64 | Limit batch per pass (0 = all). |
| max_decode_megapixelsopt | FLOAT | 0.00–400 | VRAM budget ≈ decH * decW * out_frames * batch / 1e6. 0 = off. |
| accumulate_dtypeopt | COMBO | fp32 | Accumulator dtype for blending. |
| output_dtypeopt | COMBO | auto | Final NHWC dtype ('auto' = fp16 on CUDA, else fp32). |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |