KeyframedFlowApplication
Stealing motion from one video, applying it to another
- motion_source_frames
- frames_to_warp
- Processed Frame
Most of ComfyWarp is about keeping style stable on a video you already have. This node is the weirder, more fun one: it takes the motion from one video and applies it to a different set of frames, with keyframe-based control over how much and how many times. Think of it as motion transfer - you record a camera move, then drive your own images along the same path. The README frames it as "a node that takes a video and applies motion from it to another video (frame sizes are expected to match)." Read that last clause twice: both inputs need matching frame sizes, so run everything through ResizeToFit first or the warp math silently goes sideways.
Inputs
motion_source_frames- the video whose movement you want to steal. Flow is extracted between consecutive frames of this sequence.frames_to_warp- the frames you want moved. For each frame, the node decides whether to warp it, and by how much, based on the keyframes below.keyframe_weights(default{"0": 1.0, "10": 2.0}) - a JSON dict offrame: weight. A weight above 1 exaggerates the motion at that frame (the flow is multiplied by the weight). Frames at weight ≤ 1 and not in a repeat group are passed through unwarped - they're your "keep this still" frames.keyframe_repeats(default{"0": 1, "10": 3}) - a JSON dict offrame: repeat count. Repeating a frame's flow means "hold the motion from this frame for N frames," so you can stretch a quick move into a slow pan. Repeat 3 at frame 10 reuses that frame's flow for frames 10, 11, 12.num_flow_updates(default 20) - the usual RAFT refinement count.
You can also pass lists instead of dicts (e.g. [1.0, 1.5, 2.0]), which the node indexes per frame. One warning: these are parsed with eval() under the hood, so keep them clean JSON - it's a single-machine tool, not a web service, but garbage in still errors out.
How it decides
The node walks the frame range and for each frame asks: am I in a repeat group, or does my weight exceed 1? If neither, the frame passes through untouched. Otherwise it computes (or reuses) the flow between the corresponding source frames and warps the target frame by flow × weight. The neat part is that a repeated frame warps the previous output rather than the original target, so motion chains smoothly across the hold instead of resetting.
The output
Just one Processed Frame - a batch of images you can stitch into a video or feed back into the diffusion pipeline. It's the node to reach for when you want camera-driven animation from stills, or when you want to re-time a video's motion onto a new render. Expect to iterate on the weight/repeat dicts; there's no visual editor, so it's a "tune the JSON, re-run" loop. Annoying, sure, but it's also the only way in this pack to bend motion to a keyframe curve without touching a scheduler.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| motion_source_frames | IMAGE | — | |
| frames_to_warp | IMAGE | — | |
| keyframe_weights | STRING | {"0": 1.0, "10": 2.0} | — |
| keyframe_repeats | STRING | {"0": 1, "10": 3} | — |
| num_flow_updates | INT | 205–100 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| Processed Frame | IMAGE | — |