Zura · Connect Wan Motion and Character
The six wires the sampler really needs
- frames
- mask
- pose
- face
- reference
- footage
- character_view
- width
- height
The Wan sampler doesn't take six wires. It takes one - a ZURA_FOOTAGE payload that has to carry source frames, an approved mask, a pose video, a face video and a scope, because none of those fit in a standard ComfyUI video wire. This node builds that payload from visible native nodes so you can do the whole preparation stage by hand.
Its own description is the pitch: "Connect visible native preprocessing directly to the sampler in graph view." Every other preparation node in the pack is either reading a cache or shuffling files. This one is the no-cache, all-in-memory route - you plug your own detection and masking branches in, and out comes something the sampler accepts.
Inputs
Five required, and they're the whole story:
frames- theIMAGEbatch of source frames.mask- your approved replacement mask. It has to match the frame count and the spatial size offrames.pose- the pose conditioning video, matched to the frame count.face- the face conditioning video. This is the crop reel at 512×512, not the clip resolution - the pack's native path returns face crops at that size regardless of clip size, and the sampler upscales from there.reference- the character reference. Exactly one image; more than one and you get "Wan requires one isolated character view."scope-personorhead. This becomes the replacement area ("Whole character" or "Whole head").
Optional: speech_guide_key and speech_guide_sha, both 64-char hex, for the audio-driven route. Supply one without the other and it errors - the pair is a provenance claim, and a half claim is worthless.
Outputs
footage (the payload), character_view (the reference forwarded on, so you don't need a second LoadImage for the sampler's reference_image), plus width and height as ints pulled from the frames.
What it validates
Before it returns anything, the pack runs validate_footage over the assembled payload: frames must be non-empty RGB, the mask must match frame count and raster exactly, the pose and face reels must cover every frame, the pixels must be finite, and the mask must sit in [0,1] with something actually selected. Cheap checks, done once, in the right place - you find out here rather than three minutes into sampling.
Two things it does not do, by design. It assumes a 24 fps timeline, hard-coded rather than measured, because this is the Wan path and the Wan path is 24 fps. And it sets no cache key - nothing is written to disk, so there's no preparation file to verify downstream and no way to resume a long shot from a cache. That's the trade against Zura · Cache Wan Motion and Character: this is the version you use while you're iterating, and the cached version is the one you use when the shot is long enough that re-running preparation hurts.
The scope string is also load-bearing beyond cosmetics. Downstream payloads are validated against ("Whole head", "Whole character") - you can't smuggle a third value in by editing the packer, and the windowed preparation path will reject footage whose scope it doesn't recognise.
Install
Ships with the pack:
cd ComfyUI/custom_nodes && git clone https://github.com/ZURAVFX/ComfyUI_zura_nodes
pip install -r ComfyUI_zura_nodes/requirements.txt
Manager install: Zura Nodes by ZURAVFX, then restart. The heavy dependencies belong to the nodes you're wiring into this one - ComfyUI-WanAnimatePreprocess for OnnxDetectionModelLoader / PoseAndFaceDetection / DrawViTPose, plus the detection weights (detection/vitpose-l-wholebody.onnx, detection/yolov10m.onnx) and the person segmentation YOLO at ultralytics/segm/person_yolov8m-seg.pt. This node is just assembly.
Common issues
"Wan mask must match the source frame count and size." Usually a mask at the wrong raster - a mask generated at 720p being packed against 1080p frames. The mask is combined arithmetically with the frames downstream, so the sampler has the same requirement, and this check is the earlier, friendlier version of it.
"Wan pose video must cover every source frame." The detection pass ran on a shorter batch than your frames - often because the detection branch and the frame branch diverged after a trim. Feed both from one source.
"Wan streaming requires a non-empty approved performer mask." The mask exists but is all zeros for the whole batch. There's no replacement happening, so there's nothing for the sampler to do.
Sampler says the payload didn't come from the right place. It checks the payload's shape, not its provenance - but if you've hand-built a dict, the missing video_info fps or an unexpected replacement_area will stop you. Use this node; don't improvise the dictionary.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| frames | IMAGE | — | |
| mask | MASK | — | |
| pose | IMAGE | — | |
| face | IMAGE | — | |
| reference | IMAGE | — | |
| scope | COMBO | 2 options: person, head | |
| speech_guide_keyopt | STRING | — | |
| speech_guide_shaopt | STRING | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| footage | ZURA_FOOTAGE | — |
| character_view | IMAGE | — |
| width | INT | — |
| height | INT | — |