Expression Video
Tween a still face between two expressions
- src_image
- frames
Here's the neat one. You've got a single still portrait and two expressions you want it to travel between - say, neutral to a big grin, or eyes-open to a wink. ExpressionVideoNode generates the in-between frames and hands you a smooth little animation of the face morphing from one to the other. No driving video, no acting, no camera. Just a start pose, an end pose, and a number of frames to fill the gap. The README flags this as one of the pack's newer additions - "you can now create interpolation animation between two expressions."
It pairs directly with the Expression Editor. That's where the two expressions come from: dial a face in the editor, grab its expression_json output, dial a second face, grab that one too, and this node tweens between them.
How it works
Every expression the editor produces is really a point in LivePortrait's motion space - a bundle of numbers for pose, eyes, and mouth. Given a start point and an end point, ExpressionVideoNode interpolates a path between them and re-renders the source face at each step along the way. Because it's warping the same still through the same engine, every frame is the same person in the same lighting; only the expression slides. The result is a clean tween rather than anything that had to be filmed.
The inputs and outputs that matter
src_image(IMAGE) - the one still face that gets animated.from_expression(STRING) andto_expression(STRING) - the start and end poses, as theexpression_jsonstrings produced by ExpressionEditor_. These are the whole point; without a pair of them the node has nothing to tween.interpolations_num(INT, default 1) - how many frames to generate between the two poses. This is your knob for animation length and smoothness. One frame gives you a jump; crank it up for a longer, silkier morph. (The max is absurdly large, so use common sense - dozens for a smooth beat, not thousands.)interpolation_type(enum:linear/nearest/cubic, defaultcubic) - the easing curve.cubiceases in and out for a natural feel and is the right default;linearis a constant-speed morph;nearestsnaps and is mostly there for completeness.
The output is frames (IMAGE) - the generated sequence as an image batch. Wire it into a video-save or combine node (Mixlab's, or any frames-to-video node) to write it out as an actual clip.
How to install it
ComfyUI Manager: search comfyui-liveportrait, install, restart. Manual route:
cd ComfyUI/custom_nodes
git clone https://github.com/shadowcz007/comfyui-liveportrait
then restart ComfyUI. The LivePortrait and InsightFace weights come from the Google Drive / Baidu links in the README and go under ComfyUI/models/liveportrait and ComfyUI/models/insightface. Already running comfyui-mixlab-nodes? Then the dependencies are handled.
Common issues
The most common trip-up is the two expression inputs: they have to be real expression_json strings from the Expression Editor, not hand-typed or left empty. Build the editor → this-node chain first and confirm the JSON is flowing. After that it's the pack's usual suspects - missing model files in models/liveportrait (they don't self-download), and InsightFace being InsightFace on install (a missing C++ build toolchain historically, much smoother since InsightFace 1.0). Keep interpolations_num reasonable; a giant value doesn't make it smoother past a point, it just makes it slow. And the perennial license note: the InsightFace weights underneath are non-commercial, which carries over to whatever you export.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| src_image | IMAGE | — | |
| from_expression | STRING | — | |
| to_expression | STRING | — | |
| interpolation_type | COMBO | cubic | 3 options: linear, nearest, cubic |
| interpolations_num | INT | 11–204800000000 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| frames | IMAGE | — |