Pulse Slate · MiniMax H3
The MiniMax H3 director node that stops you typing tag numbers
- model
- clip
- vae
- audio_vae
- model_fl2va
- ref_video
- ref_video_audio
- ref_music
- refs.ref_image_1
- refs.ref_image_2
- refs.ref_image_3
- refs.ref_image_4
- refs.ref_image_5
- refs.ref_image_6
- refs.ref_image_7
- refs.ref_image_8
- shots.shot_1
- shots.shot_2
- shots.shot_3
- shots.shot_4
- shots.shot_5
- shots.shot_6
- shots.shot_7
- shots.shot_8
- shots.shot_9
- shots.shot_10
- shots.shot_11
- shots.shot_12
- shots.shot_13
- shots.shot_14
- shots.shot_15
- shots.shot_16
- shots.shot_17
- shots.shot_18
- shots.shot_19
- shots.shot_20
- shots.shot_21
- shots.shot_22
- shots.shot_23
- shots.shot_24
- voices.voice_1
- voices.voice_2
- voices.voice_3
- timeline
- positive
- latent
- combined_audio
- images
- compiled_prompt
The whole point is that you never type <Picture 3> again
MiniMax H3 is the 33B omni-modal video model that ComfyUI got day-zero support for, and it's genuinely good - native stereo audio, joint generation, the works. But its reference tags are a trap. <Picture N>, <Audio j> and friends are ordinals assigned by socket position in ComfyUI's source. Insert one image at the top of your reference bin and every <Picture N> after it silently shifts by one. The render still succeeds - it just describes the wrong pictures, with no error anywhere. A reference video's soundtrack even renumbers every standalone <Audio> in your prompt before you know what happened.
Pulse Slate is the compiler node from the Pulse Studio pack that kills that whole failure class. You write @Mimi or {{mimi}} instead of a number; the node computes every ordinal from live bin order at compile time, and hand-typed tags are reported as an error rather than silently trusted. It's the difference between a tool that fights you and one that checks your work.
What it actually does: compile, don't render
Pulse Slate takes your prompts, an asset bin and (optionally) a chain of PulseShot nodes, and turns them into a PULSE_TIMELINE. It does not sample. There are two paths depending on length:
- ≤ 15 s (one window) - it hands back
positiveandlatent, and your own graph does sampler → decode → mux. This isPulseSlate_Single.json, the short path. - > 15 s (many windows) -
positiveandlatentare blocked; you take thetimelineoutput to a PulseRender node, which does the window loop, disk cache and stitching.
That blocking is a fix, not a bug: pre-3.0.0 builds handed the last window's latent back, and a still-wired sampler quietly re-rendered that one window as the whole film - a 7-second file where there should have been fifteen, no error anywhere.
The inputs that matter
The node face carries two multiline boxes plus an asset panel - nothing hidden behind a gear.
global_prompt- art style, lighting, camera rules, identity locks, score. Optional line labels:style:identity:retention:soundscape:music:; unlabelled text counts as style.shot_prompt- one shot per line, each starting with[Shot N]or a[MM:SS.mmm]timecode. Shots without a timecode spread evenly between the ones that have them. Quoted"text"becomes dialogue.- The asset bin - drag images, video or audio straight onto the node body, no loader nodes. Each drop gets a short alias you rename inline. A
descriptionpromotes a reference from a bare citation to a<Subject N>definition (that's what identity consistency is built on);retentionis written verbatim into the prompt's retention section.
Beyond those: aspect_ratio (presets fitted to H3's 1,032,192 px budget - 16:9 is 1344×768), window_seconds (each H3 call, capped at ~15.08s), partition_strategy (balanced / fill / shot_aligned), and continuity - last_frame_carry and keyframe_pairs pin real frames, which is the fl2va checkpoint's job, so they need model_fl2va wired and fail at compile time without it.
cfg stays at 1.0 - seriously
H3's reference pipeline has no negative conditioning; it runs BasicGuider, a single-conditioning path. cfg = 1.0 is the native default. Above 1.0 it switches to CFGGuider with an empty negative - offered because people asked, not because it's good. Leave it.
Install and the model haul
cd ComfyUI/custom_nodes
git clone https://github.com/Addis-Pulse-Studio/comfyui-pulse-studio
Restart ComfyUI. There's nothing to pip install - zero dependencies, just torch, numpy, Pillow and av that a working ComfyUI already has. You can also find it in ComfyUI Manager by searching "comfyui-pulse-studio". Two real requirements:
- ComfyUI 0.30.0+, carrying
comfy_extras/nodes_minimax_h3.pyandcomfy/ldm/minimax/. Older builds simply don't have H3 to direct. - The weights, not redistributed here. From Comfy-Org/MiniMax-H3: the ref2va DiT (~20 GB) and the fl2va DiT (~20 GB, only if you use anchored continuity) into
models/diffusion_models/minimax/, the qwen3vl text encoder (~15 GB) intomodels/text_encoders/minimax/, and the video + audio VAEs intomodels/vae/minimax/.
Two gotchas before you commit 40 GB of disk. On Linux and macOS the five loader widgets load red because the example graphs were authored on Windows with backslash paths - click each and re-pick the file. And the weights carry the MiniMax H3 Community License, which excludes the US, EU, UK and South Korea from its applicable territory - if you're in one of those, running the local weights isn't licensed, even though the hosted Hailuo API is.
Where people get burned
- Pre-2.0.0 workflows won't load. Widget order changed once, deliberately; those files are refused by name rather than guessed at. Rebuild from
example_workflows/PulseSlate_LongForm.json. There is no migration and won't be one. - The speed patches are wired upstream, not by this pack. The documented chain is
UNETLoader → SpectrumApplyMiniMaxH3 (history_storage: system_ram) → PatchSageAttentionKJ → Sol-Attn → PulseSlate. The node inspects what it's handed and warns - on the node face and console - when it finds no attention or offload patch, but it never blocks and never patches for you. Andsystem_ramisn't a speed setting: on a 32 GB card it's what makes a full-length window fit at all; set Spectrum tovramand expect an OOM. - Run
dry_runfirst. A wrong reference binding renders successfully and gives you a well-formed film of the wrong person; the per-shot ordinal map in the report is how you catch it before spending GPU hours.
Inputs (67)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | The ref2va (Reference) checkpoint. Read here only to detect the upstream patch chain and warn about it; this node no longer samples. Anchors and references are different checkpoints; wire the fl2va one into model_fl2va. | |
| clip | CLIP | — | |
| vae | VAE | — | |
| audio_vae | VAE | Required on both branches -- H3 always builds a joint audio+video latent, even when no reference audio is encoded. | |
| 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. |
| timeline_data | STRING | {"schema": 2, "assets": [], "cast": []} | The Asset Bin's storage: assets and cast, as JSON. Managed by the panel and never written to by the execute path. |
| global_prompt | STRING | style: identity: soundscape: music: | Art style, lighting, camera rules, character identity locks and score. Compiles into subject_definitions and retention_analysis. Optional labels at the start of a line: style: / identity: / retention: / soundscape: / music:. Unlabelled text is treated as style. Reference assets by name (@Mimi) or id ({{mimi}}) -- never by number. Ordinals are assigned from bin order at compile time. |
| shot_prompt | STRING | [Shot 1] [Shot 2] At 00:05.000, | Timecoded shots, one per line. Begin each with [Shot N] or a [MM:SS.mmm] timecode. IGNORED whenever any PulseShot node is connected -- the two are never merged. See spec §5. Quoted "text" becomes dialogue. @Name references the Asset Bin. |
| duration_seconds | FLOAT | 10.00.2–600 | Total length of the finished video. Snapped up to the 17k+5 frame grid, and split into windows if longer than window_seconds. Ignored when PulseShot nodes are connected -- their durations define the length. |
| aspect_ratio | COMBO | 16:9 landscape | Preset canvases fitted into H3's 1,032,192px budget. Choose 'custom' to use the width and height widgets instead. |
| width | INT | 134432–4096 | Used only when aspect_ratio is 'custom'. |
| height | INT | 76832–4096 | Used only when aspect_ratio is 'custom'. |
| steps | INT | 201–100 | — |
| sampler_name | COMBO | res_multistep | 6 options: res_multistep, euler, euler_ancestral, dpmpp_2m, dpmpp_2m_sde, ddim |
| scheduler | COMBO | simple | 6 options: simple, normal, beta, sgm_uniform, karras, exponential |
| cfg | FLOAT | 1.01–20 | 1.0 uses BasicGuider, which is H3's own native path -- its reference pipeline has no negative conditioning anywhere. Above 1.0 switches to CFGGuider with an empty negative prompt. Leave at 1.0 unless you are deliberately experimenting. |
| seed | INT | 00–18446744073709550000 | The base seed. Each window's actual seed is derived from this and from the set of shots in that window, so inserting a shot does not reroll the windows that did not change. |
| partition_strategy | COMBO | balanced | How a long timeline is split. 'balanced' spreads it into near-equal windows, avoiding a short trailing window below H3's 124-frame trained floor. 'fill' packs full windows and merges any short tail backwards. 'shot_aligned' puts window seams on shot boundaries wherever the 17k+5 grid allows it, so fewer shots are compiled into two windows -- it reports which cuts it could not reach, and it gives up window_seconds to do it. |
| window_seconds | FLOAT | 15.05.2–15.1 | Length of each individual H3 call. The trained ceiling is ~15.08s (362 frames) and the floor is ~5.17s (124 frames). |
| resize_method | COMBO | crop | How a reference image whose aspect does not match the canvas is fitted. 'pad' letterboxes, which makes the bars themselves reference content. |
| carry_mode | COMBO | image | What the previous window contributes to the next as a REFERENCE on the ref2va branch. Distinct from `continuity`, which chooses whether a frame is pinned as a keyframe. |
| carry_audio | BOOLEAN | true | Feed the previous window's audio tail forward. Without it each window invents its own score and the seam is audible. |
| carry_audio_seconds | FLOAT | 4.00.5–15 | — |
| ref_image_size | COMBO | match | 'match' scales references to the render's pixel area (faster). 'max' uses a 2048px short edge for stronger identity, but reference tokens ride every sampling step, so it is several times slower. |
| shift_video | FLOAT | 12.000.01–100 | — |
| shift_audio | FLOAT | 3.000.01–100 | — |
| audio_ref_ceiling | INT | 33–9 | How many standalone audio references the bin may hold. 3 is what MiniMax documents and what ComfyUI's ref_audios socket declares. Above 3 goes past both. Raise it to test, not to set and forget. |
| continuity | COMBO | none | How windows join. 'none' samples each independently. 'last_frame_carry' pins the previous window's final frame as the next one's first frame. 'keyframe_pairs' pins each shot's start and end frames. The last two need model_fl2va and fail at compile time without it -- they do not fall back silently. |
| model_fl2vaopt | MODEL | The First/Last-Frame checkpoint. Required by the 'last_frame_carry' and 'keyframe_pairs' continuity modes. Loading both checkpoints is ~42GB. | |
| ref_videoopt | IMAGE | A reference video as frames, from an upstream loader. | |
| ref_video_audioopt | AUDIO | Soundtrack of ref_video. Claims an <Audio N> ordinal ahead of every standalone audio reference. | |
| ref_musicopt | AUDIO | Non-diegetic score. Always the last audio ordinal. | |
| refs.ref_image_1opt | IMAGE | Global reference image 1, visible to every shot. | |
| refs.ref_image_2opt | IMAGE | Global reference image 2, visible to every shot. | |
| refs.ref_image_3opt | IMAGE | Global reference image 3, visible to every shot. | |
| refs.ref_image_4opt | IMAGE | Global reference image 4, visible to every shot. | |
| refs.ref_image_5opt | IMAGE | Global reference image 5, visible to every shot. | |
| refs.ref_image_6opt | IMAGE | Global reference image 6, visible to every shot. | |
| refs.ref_image_7opt | IMAGE | Global reference image 7, visible to every shot. | |
| refs.ref_image_8opt | IMAGE | Global reference image 8, visible to every shot. | |
| shots.shot_1opt | PULSE_SHOT | Shot 1. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_2opt | PULSE_SHOT | Shot 2. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_3opt | PULSE_SHOT | Shot 3. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_4opt | PULSE_SHOT | Shot 4. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_5opt | PULSE_SHOT | Shot 5. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_6opt | PULSE_SHOT | Shot 6. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_7opt | PULSE_SHOT | Shot 7. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_8opt | PULSE_SHOT | Shot 8. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_9opt | PULSE_SHOT | Shot 9. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_10opt | PULSE_SHOT | Shot 10. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_11opt | PULSE_SHOT | Shot 11. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_12opt | PULSE_SHOT | Shot 12. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_13opt | PULSE_SHOT | Shot 13. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_14opt | PULSE_SHOT | Shot 14. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_15opt | PULSE_SHOT | Shot 15. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_16opt | PULSE_SHOT | Shot 16. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_17opt | PULSE_SHOT | Shot 17. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_18opt | PULSE_SHOT | Shot 18. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_19opt | PULSE_SHOT | Shot 19. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_20opt | PULSE_SHOT | Shot 20. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_21opt | PULSE_SHOT | Shot 21. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_22opt | PULSE_SHOT | Shot 22. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_23opt | PULSE_SHOT | Shot 23. Connecting any shot socket makes the shot text box inactive. | |
| shots.shot_24opt | PULSE_SHOT | Shot 24. Connecting any shot socket makes the shot text box inactive. | |
| voices.voice_1opt | PULSE_VOICE | Global voice 1, visible to every window -- one wire instead of one per shot. Numbered ahead of ref_music, which is always the last audio ordinal. | |
| voices.voice_2opt | PULSE_VOICE | Global voice 2, visible to every window -- one wire instead of one per shot. Numbered ahead of ref_music, which is always the last audio ordinal. | |
| voices.voice_3opt | PULSE_VOICE | Global voice 3, visible to every window -- one wire instead of one per shot. Numbered ahead of ref_music, which is always the last audio ordinal. |
Outputs (6)
| Name | Type | Description |
|---|---|---|
| timeline | PULSE_TIMELINE | — |
| positive | CONDITIONING | — |
| latent | LATENT | — |
| combined_audio | AUDIO | — |
| images | IMAGE | — |
| compiled_prompt | STRING | — |