Video Transform
Pan, zoom, rotate — with keyframes and real motion blur
- video
- track
- video
A static shot is fine until you want it to move. Video Transform is ComfyTV's 2D motion node: translate, scale, rotate, skew - and crucially, animate all of it with keyframes, plus shutter-based motion blur so fast moves smear the way they should. It's the difference between a clip and a push-in.
What it does
It applies a 2D geometric transform to a COMFYTV_VIDEO and renders on ▶ Run (or defers into an FX Chain - more on that below). The controls:
- pos_x / pos_y - translation in pixels (−8192 to 8192). Positive x right, positive y down.
- scale - uniform scale, 0.01–10.0, default 1.0.
- rotation - degrees, −360 to 360, step 0.5.
- skew_x - horizontal shear, −2.0 to 2.0.
- motion_blur - 0–4, default 0 (off); higher takes more shutter samples across the move for smoother smear. Only matters when the transform is actually moving.
- shutter - 0–4, default 0.5; the shutter angle/open duration. Wider = longer smear. shutter_type (
centered/start/end/custom) and shutter_offset shape where the shutter opens. - keyframes - a JSON array
[{t, x, y, scale, rotation, interp}]animating the transform. Normally set through the node's keyframe UI, not typed by hand.
There's also a track input (COMFYTV_TEXT): when you connect a motion track, its keyframes drive the transform and the node bakes and renders on its own. That's how a tracked layer sticks to movement.
How it plays with the FX Chain
Here's the interesting bit: when the transform is not identity (something is actually changing), and it's feeding an FX Chain, the node doesn't render immediately - it emits an FX spec that renders with the rest of the chain in a single pass. That's the pack's "stack effects, render once" design. When a track is connected it bakes directly instead. And if nothing is set - no transform, no keyframes - it's a clean pass-through that does nothing, which is documented behavior, not a bug.
Use it to reframe a shot, straighten a horizon, push in on a subject with keyframes, or run a Ken Burns-style drift. For free-form four-corner warps, that's Corner Pin; for per-pixel warps, UV Remap. This one is the pan/zoom/rotate workhorse.
Install and troubleshooting
Pack-standard: git clone https://github.com/jtydhr88/ComfyTV into ComfyUI/custom_nodes, restart, under ComfyTV → VideoFX. No models, no GPU.
- Motion blur does nothing - the transform is static; blur only appears when keyframes or a track actually move the frame.
- Nothing changes on Run - identity transform = documented pass-through. Set something.
- Jumpy keyframe animation - check the
interpfield in your keyframes JSON; the UI writes it for you, but hand-edited JSON can drop it.
Output is a COMFYTV_VIDEO snapshot; cross to native nodes with a ← ComfyTV Video Bridge.
Inputs (15)
| Name | Type | Default | Description |
|---|---|---|---|
| force_run_token | INT | 00–2147483647 | Internal — bumped on Run to invalidate ComfyUI's input cache. |
| project_id | STRING | Internal — populated by the projectStore on the frontend. | |
| parent_output_id | INT | 00–2147483647 | Internal — lineage parent set by spawn handlers on the frontend. |
| pos_x | FLOAT | 0-8192–8192 | — |
| pos_y | FLOAT | 0-8192–8192 | — |
| scale | FLOAT | 1.000.01–10 | — |
| rotation | FLOAT | 0.0-360–360 | — |
| skew_x | FLOAT | 0.00-2–2 | — |
| motion_blur | FLOAT | 0.000–4 | 0 = off; higher = more shutter samples |
| shutter | FLOAT | 0.500–4 | — |
| shutter_type | COMBO | centered | 4 options: centered, start, end, custom |
| shutter_offset | FLOAT | 0.00-4–4 | — |
| keyframes | STRING | JSON [{t,x,y,scale,rotation,interp}] | |
| videoopt | COMFYTV_VIDEO | — | |
| trackopt | COMFYTV_TEXT | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| video | COMFYTV_VIDEO | — |