Coords To WanMove Tracks
Feeding hand-authored tracks into Wan-Move
- TRACKS
If you've watched the Wan-Move demo where an object gets dragged along a path and the video actually follows it, this node is the boring but necessary glue: it turns a JSON blob of coordinates into the TRACKS tensor that Wan-Move's ComfyUI nodes actually eat. No model weights, no VRAM, no dependencies beyond what ComfyUI already ships. It's a format converter, and honestly, that's its whole job.
Why you'd ever hand-write tracks
Wan-Move (Ali-VILab's latent trajectory guidance, built on top of Wan 2.1 I2V) gives Wan point-level motion control - think Kling's motion brush. In ComfyUI you normally reach it through Kijai's WanVideoWrapper, and the usual way to get tracks is to extract them from a driving video with CoTracker. That pipeline is where most of the Wan-Move work actually happens.
So who needs this node? People whose tracks aren't coming from a video. Maybe you've got coordinates exported from a script, a motion-capture tool, or a keyframe animation you drew yourself. Maybe you want a clean geometric path - a circle, an arc, an object drifting diagonally - without hunting for a reference clip. That's the niche: you have the numbers, this converts them to the format the sampler wants.
What it actually does
The node is one function and it's easy to read in the source. It parses your JSON, treats every coordinate as pixels, divides by width and height to normalize to 0–1, linearly interpolates the path up or down to your frame count, then - here's the subtle bit - multiplies back into pixels if you leave normalized off. It also stamps an all-True visibility mask, meaning every track point is considered "seen" for every frame.
The output TRACKS is a dict with two tensors:
track_path- shape(T, N, 2), time first, then number of tracks, then x/y. Float, in pixels (or 0–1).track_visibility- shape(T, N), a bool mask saying each point is visible at each frame.
That's exactly the format Kijai's Wan-Move nodes in WanVideoWrapper consume, so you wire the TRACKS output straight into them and let the motion guidance do the rest.
The inputs that matter
Four of them, all in the required set:
coords- the JSON. A list of tracks, each track a list of points withxandykeys, like[ [{"x":355,"y":148}, ...], ... ]. This is where all your authoring happens.num_frames- default 81, which is Wan's native clip length. Set it to match the clip you're actually generating, or your motion gets stretched or squeezed to fit.normalized- output units. Default off (pixels); flip it on if whatever you're feeding downstream wants 0–1.width/height- used to normalize your input. Match them to your generation resolution.
Installing it
Clone into custom_nodes and restart ComfyUI:
cd ComfyUI/custom_nodes
git clone https://github.com/danielvw/ComfyUI-WanMove-Adapter
It shows up under WanMove/Adapters. ComfyUI Manager can grab it too - search "ComfyUI-WanMove-Adapter". There are no model downloads and no extra Python deps; it only uses torch, numpy, and json, all of which your ComfyUI install already has.
Where people get burned
The one that'll bite you first: every track must have the same number of points. The node builds a numpy array straight from your list, so a ragged set of tracks throws an inhomogeneous-shape error. Pad short tracks to the same length first.
Second: the coords input is always treated as pixels regardless of normalized - that flag only changes what comes out. Feed it 0–1 values by accident and you'll get tracks scaled by your width and height, which is a great way to generate a scene where nothing moves where you told it to.
And third, a workflow sanity check rather than a crash: num_frames has to equal your generation's frame count. Wan's default 81 matches the node's default, so this only bites if you've changed one side and not the other.
One honest take: if you're serious about motion transfer, the CoTracker-driven pipeline gives you far richer, more organic motion than anything you'll hand-type. This node is for the cases where you want precise, authored, repeatable paths - which is also, not coincidentally, the case where it's the fastest way to a working result.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| coords | STRING | — | |
| width | INT | 720 | — |
| height | INT | 720 | — |
| num_frames | INT | 81 | — |
| normalized | BOOLEAN | false | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| TRACKS | TRACKS | — |