Face/Pose Delta Editor (C2C)
Nudge a face's gaze or pose across a Wan Animate shot without destroying the landmarks
- landmarks
- landmarks_modified
- info_json
Face/Pose Delta Editor (C2C) is the node for when you've got a landmark stream - typically MediaPipe FaceMesh landmarks from WanAnimatePreprocessV2's gaze/pose detector - and you want to edit the pose, not just read it. Its whole trick is that edits are expressed in anchor-relative space: instead of "move landmark 61 down 12 pixels" (which breaks as soon as the head turns), you say "shift the right eyebrow 0.05 units toward the nose," where one unit is the distance between the eyes. The edit then follows the face as it moves, rotates, and turns.
That distinction is the whole game. Wan Animate drives its animation off these landmark streams; raw pixel deltas applied to landmarks go out of whack the instant the head turns or the camera moves. A delta defined relative to the inter-ocular distance survives all of that. It's part of ComfyUI-CustomNodePacks, the ~72-node pack from Code2Collapse (Likhith-24, active on r/comfyui), and it sits in the Pose category because it plugs directly into the Wan Animate preprocessing chain.
How it works
- Compute a per-frame anchor. In
intereyemode (default), the anchor is the midpoint of the two outer eye corners with scale = inter-ocular distance - robust under head turns.centroidmode falls back to the mean of all landmarks, useful for non-face landmark sets. - Convert all landmarks to anchor-relative coordinates (
rel = (px - c) / s). - Apply your keyframed deltas in that relative space, easing between keyframes (smooth_step by default) and extrapolating beyond them (
hold). - Convert back to pixels per frame, so the modified stream stays in the same shape and ordering as the input.
The key input is keyframe_edits_json, a JSON blob listing which landmarks to move on which frames. The default shows the shape:
{
"keyframes": [
{"frame": 0, "deltas": {"61": [-0.02, 0.01]}},
{"frame": 30, "deltas": {"61": [-0.08, 0.04]}}
],
"ease": "smooth_step",
"extrapolate": "hold"
}
Deltas are per-landmark-index [dx, dy] in anchor-relative units - remember, 1.0 unit = one inter-ocular distance, so [-0.08, 0.04] is a small nudge, not a teleport. Landmark indices are MediaPipe FaceMesh indices; left_eye_idx (default 33) and right_eye_idx (default 263) define the inter-eye anchor. The optional external_anchors_json lets you override the computed anchors entirely ({"centers": [...], "scales": [...]}) if you have a better tracker upstream.
The inputs and outputs that matter
landmarks- the LANDMARKS stream in. Single-frame (N,2) is auto-promoted to T=1.keyframe_edits_json- where you actually describe the edit. This is the one to learn.anchor_mode-intereye(default, robust) orcentroid(fallback).landmarks_modified- the edited stream, same shape, ready to feed the same downstream you were feeding before.info_json- per-frame anchor stats, keyframes used, deltas applied, weights. Read it once to check your blend before committing a long render.
Installing it
Ships in ComfyUI-CustomNodePacks. ComfyUI Manager → search "CustomNodePacks", or:
cd ComfyUI/custom_nodes
git clone https://github.com/Code2Collapse/ComfyUI-CustomNodePacks.git
Pure NumPy/torch math - no models, no downloads. The pack overall wants opencv-python>=4.7.0 and scipy>=1.10.0 for other nodes; install just those if missing (not the full requirements.txt, which can clobber ComfyUI's torch). Restart ComfyUI.
Common issues
- Landmarks not found / wrong face - the indices must match your landmark source. Defaults assume MediaPipe FaceMesh (33/263); a different detector uses different indices.
- Edits blow up when the head turns - that usually means your delta is too large in absolute terms or
anchor_modeis wrong for the landmark set. If it's not a face set, switch tocentroid. intereyefell back to centroid - logged when the stream has too few landmarks for the eye indices; a sign your landmark source is incomplete.
The honest take: this node assumes you're already comfortable inside the Wan Animate landmark world - it's not a beginner's first node. But it's exactly the kind of surgical edit that's otherwise impossible: change the gaze on frame 30 without redrawing the whole shot.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| landmarks | LANDMARKS | Per-frame landmarks (T,N,2) — typically MediaPipe FaceMesh from WanAnimatePreprocessV2's gaze/pose detector. Single frame (N,2) is auto-promoted to T=1. | |
| keyframe_edits_json | STRING | { "keyframes": [ {"frame": 0, "deltas": {"61": [-0.02, 0.01]}}, {"frame": 30, "deltas": {"61": [-0.08, 0.04]}} ], "ease": "smooth_step", "extrapolate": "hold" } | JSON describing one or more keyframe edits in anchor-relative space (1.0 unit = inter-ocular distance). |
| anchor_mode | COMBO | intereye | How to compute the per-frame face anchor. 'intereye' is robust under head turns; 'centroid' is a fallback for non-face landmark sets. |
| left_eye_idx | INT | 330–100000 | Landmark index of the outer-left eye corner. MediaPipe FaceMesh = 33. |
| right_eye_idx | INT | 2630–100000 | Landmark index of the outer-right eye corner. MediaPipe FaceMesh = 263. |
| external_anchors_jsonopt | STRING | Optional override for per-frame anchors. JSON: {"centers": [[cx,cy], ...], "scales": [s, ...]}. If present, replaces the computed intereye/centroid anchors. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| landmarks_modified | LANDMARKS | Modified landmarks (T, N, 2) — same shape and ordering as input. |
| info_json | STRING | JSON: per-frame anchor stats, keyframes used, deltas applied, weights at head/tail frames. Useful for debugging the blend. |