Nodes/MKRShift_Nodes/Face Performance Pose Merge
ComfyUI Node

Face Performance Pose Merge

Merge body, face and facial streams into one stable pose without the arguments

By criskb·Created 7 months ago·Updated 5 months ago· 0
Face Performance Pose Merge
    • pose_frames_json
    • diagnostics_json
    • summary
    body_frames_json[]
    face_frames_json[]
    facial_frames_json[]
    max_delta_per_frame0.22
    divergence_threshold0.40
    settings_json{}

    Real performance capture rarely comes from one clean stream. You've got a body tracker doing neck and torso, a face tracker doing head pose, and a separate layer for the facial detail - lips, eyelids, brows - and they don't always agree. MKRFacePerformancePoseMerge is the MKRShift node that fuses three such streams into a single stabilized pose payload, and it's honest about when they fight: it emits diagnostics instead of silently picking a winner.

    It's the central hub of the pack's pose lane - the thing that takes body, face and facial channels and produces the merged pose you actually drive a rig with. Think of it as the "sit down and agree" meeting for your tracking layers.

    How it works

    Three JSON frame arrays go in, aligned by frame index:

    • body_frames_json - body and neck pose.
    • face_frames_json - face pose plus optional head offsets.
    • facial_frames_json - the fine stuff: lips, eyelids, brows.

    The combiner (in lib/face_performance/combine_pose_data.py) merges these into pose_frames_json with head, neck, lip, eye and brow channels, using per-channel confidence to decide which source wins. Facial channels prioritize the higher-confidence source - so a jittery body tracker can't stomp on a clean face-tracker's head pose.

    Two thresholds are the real tuning surface:

    • max_delta_per_frame (default 0.22) - the maximum per-frame change allowed after smoothing. This is your stability dial: lower it and the merged pose gets smoother/less twitchy, but lags more; higher it and it's more responsive but noisier. If your output wobbles, this is the first knob to turn.
    • divergence_threshold (default 0.4) - when the streams disagree by more than this, the node records a diagnostic. It doesn't fail the run, it just tells you "these two trackers are telling different stories here," so you can go look at that frame.

    settings_json overrides any CombinePoseDataConfig field - leave it {} until you've read the config class.

    Outputs: pose_frames_json (the merged result), diagnostics_json (array of divergence events - the author's docs point out these are exactly what you want when body and face trackers disagree or head offsets spike), and summary.

    Where it fits

    In the pack's performance pipeline, this is the merge step before the rig stages. Eye motion and lip refine feed the facial side; this node is where the whole performance converges into one stream that MKRFacePerformanceRigApplyDeltas can then retarget onto a neutral rig. If you're building that pipeline, this is the junction node.

    Installing and caveats

    Standard pack install:

    cd ComfyUI/custom_nodes
    git clone https://github.com/criskb/MKRShift_Nodes
    

    Restart ComfyUI, or ComfyUI Manager search "MKRShift_Nodes". Pure Python, no new deps.

    The validator is strict that all three inputs must be JSON arrays (it will return a friendly error string otherwise). And the honest warning: this node is deep in the niche corner of an already-niche pack. No community discussion exists for it yet, there are no presets, and you'll want to read the combine config before tuning settings_json. But if you're fusing multi-source capture into one pose, this is a clean, well-behaved merge - and the diagnostics output alone beats the usual "which tracker is right" guessing game.

    CategoryMKRShift Nodes/Performance/Pose

    Inputs (6)

    NameTypeDefaultDescription
    body_frames_jsonSTRING[]
    face_frames_jsonSTRING[]
    facial_frames_jsonSTRING[]
    max_delta_per_frameFLOAT0.220–2
    divergence_thresholdFLOAT0.400–4
    settings_jsonSTRING{}

    Outputs (3)

    NameTypeDescription
    pose_frames_jsonSTRING
    diagnostics_jsonSTRING
    summarySTRING