Smart Painter FLF2V
Fix the end-frame flash in WAN FLF2V video
- positive
- negative
- vae
- clip_vision_start_image
- clip_vision_end_image
- clip_vision_reference_image
- start_image
- end_image
- initial_reference_image
- positive_high
- positive_low
- negative
- latent
You've got a WAN 2.2 workflow that starts on one image, is supposed to end on another, and the last few frames dissolve into noise every single time. That's the bug this node exists to kill, and it's the reason "Smart Painter FLF2V" is worth adding even if you never touch another knob.
The name is a riff on PainterVideo's PainterFLF2V (from the WanVideoWrapper pack), and it's built for WAN 2.2's two-expert Mixture-of-Experts architecture. FLF2V means First-Last-Frame to Video: you hand it a start frame and an end frame, it builds the conditioning, and the model interpolates between them. WAN 2.2 runs two experts - a high-noise one that settles coarse structure, then a low-noise one that refines detail - and the stock FLF2V setup drives both from one shared conditioning. That's where the trouble starts.
Here's the asymmetry the author found: the start frame gets extra mask slots so it fully anchors a latent block, but the end frame doesn't, which leaves the last latent block about 75% free. Early in denoising the model sees a nearly empty end slot, and the tail of the clip comes out as that jittery flash. SmartPainterFLF2V fixes it with end_anchor_extra, default 3, which mirrors the start-frame treatment: the full last latent block gets anchored and the three pre-end pixel frames blend toward the end image. If you've been living with flashy endings, that default alone justifies the install.
Beyond the fix, the headline feature is the split outputs. You get positive_high and positive_low - separate conditioning for each expert - so you decide how hard the end frame grips each one independently. high_noise_end_strength at 0.6–0.8 is the move the tooltip recommends: loosen the high-noise expert's end-frame anchor, it finds a more natural motion path, and the noise burst shrinks further. Keep low_noise_end_strength at 1.0 so the refiner locks the final frame onto your target.
The dial you'll actually spend time on is motion_amplitude (0–2, default 1 = official vanilla behavior). Below 1 pulls the conditioning toward a linear crossfade between start and end - static-camera shots feel steadier around 0.3–0.7. Above 1 amplifies the structural difference signal so the subject really moves; try 1.2–1.8 if your clips feel like slow-motion. mask_fade_frames replaces hard mask edges with a gradual ramp (more anchor zone, steadier camera), and temporal_smooth_sigma Gaussian-blurs the conditioning across time to kill camera jitter - keep the kernel at least 2×sigma+1. end_frame_offset shifts the end frame earlier so the model passes through the pose and keeps moving rather than decelerating into a hard stop; keep the offset moderate, 8–16.
The optional CLIP Vision slots let you inject reference imagery that isn't a keyframe. clip_vision_reference_image merges into the img_emb cross-attention and works on stock WAN 2.2 checkpoints (no VACE, no ref_conv) - semantic/identity guidance, not pixel-faithful. initial_reference_image is a VAE-encoded reference_latents entry that's silently ignored by stock WAN 2.2 and only bites on checkpoints with a ref_conv layer (Fun Control, Animate, VACE merges), so don't reach for it first.
Wire positive_high and positive_low into your two-stage WAN sampler's positive inputs, negative to its negative, and latent to the sampler. That's the whole wiring - no intermediate nodes.
Install through ComfyUI Manager (search "Smart Helper Nodes"), or clone it yourself:
cd ComfyUI/custom_nodes
git clone https://github.com/slvslvslv/ComfyUI-SmartHelperNodes
# restart ComfyUI
No model files to download and the pack's requirements.txt is empty - everything runs on what ComfyUI already ships.
Inputs (24)
| Name | Type | Default | Description |
|---|---|---|---|
| positive | CONDITIONING | — | |
| negative | CONDITIONING | — | |
| vae | VAE | — | |
| width | INT | 83216–8192 | — |
| height | INT | 48016–8192 | — |
| length | INT | 811–8192 | — |
| batch_size | INT | 11–4096 | — |
| motion_amplitude | FLOAT | 1.000–2 | DISABLED at 1.0 — outputs official vanilla behavior (gray-fill between keyframes). Controls how much the model deviates from a smooth start-to-end transition. Lowering toward 0.0: blends the conditioning latent toward a linear interpolation between start and end frames — suppresses camera drift/reinterpretation, making the output increasingly static. At 0.0 it is pure linear crossfade. Raising above 1.0: amplifies high-frequency structural differences (the 'anti-ghost' signal), boosting subject motion and removing slow-motion feel, but may increase camera instability. For static camera try 0.3-0.7. For dynamic motion try 1.2-1.8. |
| mask_fade_frames | INT | 00–40 | DISABLED at 0 — mask uses hard edges only (official behavior). Number of extra pixel frames after the start keyframe (and before the end keyframe) where the mask gradually transitions instead of jumping from 0 to 1. Raising this extends the 'influence zone' of your keyframes deeper into the video, giving the model a gradual ramp from anchored to free. Higher values = wider gradient = more frames stay partially anchored = more stable camera but less creative freedom for the model in the middle. For static camera try 8-16. For dynamic scenes keep at 0-4. |
| mask_fade_min | FLOAT | 0.000–1 | Only active when mask_fade_frames > 0. Mask value at the keyframe edge of the fade ramp (closest to start/end image). 0.0 = fully anchored (model must follow the conditioning exactly). Raising this loosens the anchor even at the boundary — the model gets partial freedom right next to keyframes. Useful if you want soft transitions rather than hard locks. For maximum camera stability keep at 0.0. |
| mask_fade_max | FLOAT | 1.000–1 | Only active when mask_fade_frames > 0. Mask value at the far edge of the fade ramp (farthest from the keyframe). 1.0 = fully free (model can generate whatever it wants beyond the fade). Lowering this caps how free the model gets even in the middle of the video — e.g. 0.6 means the model is never more than 60%% free, always partially anchored. Lower values = tighter overall constraint = more stable but less dynamic. For static camera try 0.5-0.7. For full motion keep at 1.0. |
| high_noise_end_strength | FLOAT | 1.000–1 | DISABLED at 1.0 — end frame has full influence for the high-noise expert. Controls how strongly the end frame conditions the HIGH-noise model step. Affects both the mask (anchoring strength) and the pixel image (blends end frame toward neutral gray). The high-noise expert establishes coarse structure — giving it less end-frame constraint lets it find a more natural motion path, reducing the end-frame noise burst. Lowering toward 0.0: weakens end-frame conditioning for the high-noise step. At 0.0 the high-noise expert completely ignores the end frame. For two-step WAN, try 0.6-0.8 to reduce end noise while still guiding toward the target. |
| low_noise_end_strength | FLOAT | 1.000–1 | DISABLED at 1.0 — end frame has full influence for the low-noise expert. Controls how strongly the end frame conditions the LOW-noise model step. Affects both the mask (anchoring strength) and the pixel image (blends end frame toward neutral gray). The low-noise expert refines details — keeping it tightly anchored to the end frame ensures the final result actually arrives at the target. Lowering toward 0.0: weakens end-frame conditioning for the low-noise step. At 0.0 the low-noise expert completely ignores the end frame. Usually keep at 1.0 (full strength) so the refiner locks in the end frame. |
| end_anchor_extra | INT | 30–12 | DISABLED at 0 — end frame uses official mask (only the end frame pixel slot is anchored, leaving the last latent block 75%% free — this causes the end-frame noise burst). Fixes a WAN FLF2V design asymmetry: the start frame gets +3 extra mask slots to fill a full latent block, but the end frame does not. At 3 (recommended): mirrors the start-frame treatment, anchoring the full last latent block and blending the 3 pre-end pixel frames toward the end image for smoother VAE encoding. Higher values extend anchoring + blending even deeper before the end frame. This eliminates most of the noise burst visible in the last 3-7 frames during early denoising. |
| end_frame_offset | INT | 00–8192 | DISABLED at 0 — end frame is placed at the very last frame (official behavior). Shifts the end frame forward by this many pixel frames. The model anchors to the end image at the shifted position, then continues generating freely for the remaining tail frames. Snapped to multiples of 4 internally to align with VAE latent block boundaries. Example: length=81, offset=8 → end image is placed at frame 72, frames 73-80 are free continuation. Use this to make the model 'pass through' a target pose and keep moving, rather than decelerating into a hard stop at the end frame. The tail frames have no target — expect some drift. Keep offset moderate (8-16) for best quality. |
| temporal_smooth_sigma | FLOAT | 0.00–5 | DISABLED at 0.0 — no temporal smoothing applied. Gaussian sigma for temporal smoothing of the conditioning latent. Higher values widen the blur kernel, averaging each frame with more distant neighbors — strongly stabilizes camera and reduces jitter but dampens subject motion. Lower values only blend adjacent frames, removing high-freq temporal noise while preserving most motion dynamics. Start around 1.0-2.0 for static camera. |
| temporal_smooth_kernel | INT | 51–21 | Only active when temporal_smooth_sigma > 0. At 1: smoothing is a no-op (single-sample kernel). Size of the temporal Gaussian smoothing window (odd values work best; even rounded up). Raising this allows the Gaussian to reach more distant frames — needed when sigma is high, otherwise the kernel gets clipped and the smoothing is weaker than expected. Lowering this limits the reach even if sigma is large, capping the blur range. Rule of thumb: kernel >= 2*sigma + 1. For sigma 1.0 use 5. For sigma 3.0 use 9-11. |
| clip_vision_start_imageopt | CLIP_VISION_OUTPUT | — | |
| clip_vision_end_imageopt | CLIP_VISION_OUTPUT | — | |
| clip_vision_reference_imageopt | CLIP_VISION_OUTPUT | Optional third CLIP Vision slot for an off-screen reference image (subject identity, clothing, object that isn't in start/end frames). Merged with clip_vision_start_image and clip_vision_end_image by concatenating their token sequences along the token axis. Feeds the model's img_emb cross-attention pathway — the only non-keyframe image-guidance channel available on stock WAN 2.2 base checkpoints (no VACE / no ref_conv required). Provides semantic / style / identity guidance. NOT pixel-faithful — good for 'keep the jacket this color', weak for 'reproduce this exact logo'. Leave unconnected to keep the original pipeline unchanged. | |
| cv_reference_strengthopt | FLOAT | 1.000–3 | Only active when clip_vision_reference_image is connected. Scales the reference image's CLIP token magnitudes before they are merged with the start/end CLIP tokens. Higher values make the reference dominate the cross-attention, lower values let start/end guidance win. 0.0 = reference disabled (same as leaving clip_vision_reference_image unconnected). 1.0 = equal weight with start/end (one reference token = one start/end token). 2.0-3.0 = reference boosted (useful if the reference subject is being ignored by the model). Defaults to 1.0. |
| start_imageopt | IMAGE | — | |
| end_imageopt | IMAGE | — | |
| initial_reference_imageopt | IMAGE | Optional VAE-encoded reference injected as a reference_latent conditioning key. NOTE: Only effective on WAN checkpoints that ship with a ref_conv layer (Fun Control, Animate, SCAIL, HuMo, VACE-merged). Silently ignored by stock WAN 2.2 FLF2V / I2V / T2V and most custom merges. For a reference-image channel that works on any base WAN 2.2 checkpoint, use clip_vision_reference_image above instead. Encoded via the VAE and appended to the conditioning's reference_latents list (a zero latent is appended on the negative side). Acts as a persistent identity/style anchor across the whole clip, independent of start_image and end_image. Leave unconnected to keep the original pipeline unchanged. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| positive_high | CONDITIONING | — |
| positive_low | CONDITIONING | — |
| negative | CONDITIONING | — |
| latent | LATENT | — |