π§ WanSCAILToVideo (Latent Masked)
Lock the original video in place during SCAIL-2 motion transfer
- positive
- negative
- vae
- pose_video
- pose_video_mask
- reference_image
- reference_image_mask
- clip_vision_output
- previous_frames
- original_frames
- original_frame_masks
- positive
- negative
- latent
- video_frame_offset
- quantized_mask
If you've run SCAIL-2 in ComfyUI, you know the annoyance: the model is great at moving a character, but it also re-draws everything - including the background and the parts of the frame you wanted kept exactly as filmed. π§ WanSCAILToVideo (Latent Masked) is a modified copy of ComfyUI's core WanSCAILToVideo that fixes that specific problem. Give it the original video plus a mask, and the masked regions are hard-preserved: encoded into the latent and locked there so the sampler physically cannot change them, instead of being trusted to recreate them and getting them subtly wrong.
The context matters here. SCAIL-2 is Z.ai's Apache 2.0 motion-transfer model on a Wan 2.1 base - the one that dropped the stick-figure skeleton entirely and took reference image + driving video + masks directly, which is what unlocked cross-identity replacement and animal driving. It shipped into ComfyUI core (no separate model pack needed), and its defining costs are compute (plan on minutes per clip, fp8 on 16GB, Q4 GGUF on 8GB) and its 81-frame chunking. This node sits on top of all of that, adding one feature: don't let the model touch what you say not to touch.
How it works
The preservation works at the latent level. original_frames (your video at output resolution) are VAE-encoded and written into the latent wherever original_frame_masks are white (1.0). The matching noise_mask is set to zero there, which is the sampler's signal to keep those latent values exactly as they are - preservation, not recreation. Black mask areas (0.0) generate normally.
The subtleties are where this node earns its keep:
- Token-grid snapping. Wan's VAE is 8Γ and the transformer patches 2Γ2, so one latent token is 16Γ16 pixels. Your mask is snapped outward onto that grid, so the boundary lands on token edges instead of cutting through them.
- frame_mask_grow shifts that boundary afterwards, in 8-px latent-cell units. Positive = dilate into generated territory (your masked area stays untouched, but the transition boundary moves). Negative = erode into preserved territory (the model recreates a boundary strip, sacrificing some original pixels for a more seamless blend).
- Chunking is built in.
video_frame_offsettracks where in the longer video each chunk begins - wire the previous chunk'svideo_frame_offsetoutput into the next chunk's input, SCAIL-2's standard 81-frame window with the 76-frame step.previous_framesanchors each new chunk, and those anchor frames override preservation on the leading frames. - enable_latent_mask (default on) toggles the whole feature - turn it off and the node behaves exactly like stock WanSCAILToVideo.
The inputs that matter for the core model are the usual suspects (positive, negative, vae, width, height, length, batch_size, pose_strength, plus the optional pose_video, reference_image, and their masks - see the SCAIL-2 docs for what those mean). The four that matter for this node's feature are enable_latent_mask, original_frames, original_frame_masks, and frame_mask_grow. Outputs are positive, negative, latent (the generation-size latent with preserved regions encoded in), and video_frame_offset for chaining.
Installing it
Part of ComfyUI-SuperNodes (GitHub: sonnybox/ComfyUI-SuperNodes) by SuperCC - ComfyUI Manager β search SuperNodes β install β restart, or:
cd ComfyUI/custom_nodes
git clone https://github.com/sonnybox/ComfyUI-SuperNodes
# restart ComfyUI
This one has real prerequisites beyond matplotlib: it imports from comfy_extras.nodes_scail, so you need a ComfyUI build that already ships SCAIL support, plus the SCAIL-2 weights (Comfy-Org's scaled/fp8 versions are the sane download). The node is flagged experimental - expect rough edges.
Where people get burned
The node checks its own inputs and errors loudly if original_frame_masks don't match original_frames dimensions - resize them together so they can't drift. A single mask broadcasts to all frames; otherwise it's one mask per frame. And the "locked" promise is only as good as the mask: after token snapping and any frame_mask_grow, what's preserved is the quantized region, so a mask that hugs a boundary tightly will preserve a few more pixels than you drew. If a background you meant to keep gets re-rendered anyway, check that the mask covers it at output resolution - a mask made at half resolution silently shifts on you.
Inputs (22)
| Name | Type | Default | Description |
|---|---|---|---|
| positive | CONDITIONING | β | |
| negative | CONDITIONING | β | |
| vae | VAE | β | |
| width | INT | 51232β16384 | β |
| height | INT | 89632β16384 | β |
| length | INT | 811β16384 | β |
| batch_size | INT | 11β4096 | β |
| pose_strength | FLOAT | 1.000β10 | Strength of the pose latent. |
| pose_start | FLOAT | 0.000β1 | Start step of the pose conditioning. |
| pose_end | FLOAT | 1.000β1 | End step of the pose conditioning. |
| video_frame_offset | INT | 00β16384 | Cumulative output frame this chunk begins at. Wire from the previous chunk's video_frame_offset output. |
| previous_frame_count | INT | 51β16384 | Tail frames of previous_frames to anchor. SCAIL-2 trained at 5 (81-frame chunks, 76-frame step). |
| pose_videoopt | IMAGE | Video used for pose conditioning. Will be downscaled to half the resolution of the main video. | |
| pose_video_maskopt | IMAGE | SCAIL-2 only. Colored per-identity SAM3 mask video at the same resolution as pose_video. | |
| replacement_modeopt | BOOLEAN | false | SCAIL-2 only. False = Animation Mode (pose_video_mask should have black background). True = Replacement Mode (pose_video_mask should have white background). |
| reference_imageopt | IMAGE | Reference image. The first image is the primary reference (composite all identities onto it). SCAIL-2: extra batch images are used as additional views (back view, close-up, occluded background), each needing a matching reference_image_mask in that identity's color. | |
| reference_image_maskopt | IMAGE | SCAIL-2 only. Colored reference mask, batch matching reference_image (first = primary reference mask, rest = identity masks for the additional reference_image). | |
| clip_vision_outputopt | CLIP_VISION_OUTPUT | CLIP vision features for conditioning. Model is trained with stretch resize to aspect ratio. | |
| previous_framesopt | IMAGE | SCAIL-2 only. Full decoded output of the previous chunk. Only the last previous_frame_count are used as the extension anchor. | |
| enable_latent_maskopt | BOOLEAN | true | Toggle the original-frame preservation feature. Off = original_frames/original_frame_masks are ignored entirely and the node behaves exactly like stock WanSCAILToVideo. |
| original_framesopt | IMAGE | Original video frames at the output resolution. Areas covered by original_frame_masks are encoded into the latent and locked so the sampler preserves them exactly. Offset by video_frame_offset like pose_video. Ignored if original_frame_masks is not connected. | |
| original_frame_masksopt | MASK | Per-frame masks matching original_frames (a single mask is broadcast to all frames). White (1.0) = hard-preserve the original video content, black (0.0) = generate normally. Quantized onto the 16 px token grid. Ignored if original_frames is not connected. |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| positive | CONDITIONING | β |
| negative | CONDITIONING | β |
| latent | LATENT | Latent of the generation size. Contains the encoded original frames (with matching noise mask) where original_frame_masks preserves them; empty elsewhere. |
| video_frame_offset | INT | Adjusted offset + length. Wire into the next chunk. |
| quantized_mask | MASK | What actually got locked, painted back at output resolution: white = preserved from the original, black = generated. One frame per input mask frame, so frames sharing a latent frame are identical and it plays back at the latent's real temporal resolution. All black if no preservation is active. |