π΅ Music2Video Motion Enhancer
Makes the video prompt describe the frame it's actually starting from
- pipe
- images
- pipe
- video_prompts_i2va
- report
Here's the problem this node exists to fix, and it's a good one. The video prompts from the main Music2Video node are written before a single pixel exists. The LLM describes what it intends the first frame to contain; the image model then draws its own reading of that description. If the image came back different - a different framing, one subject where the prompt said two, a coat that turned out red - the video prompt still describes the intention. And MiniMax H3 resolves that disagreement the only way it can: it starts on the frame it was handed and dissolves into the scene it was told about. Every shot becomes a morph.
The Motion Enhancer closes that loop. Give it the pipe and the rendered start frames, and it shows each frame to a vision-capable LLM together with that shot's own i2va prompt, then rewrites the description so it describes that frame - and so the motion in it fits the shot's real length. The word budget is words_per_second Γ the shot's duration, clamped to 25β220, which is what stops a six-second shot being described as a minute of events. The H3 skeleton, the <Picture 1> reference and the two sound fields are kept (rewrite_sound opts the sound in), so what comes back is the same prompt with its picture of the world corrected.
The inputs that matter
The pipe and the images are the meat. The rest is mostly LLM plumbing:
pipe- the main node's pipe.video_prompts_i2vais what gets rewritten;durationsdecides how much motion each shot can hold.images- the start frames, one per shot, in shot order. Your sampler's output works as well as the node's ownimagessocket. Fewer frames than shots is fine: a shot without one keeps its prompt.llm_provider- same four providers as the main node (lmstudio,openrouter,openai,anthropic). It must be a vision model - a text-only model "returns a description of nothing" and every shot keeps its original prompt. Load a qwen-vl, llava-class, or gemma vision model in LM Studio.words_per_second- how many words of description one second of clip is worth. 8 is the default; raise it for a model that writes thin, lower it for one that rambles.image_detail- longest side of the frame as sent, in pixels. Bigger sees more and costs more; on a cloud provider this is most of the bill.rewrite_sound- off by default, because a still frame says nothing about sound and the originals were written against the track. Turn it on when the image turned out to be a different place than the prompt assumed.
Outputs: pipe (same pipe with the rewritten video_prompts_i2va), video_prompts_i2va (the rewritten list, usable without an expander), and report - one line per shot saying what disagreed with the frame, or what went wrong. That report is your debugging friend.
Install
Same pack, nothing extra: ComfyUI Manager β search Music2Video β install, restart, or git clone https://github.com/lazniak/comfyui-music2video ComfyUI/custom_nodes/music2video. Needs ComfyUI β₯ 0.3.48.
Gotchas
Two real ones. It bills per shot on cloud providers - one LLM call per shot, free on LM Studio, billed per token on the rest, and image_detail is where most of that bill goes. lm_temperature defaults to 0.4 deliberately: this is a correction, not an invention, and a hot model will "fix" the frame into something new. And a failed shot keeps its original prompt rather than failing the run - so if every shot comes back unchanged, check that the model you picked can actually see images before you blame the node.
Inputs (20)
| Name | Type | Default | Description |
|---|---|---|---|
| pipe | M2P_PIPE | The pipe from the Music2Video node. 'video_prompts_i2va' is what gets rewritten; 'durations' decides how much motion each shot can hold, and 'image_prompts_start' is shown to the model as what the frame was meant to be. | |
| images | IMAGE | The start frames, one per shot, in shot order - the node's own 'images' socket, or whatever your sampler produced from 'image_prompts_start'. A batch of N frames and a list of N frames are read the same way. Fewer frames than shots is fine: the shots without one keep their prompt untouched. | |
| llm_provider | COMBO | lmstudio | Which LLM rewrites the prompts. It must be able to see images: a text-only model returns a description of nothing and the node keeps the original prompt instead. 'lmstudio' is local and free - load a vision model there (gemma, qwen-vl, llava class). The cloud providers bill per token, once per shot. |
| lm_model | COMBO | Model served by LM Studio. It has to be a vision model. | |
| openrouter_model | COMBO | Model used when 'llm_provider' is 'openrouter'. Pick one that accepts images. | |
| openai_model | COMBO | Model used when 'llm_provider' is 'openai'. Pick one that accepts images. | |
| anthropic_model | COMBO | Model used when 'llm_provider' is 'anthropic'. Pick one that accepts images. | |
| lm_url | STRING | http://127.0.0.1:1234 | Address of the LM Studio server. Used only for 'lmstudio'. |
| lm_api_key | STRING | Only if your LM Studio server asks for one. | |
| openrouter_api_key | STRING | Empty reads OPENROUTER_API_KEY from the environment. | |
| openai_api_key | STRING | Empty reads OPENAI_API_KEY from the environment. | |
| anthropic_api_key | STRING | Empty reads ANTHROPIC_API_KEY from the environment. | |
| words_per_second | FLOAT | 8.02β25 | How many words of description one second of clip is worth. The model is given a word budget of duration x this, clamped to 25-220, which is what keeps a six-second shot from being described as a minute of events. Raise it for a model that writes thin, lower it for one that rambles. |
| rewrite_sound | BOOLEAN | false | Off: 'overall_soundscape' and 'non_diegetic_music' are carried over untouched, because a still frame says nothing about sound and the originals were written against the track. On: the model rewrites them too, which is worth it when the image turned out to be a different place than the prompt assumed. |
| image_detail | INT | 768256β1536 | Longest side of the frame as it is sent, in pixels. Bigger sees more and costs more; on a cloud provider this is most of the bill. |
| lm_temperature | FLOAT | 0.400β2 | Lower than the writing stages on purpose: this is a correction, not an invention. |
| lm_max_tokens | INT | 2048256β32768 | Cap on each reply. One shot's description, not the whole film. |
| lm_timeout | INT | 30010β3600 | Seconds to wait for one reply. |
| lm_retries | INT | 20β5 | Retries per shot. A shot that still fails keeps its original prompt. |
| verbose | BOOLEAN | false | Log each shot's before and after lengths. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| pipe | M2P_PIPE | The same pipe with the rewritten 'video_prompts_i2va' in it. Everything else is untouched. |
| video_prompts_i2va | STRING | The rewritten prompts, in shot order, so they can be used without an expander. |
| report | STRING | One line per shot: what disagreed with the frame, or what went wrong. |