X-Portrait
Turn a Stiff Photo Into a Talking Head That Moves Its Eyes
- xportrait_model
- source_image
- driving_video
- image
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-1and the node will auto-detect by comparing head poses, but the author's example workflow sets it by hand.seed(INT, default 999) andddim_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_videois 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.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| xportrait_model | XPORTRAIT_MODEL | — | |
| source_image | IMAGE | — | |
| driving_video | IMAGE | — | |
| seed | INT | 999 | — |
| ddim_steps | INT | 15 | — |
| best_frame | INT | 0 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |