ComfyUI Node

X-Portrait

Turn a Stiff Photo Into a Talking Head That Moves Its Eyes

By akatz-ai·Created 2 years ago·Updated about a year ago· 86
X-Portrait
  • xportrait_model
  • source_image
  • driving_video
  • image
seed999
ddim_steps15
best_frame0

This is the node you actually came for. The XPortrait node takes a still portrait and a video of somebody else's face, then reanimates the photo - your person's identity, the driver's expression, head motion, eye movement and lip sync. It's a direct ComfyUI port of ByteDance's X-Portrait (the ControlNet-era one from late 2024, not the long-awaited X-Portrait 2, which still doesn't have code out). If you've seen the "make a painting talk" demos, this is the same family, and it's the portrait-animation half of the pack; its only dependency is the (Down)load X-Portrait Model node feeding it a model.

How it works

X-Portrait is a ControlLDM built on SD 1.5, which means the "model" is really three ControlNets working together: a reference-only appearance ControlNet that holds your portrait's identity, a pose ControlNet that follows the driving frames, and a local ControlNet conditioned on cropped eye and mouth regions - that local branch is what makes the lip sync and gaze believable instead of mushy. Before diffusion, face-alignment extracts 68 facial landmarks from both the source and the driver, then affine-warps every driving frame to match the source's face geometry. So the output tracks your portrait's face shape, not the driver's - that's the trick that makes it look like the photo itself is coming alive.

The inputs that matter

  • source_image (IMAGE) - your portrait. Square, max 512x512. This is not a suggestion; the pipeline internally rescales to 256/512 and non-square images come out looking like someone stretched a painting.
  • driving_video (IMAGE) - the motion source, loaded as a batch of frames, not an mp4. Keep it the same resolution as the source, max 512x512.
  • best_frame (INT, default 0) - which frame of the driving video best matches the source's head pose. This is the single most important dial. The README's whole point is that you're supposed to find it; set -1 and the node will auto-detect by comparing head poses, but the author's example workflow sets it by hand.
  • seed (INT, default 999) and ddim_steps (INT, default 15) - standard diffusion knobs. Diminishing returns after 15 steps, per the README, so the default is already sensible.

The output

One output, image (IMAGE): a batch of generated frames at the source resolution. It is not an mp4. You wire it into VHS_VideoCombine or a SaveAnimatedWEBP to get a file. The included example workflow is VHS_LoadVideo → ImageScale(512) → XPortrait → VHS_VideoCombine, so you'll want VideoHelperSuite installed alongside this pack.

Install, gotchas, and the parts that bite

Install via ComfyUI Manager (search "X-Portrait" → "X-Portrait Nodes") or git clone https://github.com/akatz-ai/ComfyUI-X-Portrait-Nodes.git into custom_nodes, then restart. The dependency list is heavy - face-alignment, xformers, sageattention, triton, diffusers, imageio[ffmpeg] - and the model download is a 12 GB file on first run. If dependency hell strikes, the author ships a prebuilt Docker image (akatzai/comfy-env-xportrait) for their Environment Manager.

Where people get burned:

  • Feeding it an actual video file. driving_video is IMAGE type. Load frames with VHS, or the node errors immediately.
  • Resolution mismatch. Driver at 1024 or the source not square, and you'll get warped, wobbly garbage rather than a clean failure.
  • "undetected faces". That's face-alignment giving up - tiny, side-on, or badly lit faces print this to the console and the node plows ahead with garbage output. Front, centered, well-lit is the way.
  • The wrong best_frame. Default 0 is often wrong if the driver's first frame isn't a neutral, similar-pose shot. The motion looks desynced. That's the fix, not a new model.

One honest caveat: community takes on X-Portrait are split - some found it a big step up from LivePortrait for identity and lip fidelity, others found it "pretty bad compared to liveportrait" and heavier. It's diffusion-based, so it's slower and VRAM-hungrier (fp16 is hardcoded) than LivePortrait's lighter approach. For still-to-video expression transfer with high likeness, it's worth the weight; for quick head-driving on a modest GPU, LivePortrait is the easier life.

CategoryX-Portrait

Inputs (6)

NameTypeDefaultDescription
xportrait_modelXPORTRAIT_MODEL
source_imageIMAGE
driving_videoIMAGE
seedINT999
ddim_stepsINT15
best_frameINT0

Outputs (1)

NameTypeDescription
imageIMAGE