Pulse Shot
Give MiniMax H3 a real edit with Pulse Shot
- start_image
- end_image
- ref_audio
- voice
- refs.ref_image_1
- refs.ref_image_2
- refs.ref_image_3
- refs.ref_image_4
- shot
The upgrade path from the text box to a real shot list
Pulse Slate lets you write your whole film in one shot_prompt box, and that's fine for a storyboard. But the moment you want per-shot control - a shot that opens on a frame generated elsewhere in the same graph, a scene-local reference, a different continuity for one cut - the text box stops being enough. That's what Pulse Shot is for: one node per shot, chained into Pulse Slate's shots.shot_N sockets.
Here's the part that surprises people: connect any shot socket and the shot_prompt box on Pulse Slate goes inactive. The two are never merged and neither is silently preferred - compiled_prompt on Pulse Slate's output tells you which one won, at the top, every time. This is a deliberate "no ambiguity" design, and it saves you the exact failure it looks like it might cause.
What lives on a shot
Each Pulse Shot node carries:
visualandaudio_line- the on-screen action and what's heard. Quoted"text"in the audio line becomes dialogue. Reference assets by@Name(the asset bin or this node's own sockets) - still no ordinals.duration_seconds- capped at 15.08s, H3's trained window length. Longer stories are built from more shots, not longer ones.continuity- how this shot joins the one before it.inherittakes the Pulse Slate setting, which is what you want unless one particular cut needs different handling.start_image/end_image- realIMAGEinputs. This is the node's hidden superpower: because they're tensors, a shot can open on a frame produced by a PulseStill, a photo, or another sampler in the same graph, instead of on a file you had to export first. Note H3 only accepts a keyframe at frame 0 or the final frame - nowhere else - so these pin the ends of the window the shot lands in.refs.ref_image_1…refs.ref_image_4- scene-local references, visible only to this shot, numbered after the global ones. Put shared references in the Asset Bin instead.
The one output is shot (type PULSE_SHOT), which feeds a Pulse Slate shot socket. Under the hood each shot gets a stable shot_id, written once and never changed - that's what keeps its seed and its cached segment attached to it when you insert a shot above it or reorder the film. Inserting a shot rerolls only the windows whose shot set changed.
ref_audio and the ref_audio_mode decision
H3 always generates its own audio track; a reference recording only changes what the model does alongside it. The two modes are genuinely different jobs:
lip_sync(default) - the character's mouth matches your recording. The mechanism is subtle: the tokenizer emits only the marker<Audio j>and the waveform never reaches the language model, so the sentence naming the tag in the prompt is what does the work. The clip is also trimmed to the window's exact span - a 30-second file against a 9-second window asks the model to align two different stretches of time, and you get a mouth that tracks nothing.voice_timbre- the model speaks this shot's own dialogue and borrows only the character of the voice. No alignment, no trim.
The mode is part of the cache key, so switching it re-renders rather than handing back a segment made under the other instruction.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/Addis-Pulse-Studio/comfyui-pulse-studio
Restart ComfyUI; no pip install, no dependencies - the pack uses only what a working ComfyUI already has, and needs ComfyUI 0.30.0+ with H3 support in the host build (comfy_extras/nodes_minimax_h3.py and comfy/ldm/minimax/). You'll also need the MiniMax H3 weights from Comfy-Org/MiniMax-H3 - the ref2va DiT (~20 GB) into models/diffusion_models/minimax/, the text encoder (~15 GB), and the two VAEs. Remember the weights carry the geofenced MiniMax H3 Community License (no US, EU, UK, Korea), and on Linux/macOS the example graphs' loader widgets load red with Windows-style paths - re-pick each file.
The trap nobody warns you about
A lip_sync reference on a shot with no dialogue produces no mouth movement, and nothing tells you why. There's no speech to match. Put the recording on the shot that actually speaks.
The inverse gets reported: a shot carrying both a quoted line and a lip_sync reference gives the model two answers to "what is she saying". The compiler names it in the report rather than refusing, because a quote that is the recording's transcript is legitimate and only you know that. Otherwise drop the quote, let the audio carry the words, and keep the @Voice tag - that's what tells the model whose shot the recording belongs to.
Inputs (17)
| Name | Type | Default | Description |
|---|---|---|---|
| schema_version | STRING | 3.0.0 | Which widget layout this node was saved with. Written by the node, read at load time to restore values by name. Do not edit. |
| shot_id | STRING | This shot's stable identity. Written once when the node is created and never changed -- it is what keeps this shot's seed and its cached segment attached to it when you insert a shot above it. Do not edit. | |
| label | STRING | A name for this shot, shown in the render report. Not sent to the model. | |
| visual | STRING | What happens on screen. @Name references the Asset Bin or this node's own reference sockets -- never type an ordinal. | |
| audio_line | STRING | What is heard. Quoted "text" becomes dialogue. | |
| duration_seconds | FLOAT | 5.000.5–15.08333333333333 | How long this shot runs. The ceiling is H3's trained window length; longer stories are built from more shots, not longer ones. |
| continuity | COMBO | inherit | How this shot joins the one before it. 'inherit' takes the PulseSlate setting, which is what you want unless one particular cut needs different handling. |
| ref_audio_mode | COMBO | lip_sync | What the ref_audio socket is for. 'lip_sync': the character's mouth matches that recording, and the clip is trimmed to this window's exact span so the two describe the same seconds. 'voice_timbre': the model speaks this shot's own dialogue and only borrows the voice's character. Ignored when nothing is connected to ref_audio. |
| speaker | STRING | Who is talking in this shot, as an @Name from the Asset Bin or from this node's own reference sockets -- @Ref1 for the picture that defines them. That character gets a speaker id, (S1), (S2), stable across the whole film, and this shot's ref_audio is bound to it by name. Leave blank on a shot with nobody speaking, or on a one-hander where there is nothing to confuse. | |
| start_imageopt | IMAGE | First frame of this shot. With continuity 'keyframe_pairs' this is the frame the render is pinned to at time zero. | |
| end_imageopt | IMAGE | Last frame of this shot. H3 accepts a keyframe at frame 0 or at the final frame and nowhere else, so this pins the end of the window this shot lands in. | |
| ref_audioopt | AUDIO | A voice or effect sample scoped to this scene -- not visible to any other shot, so put shared references in the Asset Bin instead. Scope is not timing. On 'lip_sync' the clip is cut to each window's span starting at that window's position on the FILM clock, so this socket expects a recording that runs alongside the whole film. A take recorded for this shot alone begins at the shot, not at the film, and wants a PulseVoice on the `voice` input to say so. | |
| voiceopt | PULSE_VOICE | A PulseVoice for this shot alone. Supersedes ref_audio when both are connected, and unlike ref_audio it says where on the film clock the recording starts -- set its aligns_to to 'shot_start' for a take recorded for this shot. | |
| refs.ref_image_1opt | IMAGE | Scene-local reference image 1. Numbered after the global references, and visible only to this shot. | |
| refs.ref_image_2opt | IMAGE | Scene-local reference image 2. Numbered after the global references, and visible only to this shot. | |
| refs.ref_image_3opt | IMAGE | Scene-local reference image 3. Numbered after the global references, and visible only to this shot. | |
| refs.ref_image_4opt | IMAGE | Scene-local reference image 4. Numbered after the global references, and visible only to this shot. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| shot | PULSE_SHOT | — |