Generated reference · ComfyUI workflow · motion

SCAIL-2 Character Animation

SCAIL-2 (Wan 2.1 14B): the character or characters in a reference image replace the people in a driving clip, or perform its motion on a new background. SAM 3.1 tracks every person in both, and each identity is painted one colour in both …

motion29nodesGPU H100out unspecified~479sv1.0.0

On this page

SCAIL-2 (Wan 2.1 14B): the character or characters in a reference image replace the people in a driving clip, or perform its motion on a new background. SAM 3.1 tracks every person in both, and each identity is painted one colour in both masks, so several characters bind to several performers in left-to-right order. One segment of the ComfyUI SCAIL-2 character-replacement template (Base subgraph) on its non-turbo schedule, with the DPO and relight LoRAs as toggles.

29Graph nodes
24Node classes
20Inputs
7Models

Tags: motionscail-2wan2.1character-replacementperformance-transfermulti-characterrunpod-serverlessvolume-backed

Inputs (20)#

The typed parameter surface callers bind when they request this workflow. Enum options and numeric bounds are the values the workflow document declares.

reference_imageimagerequired
The character, or several characters side by side in one image. SAM 3.1 finds each person named by `reference_subject`; in `replacement` the reference is cut out on black by its mask, so its background does not carry over.
driving_videovideorequired
The performance. Its frames drive the pose from the first frame (downscaled to half size inside WanSCAILToVideo), SAM 3.1 tracks its people for the colour mask, and its frame rate and audio are carried into the output. A clip shorter than `length` gives a shorter output.
promptstringrequireddefault The character from the reference image dances in the scene of the driving video, natural lighting, cinematic.
The output video described in full: who, wearing what, doing what, where. The template's own prompt is written that way. SAM 3.1's words are `driving_subject` and `reference_subject`, not this.
negative_promptstringdefault
The template leaves it empty; it is used at the default CFG 5.
modeenumdefault replacement
replacement (the template's default): the reference characters take the place of the tracked people in the driving clip, whose scene is kept. animation: the reference characters perform the driving motion and the scene comes from the prompt. Sets replacement_mode on both SCAIL2ColoredMask and WanSCAILToVideo, which flips the masks' background colours.
options: replacementanimation
driving_subjectstringdefault human
SAM 3.1 text for the people to track in the driving clip (the template's `sam3_video_object`, default `human`). In `replacement` these are who gets replaced; be more specific in a crowded scene.
reference_subjectstringdefault human
SAM 3.1 text for the characters in the reference image (the template's `sam3_image_object`, default `human`). Use the same word as `driving_subject` when the subjects match.
character_indicesstringdefault
Which tracked identities to include, comma-separated in `identity_order` (e.g. `0` for only the first, `0,2`); empty is all. Applied to both the reference and the driving masks (SCAIL2ColoredMask object_indices), so it is how one character is chosen for one performer.
identity_orderenumdefault left_to_right
How identities are numbered and coloured, the same in the reference and the driving clip (SCAIL2ColoredMask sort_by): left_to_right (template) pairs the leftmost reference character with the leftmost performer; area pairs the largest with the largest; none keeps SAM 3.1's order.
options: noneleft_to_rightarea
max_charactersintegerdefault 4min 1max 64
Most people SAM 3.1 tracks in the reference and in the driving clip (SAM3_VideoTrack max_objects; the template's 4).
dpo_lorabooleandefault true
Load wan2.1_SCAIL_2_DPO_lora_bf16 at strength 1.0, as the template always does (its quality LoRA). Off samples the bare SCAIL-2 transformer.
relight_lorabooleandefault false
Load wan2.1_SCAIL_2_relight_lora_bf16, meant to match the characters' lighting to the scene. No ComfyUI SCAIL-2 template loads it, so it is chained after the DPO LoRA the way the template chains that one, at LoraLoaderModelOnly's default strength 1.0; its effect is unproved until a live render.
resolutionenumdefault 896x512
Output size, multiples of 32 as the template rounds them. The driving frames are scaled to it with a centre crop (area filter).
options: 896x512512x896832x480480x832
lengthintegerdefault 33min 5max 81
Driving frames taken from the start of the clip, 4n+1. 81 is the template's segment (76 new frames plus a 5-frame overlap when extending); 33 is the default so the non-turbo schedule stays inside the 900 s execution timeout (cost_note). Longer clips chain Extend segments, which this graph does not build.
stepsintegerdefault 40min 1max 40
Sampling steps; the template's non-turbo value is 40.
cfgfloatdefault 5.0min 1.0max 20.0
Classifier-free guidance; the template's non-turbo value is 5.
pose_strengthfloatdefault 1.0min 0.0max 10.0
Scales the pose latent (WanSCAILToVideo).
pose_startfloatdefault 0.0min 0.0max 1.0
Sampling percent at which pose conditioning starts.
pose_endfloatdefault 1.0min 0.0max 1.0
Sampling percent at which pose conditioning ends.
seedintegerdefault 0min 0
Sampler noise seed.

ComfyUI node graph (29)#

The executable ComfyUI prompt graph: 29 nodes across 24 distinct node classes, wired by 43 data dependencies. Nodes tinted green come from a custom node pack this workflow declares; the rest are ComfyUI core / baked-community classes.

flowchart TD n0["1: UNETLoader"] n1["2: LoraLoaderModelOnly"] n2["3: LoraLoaderModelOnly"] n3["4: ModelSamplingSD3"] n4["5: CLIPLoader"] n5["6: CLIPTextEncode"] n6["7: CLIPTextEncode"] n7["8: VAELoader"] n8["9: CLIPVisionLoader"] n9["10: LoadImage"] n10["11: CLIPVisionEncode"] n11["12: LoadVideo"] n12["13: GetVideoComponents"] n13["14: ImageFromBatch"] n14["15: ResizeImageMaskNode"] n15["16: GetImageSize"] n16["17: CheckpointLoaderSimple"] n17["18: CLIPTextEncode"] n18["19: SAM3_VideoTrack"] n19["20: CLIPTextEncode"] n20["21: SAM3_VideoTrack"] n21["22: SCAIL2ColoredMask"] n22["23: WanSCAILToVideo"] n23["24: KSamplerSelect"] n24["25: BasicScheduler"] n25["26: SamplerCustom"] n26["27: VAEDecode"] n27["28: CreateVideo"] n28["29: SaveVideo"] n0 -->|model| n1 n1 -->|model| n2 n2 -->|model| n3 n4 -->|clip| n5 n4 -->|clip| n6 n8 -->|clip_vision| n10 n9 -->|image| n10 n11 -->|video| n12 n12 -->|image| n13 n13 -->|input| n14 n14 -->|image| n15 n16 -->|clip| n17 n14 -->|images| n18 n16 -->|model| n18 n17 -->|conditioning| n18 n16 -->|clip| n19 n9 -->|images| n20 n16 -->|model| n20 n19 -->|conditioning| n20 n18 -->|driving_track_data| n21 n20 -->|ref_track_data| n21 n5 -->|positive| n22 n6 -->|negative| n22 n7 -->|vae| n22 n14 -->|pose_video| n22 n21 -->|pose_video_mask| n22 n9 -->|reference_image| n22 n21 -->|reference_image_mask| n22 n10 -->|clip_vision_output| n22 n15 -->|length| n22 n2 -->|model| n24 n3 -->|model| n25 n22 -->|positive| n25 n22 -->|negative| n25 n23 -->|sampler| n25 n24 -->|sigmas| n25 n22 -->|latent_image| n25 n25 -->|samples| n26 n7 -->|vae| n26 n26 -->|images| n27 n12 -->|audio| n27 n12 -->|fps| n27 n27 -->|video| n28
Data-flow DAG — scroll to zoom, drag to pan.

Nodes (29)#

1UNETLoadercore
unet_name = wan2.1_14B_SCAIL_2_fp16.safetensorsweight_dtype = default
outputs: MODEL
2LoraLoaderModelOnlycore
model = ◂ node 1 · out[0]lora_name = wan2.1_SCAIL_2_DPO_lora_bf16.safetensorsstrength_model = 1.0
outputs: MODEL
3LoraLoaderModelOnlycore
model = ◂ node 2 · out[0]lora_name = wan2.1_SCAIL_2_relight_lora_bf16.safetensorsstrength_model = 1.0
outputs: MODEL
4ModelSamplingSD3core
model = ◂ node 3 · out[0]shift = 5
outputs: MODEL
5CLIPLoadercore
clip_name = umt5_xxl_fp16.safetensorstype = wandevice = default
outputs: CLIP
6CLIPTextEncodecore
clip = ◂ node 5 · out[0]text = {{prompt}} tmpl
outputs: CONDITIONING
7CLIPTextEncodecore
clip = ◂ node 5 · out[0]text = {{negative_prompt}} tmpl
outputs: CONDITIONING
8VAELoadercore
vae_name = wan_2.1_vae.safetensors
outputs: VAE
9CLIPVisionLoadercore
clip_name = clip_vision_h.safetensors
outputs: CLIP_VISION
10LoadImagecore
image = {{reference_image}} tmpl
outputs: IMAGEMASK
11CLIPVisionEncodecore
clip_vision = ◂ node 9 · out[0]image = ◂ node 10 · out[0]crop = none
outputs: CLIP_VISION_OUTPUT
12LoadVideocore
file = {{driving_video}} tmpl
outputs: VIDEO
13GetVideoComponentscore
video = ◂ node 12 · out[0]
outputs: IMAGEAUDIOFLOATCOMBOCOMBO
14ImageFromBatchcore
image = ◂ node 13 · out[0]batch_index = 0length = {{length}} tmpl
outputs: IMAGE
15ResizeImageMaskNodecore
input = ◂ node 14 · out[0]resize_type = scale dimensionsresize_type.width = {{resolution_map[resolution].width}} tmplresize_type.height = {{resolution_map[resolution].height}} tmplresize_type.crop = centerscale_method = area
outputs: IMAGE
16GetImageSizecore
image = ◂ node 15 · out[0]
outputs: INTINTINT
17CheckpointLoaderSimplecore
ckpt_name = sam3.1_multiplex_fp16.safetensors
outputs: MODELCLIPVAE
18CLIPTextEncodecore
clip = ◂ node 17 · out[1]text = {{driving_subject}} tmpl
outputs: CONDITIONING
19SAM3_VideoTrackcore
images = ◂ node 15 · out[0]model = ◂ node 17 · out[0]conditioning = ◂ node 18 · out[0]detection_threshold = 0.5max_objects = {{max_characters}} tmpldetect_interval = 1
outputs: SAM3_TRACK_DATA
20CLIPTextEncodecore
clip = ◂ node 17 · out[1]text = {{reference_subject}} tmpl
outputs: CONDITIONING
21SAM3_VideoTrackcore
images = ◂ node 10 · out[0]model = ◂ node 17 · out[0]conditioning = ◂ node 20 · out[0]detection_threshold = 0.5max_objects = {{max_characters}} tmpldetect_interval = 1
outputs: SAM3_TRACK_DATA
22SCAIL2ColoredMaskcore
driving_track_data = ◂ node 19 · out[0]ref_track_data = ◂ node 21 · out[0]object_indices = {{character_indices}} tmplsort_by = {{identity_order}} tmplreplacement_mode = {{mode == 'replacement'}} tmpl
outputs: IMAGEIMAGE
23WanSCAILToVideocore
positive = ◂ node 6 · out[0]negative = ◂ node 7 · out[0]vae = ◂ node 8 · out[0]pose_video = ◂ node 15 · out[0]pose_video_mask = ◂ node 22 · out[0]reference_image = ◂ node 10 · out[0]reference_image_mask = ◂ node 22 · out[1]clip_vision_output = ◂ node 11 · out[0]width = {{resolution_map[resolution].width}} tmplheight = {{resolution_map[resolution].height}} tmpllength = ◂ node 16 · out[2]batch_size = 1pose_strength = {{pose_strength}} tmplpose_start = {{pose_start}} tmplpose_end = {{pose_end}} tmplvideo_frame_offset = 0previous_frame_count = 5replacement_mode = {{mode == 'replacement'}} tmpl
outputs: CONDITIONINGCONDITIONINGLATENTINT
24KSamplerSelectcore
sampler_name = euler
outputs: SAMPLER
25BasicSchedulercore
model = ◂ node 3 · out[0]scheduler = simplesteps = {{steps}} tmpldenoise = 1.0
outputs: SIGMAS
26SamplerCustomcore
model = ◂ node 4 · out[0]add_noise = truenoise_seed = {{seed}} tmplcfg = {{cfg}} tmplpositive = ◂ node 23 · out[0]negative = ◂ node 23 · out[1]sampler = ◂ node 24 · out[0]sigmas = ◂ node 25 · out[0]latent_image = ◂ node 23 · out[2]
outputs: LATENTLATENT
27VAEDecodecore
samples = ◂ node 26 · out[1]vae = ◂ node 8 · out[0]
outputs: IMAGE
28CreateVideocore
images = ◂ node 27 · out[0]audio = ◂ node 13 · out[1]fps = ◂ node 13 · out[2]
outputs: VIDEO
29SaveVideocore
video = ◂ node 28 · out[0]filename_prefix = scail2-animateformat = mp4

Parameter banks (2)#

The prompt / configuration lookup tables this workflow keys into from its inputs — the vocabulary that turns a style / palette / preset selection into graph parameters.

resolution_map (4)#

896x512
{"width": 896, "height": 512}
512x896
{"width": 512, "height": 896}
832x480
{"width": 832, "height": 480}
480x832
{"width": 480, "height": 832}

requires_families (1)#

scail2

Models & dependencies#

Models required (7)#

wan2.1_14B_SCAIL_2_fp16.safetensors
wan2.1_SCAIL_2_DPO_lora_bf16.safetensors
wan2.1_SCAIL_2_relight_lora_bf16.safetensors
umt5_xxl_fp16.safetensors
wan_2.1_vae.safetensors
clip_vision_h.safetensors
sam3.1_multiplex_fp16.safetensors

Output contract#

What a successful run of this workflow returns.

primary
{"type": "video", "format": "mp4", "codec": "h264", "fps_source": "source", "audio": true, "alpha": false, "description": "The reference characters in the driving performance, at the driving clip's frame rate with its audio track when it has one."}

Taxonomy & routing#

How the control plane classifies this workflow — from the committed workflow-taxonomy-registry.json. It drives the consistency / control surface the agentic director can exercise over the workflow.

assetFamily
anim-character-turnaround-pack
outputPackageProfile
video-master-profile
controlModalities
model-lockprompt-template-locksampler-scheduler-lockseed-lockidentity-lockpose-constraintcontrolnet-segmentationtemporal-lockreference-ensemble
consistencyDimensions
identitymotionlightingwardrobe
notes
SCAIL-2 (A.02.06). controlnet-segmentation is literal: SAM 3.1 tracks every person in the driving clip and the reference, and SCAIL2ColoredMask paints each identity one colour in both, which binds reference characters to driving performers (reference-ensemble when there are several). lighting is a real dimension only with the relight LoRA on.