LC Vision Moviemaker π₯
One LLM call, twenty H3 prompts that actually connect
- vision_model
- reference_image_1
- reference_image_2
- reference_image_3
- reference_image_4
- reference_video
- segment_1
- segment_2
- segment_3
- segment_4
- segment_5
- segment_6
- segment_7
- segment_8
- segment_9
- segment_10
- segment_11
- segment_12
- segment_13
- segment_14
- segment_15
- segment_16
- segment_17
- segment_18
- segment_19
- segment_20
You want a 30-second video out of a model that generates ten seconds at a time, so you write four prompts and stitch the clips. Segment 1 ends with a woman walking into a bar; segment 2 opens with her already at a table, because nothing told the second call what happened in the first.
Moviemaker's answer is boring and correct: plan every segment in one LLM call. The model writes them all in a single response, so the arc stays continuous while each output is still a self-contained prompt for its own generation call.
What it's writing for
The presets target MiniMax H3, the 33B omni-modal video model MiniMax published in August 2026 - one input context for text, image, video and audio, 4 to 15 second clips with native stereo audio. Strong model, awkward licence: the Community License scopes its grant to a territory that excludes the US, EU, UK and South Korea, and the exclusion covers outputs, not just weights. In those regions the local weights are the unlicensed path and the hosted Hailuo API is the only clean one.
The pack covers six H3 modes: T2V, R2VA and FL2VA, each with an NSFW variant, plus a Custom entry that reads your own system prompt off the custom_system_prompt socket. The author is upfront that these presets are a first draft from observed conventions, not a spec - edit lc_vision_moviemaker_presets.json directly if your own experience with H3 disagrees. No code change needed.
How it plans
The system prompt has two halves: a structural half the node always applies (segment count, per-segment duration, reference handling, continuity rules) and the creative half from your preset. The model writes segments separated by a ###SEGMENT_N### delimiter this node controls, so parsing never depends on guessing at formatting. Results are checked for what LLMs actually get wrong here - a missing segment, meta-commentary leaking into the prompt text, timestamps that don't reach the segment's stated duration. Issues trigger one retry at lower temperature, and the two passes are merged per segment, so one malformed segment doesn't cost you the whole plan.
The clock handling reads like someone actually tested it. Early on the model tried to track a running global timestamp across all segments and kept resetting to 0:00, however explicitly that was spelled out. So each segment plans on its own 0.00-based local clock - the thing it's good at - and code shifts every timestamp onto the global timeline afterward.
end_pad_seconds covers a related quirk: language models consistently undershoot stated durations. Half a second is silently shaved off length_seconds before the model ever sees it, spread proportionally across segments, so its undershoot lands near your real target. Raise it if segments keep coming up short.
Inputs and outputs
Required: vision_model (from the Loader), story, preset, length_seconds (default 10) and segments (default 1, max 20). Keep per-segment length inside H3's 4β15 second window - 30 seconds across 3 segments is comfortable.
segments is the one to watch, because the output sockets grow with it. The class declares all 20 at definition time and a small front-end script adds or removes real sockets to match the widget - raise it and a new wire appears, drop it and sockets disappear entirely. Mind the mismatch in a saved workflow: shrink the count after wiring things and those wires have nothing to land on.
Optional references: reference_image_1 through reference_image_4 for the R2VA/FL2VA presets, plus reference_video as a frame-sequence batch. Images go in labeled <Reference N> and come back written as <Picture N>, tied to a [Shot N] label. Here's the trap the README calls out: [Shot N] identifies which reference a moment involves, not a cut - if the model starts cross-cutting, ask for one continuous take per segment. The video reference is labeled <Target Video> and feeds motion and style continuity instead. Frame sampling and downscaling work like Caption's: video_max_frames, max_image_side, jpeg_quality.
Finally, max_tokens (4096) caps the whole response, all segments together - raise it as you raise segment count or later segments get truncated. Drop temperature from its 0.7 default if you want repeatable plans.
Install
ComfyUI Manager, search ComfyUI LC Vision Nodes, or:
cd ComfyUI/custom_nodes
git clone https://github.com/lonecatone23/ComfyUI_LC_Vision_nodes.git
__init__.py sits at the top of that folder, then restart ComfyUI. Models come from the Loader node. The shared dependency - a vision-capable llama-cpp-python wheel from the JamePeng fork, which never made it to PyPI - is handled by install.py, which picks the right build for your Python, platform and CUDA. Hard-refresh your browser after updating; the socket-grow code lives in web/.
When it goes wrong
Only some segments came back. Check max_tokens first - it's a shared ceiling. Then look for the console retry line; a partial split is exactly what the retry-and-merge path exists for.
Segments read like separate scenes. The plan drifted. Say so in story - "one continuous take, the camera follows her the whole time" - and lower temperature a notch.
Segments are shorter than requested. Raise end_pad_seconds. It exists purely to compensate for the model's bad time sense.
Long plans on an 8B. Moviemaker's responses are long and the author recommends the 4B model here for headroom. And a badly split plan isn't a decode error - the per-segment retry and merge handles that.
Inputs (21)
| Name | Type | Default | Description |
|---|---|---|---|
| vision_model | LC_VISION_MODEL | Handle from LC Vision Loader. | |
| story | STRING | The overall scene or story to plan across every segment. | |
| preset | COMBO | Minimax H3 T2V | Which MiniMax H3 mode/format to write each segment for. 'Custom' reads its system prompt from the custom_system_prompt socket instead (falls back to this default preset if that's left empty). Add more presets in lc_vision_moviemaker_presets.json. |
| length_seconds | FLOAT | 10.01β3600 | Total planned length across every segment combined. |
| segments | INT | 11β20 | Number of separate generation calls to plan for (max 20). Output sockets grow to match -- add another wire and this raises automatically, or set it directly. |
| custom_system_promptopt | STRING | Used only when preset is 'Custom' -- read directly instead of a lc_vision_moviemaker_presets.json entry. The structural timing/continuity/reference rules still apply on top of this. | |
| style_tagopt | COMBO | None | Commits every segment to a specific visual style. 'None' leaves style unconstrained. |
| reference_image_1opt | IMAGE | For R2VA/FL2VA presets. Labeled <Reference 1> to the model, written back as <Picture 1> in segment text. | |
| reference_image_2opt | IMAGE | Labeled <Reference 2> / <Picture 2>. | |
| reference_image_3opt | IMAGE | Labeled <Reference 3> / <Picture 3>. | |
| reference_image_4opt | IMAGE | Labeled <Reference 4> / <Picture 4>. | |
| reference_videoopt | IMAGE | A frame-sequence batch, labeled <Target Video>. Informs motion/style/continuity rather than being treated as a person to individually describe. Sampled down to video_max_frames. | |
| video_max_framesopt | INT | 81β64 | Reference video frames are evenly sampled down to this count before sending -- sending every frame of a long clip would blow the token budget. |
| max_image_sideopt | INT | 7680β4096 | Downscale any reference longer edge to this before sending. 0 = no resize. |
| jpeg_qualityopt | INT | 9010β100 | JPEG encoding quality for references sent to the model. |
| end_pad_secondsopt | FLOAT | 0.50β10 | LLMs are consistently bad at judging elapsed time and tend to undershoot a stated duration. This much is silently shaved off length_seconds before it's ever shown to the model (proportionally, across every segment), so its own undershoot lands close to your real target instead of well short of it. Raise it if segments still stop noticeably early; 0 disables. |
| max_tokensopt | INT | 409664β32768 | Upper limit on generated tokens for the WHOLE response (all segments together) -- raise this along with segment count. |
| temperatureopt | FLOAT | 0.700β2 | Sampling randomness. 0 = deterministic and literal, higher = more varied wording at some cost to focus. |
| top_popt | FLOAT | 0.900β1 | Nucleus sampling threshold. Lower = more focused, higher = more varied. |
| repetition_penaltyopt | FLOAT | 1.100.5β2 | Penalizes tokens the model has already used. 1.0 = no penalty. |
| seedopt | INT | 00β4294967295 | Sampling seed. |
Outputs (20)
| Name | Type | Description |
|---|---|---|
| segment_1 | STRING | β |
| segment_2 | STRING | β |
| segment_3 | STRING | β |
| segment_4 | STRING | β |
| segment_5 | STRING | β |
| segment_6 | STRING | β |
| segment_7 | STRING | β |
| segment_8 | STRING | β |
| segment_9 | STRING | β |
| segment_10 | STRING | β |
| segment_11 | STRING | β |
| segment_12 | STRING | β |
| segment_13 | STRING | β |
| segment_14 | STRING | β |
| segment_15 | STRING | β |
| segment_16 | STRING | β |
| segment_17 | STRING | β |
| segment_18 | STRING | β |
| segment_19 | STRING | β |
| segment_20 | STRING | β |