ComfyUI Node
Wan Face Controller 3D
All-in-one face controller for the V2 pose pipeline. Combines four independent stages, applied in this order: (1) Optional reference-shape blend (mouth/brows/eyes/jaw). (2) 12 FACS-inspired expression dials via expression_coeffs_json. (3) 3-DoF head rotation (yaw/pitch/roll) using a canonical iBUG-68 depth map. (4) Gaze offset applied to iris_data pupil/iris keypoints. Leaving a stage's controls at their defaults makes that stage a no-op, so the same node covers any subset. Schemas: see source file docstring.
Wan Face Controller 3D
- pose_data
- images
- model
- reference_pose_data
- pose_data
- info
- coeff_time_series_json
- preview_image
- overlay_video
- keyframes_csv
- pose_diff_json
- lp_rotate_pitch
- lp_rotate_yaw
- lp_rotate_roll
- lp_pupil_x
- lp_pupil_y
◄detection_threshold0.30►
◄pose_threshold0.30►
◄use_clahetrue►
◄detect_rescale1.25►
◄fallback_to_full_frametrue►
◄fc3d_config_json{"expression_coeffs_json":"","expression_strength":1.0,"expression_clamp":1.5,"expression_clamp_per_axis_json":"","propagate_expression":"off","head_pose_json":"","head_yaw_deg":0.0,"head_pitch_deg":0.0,"head_roll_deg":0.0,"head_tx":0.0,"head_ty":0.0,"head_tz":0.0,"head_scale":1.0,"jaw_rot_deg":0.0,"neck_yaw_deg":0.0,"neck_pitch_deg":0.0,"propagate_head":"off","propagate_gaze":"off","gaze_json":"","gaze_yaw_deg":0.0,"gaze_pitch_deg":0.0,"blend_strength":0.0,"blend_mouth":true,"blend_brows":false,"blend_eyes":false,"blend_jaw":false,"use_metas":"edited","frame_start":-1,"frame_end":-1,"preview_frame_idx":0,"preview_size":512,"preview_max_video_frames":120}►
◄landmark_overrides_json►
◄pose_overrides_json►
◄gaze_overrides_json►
CategoryWanAnimatePreprocessV2/extras
Inputs (13)
| Name | Type | Default | Description |
|---|---|---|---|
| pose_dataopt | POSEDATA | Pre-detected pose bundle (iBUG-68 face landmarks). OPTIONAL if you wire `images` + `model` to detect inside this node. | |
| imagesopt | IMAGE | Video frames (B,H,W,3). Wire a video loader here to run ViTPose pose estimation INSIDE this node — no separate detector node needed. | |
| modelopt | POSEMODEL | ViTPose+YOLO bundle from 'ONNX Detection Model Loader (V2)'. Used for internal detection (when `images` is wired and `pose_data` is empty). | |
| detection_thresholdopt | FLOAT | 0.300–1 | Internal detection: YOLO person-detection confidence threshold. |
| pose_thresholdopt | FLOAT | 0.300–1 | Internal detection: per-keypoint confidence threshold. |
| use_claheopt | BOOLEAN | true | Internal detection: CLAHE contrast enhancement on the pose crop. |
| detect_rescaleopt | FLOAT | 1.251–2 | Internal detection: bbox padding factor before the ViTPose crop. |
| fallback_to_full_frameopt | BOOLEAN | true | Internal detection: run ViTPose on the full frame when YOLO finds no person. |
| reference_pose_dataopt | POSEDATA | Optional single-frame POSEDATA used as a region-wise shape target. | |
| fc3d_config_jsonopt | STRING | {"expression_coeffs_json":"","expression_strength":1.0,"expression_clamp":1.5,"expression_clamp_per_axis_json":"","propagate_expression":"off","head_pose_json":"","head_yaw_deg":0.0,"head_pitch_deg":0.0,"head_roll_deg":0.0,"head_tx":0.0,"head_ty":0.0,"head_tz":0.0,"head_scale":1.0,"jaw_rot_deg":0.0,"neck_yaw_deg":0.0,"neck_pitch_deg":0.0,"propagate_head":"off","propagate_gaze":"off","gaze_json":"","gaze_yaw_deg":0.0,"gaze_pitch_deg":0.0,"blend_strength":0.0,"blend_mouth":true,"blend_brows":false,"blend_eyes":false,"blend_jaw":false,"use_metas":"edited","frame_start":-1,"frame_end":-1,"preview_frame_idx":0,"preview_size":512,"preview_max_video_frames":120} | Editor-owned JSON for expression/head/gaze/blend/preview params. Synced automatically by face_controller_3d.js. |
| landmark_overrides_jsonopt | STRING | JSON {"frames":{"<idx>":{"<lm>":[x_img_norm,y_img_norm]}}} from the in-canvas face viewer (image-normalised 0..1). Empty = no override. | |
| pose_overrides_jsonopt | STRING | JSON {"frames":{"<idx>":{"<joint>":[x_img_norm,y_img_norm]}}} from the in-canvas pose viewer (OpenPose-18). | |
| gaze_overrides_jsonopt | STRING | JSON {"frames":{"<idx>":{"l":[yaw_rad,pitch_rad],"r":[yaw_rad,pitch_rad]}}} from the in-canvas gaze handles. |
Outputs (12)
| Name | Type | Description |
|---|---|---|
| pose_data | POSEDATA | — |
| info | STRING | — |
| coeff_time_series_json | STRING | — |
| preview_image | IMAGE | — |
| overlay_video | IMAGE | — |
| keyframes_csv | STRING | — |
| pose_diff_json | STRING | — |
| lp_rotate_pitch | FLOAT | Head pitch for LivePortrait ExpressionEditor (clamped ±20°). Wire to rotate_pitch. |
| lp_rotate_yaw | FLOAT | Head yaw for ExpressionEditor (clamped ±20°). Wire to rotate_yaw. |
| lp_rotate_roll | FLOAT | Head roll for ExpressionEditor (clamped ±20°). Wire to rotate_roll. |
| lp_pupil_x | FLOAT | Gaze→pupil_x for ExpressionEditor (clamped ±15). Wire to pupil_x. |
| lp_pupil_y | FLOAT | Gaze→pupil_y for ExpressionEditor (clamped ±15). Wire to pupil_y. |