lasdkjf larryvrh commited on
Commit
cd6422a
·
0 Parent(s):

Duplicate from larryvrh/MiniMax-H3-Turbo-Lora

Browse files

Co-authored-by: larryvrh <larryvrh@users.noreply.huggingface.co>

.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ base_model: Comfy-Org/MiniMax-H3
4
+ tags:
5
+ - text-to-video
6
+ - text-to-audio
7
+ - audio-video
8
+ - lora
9
+ - minimax-h3
10
+ - comfyui
11
+ pipeline_tag: text-to-video
12
+ ---
13
+
14
+ # MiniMax-H3 Turbo LoRA — few-step audio-video generation
15
+
16
+ A LoRA for [MiniMax-H3](https://huggingface.co/Comfy-Org/MiniMax-H3) that renders
17
+ joint **video + synchronized stereo audio** in as few as **4 sampling steps**
18
+ instead of the usual ~20 — a ~5× sampling speedup — and keeps getting better as
19
+ you add steps.
20
+
21
+ ## Which checkpoint — `v4` (step 600) or `v1` (850)?
22
+
23
+ For **most** work, use **`minimax_h3_turbo_v4_step600_ema.safetensors`**. It's the
24
+ strongest checkpoint we've released: much better static and small-motion shots,
25
+ markedly better micro-detail (faces, fingers, fine texture), and the
26
+ over-sharpening / plastic look of the earlier `v1` (~850) line is **fully
27
+ resolved**.
28
+
29
+ v4 introduced a **static-frame enhancement** — a big win for static and
30
+ small-motion content. The one trade-off shows up **only at 4 steps with large,
31
+ fast motion**, where v4 can produce **motion-smear / trailing ghosting** (we're
32
+ actively fixing this). Two things address it:
33
+
34
+ - **Use 6–8 steps.** This **largely removes the smear** and is where v4 looks its
35
+ best. v4 also tolerates higher step counts better than v1, which tends to
36
+ over-sharpen at high steps + strength 1.0.
37
+ - For the specific case of **4 steps *and* heavy motion**, the older **`v1` ~850**
38
+ checkpoint can still be the friendlier pick.
39
+
40
+ ```
41
+ Using 6–8 steps? ── yes ──► v4-600 (recommended)
42
+ │ no (4 steps)
43
+
44
+ Heavy / fast motion? ── no ──► v4-600 (recommended)
45
+ │ yes
46
+
47
+ v1-850 (friendlier at 4-step heavy motion)
48
+ ```
49
+
50
+ Still a preview — training continues; the two areas still being improved are
51
+ **audio** and **behaviour under fast, intense motion**.
52
+
53
+ ## Steps and strength — read this
54
+
55
+ - **4 steps is the recommended *minimum*; 4–8 is the useful range.** 6–8 steps
56
+ look noticeably better than 4, so add steps if you can afford them. Past **8
57
+ steps** it stops helping and can start to introduce **over-sharp artifacts** —
58
+ there's no benefit to going higher, so stay in **4–8**.
59
+ - **Keep strength at `1.0`.** It's tuned for 1.0 and holds up well across the 4–8
60
+ step range. Only reach for the strength dial if a *specific* clip misbehaves —
61
+ then **blurry ghosting / smear → nudge up** (`~1.05–1.2`), **over-sharp grain →
62
+ nudge down** (`~0.8–0.95`).
63
+ - Keep the scheduler on `simple`.
64
+
65
+ ## Use it in ComfyUI (recommended)
66
+
67
+ Custom nodes: **[Larryvrh/ComfyUI-MiniMax-H3-Turbo](https://github.com/Larryvrh/ComfyUI-MiniMax-H3-Turbo)**
68
+ — or search **"MiniMax-H3 Turbo"** in ComfyUI-Manager. (Keep the node updated; it
69
+ evolves alongside these weights.)
70
+
71
+ 1. Install the nodes (Manager, or `git clone` into `ComfyUI/custom_nodes`) and put
72
+ a `.safetensors` from this repo into `ComfyUI/models/loras/`. You also need the
73
+ base MiniMax-H3 model, VAEs and text encoder — see the
74
+ [MiniMax-H3 tutorial](https://docs.comfy.org/tutorials/video/minimax/minimax-h3).
75
+ 2. Start from the official MiniMax-H3 workflow (t2v or i2v) and make two changes:
76
+ - insert **MiniMax-H3 Turbo LoRA** between the model loader and the sampler;
77
+ - feed `SamplerCustomAdvanced` from **MiniMax-H3 Turbo Sampler**, and set the
78
+ scheduler to `simple` at **≥ 4 steps**.
79
+
80
+ Everything else stays as in the official graph, so both text-to-video and
81
+ image-to-video work. A ready-made t2v workflow ships in the
82
+ [node repo](https://github.com/Larryvrh/ComfyUI-MiniMax-H3-Turbo/tree/main/example_workflows)
83
+ (and here as `minimax_h3_t2v_turbo.json`) — drag it in.
84
+
85
+ - **Base model**: any MiniMax-H3 base — full (`bf16`, `int8_convrot`) **and the
86
+ pruned/curve variants** (`pruned_int8`, `pruned_fp8`). The node auto-detects a
87
+ pruned base and re-injects the time-conditioning at run time, so **one LoRA file
88
+ covers every base**.
89
+ - **`low_vram`** switch: **off** applies the LoRA at run time (sharpest,
90
+ recommended); **on** merges it into the weights for the lowest peak VRAM (a bit
91
+ softer on quantized bases). Turn it on only if you run out of memory.
92
+ - The custom sampler **auto-adapts to your ComfyUI version**: MiniMax-H3 runs
93
+ video and audio on two different flow schedules; recent ComfyUI handles that
94
+ natively (`ModelSamplingAV`) and older ComfyUI doesn't — the Turbo Sampler
95
+ detects which and does the right thing either way, so nothing to change when you
96
+ update ComfyUI.
97
+
98
+ ## Weights
99
+
100
+ All bf16, ~744 MB, applied as a plain low-rank update
101
+ (`W_eff = W + lora_B @ lora_A`, alpha = rank, so no extra scaling). **Prefer the
102
+ EMA files**; the non-EMA ones are for comparison.
103
+
104
+ | file | notes |
105
+ |---|---|
106
+ | **`minimax_h3_turbo_v4_step600_ema.safetensors`** | **recommended — current best.** Strong static/small-motion, good micro-detail, no over-sharpening. |
107
+ | `minimax_h3_turbo_v4_step600.safetensors` | v4-600 non-EMA (comparison). |
108
+ | `minimax_h3_turbo_v4_step150_ema.safetensors` | earlier v4 checkpoint. |
109
+ | `minimax_h3_turbo_4step_ema_ckpt850.safetensors` | `v1` line (~850) — over-sharpened / plastic in general, but the friendlier pick for **4-step heavy motion** (see above). |
110
+ | `minimax_h3_turbo_4step_ema_ckpt500.safetensors` | older `v1` (~500), softer. |
111
+ | `minimax_h3_turbo_4step_ema.safetensors` | initial release (~200). |
112
+
113
+ *Naming:* `v4` is the current training recipe and `stepN` is the training step.
114
+ Older files carry the previous `4step_ckptN` naming, where `4step` referred to the
115
+ sampler-step count.
116
+
117
+ ## Standalone (no ComfyUI graph)
118
+
119
+ `generate.py` is a single self-contained file — it loads the base DiT + a LoRA,
120
+ encodes the prompt, runs the few-step dual-schedule sampler, decodes and muxes an
121
+ mp4. It still needs a ComfyUI checkout for the H3 model / VAE / text-encoder
122
+ definitions:
123
+
124
+ ```bash
125
+ git clone https://github.com/comfyanonymous/ComfyUI
126
+ cd ComfyUI && pip install -r requirements.txt && cd ..
127
+ pip install -r requirements.txt # this repo: torch, safetensors, imageio-ffmpeg
128
+
129
+ # base weights from Comfy-Org/MiniMax-H3 into a models/ tree, then:
130
+ python generate.py \
131
+ --comfyui ./ComfyUI \
132
+ --base models/diffusion_models/minimax_h3_fl2va_bf16.safetensors \
133
+ --lora minimax_h3_turbo_v4_step600_ema.safetensors \
134
+ --te models/text_encoders/qwen3vl_32b_minimax_h3_int8_convrot.safetensors \
135
+ --video-vae models/vae/minimax_h3_video_vae_fp16.safetensors \
136
+ --audio-vae models/vae/minimax_h3_audio_vae_fp32.safetensors \
137
+ --prompt "A corgi in a chef hat flipping a pancake, sizzling sounds and a cheerful bark." \
138
+ --width 1344 --height 768 --frames 124 --steps 6 --out corgi.mp4
139
+ ```
140
+
141
+ ## Notes
142
+
143
+ - **Resolution / duration**: width and height are multiples of 32 (short edge
144
+ typically 768). Frame count is at 24 fps and snaps to the model's 17·k+5 grid
145
+ (124 ≈ 5 s). Validated range ~124–362 frames (~5–15 s).
146
+ - **VRAM**: the base model is large (~33 B); an 80 GB GPU is comfortable at the
147
+ largest resolutions. The ComfyUI node streams the base and adds the `low_vram`
148
+ switch, so it runs on much smaller GPUs. In the standalone script,
149
+ `--offload-adaln` trades ~13 GB of VRAM for CPU RAM.
150
+ - **Audio**: 32 kHz stereo, aligned to the video; the two streams ride different
151
+ flow schedules and are integrated each on its own clock. (Audio is one of the
152
+ two areas still being improved — see the top.)
experimental_step_149.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6e6684c0c041c70e00c86d2da7d19b32386e78f7a5287a76ace842974ef49bc9
3
+ size 10918430752
experimental_step_490.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e95764286503eaf4998da4c8b489447c5c2f90da5425380023dbae87fa7b1cf2
3
+ size 10918430752
experimental_step_729.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4523eccc68fe474c76494f19844c37e2d02859d078ecae39a086bcb943824ee3
3
+ size 10918440992
experimental_step_850.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8c07df0e2c4df7121096da3ddb1209c4e07b70e24c94f313a4cf0c2d39f22234
3
+ size 10918440992
experimental_step_922.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7026879187f3e29a1c0107df74d1f1d6e7e9dae802f6d73febaada4553698d53
3
+ size 10918440992
experimental_v2_step_298.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:02091565e94ac7618ccf1ef5c2dafe83e485e6d7717b2ef8028e5891924c32ac
3
+ size 7798814752
experimental_v3_step_300.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:afb009bf49588ee5aa9250d1c64f2630934bec193d2d2fc136f93b41c278ac25
3
+ size 10918430752
experimental_v4_step_150.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:78487982233383dd91e8cae158f6d64ac68580835733a4262e8bfa97b4fabdca
3
+ size 10918430752
experimental_v4_step_600.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5717f204f819505795de519467e20dd23948b5da3ba97732795094fb566a6aa2
3
+ size 10918430752
experimental_v5_step_600.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a57e51bce769d8d3934c0e90926e18f84fa915dd08f9981864d8bafceb4a3551
3
+ size 10918430752
generate.py ADDED
@@ -0,0 +1,517 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """MiniMax-H3 Turbo LoRA — 4-step text-to-audio-video generation.
2
+
3
+ A lightweight LoRA that lets MiniMax-H3 render joint video + stereo audio in
4
+ **4 sampling steps** instead of the usual ~20, at a fraction of the wall-clock
5
+ cost. This single file is a self-contained generator: it loads the base H3 DiT
6
+ plus this LoRA, encodes the prompt with the Qwen3-VL text encoder, runs the
7
+ model's native dual-schedule sampler for 4 steps, decodes both streams and muxes
8
+ a playable mp4.
9
+
10
+ The audio stream runs on its own shifted flow schedule (video shift 12, audio
11
+ shift 3); each stream is integrated on its own clock, which is the schedule
12
+ semantics MiniMax-H3 was designed around. That is the only non-obvious part of
13
+ sampling — everything else is a plain Euler flow sampler.
14
+
15
+ Dependencies (see requirements.txt), plus a ComfyUI checkout for the H3 model /
16
+ VAE / text-encoder module definitions:
17
+
18
+ git clone https://github.com/comfyanonymous/ComfyUI
19
+ cd ComfyUI && git checkout 14b05228cef127ce529bc0c08660770d4af3e9a8
20
+
21
+ Base weights come from the official MiniMax-H3 release
22
+ (Comfy-Org/MiniMax-H3 on the Hugging Face Hub): the bf16 DiT, the int8 Qwen3-VL
23
+ text encoder, and the video + audio VAEs.
24
+
25
+ Usage:
26
+ python generate.py \
27
+ --comfyui /path/to/ComfyUI \
28
+ --base models/diffusion_models/minimax_h3_fl2va_bf16.safetensors \
29
+ --lora minimax_h3_turbo_4step.safetensors \
30
+ --te models/text_encoders/qwen3vl_32b_minimax_h3_int8_convrot.safetensors \
31
+ --video-vae models/vae/minimax_h3_video_vae_fp16.safetensors \
32
+ --audio-vae models/vae/minimax_h3_audio_vae_fp32.safetensors \
33
+ --prompt "A corgi in a tiny chef hat flipping a pancake, sizzling sounds." \
34
+ --width 1344 --height 768 --frames 124 --out corgi.mp4
35
+
36
+ `minimax_h3_turbo_4step.safetensors` is the trained LoRA; the accompanying
37
+ `minimax_h3_turbo_4step_ema.safetensors` is a time-averaged variant — try both,
38
+ the trained one tends to be crisper on fast motion, the averaged one smoother.
39
+ """
40
+
41
+ import argparse
42
+ import math
43
+ import os
44
+ import subprocess
45
+ import sys
46
+ import time
47
+ import wave
48
+
49
+ import torch
50
+ import torch.nn.functional as F
51
+
52
+
53
+ def log(msg):
54
+ print(f"[{time.strftime('%H:%M:%S')}] {msg}", flush=True)
55
+
56
+
57
+ # ======================================================================
58
+ # Flow schedule (video shift 12 / audio shift 3, closed-form dual mapping)
59
+ # ======================================================================
60
+ SHIFT_VIDEO = 12.0
61
+ SHIFT_AUDIO = 3.0
62
+
63
+
64
+ def shift_sigma(u, shift):
65
+ return shift * u / (1.0 + (shift - 1.0) * u)
66
+
67
+
68
+ def time_shift_sigma(sigma, from_shift, to_shift):
69
+ base = sigma / (from_shift + sigma * (1.0 - from_shift))
70
+ return to_shift * base / (1.0 + (to_shift - 1.0) * base)
71
+
72
+
73
+ def time_shift_slope(sigma, from_shift, to_shift):
74
+ base = sigma / (from_shift + sigma * (1.0 - from_shift))
75
+ return (to_shift * (1.0 + (from_shift - 1.0) * base) ** 2) / (
76
+ from_shift * (1.0 + (to_shift - 1.0) * base) ** 2)
77
+
78
+
79
+ def timesteps(n, shift=SHIFT_VIDEO):
80
+ """n-step video sigma grid: ts[0]=1 (pure noise) > ... > ts[n]=0."""
81
+ return [shift_sigma(1.0 - i / n, shift) for i in range(n + 1)]
82
+
83
+
84
+ def audio_sigma(sigma_v):
85
+ return time_shift_sigma(sigma_v, SHIFT_VIDEO, SHIFT_AUDIO)
86
+
87
+
88
+ def audio_slope(sigma_v):
89
+ return time_shift_slope(sigma_v, SHIFT_VIDEO, SHIFT_AUDIO)
90
+
91
+
92
+ @torch.no_grad()
93
+ def sample(vfn, xv, xa, ts):
94
+ """4-step Euler on the joint flow. The model returns the audio velocity
95
+ already scaled by d(sigma_a)/d(sigma_v), so video steps on its own sigma
96
+ delta while audio steps on its own schedule's delta (recovering the raw
97
+ audio velocity by dividing out the slope). This dual-clock stepping is the
98
+ schedule MiniMax-H3 expects; a single flat step on the video clock would
99
+ over/under-shoot the audio stream badly at 4 steps.
100
+ """
101
+ for i in range(len(ts) - 1):
102
+ ov, oa = vfn(xv, xa, ts[i])
103
+ hv = ts[i + 1] - ts[i]
104
+ sl = audio_slope(max(ts[i], 1e-6))
105
+ ha = audio_sigma(ts[i + 1]) - audio_sigma(ts[i])
106
+ xv = xv + hv * ov
107
+ xa = xa + ha * (oa / sl)
108
+ return xv, xa
109
+
110
+
111
+ # ======================================================================
112
+ # Functional forward (out-of-place, mirrors the reference module math)
113
+ # ======================================================================
114
+ def _rms(x, weight, eps):
115
+ return F.rms_norm(x, (x.shape[-1],), weight, eps)
116
+
117
+
118
+ def _attn(attn, x, rope_cos, rope_sin):
119
+ s = x.shape[0]
120
+ heads, hd = attn.heads, attn.head_dim
121
+ q, k, v = attn.qkv_proj(x).split(heads * hd, dim=-1)
122
+ q = _rms(q.view(s, heads, hd), attn.q_norm.weight, attn.q_norm.eps)
123
+ k = _rms(k.view(s, heads, hd), attn.k_norm.weight, attn.k_norm.eps)
124
+ v = v.view(s, heads, hd)
125
+ if rope_cos is not None:
126
+ c, si = rope_cos[:, None, :], rope_sin[:, None, :]
127
+
128
+ def rot(t):
129
+ t96 = t[..., :96].float()
130
+ x1, x2 = t96[..., :48], t96[..., 48:]
131
+ return torch.cat([(x1 * c - x2 * si).to(t.dtype),
132
+ (x1 * si + x2 * c).to(t.dtype),
133
+ t[..., 96:]], dim=-1)
134
+
135
+ q, k = rot(q), rot(k)
136
+ q, k, v = (t.transpose(0, 1).unsqueeze(0) for t in (q, k, v))
137
+ out = F.scaled_dot_product_attention(q, k, v)
138
+ return attn.out_proj(out.squeeze(0).transpose(0, 1).reshape(s, heads * hd))
139
+
140
+
141
+ def _mlp(mlp, x):
142
+ x1, x2 = mlp.fc1(x).chunk(2, dim=-1)
143
+ return mlp.fc2(F.silu(x1) * x2)
144
+
145
+
146
+ def _refiner(refiner, x):
147
+ for blk in refiner.blocks:
148
+ x = x + _attn(blk.attn, _rms(x, blk.norm1.weight, blk.norm1.eps),
149
+ None, None)
150
+ x = x + _mlp(blk.mlp, _rms(x, blk.norm2.weight, blk.norm2.eps))
151
+ return _rms(x, refiner.final_norm.weight, refiner.final_norm.eps)
152
+
153
+
154
+ def _apply_mod(h, shift, scale, segments):
155
+ parts = []
156
+ for a, b, row in segments:
157
+ parts.append(h[a:b] * (1.0 + scale[row].to(h.dtype)) + shift[row].to(h.dtype))
158
+ return torch.cat(parts)
159
+
160
+
161
+ def _apply_gate(x, gate, other, segments):
162
+ parts = []
163
+ for a, b, row in segments:
164
+ parts.append(x[a:b] + other[a:b] * gate[row].to(x.dtype))
165
+ return torch.cat(parts)
166
+
167
+
168
+ def _block(blk, h, mods, segments, rope_cos, rope_sin):
169
+ sh_msa, sc_msa, g_msa, sh_mlp, sc_mlp, g_mlp = mods.unbind(dim=1)
170
+ hn = _apply_mod(_rms(h, blk.norm1.weight, blk.norm1.eps), sh_msa, sc_msa, segments)
171
+ h = _apply_gate(h, g_msa, _attn(blk.attn, hn, rope_cos, rope_sin), segments)
172
+ hn = _apply_mod(_rms(h, blk.norm2.weight, blk.norm2.eps), sh_mlp, sc_mlp, segments)
173
+ return _apply_gate(h, g_mlp, _mlp(blk.mlp, hn), segments)
174
+
175
+
176
+ class LoRALinear(torch.nn.Module):
177
+ """Applies the low-rank update at run time in activation space:
178
+ y = base(x) + B(A(x)). Folding it into the (bf16) base weight instead would
179
+ round most of the update away when it is small relative to the weight, so we
180
+ keep it as a separate matmul — same as how the update is meant to act."""
181
+
182
+ def __init__(self, base, a, b):
183
+ super().__init__()
184
+ self.base = base
185
+ self.a, self.b = a, b # [rank, in], [out, rank]; alpha == rank -> scale 1
186
+
187
+ def forward(self, x):
188
+ return self.base(x) + F.linear(F.linear(x, self.a), self.b)
189
+
190
+
191
+ # ======================================================================
192
+ # Model load + LoRA (applied at run time, not merged)
193
+ # ======================================================================
194
+ def load_model(comfyui, base_path, lora_path, device, offload_adaln):
195
+ import comfy.ldm.minimax.model as h3ref
196
+ import comfy.ops
197
+ import comfy.utils
198
+ from safetensors.torch import load_file
199
+
200
+ log(f"loading base DiT: {base_path}")
201
+ sd = comfy.utils.load_torch_file(base_path)
202
+ model = h3ref.MiniMaxH3Model(dtype=torch.bfloat16, device="cpu",
203
+ operations=comfy.ops.disable_weight_init)
204
+ missing, unexpected = model.load_state_dict(sd, strict=True, assign=True)
205
+ assert not missing and not unexpected, (missing[:3], unexpected[:3])
206
+ model.requires_grad_(False)
207
+ model.eval()
208
+
209
+ for i, blk in enumerate(model.blocks):
210
+ blk.to(device)
211
+ for mod in (model.token_refiner, model.final_layer, model.condition_proj,
212
+ model.video_patch_proj, model.audio_patch_proj,
213
+ model.time_embedder, model.rope):
214
+ mod.to(device)
215
+
216
+ log(f"applying LoRA: {lora_path}")
217
+ lora = load_file(lora_path)
218
+ names = sorted({k.rsplit(".lora_", 1)[0] for k in lora})
219
+ # adaLN projections are read weight-first (bypassing their module), so their
220
+ # LoRA can't ride a wrapper — stash it and add the delta where adaLN is built.
221
+ model._adaln_lora = {} # block index -> (a, b)
222
+ model._final_adaln_lora = None
223
+ n_wrap = 0
224
+ for name in names:
225
+ a = lora[name + ".lora_A.weight"].to(device, torch.bfloat16)
226
+ b = lora[name + ".lora_B.weight"].to(device, torch.bfloat16)
227
+ if name.endswith("adaln_proj.linear"):
228
+ if name.startswith("final_layer"):
229
+ model._final_adaln_lora = (a, b)
230
+ else:
231
+ model._adaln_lora[int(name.split(".")[1])] = (a, b)
232
+ else:
233
+ parent = model.get_submodule(name.rsplit(".", 1)[0])
234
+ setattr(parent, name.rsplit(".", 1)[1],
235
+ LoRALinear(model.get_submodule(name), a, b))
236
+ n_wrap += 1
237
+ log(f"LoRA: {n_wrap} wrapped + {len(model._adaln_lora)} adaLN "
238
+ f"+ {1 if model._final_adaln_lora else 0} final")
239
+
240
+ if offload_adaln:
241
+ # The per-layer adaLN projection is huge (2688 -> 96768) but depends only
242
+ # on the timestep, of which there are a handful per denoise. Keep it in
243
+ # CPU fp32 to save ~13 GB of VRAM; the matmul is cheap at 4 steps.
244
+ for blk in model.blocks:
245
+ lin = blk.adaln_proj.linear
246
+ lin.weight.data = lin.weight.data.float().cpu()
247
+ lin.bias.data = lin.bias.data.float().cpu()
248
+ return model, h3ref
249
+
250
+
251
+ VISUAL_COND_T = 0.999
252
+
253
+
254
+ def timestep_rows(model, sigma_v):
255
+ sigma_v = float(max(sigma_v, 1e-6))
256
+ t_v = 1.0 - sigma_v
257
+ t_a = 1.0 - time_shift_sigma(sigma_v, model.sigma_shift_video,
258
+ model.sigma_shift_audio)
259
+ seg_t = {"text": t_v, "video": t_v, "audio": t_a}
260
+ unique_t = sorted({t_v, t_a})
261
+ return seg_t, unique_t, {t: i for i, t in enumerate(unique_t)}
262
+
263
+
264
+ def adaln_mods(model, unique_t, device, offload, cache):
265
+ key = tuple(round(t, 9) for t in unique_t)
266
+ if key in cache:
267
+ return cache[key]
268
+ ts = torch.tensor(unique_t, dtype=torch.float32, device=device)
269
+ with torch.no_grad():
270
+ temb = model.time_embedder(ts).float() # [M, 2688] GPU
271
+ si = F.silu(temb)
272
+ si_base = si.cpu() if offload else si.to(torch.bfloat16)
273
+ outs = torch.stack([F.linear(si_base, b.adaln_proj.linear.weight,
274
+ b.adaln_proj.linear.bias)
275
+ for b in model.blocks]) # [50, M, 96768]
276
+ mods = outs.to(device, torch.bfloat16)
277
+ if getattr(model, "_adaln_lora", None):
278
+ # run-time low-rank delta, on GPU (base built in CPU fp32 under offload)
279
+ si_g = si.to(torch.bfloat16)
280
+ for idx, (a, b) in model._adaln_lora.items():
281
+ mods[idx] = mods[idx] + F.linear(F.linear(si_g, a), b)
282
+ M, H = len(unique_t), model.hidden_size
283
+ mods = mods.view(len(model.blocks), M, 3, 6, H).reshape(
284
+ len(model.blocks), M * 3, 6, H)
285
+ temb_bf = temb.to(torch.bfloat16)
286
+ cache[key] = (mods, temb_bf)
287
+ return mods, temb_bf
288
+
289
+
290
+ class Prepared:
291
+ """Static packed-sequence structure for one (text_len, shape) signature."""
292
+
293
+ def __init__(self, model, h3ref, text_len, video_shape, audio_t, tags,
294
+ device):
295
+ _, _, lt, lh, lw = video_shape
296
+ self.video_shape = tuple(video_shape)
297
+ self.lat_pad = ((lh + 1) // 2 * 2, (lw + 1) // 2 * 2)
298
+ self.layout = h3ref.PackedLayout(text_len, lt, *self.lat_pad, audio_t)
299
+ pos = self.layout.position_ids.to(torch.float32).to(device)
300
+ inv = model.rope.inv_freq.to(device)
301
+ ang = (pos.unsqueeze(-1) * inv.view(1, 1, -1)).flatten(1)
302
+ self.rope_cos, self.rope_sin = torch.cos(ang), torch.sin(ang)
303
+
304
+ segs = []
305
+ for a, b, kind in self.layout.segments:
306
+ if kind == "text" and tags is not None:
307
+ tg = tags.view(-1).tolist()
308
+ run = 0
309
+ for i in range(1, b - a + 1):
310
+ if i == b - a or tg[i] != tg[run]:
311
+ segs.append((a + run, a + i, int(tg[run]), kind))
312
+ run = i
313
+ else:
314
+ tag = {"text": 1, "video": 0, "audio": 2}[kind]
315
+ segs.append((a, b, tag, kind))
316
+ self.seg_template = segs
317
+ (self.video_seg,) = [(a, b) for a, b, k in self.layout.segments if k == "video"]
318
+ (self.audio_seg,) = [(a, b) for a, b, k in self.layout.segments if k == "audio"]
319
+
320
+
321
+ @torch.no_grad()
322
+ def forward(model, h3ref, prep, video_x, audio_x, sigma_v, context, device,
323
+ offload, cache):
324
+ """One denoise evaluation in the sigma_v domain. Returns
325
+ (video_velocity, audio_velocity * slope), matching what the sampler wants."""
326
+ import comfy.ldm.common_dit
327
+ video_x = comfy.ldm.common_dit.pad_to_patch_size(video_x, model.patch_size)
328
+ orig_t, orig_h, orig_w = prep.video_shape[2:]
329
+
330
+ sigma_v = float(max(sigma_v, 1e-6))
331
+ seg_t, unique_t, t_row = timestep_rows(model, sigma_v)
332
+ segments = [(a, b, t_row[seg_t[k]] * 3 + tag)
333
+ for a, b, tag, k in prep.seg_template]
334
+
335
+ base_mods, t_emb = adaln_mods(model, unique_t, device, offload, cache)
336
+ silu_temb = F.silu(t_emb)
337
+
338
+ video_rows = h3ref.patchify_video(video_x.to(torch.float32), model.patch_size)
339
+ audio_rows = h3ref.pack_audio(audio_x.to(torch.float32))
340
+ video_embed = model.video_patch_proj(video_rows).to(torch.bfloat16)
341
+ audio_embed = model.audio_patch_proj(audio_rows).to(torch.bfloat16)
342
+
343
+ with torch.autocast("cuda", dtype=torch.bfloat16):
344
+ text_states = context[0]
345
+ if text_states.shape[-1] != model.hidden_size:
346
+ text_states = _refiner(model.token_refiner,
347
+ model.condition_proj(text_states))
348
+ pieces = []
349
+ for a, b, kind in prep.layout.segments:
350
+ if kind == "text":
351
+ pieces.append(text_states)
352
+ elif kind == "video":
353
+ pieces.append(video_embed)
354
+ else:
355
+ pieces.append(audio_embed)
356
+ h = torch.cat(pieces)
357
+ for i, blk in enumerate(model.blocks):
358
+ h = _block(blk, h, base_mods[i], segments,
359
+ prep.rope_cos, prep.rope_sin)
360
+
361
+ fl = model.final_layer
362
+ with torch.autocast("cuda", dtype=torch.bfloat16):
363
+ si_t = F.silu(t_emb)
364
+ f_mod = fl.adaln_proj.linear(si_t)
365
+ if getattr(model, "_final_adaln_lora", None):
366
+ a, b = model._final_adaln_lora
367
+ f_mod = f_mod + F.linear(F.linear(si_t, a), b)
368
+ f_shift, f_scale = f_mod.view(len(unique_t), 2, model.hidden_size).unbind(1)
369
+ (va, vb), (aa, ab) = prep.video_seg, prep.audio_seg
370
+ vrow, arow = t_row[seg_t["video"]], t_row[seg_t["audio"]]
371
+ hn = _rms(h, fl.norm.weight, fl.norm.eps)
372
+ hv = (hn[va:vb] * (1.0 + f_scale[vrow]) + f_shift[vrow]).to(torch.float32)
373
+ ha = (hn[aa:ab] * (1.0 + f_scale[arow]) + f_shift[arow]).to(torch.float32)
374
+ v_rows, a_rows = fl.video_out(hv), fl.audio_out(ha)
375
+
376
+ lt = video_x.shape[2]
377
+ video_out = h3ref.unpatchify_video(v_rows, lt, prep.lat_pad[0] // 2,
378
+ prep.lat_pad[1] // 2, model.latents_dim,
379
+ model.patch_size)[:, :, :orig_t, :orig_h, :orig_w]
380
+ audio_out = h3ref.unpack_audio(a_rows)
381
+ slope_a = time_shift_slope(sigma_v, model.sigma_shift_video,
382
+ model.sigma_shift_audio)
383
+ return -video_out.to(video_x.dtype), (-slope_a) * audio_out.to(audio_x.dtype)
384
+
385
+
386
+ # ======================================================================
387
+ # Text encode / decode / mux
388
+ # ======================================================================
389
+ def encode_prompt(comfyui, te_path, prompt, device):
390
+ import comfy.model_management
391
+ import comfy.sd
392
+ log(f"loading text encoder: {te_path}")
393
+ clip = comfy.sd.load_clip([te_path], clip_type=comfy.sd.CLIPType.MINIMAX)
394
+ cond = clip.encode_from_tokens_scheduled(clip.tokenize(prompt))
395
+ ca, ex = cond[0][0], cond[0][1]
396
+ tags = ex.get("minimax_token_tags")
397
+ ctx = ca.to(device, torch.bfloat16)
398
+ tags = tags.to(device) if torch.is_tensor(tags) else tags
399
+ del clip
400
+ comfy.model_management.unload_all_models()
401
+ comfy.model_management.soft_empty_cache()
402
+ return ctx, tags
403
+
404
+
405
+ def _write_wav(path, waveform, sr):
406
+ w = waveform.detach().cpu().float()
407
+ if w.ndim == 3:
408
+ w = w[0]
409
+ w = w.clamp(-1.0, 1.0)
410
+ ch = w.shape[0]
411
+ pcm = (w.transpose(0, 1).contiguous().numpy() * 32767.0).astype("<i2")
412
+ with wave.open(path, "wb") as f:
413
+ f.setnchannels(ch)
414
+ f.setsampwidth(2)
415
+ f.setframerate(int(sr))
416
+ f.writeframes(pcm.tobytes())
417
+
418
+
419
+ def save_mp4(images, waveform, sr, fps, out_path):
420
+ import imageio.v2 as imageio
421
+ import imageio_ffmpeg
422
+ frames = images.detach().cpu().float().clamp(0, 1).mul(255).round().to(
423
+ torch.uint8).numpy()
424
+ tv, ta = out_path + ".v.mp4", out_path + ".a.wav"
425
+ writer = imageio.get_writer(tv, fps=fps, codec="libx264", quality=8,
426
+ pixelformat="yuv420p", macro_block_size=1,
427
+ ffmpeg_log_level="error")
428
+ for fr in frames:
429
+ writer.append_data(fr)
430
+ writer.close()
431
+ _write_wav(ta, waveform, sr)
432
+ ffmpeg = imageio_ffmpeg.get_ffmpeg_exe()
433
+ subprocess.run([ffmpeg, "-y", "-loglevel", "error", "-i", tv, "-i", ta,
434
+ "-c:v", "copy", "-c:a", "aac", "-b:a", "192k", "-shortest",
435
+ out_path], check=True)
436
+ os.remove(tv)
437
+ os.remove(ta)
438
+
439
+
440
+ # ======================================================================
441
+ # Main
442
+ # ======================================================================
443
+ def main():
444
+ ap = argparse.ArgumentParser(description="MiniMax-H3 Turbo LoRA 4-step generator")
445
+ ap.add_argument("--comfyui", required=True, help="path to a ComfyUI checkout @14b05228")
446
+ ap.add_argument("--base", required=True, help="H3 bf16 DiT safetensors")
447
+ ap.add_argument("--lora", required=True, help="turbo LoRA safetensors")
448
+ ap.add_argument("--te", required=True, help="Qwen3-VL text encoder safetensors")
449
+ ap.add_argument("--video-vae", required=True)
450
+ ap.add_argument("--audio-vae", required=True)
451
+ ap.add_argument("--prompt", required=True)
452
+ ap.add_argument("--out", default="out.mp4")
453
+ ap.add_argument("--width", type=int, default=1344, help="multiple of 16 (canvas is 32-based)")
454
+ ap.add_argument("--height", type=int, default=768)
455
+ ap.add_argument("--frames", type=int, default=124, help="24 fps; snaps to the 17k+5 grid")
456
+ ap.add_argument("--steps", type=int, default=4)
457
+ ap.add_argument("--seed", type=int, default=42)
458
+ ap.add_argument("--offload-adaln", action="store_true",
459
+ help="keep the timestep-projection weights in CPU fp32 (saves ~13GB VRAM)")
460
+ args = ap.parse_args()
461
+ sys.path.insert(0, args.comfyui) # ComfyUI supplies the H3 module definitions
462
+
463
+ dev = "cuda"
464
+ frames = args.frames
465
+ while frames % 17 != 5:
466
+ frames += 1
467
+ lt = (frames - 5) // 17 * 5 + 2
468
+ lh, lw = args.height // 16, args.width // 16
469
+ audio_t = round(frames / 24 * 40)
470
+ v_shape, a_shape = (1, 24, lt, lh, lw), (1, 32, 2, audio_t)
471
+ ts = timesteps(args.steps)
472
+ log(f"{args.width}x{args.height}x{frames}f ({frames/24:.1f}s) -> "
473
+ f"video{v_shape} audio{a_shape}; {args.steps}-step grid "
474
+ f"{['%.3f' % t for t in ts]}")
475
+
476
+ ctx, tags = encode_prompt(args.comfyui, args.te, args.prompt, dev)
477
+ model, h3ref = load_model(args.comfyui, args.base, args.lora, dev,
478
+ args.offload_adaln)
479
+ prep = Prepared(model, h3ref, ctx.shape[1], v_shape, audio_t, tags, dev)
480
+
481
+ g = torch.Generator(dev).manual_seed(args.seed)
482
+ ga = torch.Generator(dev).manual_seed(args.seed + 1)
483
+ nv = torch.randn(v_shape, generator=g, device=dev, dtype=torch.bfloat16)
484
+ na = torch.randn(a_shape, generator=ga, device=dev, dtype=torch.bfloat16)
485
+
486
+ cache = {}
487
+
488
+ def vfn(xv, xa, sv):
489
+ return forward(model, h3ref, prep, xv, xa, sv, ctx, dev,
490
+ args.offload_adaln, cache)
491
+
492
+ log("sampling ...")
493
+ t0 = time.time()
494
+ with torch.inference_mode():
495
+ zv, za = sample(vfn, nv, na, ts)
496
+ log(f"sampled in {time.time()-t0:.1f}s")
497
+
498
+ import comfy.sd
499
+ import comfy.utils
500
+ video_vae = comfy.sd.VAE(sd=comfy.utils.load_torch_file(args.video_vae))
501
+ audio_vae = comfy.sd.VAE(sd=comfy.utils.load_torch_file(args.audio_vae))
502
+ with torch.inference_mode():
503
+ images = video_vae.decode(zv.float())
504
+ if images.ndim == 5:
505
+ images = images.reshape(-1, *images.shape[-3:])
506
+ waveform = audio_vae.decode(za.float()).movedim(-1, 1)
507
+ std = torch.std(waveform, dim=[1, 2], keepdim=True) * 5.0
508
+ std[std < 1.0] = 1.0
509
+ waveform = waveform / std
510
+ sr = getattr(audio_vae, "audio_sample_rate_output",
511
+ getattr(audio_vae, "audio_sample_rate", 44100))
512
+ save_mp4(images, waveform, sr, 24, args.out)
513
+ log(f"done -> {args.out} ({os.path.getsize(args.out)/2**20:.1f}MB)")
514
+
515
+
516
+ if __name__ == "__main__":
517
+ main()
minimax_h3_t2v_turbo.json ADDED
@@ -0,0 +1,1072 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "e3f2b845-8f2c-4b5a-9caf-eac1029d3e7e",
3
+ "revision": 0,
4
+ "last_node_id": 135,
5
+ "last_link_id": 252,
6
+ "nodes": [
7
+ {
8
+ "id": 119,
9
+ "type": "VAELoader",
10
+ "pos": [
11
+ -2393.7830229552733,
12
+ 5342.158989520116
13
+ ],
14
+ "size": [
15
+ 640,
16
+ 70
17
+ ],
18
+ "flags": {},
19
+ "order": 0,
20
+ "mode": 0,
21
+ "inputs": [],
22
+ "outputs": [
23
+ {
24
+ "name": "VAE",
25
+ "type": "VAE",
26
+ "links": [
27
+ 229,
28
+ 241
29
+ ]
30
+ }
31
+ ],
32
+ "properties": {
33
+ "Node name for S&R": "VAELoader"
34
+ },
35
+ "widgets_values": [
36
+ "minimax_h3_video_vae_fp16.safetensors"
37
+ ]
38
+ },
39
+ {
40
+ "id": 120,
41
+ "type": "VAELoader",
42
+ "pos": [
43
+ -2393.7830229552733,
44
+ 5472.158989520116
45
+ ],
46
+ "size": [
47
+ 650,
48
+ 70
49
+ ],
50
+ "flags": {},
51
+ "order": 1,
52
+ "mode": 0,
53
+ "inputs": [],
54
+ "outputs": [
55
+ {
56
+ "name": "VAE",
57
+ "type": "VAE",
58
+ "links": [
59
+ 228
60
+ ]
61
+ }
62
+ ],
63
+ "properties": {
64
+ "Node name for S&R": "VAELoader"
65
+ },
66
+ "widgets_values": [
67
+ "minimax_h3_audio_vae_fp32.safetensors"
68
+ ]
69
+ },
70
+ {
71
+ "id": 121,
72
+ "type": "VAEDecodeAudio",
73
+ "pos": [
74
+ -243.2871350097655,
75
+ 5020.092864990236
76
+ ],
77
+ "size": [
78
+ 230,
79
+ 60
80
+ ],
81
+ "flags": {
82
+ "collapsed": false
83
+ },
84
+ "order": 18,
85
+ "mode": 0,
86
+ "inputs": [
87
+ {
88
+ "name": "samples",
89
+ "type": "LATENT",
90
+ "link": 248
91
+ },
92
+ {
93
+ "name": "vae",
94
+ "type": "VAE",
95
+ "link": 228
96
+ }
97
+ ],
98
+ "outputs": [
99
+ {
100
+ "name": "AUDIO",
101
+ "type": "AUDIO",
102
+ "links": [
103
+ 239
104
+ ]
105
+ }
106
+ ],
107
+ "properties": {
108
+ "Node name for S&R": "VAEDecodeAudio"
109
+ },
110
+ "widgets_values": []
111
+ },
112
+ {
113
+ "id": 122,
114
+ "type": "VAEDecode",
115
+ "pos": [
116
+ -243.2871350097655,
117
+ 4900.092864990236
118
+ ],
119
+ "size": [
120
+ 230,
121
+ 60
122
+ ],
123
+ "flags": {
124
+ "collapsed": false
125
+ },
126
+ "order": 17,
127
+ "mode": 0,
128
+ "inputs": [
129
+ {
130
+ "name": "samples",
131
+ "type": "LATENT",
132
+ "link": 247
133
+ },
134
+ {
135
+ "name": "vae",
136
+ "type": "VAE",
137
+ "link": 229
138
+ }
139
+ ],
140
+ "outputs": [
141
+ {
142
+ "name": "IMAGE",
143
+ "type": "IMAGE",
144
+ "links": [
145
+ 238
146
+ ]
147
+ }
148
+ ],
149
+ "properties": {
150
+ "Node name for S&R": "VAEDecode"
151
+ },
152
+ "widgets_values": []
153
+ },
154
+ {
155
+ "id": 125,
156
+ "type": "SamplerCustomAdvanced",
157
+ "pos": [
158
+ -553.2871350097655,
159
+ 4960.092864990236
160
+ ],
161
+ "size": [
162
+ 230,
163
+ 140
164
+ ],
165
+ "flags": {},
166
+ "order": 16,
167
+ "mode": 0,
168
+ "inputs": [
169
+ {
170
+ "name": "noise",
171
+ "type": "NOISE",
172
+ "link": 231
173
+ },
174
+ {
175
+ "name": "guider",
176
+ "type": "GUIDER",
177
+ "link": 232
178
+ },
179
+ {
180
+ "name": "sampler",
181
+ "type": "SAMPLER",
182
+ "link": 252
183
+ },
184
+ {
185
+ "name": "sigmas",
186
+ "type": "SIGMAS",
187
+ "link": 234
188
+ },
189
+ {
190
+ "name": "latent_image",
191
+ "type": "LATENT",
192
+ "link": 235
193
+ }
194
+ ],
195
+ "outputs": [
196
+ {
197
+ "name": "output",
198
+ "type": "LATENT",
199
+ "links": [
200
+ 247,
201
+ 248
202
+ ]
203
+ },
204
+ {
205
+ "name": "denoised_output",
206
+ "type": "LATENT",
207
+ "links": []
208
+ }
209
+ ],
210
+ "properties": {
211
+ "Node name for S&R": "SamplerCustomAdvanced"
212
+ },
213
+ "widgets_values": []
214
+ },
215
+ {
216
+ "id": 126,
217
+ "type": "BasicGuider",
218
+ "pos": [
219
+ -983.2871350097655,
220
+ 4940.092864990236
221
+ ],
222
+ "size": [
223
+ 360,
224
+ 60
225
+ ],
226
+ "flags": {},
227
+ "order": 15,
228
+ "mode": 0,
229
+ "inputs": [
230
+ {
231
+ "name": "model",
232
+ "type": "MODEL",
233
+ "link": 250
234
+ },
235
+ {
236
+ "name": "conditioning",
237
+ "type": "CONDITIONING",
238
+ "link": 237
239
+ }
240
+ ],
241
+ "outputs": [
242
+ {
243
+ "name": "GUIDER",
244
+ "type": "GUIDER",
245
+ "links": [
246
+ 232
247
+ ]
248
+ }
249
+ ],
250
+ "properties": {
251
+ "Node name for S&R": "BasicGuider"
252
+ },
253
+ "widgets_values": []
254
+ },
255
+ {
256
+ "id": 129,
257
+ "type": "RandomNoise",
258
+ "pos": [
259
+ -983.2871350097655,
260
+ 4800.092864990236
261
+ ],
262
+ "size": [
263
+ 360,
264
+ 90
265
+ ],
266
+ "flags": {},
267
+ "order": 2,
268
+ "mode": 0,
269
+ "inputs": [],
270
+ "outputs": [
271
+ {
272
+ "name": "NOISE",
273
+ "type": "NOISE",
274
+ "links": [
275
+ 231
276
+ ]
277
+ }
278
+ ],
279
+ "properties": {
280
+ "Node name for S&R": "RandomNoise"
281
+ },
282
+ "widgets_values": [
283
+ 953478908379578,
284
+ "randomize"
285
+ ]
286
+ },
287
+ {
288
+ "id": 128,
289
+ "type": "CLIPLoader",
290
+ "pos": [
291
+ -2393.7830229552733,
292
+ 5152.158989520116
293
+ ],
294
+ "size": [
295
+ 640,
296
+ 120
297
+ ],
298
+ "flags": {},
299
+ "order": 3,
300
+ "mode": 0,
301
+ "inputs": [],
302
+ "outputs": [
303
+ {
304
+ "name": "CLIP",
305
+ "type": "CLIP",
306
+ "links": [
307
+ 240
308
+ ]
309
+ }
310
+ ],
311
+ "properties": {
312
+ "Node name for S&R": "CLIPLoader"
313
+ },
314
+ "widgets_values": [
315
+ "qwen3vl_32b_minimax_h3_int8_convrot.safetensors",
316
+ "minimax",
317
+ "default"
318
+ ]
319
+ },
320
+ {
321
+ "id": 127,
322
+ "type": "UNETLoader",
323
+ "pos": [
324
+ -2393.7830229552733,
325
+ 5002.158989520116
326
+ ],
327
+ "size": [
328
+ 640,
329
+ 90
330
+ ],
331
+ "flags": {},
332
+ "order": 4,
333
+ "mode": 0,
334
+ "inputs": [],
335
+ "outputs": [
336
+ {
337
+ "name": "MODEL",
338
+ "type": "MODEL",
339
+ "links": [
340
+ 249
341
+ ]
342
+ }
343
+ ],
344
+ "properties": {
345
+ "Node name for S&R": "UNETLoader"
346
+ },
347
+ "widgets_values": [
348
+ "minimax_h3_fl2va_int8_convrot.safetensors",
349
+ "default"
350
+ ]
351
+ },
352
+ {
353
+ "id": 134,
354
+ "type": "MiniMaxH3TurboLoRA",
355
+ "pos": [
356
+ -1537.408311244107,
357
+ 4546.676307402845
358
+ ],
359
+ "size": [
360
+ 270,
361
+ 82
362
+ ],
363
+ "flags": {},
364
+ "order": 11,
365
+ "mode": 0,
366
+ "inputs": [
367
+ {
368
+ "name": "model",
369
+ "type": "MODEL",
370
+ "link": 249
371
+ }
372
+ ],
373
+ "outputs": [
374
+ {
375
+ "name": "MODEL",
376
+ "type": "MODEL",
377
+ "links": [
378
+ 250,
379
+ 251
380
+ ]
381
+ }
382
+ ],
383
+ "properties": {
384
+ "Node name for S&R": "MiniMaxH3TurboLoRA"
385
+ },
386
+ "widgets_values": [
387
+ "minimax_h3_turbo_4step_ema_ckpt500.safetensors",
388
+ 1
389
+ ]
390
+ },
391
+ {
392
+ "id": 130,
393
+ "type": "CreateVideo",
394
+ "pos": [
395
+ 76.39285156250011,
396
+ 5059.452871704105
397
+ ],
398
+ "size": [
399
+ 270,
400
+ 110
401
+ ],
402
+ "flags": {},
403
+ "order": 19,
404
+ "mode": 0,
405
+ "inputs": [
406
+ {
407
+ "name": "images",
408
+ "type": "IMAGE",
409
+ "link": 238
410
+ },
411
+ {
412
+ "name": "audio",
413
+ "shape": 7,
414
+ "type": "AUDIO",
415
+ "link": 239
416
+ }
417
+ ],
418
+ "outputs": [
419
+ {
420
+ "name": "VIDEO",
421
+ "type": "VIDEO",
422
+ "links": [
423
+ 242
424
+ ]
425
+ }
426
+ ],
427
+ "properties": {
428
+ "Node name for S&R": "CreateVideo"
429
+ },
430
+ "widgets_values": [
431
+ 24,
432
+ 8
433
+ ]
434
+ },
435
+ {
436
+ "id": 92,
437
+ "type": "SaveVideo",
438
+ "pos": [
439
+ 529.3816487426757,
440
+ 4466.255195938108
441
+ ],
442
+ "size": [
443
+ 1070,
444
+ 692.4444444444445
445
+ ],
446
+ "flags": {},
447
+ "order": 20,
448
+ "mode": 0,
449
+ "inputs": [
450
+ {
451
+ "name": "video",
452
+ "type": "VIDEO",
453
+ "link": 242
454
+ }
455
+ ],
456
+ "outputs": [
457
+ {
458
+ "name": "video",
459
+ "type": "VIDEO",
460
+ "links": null
461
+ }
462
+ ],
463
+ "properties": {},
464
+ "widgets_values": [
465
+ "video/MiniMax_H3",
466
+ "auto",
467
+ "auto"
468
+ ]
469
+ },
470
+ {
471
+ "id": 131,
472
+ "type": "MiniMaxH3ImageToVideo",
473
+ "pos": [
474
+ -1547.4412168457013,
475
+ 4768.115383134615
476
+ ],
477
+ "size": [
478
+ 410,
479
+ 510
480
+ ],
481
+ "flags": {},
482
+ "order": 14,
483
+ "mode": 0,
484
+ "inputs": [
485
+ {
486
+ "name": "clip",
487
+ "type": "CLIP",
488
+ "link": 240
489
+ },
490
+ {
491
+ "name": "vae",
492
+ "type": "VAE",
493
+ "link": 241
494
+ },
495
+ {
496
+ "name": "first_frame",
497
+ "shape": 7,
498
+ "type": "IMAGE",
499
+ "link": null
500
+ },
501
+ {
502
+ "name": "last_frame",
503
+ "shape": 7,
504
+ "type": "IMAGE",
505
+ "link": null
506
+ },
507
+ {
508
+ "name": "width",
509
+ "type": "INT",
510
+ "widget": {
511
+ "name": "width"
512
+ },
513
+ "link": 244
514
+ },
515
+ {
516
+ "name": "height",
517
+ "type": "INT",
518
+ "widget": {
519
+ "name": "height"
520
+ },
521
+ "link": 245
522
+ },
523
+ {
524
+ "name": "length",
525
+ "type": "INT",
526
+ "widget": {
527
+ "name": "length"
528
+ },
529
+ "link": 243
530
+ }
531
+ ],
532
+ "outputs": [
533
+ {
534
+ "name": "positive",
535
+ "type": "CONDITIONING",
536
+ "links": [
537
+ 237
538
+ ]
539
+ },
540
+ {
541
+ "name": "LATENT",
542
+ "type": "LATENT",
543
+ "links": [
544
+ 235
545
+ ]
546
+ }
547
+ ],
548
+ "properties": {
549
+ "Node name for S&R": "MiniMaxH3ImageToVideo"
550
+ },
551
+ "widgets_values": [
552
+ "Vaporwave title sequence look: pink and blue gradient palette, VHS tracking artifacts, Greek statue motifs, chrome palm trees, RGB chromatic aberration, lo-fi retro atmosphere, mood languid and nostalgic.\n\nTimeline:\n[0s-1s] VHS static opens the frame, the title \"COMFYUI\" appears with RGB split and a slight horizontal jitter.\n[1s-2.5s] Hard cut, a Greek plaster bust close-up, pink-purple gradient sky, a pixelated sun.\n[2.5s-4s] Clean \"STARRING\" credits appear, \"LATENT\" and \"CONTROLNET\" each shown exactly once.\n[4s-5s] Final card \"DIRECTED BY COMFYUI\" holds, one VHS tracking glitch settling into stability.\n\nHard cuts only, transitions landing with tape jumps, no push-ins, no dissolves.\n\nAudio: lo-fi vaporwave score, slow drum machine with soft bass, VHS tape-noise sample joins at 2.5s, melody fading for the last 1s.\n\nAll text must be clearly legible, do not misspell English, no Chinese characters, do not repeat names or job titles, no soft dissolves, no subtitle bars.",
553
+ 1344,
554
+ 768,
555
+ 73
556
+ ]
557
+ },
558
+ {
559
+ "id": 124,
560
+ "type": "BasicScheduler",
561
+ "pos": [
562
+ -983.2871350097655,
563
+ 5170.092864990236
564
+ ],
565
+ "size": [
566
+ 370,
567
+ 130
568
+ ],
569
+ "flags": {},
570
+ "order": 13,
571
+ "mode": 0,
572
+ "inputs": [
573
+ {
574
+ "name": "model",
575
+ "type": "MODEL",
576
+ "link": 251
577
+ }
578
+ ],
579
+ "outputs": [
580
+ {
581
+ "name": "SIGMAS",
582
+ "type": "SIGMAS",
583
+ "links": [
584
+ 234
585
+ ]
586
+ }
587
+ ],
588
+ "properties": {
589
+ "Node name for S&R": "BasicScheduler"
590
+ },
591
+ "widgets_values": [
592
+ "simple",
593
+ 6,
594
+ 1
595
+ ]
596
+ },
597
+ {
598
+ "id": 116,
599
+ "type": "MarkdownNote",
600
+ "pos": [
601
+ -3481.88621199646,
602
+ 4686.487353762512
603
+ ],
604
+ "size": [
605
+ 450,
606
+ 740
607
+ ],
608
+ "flags": {},
609
+ "order": 5,
610
+ "mode": 0,
611
+ "inputs": [],
612
+ "outputs": [],
613
+ "title": "Note: MiniMax H3",
614
+ "properties": {},
615
+ "widgets_values": [
616
+ "## MiniMax H3\n\n[MiniMax H3](https://www.minimax.io/blog/minimax-h3) is MiniMax's general-purpose, omni-modal generation model. It jointly understands text, image, video, and audio, and generates video with **native stereo audio**: voice, sound effects, and music are modeled jointly in a single forward pass, not layered on afterward. Output is up to 2K resolution, 24fps, and up to about 15 seconds.\n\n## ComfyUI links\n- [ComfyUI#15224](https://github.com/Comfy-Org/ComfyUI/pull/15224)\n- [🤗 Comfy-Org/MiniMax-H3](https://huggingface.co/Comfy-Org/MiniMax-H3)\n\n## About this workflow\n\n**Key inputs**\n\n- **prompt**: describe the shots, camera moves, and the accompanying audio (dialogue, SFX, music) in one block\n- **width / height**: set via Resolution Selector. H3's native canvas is a 768px short edge, capped at 768x1344 pixels, rounded to a multiple of 32\n- **duration (seconds)**: converted to a valid frame `length` by the Math Expression node, snapping up to the model's 17-frame-per-block (17k+5) grid at 24fps\n"
617
+ ],
618
+ "color": "#222",
619
+ "bgcolor": "#000"
620
+ },
621
+ {
622
+ "id": 117,
623
+ "type": "MarkdownNote",
624
+ "pos": [
625
+ -2949.206052406617,
626
+ 4685.120018463135
627
+ ],
628
+ "size": [
629
+ 440,
630
+ 740
631
+ ],
632
+ "flags": {},
633
+ "order": 6,
634
+ "mode": 0,
635
+ "inputs": [],
636
+ "outputs": [],
637
+ "title": "Note: Model Links",
638
+ "properties": {},
639
+ "widgets_values": [
640
+ "## Model Links\n\n**vae**\n\n- [minimax_h3_video_vae_fp16.safetensors](https://huggingface.co/Comfy-Org/MiniMax-H3/resolve/main/vae/minimax_h3_video_vae_fp16.safetensors)\n- [minimax_h3_audio_vae_fp32.safetensors](https://huggingface.co/Comfy-Org/MiniMax-H3/resolve/main/vae/minimax_h3_audio_vae_fp32.safetensors)\n\n**diffusion_models**\n\n- [minimax_h3_fl2va_pruned_int8_convrot.safetensors](https://huggingface.co/Comfy-Org/MiniMax-H3/resolve/main/diffusion_models/minimax_h3_fl2va_pruned_int8_convrot.safetensors)\n\n**text_encoders**\n\n- [qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors](https://huggingface.co/Comfy-Org/MiniMax-H3/resolve/main/text_encoders/qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors)\n\n\n## Model Storage Location\n\n```\n📂 ComfyUI/\n├── 📂 models/\n│ ├── 📂 vae/\n│ │ ├── minimax_h3_video_vae_fp16.safetensors\n│ │ └── minimax_h3_audio_vae_fp32.safetensors\n│ ├── 📂 diffusion_models/\n│ │ └── minimax_h3_fl2va_pruned_int8_convrot.safetensors\n│ └── 📂 text_encoders/\n│ └── qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors\n```\n\n## Report Issue\n\nNote: Please update ComfyUI first ([guide](https://docs.comfy.org/installation/update_comfyui)) and prepare required models. Desktop/Cloud updates follow stable releases, so some nightly-supported models may not be available yet.\n\n- Cannot run / runtime errors: [ComfyUI/issues](https://github.com/comfyanonymous/ComfyUI/issues)\n- UI / frontend issues: [ComfyUI_frontend/issues](https://github.com/Comfy-Org/ComfyUI_frontend/issues)\n- Workflow issues: [workflow_templates/issues](https://github.com/Comfy-Org/workflow_templates/issues)\n"
641
+ ],
642
+ "color": "#222",
643
+ "bgcolor": "#000"
644
+ },
645
+ {
646
+ "id": 133,
647
+ "type": "PrimitiveFloat",
648
+ "pos": [
649
+ -2415.5672499884035,
650
+ 5729.984664990233
651
+ ],
652
+ "size": [
653
+ 270,
654
+ 70
655
+ ],
656
+ "flags": {},
657
+ "order": 7,
658
+ "mode": 0,
659
+ "inputs": [],
660
+ "outputs": [
661
+ {
662
+ "name": "FLOAT",
663
+ "type": "FLOAT",
664
+ "links": [
665
+ 246
666
+ ]
667
+ }
668
+ ],
669
+ "title": "Float (duration)",
670
+ "properties": {
671
+ "Node name for S&R": "PrimitiveFloat"
672
+ },
673
+ "widgets_values": [
674
+ 5
675
+ ]
676
+ },
677
+ {
678
+ "id": 132,
679
+ "type": "ComfyMathExpression",
680
+ "pos": [
681
+ -2056.6077066693106,
682
+ 5696.486027500916
683
+ ],
684
+ "size": [
685
+ 360,
686
+ 160
687
+ ],
688
+ "flags": {
689
+ "collapsed": false
690
+ },
691
+ "order": 12,
692
+ "mode": 0,
693
+ "inputs": [
694
+ {
695
+ "label": "a",
696
+ "name": "values.a",
697
+ "type": "FLOAT,INT,BOOLEAN",
698
+ "link": 246
699
+ },
700
+ {
701
+ "label": "b",
702
+ "name": "values.b",
703
+ "shape": 7,
704
+ "type": "FLOAT,INT,BOOLEAN",
705
+ "link": null
706
+ }
707
+ ],
708
+ "outputs": [
709
+ {
710
+ "name": "FLOAT",
711
+ "type": "FLOAT",
712
+ "links": []
713
+ },
714
+ {
715
+ "name": "INT",
716
+ "type": "INT",
717
+ "links": [
718
+ 243
719
+ ]
720
+ },
721
+ {
722
+ "name": "BOOL",
723
+ "type": "BOOLEAN",
724
+ "links": []
725
+ }
726
+ ],
727
+ "properties": {
728
+ "Node name for S&R": "ComfyMathExpression"
729
+ },
730
+ "widgets_values": [
731
+ "max(5, round(a * 24)) + (5 - (max(5, round(a * 24)) % 17)) % 17"
732
+ ]
733
+ },
734
+ {
735
+ "id": 118,
736
+ "type": "MarkdownNote",
737
+ "pos": [
738
+ -1521.0404566809086,
739
+ 5724.123689148864
740
+ ],
741
+ "size": [
742
+ 300,
743
+ 520
744
+ ],
745
+ "flags": {},
746
+ "order": 8,
747
+ "mode": 0,
748
+ "inputs": [],
749
+ "outputs": [],
750
+ "title": "Note: Size Settings Reference",
751
+ "properties": {},
752
+ "widgets_values": [
753
+ "| megapixels | Aspect | Output (multiple=32) |\n|---|---|---|\n| 0.2 | 16:9 | 608 x 352 |\n| 0.3 | 16:9 | 736 x 416 |\n| 0.4 | 16:9 | 864 x 480 |\n| 0.5 | 16:9 | 960 x 544 |\n| 0.6 | 16:9 | 1056 x 608 |\n| 0.7 | 16:9 | 1152 x 640 |\n| 0.8 | 16:9 | 1216 x 672 |\n| 0.9 | 16:9 | 1280 x 736 |\n| 0.98 | 16:9 | 1344 x 768 |\n| 1.0 | 16:9 | 1376 x 768 |\n| 1.2 | 16:9 | 1504 x 832 |\n| 1.5 | 16:9 | 1664 x 928 |\n| 1.8 | 16:9 | 1824 x 1024 |\n| 2.0 | 16:9 | 1920 x 1088 |\n"
754
+ ],
755
+ "color": "#222",
756
+ "bgcolor": "#000"
757
+ },
758
+ {
759
+ "id": 115,
760
+ "type": "ResolutionSelector",
761
+ "pos": [
762
+ -1580.9217133618172,
763
+ 5487.988854170227
764
+ ],
765
+ "size": [
766
+ 270,
767
+ 170
768
+ ],
769
+ "flags": {},
770
+ "order": 9,
771
+ "mode": 0,
772
+ "showAdvanced": true,
773
+ "inputs": [],
774
+ "outputs": [
775
+ {
776
+ "name": "width",
777
+ "type": "INT",
778
+ "links": [
779
+ 244
780
+ ]
781
+ },
782
+ {
783
+ "name": "height",
784
+ "type": "INT",
785
+ "links": [
786
+ 245
787
+ ]
788
+ }
789
+ ],
790
+ "properties": {
791
+ "Node name for S&R": "ResolutionSelector"
792
+ },
793
+ "widgets_values": [
794
+ "16:9 (Widescreen)",
795
+ 0.4,
796
+ 32
797
+ ]
798
+ },
799
+ {
800
+ "id": 135,
801
+ "type": "MiniMaxH3TurboSampler",
802
+ "pos": [
803
+ -826.1238619112232,
804
+ 5411.853328422132
805
+ ],
806
+ "size": [
807
+ 280.2447265625,
808
+ 26
809
+ ],
810
+ "flags": {},
811
+ "order": 10,
812
+ "mode": 0,
813
+ "inputs": [],
814
+ "outputs": [
815
+ {
816
+ "name": "SAMPLER",
817
+ "type": "SAMPLER",
818
+ "links": [
819
+ 252
820
+ ]
821
+ }
822
+ ],
823
+ "properties": {
824
+ "Node name for S&R": "MiniMaxH3TurboSampler"
825
+ }
826
+ }
827
+ ],
828
+ "links": [
829
+ [
830
+ 228,
831
+ 120,
832
+ 0,
833
+ 121,
834
+ 1,
835
+ "VAE"
836
+ ],
837
+ [
838
+ 229,
839
+ 119,
840
+ 0,
841
+ 122,
842
+ 1,
843
+ "VAE"
844
+ ],
845
+ [
846
+ 231,
847
+ 129,
848
+ 0,
849
+ 125,
850
+ 0,
851
+ "NOISE"
852
+ ],
853
+ [
854
+ 232,
855
+ 126,
856
+ 0,
857
+ 125,
858
+ 1,
859
+ "GUIDER"
860
+ ],
861
+ [
862
+ 234,
863
+ 124,
864
+ 0,
865
+ 125,
866
+ 3,
867
+ "SIGMAS"
868
+ ],
869
+ [
870
+ 235,
871
+ 131,
872
+ 1,
873
+ 125,
874
+ 4,
875
+ "LATENT"
876
+ ],
877
+ [
878
+ 237,
879
+ 131,
880
+ 0,
881
+ 126,
882
+ 1,
883
+ "CONDITIONING"
884
+ ],
885
+ [
886
+ 238,
887
+ 122,
888
+ 0,
889
+ 130,
890
+ 0,
891
+ "IMAGE"
892
+ ],
893
+ [
894
+ 239,
895
+ 121,
896
+ 0,
897
+ 130,
898
+ 1,
899
+ "AUDIO"
900
+ ],
901
+ [
902
+ 240,
903
+ 128,
904
+ 0,
905
+ 131,
906
+ 0,
907
+ "CLIP"
908
+ ],
909
+ [
910
+ 241,
911
+ 119,
912
+ 0,
913
+ 131,
914
+ 1,
915
+ "VAE"
916
+ ],
917
+ [
918
+ 242,
919
+ 130,
920
+ 0,
921
+ 92,
922
+ 0,
923
+ "VIDEO"
924
+ ],
925
+ [
926
+ 243,
927
+ 132,
928
+ 1,
929
+ 131,
930
+ 6,
931
+ "INT"
932
+ ],
933
+ [
934
+ 244,
935
+ 115,
936
+ 0,
937
+ 131,
938
+ 4,
939
+ "INT"
940
+ ],
941
+ [
942
+ 245,
943
+ 115,
944
+ 1,
945
+ 131,
946
+ 5,
947
+ "INT"
948
+ ],
949
+ [
950
+ 246,
951
+ 133,
952
+ 0,
953
+ 132,
954
+ 0,
955
+ "FLOAT"
956
+ ],
957
+ [
958
+ 247,
959
+ 125,
960
+ 0,
961
+ 122,
962
+ 0,
963
+ "LATENT"
964
+ ],
965
+ [
966
+ 248,
967
+ 125,
968
+ 0,
969
+ 121,
970
+ 0,
971
+ "LATENT"
972
+ ],
973
+ [
974
+ 249,
975
+ 127,
976
+ 0,
977
+ 134,
978
+ 0,
979
+ "MODEL"
980
+ ],
981
+ [
982
+ 250,
983
+ 134,
984
+ 0,
985
+ 126,
986
+ 0,
987
+ "MODEL"
988
+ ],
989
+ [
990
+ 251,
991
+ 134,
992
+ 0,
993
+ 124,
994
+ 0,
995
+ "MODEL"
996
+ ],
997
+ [
998
+ 252,
999
+ 135,
1000
+ 0,
1001
+ 125,
1002
+ 2,
1003
+ "SAMPLER"
1004
+ ]
1005
+ ],
1006
+ "groups": [
1007
+ {
1008
+ "id": 1,
1009
+ "title": "Models",
1010
+ "bounding": [
1011
+ -2423.7830229552733,
1012
+ 4912.158989520116,
1013
+ 700,
1014
+ 670
1015
+ ],
1016
+ "color": "#3f789e",
1017
+ "flags": {}
1018
+ },
1019
+ {
1020
+ "id": 2,
1021
+ "title": "Sampling",
1022
+ "bounding": [
1023
+ -1003.2871350097655,
1024
+ 4680.092864990236,
1025
+ 690,
1026
+ 670
1027
+ ],
1028
+ "color": "#3f789e",
1029
+ "flags": {}
1030
+ },
1031
+ {
1032
+ "id": 3,
1033
+ "title": "Conditioning",
1034
+ "bounding": [
1035
+ -1545.4973350097653,
1036
+ 4700.707397905124,
1037
+ 480,
1038
+ 670
1039
+ ],
1040
+ "color": "#3f789e",
1041
+ "flags": {}
1042
+ },
1043
+ {
1044
+ "id": 4,
1045
+ "title": "Decoding and create video",
1046
+ "bounding": [
1047
+ -283.2871350097655,
1048
+ 4680.092864990236,
1049
+ 670,
1050
+ 670
1051
+ ],
1052
+ "color": "#3f789e",
1053
+ "flags": {}
1054
+ }
1055
+ ],
1056
+ "config": {},
1057
+ "extra": {
1058
+ "frontendVersion": "1.47.12",
1059
+ "VHS_latentpreview": false,
1060
+ "VHS_latentpreviewrate": 0,
1061
+ "VHS_MetadataImage": true,
1062
+ "VHS_KeepIntermediate": true,
1063
+ "ds": {
1064
+ "scale": 0.6209213230591556,
1065
+ "offset": [
1066
+ 2494.182489857376,
1067
+ -4486.16972718581
1068
+ ]
1069
+ }
1070
+ },
1071
+ "version": 0.4
1072
+ }
minimax_h3_turbo_4step.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c468c61ebf715699b5a710fed016654e4a244195ce2e1c4f9f84dc63f82da905
3
+ size 779849872
minimax_h3_turbo_4step_ckpt500.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:82d0acff583b04ad9a4238a7440b584b56094bfb7c4fdb2981f67c7a4784b62d
3
+ size 779849872
minimax_h3_turbo_4step_ckpt850.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b6d1be058a76ec7b228a2e2ef36b2cdcdfa65f32e98ccb4009cbdc9224914bc3
3
+ size 779849816
minimax_h3_turbo_4step_ema.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8d645b67e606874e9179b277cea721c1f1e75830532fcc2206e23353cb33edc5
3
+ size 779849872
minimax_h3_turbo_4step_ema_ckpt500.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6183247bacc434f382f1e0e3e03265f677c50928313ea806756d18fba5cc9160
3
+ size 779849872
minimax_h3_turbo_4step_ema_ckpt850.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5a6eeba171cf183020a4ad48774bb2968f29f8168afd6ec17a04987f3528b4ea
3
+ size 779849816
minimax_h3_turbo_v4_step600.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6c280b58f03b5dc89e791347679b821b8a85615bedae04d3c65bbbe753e07ce8
3
+ size 779849816
minimax_h3_turbo_v4_step600_ema.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5f3a626cd72c93a8b9318d6760c510bc5092d2ab13aaba1f932c5bab07a416d3
3
+ size 779849816
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ # MiniMax-H3 Turbo LoRA 4-step generator.
2
+ # The base model / VAE / text-encoder module definitions come from a ComfyUI
3
+ # checkout (see generate.py header): git clone + checkout 14b05228, then install
4
+ # ComfyUI's own requirements. The packages below are what generate.py itself
5
+ # needs on top of that.
6
+ torch>=2.4
7
+ safetensors>=0.4
8
+ imageio>=2.34
9
+ imageio-ffmpeg>=0.5
10
+ numpy>=1.24