Expression Editor
Pose a face with sliders, no driving video needed
- src_image
- image
- expression_json
Most of LivePortrait is about copying motion off a driving video. Expression Editor throws the driving video out. Instead you get a rack of sliders and you pose the face by hand - nudge the head, open the mouth, add a smile, close one eye. It's the fast way to fix a single frame: the render came out with dead eyes or a flat mouth, and you want to add a little life without regenerating the whole thing. The expression code here is adapted from PowerHouseMan's ComfyUI-AdvancedLivePortrait, which is the node that made this slider-driven approach popular in the first place.
Think of it as photo-retouching for facial performance. Same identity, same lighting, same everything - you're just steering the underlying LivePortrait keypoints directly with numbers instead of borrowing them from a video.
How it works
LivePortrait already separates a face into an appearance code and a set of motion codes (pose, eyes, mouth). Expression Editor exposes those motion dimensions as individual controls. Each slider adds an offset to one axis of the expression space, the model re-warps the source, and you get the edited face back. Because it's the same warping engine as the animation nodes, edits stay photoreal instead of looking painted-on.
The inputs that matter
The one required image input is src_image (IMAGE) - the face you're editing. Everything else is a FLOAT knob, and you'll only ever touch a handful:
smile(−0.3 to 1.3) - the single most useful one. Small positive values add a natural smile; push it and it gets toothy.aaa,eee,woo- mouth shapes, i.e. visemes.aaaopens the jaw for an "ah" (range −30 to 120, it's the big one),eeespreads for an "ee",woopurses for an "oo". These are how you fake a spoken shape on a still.blink/wink- eyelid control.blinkcloses both eyes (−30 to 15),winkdoes one.rotate_pitch/rotate_yaw/rotate_roll(−50 to 50) - tilt the head up-down, turn left-right, roll sideways.eyebrow,pupil_x/pupil_y- raise brows, aim the gaze.src_weight(0 to 1) - how strongly the original expression is preserved versus your edits.
Start with everything at zero (the default, meaning "unchanged") and move one at a time - it's very easy to drive a face into the uncanny valley by stacking three sliders at once.
There's also an optional expression_json (STRING) input for loading a saved expression state.
The outputs
Two: image (IMAGE), the edited face, and expression_json (STRING), a serialized snapshot of the exact expression you dialed in. That JSON output is the connective tissue of this whole pack - feed it into ExpressionVideoNode or ExpressionVideo2VideoNode as a from_expression or to_expression, and you can animate between two poses you designed by hand. Dial a neutral face, save its JSON; dial a big smile, save that one; hand both to ExpressionVideoNode and it tweens between them.
How to install it
Via ComfyUI Manager, search comfyui-liveportrait, install, restart. Or clone it:
cd ComfyUI/custom_nodes
git clone https://github.com/shadowcz007/comfyui-liveportrait
and restart. Put the LivePortrait and InsightFace weights (Google Drive / Baidu links in the README) under ComfyUI/models/liveportrait and ComfyUI/models/insightface. If you already run comfyui-mixlab-nodes, the dependencies are already there.
Common issues
If the graph won't even import, it's almost always InsightFace - it's the hardest dependency in ComfyUI and the usual failure is a missing C++ build toolchain (much better since InsightFace 1.0, but plenty of setups predate it). If the node loads but the edit does nothing or looks garbled, check the model files actually landed in models/liveportrait - they don't download automatically. And a gentle reminder that this drives the same non-commercially-licensed InsightFace weights every face tool leans on, which matters the moment you try to sell the output.
Inputs (15)
| Name | Type | Default | Description |
|---|---|---|---|
| src_image | IMAGE | — | |
| rotate_pitch | FLOAT | 0.0-50–50 | — |
| rotate_yaw | FLOAT | 0.0-50–50 | — |
| rotate_roll | FLOAT | 0.0-50–50 | — |
| blink | FLOAT | 0.0-30–15 | — |
| eyebrow | FLOAT | 0.0-30–25 | — |
| wink | FLOAT | 0.0-10–25 | — |
| pupil_x | FLOAT | 0.0-15–15 | — |
| pupil_y | FLOAT | 0.0-15–15 | — |
| aaa | FLOAT | 0-30–120 | — |
| eee | FLOAT | 0.0-20–15 | — |
| woo | FLOAT | 0.0-20–15 | — |
| smile | FLOAT | 0.00-0.3–1.3 | — |
| src_weight | FLOAT | 1.000–1 | — |
| expression_jsonopt | STRING | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| expression_json | STRING | — |