MMH3 Spatial Extend Video
Make MiniMax H3 video wider than its training box
- model
- tile_config
- clip
- vae
- noise
- sampler
- sigmas
- sampler_2nd
- sigmas_2nd
- latent_tile_0
- bug_patch
- latent
- latent_tile_0
MiniMax H3 is one of the best reasons to run local video in 2026: a 33B omni-modal model with native stereo audio, and ComfyUI support that landed the day the weights dropped. The catch is that it's trained inside a box. Ask it for a frame much wider or taller than it knows and it starts lying to you - squeezing the scene, never actually showing more of it. MMH3 Spatial Extend Video's trick is to not ask for that at all. Instead of one oversized generation, it samples several normal-sized H3 clips as tiles and merges them into a single wide or tall latent. "Ultimate SD Upscale," applied to video-and-audio latents instead of pixels.
The author, bbaudio-2025, posts on r/comfyui as bbaudio2024 and built ComfyUI-SuperUltimateVaceTools, the tiled VACE video pack this grew out of. The lineage shows: it's tile-and-stitch, and the seam is the boss.
How the stitching works
You wire it like a sampler, because that's what it is. Connect model (an H3 diffusion UNET), clip (H3's Qwen3-VL-based CLIP), vae (the H3 video VAE), plus noise, sampler, and sigmas - and tile_config from the companion MMH3 Spatial Tile Editor node. Prompts and reference images don't plug in here; they live inside that config, and the node builds the per-tile conditioning internally. Tile 0 is sampled first - the center in a four-quadrant layout, the anchor at one end of a strip. Every extension tile after it generates with its own conditioning while overlapping the already-sampled neighbour by a band that's frozen and faded through H3's noise masks: mask 0 pins the stitched content exactly, and the fade band lets the new tile blend in. H3 samples are AV latents (video and audio together), so the merged result keeps audio - tile 0's.
The three knobs that actually matter
Most of the schema is expert-grade surface. A beginner should touch these:
length- the frame count, and it's finicky: it must be17*n+5, so 124 ≈ 5 s at 24 fps. The shipped example wires a ComfyMath expression to convert a seconds slider into a compliant count - that's how often this bites.fade_mode/flat_fade_value- how hard the overlap is pinned.flatwithflat_fade_value0.5 is the author's recommended default: lower freezes more neighbour content, higher lets the tile roam.latent_tile_0- connect an existing H3 latent and tile 0 isn't re-sampled: your clip becomes the anchor and everything extends around it.
If seams still show, there's an experimental second pass (sampler_2nd + sigmas_2nd), and a bug_patch input for the four-quadrant layout - the author's shipped workflow labels that whole group "usually not needed."
Outputs
latent is the merged result: straight into VAEDecode for the frames and VAEDecodeAudio for the sound, then CreateVideo → SaveVideo. latent_tile_0 hands you tile 0 alone to inspect before committing.
The model downloads are the real cost
Before any of this runs you need the actual MiniMax H3 stack, and it's big. Full weights run ~42 GB and this is a 33B model, so the realistic path - the one the repo's example workflow uses - is quantized: an int8-convrot H3 diffusion model, the Qwen3-VL text encoder as a Q4 GGUF (which needs ComfyUI-GGUF installed), the H3 video VAE, an audio VAE, and the 4-step turbo LoRA, all from the Comfy-Org/MiniMax-H3 repo on HuggingFace. One licence note worth reading before you invest: H3's community licence excludes the US, EU, UK and South Korea from running the local weights - in those regions the hosted Hailuo API is the licensed path.
How to install
ComfyUI Manager should find it by the pack title "Comfyui-MMH3-UltimateExtend" once it's published to the registry (the repo ships a publish workflow for that). If Manager comes up empty, clone it:
cd ComfyUI/custom_nodes
git clone https://github.com/bbaudio-2025/Comfyui-MMH3-UltimateExtend
then restart ComfyUI. There's no requirements.txt and the pyproject declares zero Python dependencies - everything it calls is core ComfyUI. The caveat: it needs a recent ComfyUI, because the nodes are written against the 2026-era comfy_api node API and lean on Comfy's native H3 nodes (MiniMaxH3SigmaShift, VAEDecodeAudio), not the legacy node.py style.
Where people get tripped up
Be honest about what this is: version 0.0.1, README literally marked "under development," and some inputs are labelled TEST PARAM or EXPERIMENTAL in the schema. Refreshing candour, but defaults shift between updates - re-read the tooltips when you update. The frame-count constraint (17n+5) is the error beginners hit first: a mismatched length and ComfyUI complains about latent shapes that don't fit the schedule. And manage your expectations on cost. Each tile is its own multi-second generation, so a three-tile pan is three generations plus a stitch. Bring a patient GPU.
Inputs (21)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | The diffusion model used to sample every tile. | |
| tile_config | DICT | Output of 'MMH3 Spatial Tile Editor'. Provides per-tile prompts, reference images, dimensions, overlap/fade, and layout scheme. | |
| clip | CLIP | MiniMax H3 CLIP model for encoding prompts and reference images. | |
| vae | VAE | MiniMax H3 Video VAE for encoding reference images. | |
| noise | NOISE | Noise source; one noise tensor is generated per tile. | |
| sampler | SAMPLER | Sampler used for every tile. | |
| sigmas | SIGMAS | Sigma schedule used for every tile. | |
| cfg | FLOAT | 1.00–100 | CFG scale used for tiles whose negative is connected. |
| length | INT | 1245–3600 | Frame count at 24 fps (124 = ~5s, trained range ~124-362). Must be 17*n+5. Overridden by latent_tile_0 frame count when it is connected. |
| ref_image_size | COMBO | match | Reference image sizing. 'match' scales each ref to the generation's pixel area; 'max' uses 2048px short edge for best identity fidelity. Only used in Ref2VA mode; ignored in FL2VA. |
| masked_area_noise | FLOAT | 0.000–1 | TEST PARAM. Raises every mask value toward 1 (label, input mix and output blend together): 0 (default) keeps the frozen/fade bands as configured; 1.0 disables the mask entirely and every tile is sampled freely. |
| fade_mode | COMBO | flat | How the fade band's noise mask is shaped. The H3 invariant is per-token label-input consistency: the mask value becomes each 2x2-pooled token's timestep label AND mixes that token's input (m*noise+(1-m)*anchor = anchor noised to level m*sigma). 'flat': constant flat_fade_value band - consistent at every level, empirically the most stable. 'gradient': legacy 0->1 ramp - consistent per pixel, mild mismatch at the 2x2 patch pooling (label takes the patch max); empirically close to flat. 'hybrid': same labels as 'gradient', but during sampling the masked-input mix is patched - each masked pixel's input becomes full-strength anchor + pool(m)*sigma*noise (the stock mix scales the anchor to (1-m) brightness, an out-of-distribution half-brightness signal that likely reads as the fade band's residual grain; H3's reference tokens use the full-strength construction). Experimental - A/B against 'gradient' to isolate the input-mix effect. |
| flat_fade_value | FLOAT | 0.500–1 | Mask value of the whole fade band (fade_mode 'flat'). LOWER = stronger freeze, more neighbour content preserved; HIGHER = more noise injected, freer generation. 0 = fully frozen band, 0.5 = half-preserved (recommended), 1 = mask off. Ignored in 'gradient' mode. |
| ref_mode | COMBO | use_ref_image | What extension tiles (tiles 1..N) condition on. The two modes are mutually exclusive. 'use_ref_image': each tile uses its own reference source from the Tile Editor under that tile's own cond_mode (FL2VA: first/last keyframes; Ref2VA: reference blocks; modes can mix per tile) - the standard behaviour. 'use_overlap': tile 0 is finalized FIRST (freely sampled, or taken from latent_tile_0 when that anchor is connected), then each extension tile's reference images are IGNORED and replaced by the part of tile 0 its frozen band covers, injected as a leading Ref2VA reference block (<Picture 1>): in serpentine layouts a single full-length edge strip; in 4_quadrants the single corner rect the tile shares with the center (full-length edge strips there would leak the neighbouring quadrants' overlap zones into the reference). The strip's frame-0 latent is sliced directly from tile 0's latent (no VAE round-trip; the Qwen3-VL vision stream sees a neutral gray placeholder). Anchors only the seam, so the rest generates freely. Strips always come from tile 0 (the only tile that exists when extension conditionings are baked); in 4+ tile serpentine layouts the bands of tiles 3+ abut tiles 1/2 instead, so their strips carry tile 0's corresponding edge as scene context rather than the exact seam. A tile with zero overlap gets no strip reference. Total sampling work is unchanged. |
| overlap_mode | COMBO | earlier | Who wins each shared overlap band when stitching. |
| overlap_blend | COMBO | linear | How the overlap band transitions when stitching. |
| second_pass_mode | COMBO | unmasked | Second-pass masking (needs sampler_2nd/sigmas_2nd connected). 'unmasked': no noise mask - the ENTIRE tile region (overlap bands included) is sampled freely by sigmas_2nd; the blended tile latent is the starting point, so a full-strength schedule regenerates the tile while a low-denoise schedule refines it globally. 'tile_mask': the tile's baked overlap/fade mask is reused - the frozen band keeps the stitched content and only the feathered/free band is re-denoised. 'shrink_32_no_fade' / 'shrink_64_no_fade': SECOND-PASS-ONLY mask variants - each seam's frozen band is shrunk by 32/64 px toward the seam and the fade band is dropped (hard frozen/free cut, no mid-value tokens), so the released strip plus the former fade band are re-denoised from the blended latent while a reduced frozen anchor stays glued to the seam. Narrow bands (e.g. the edge overlaps of 4_quadrants) keep at least 32 px of freeze (or their full width when narrower). Audio stays frozen as in 'tile_mask'. |
| sampler_2ndopt | SAMPLER | EXPERIMENTAL. Sampler for a second refinement pass run on each block (from the 2nd block on) after its first pass and overlap blend. Leave unconnected to disable the second pass. | |
| sigmas_2ndopt | SIGMAS | EXPERIMENTAL. Sigma schedule for the second pass; supply its own scheduler/steps/denoise (e.g. a BasicScheduler). The second pass is skipped when this is unconnected, empty, or has fewer than one step (denoise 0 / steps 0). | |
| latent_tile_0opt | LATENT | Existing anchor video latent. When connected, the first tile is NOT sampled - its video/audio are taken straight from this latent as an already-generated anchor and the remaining tiles are extended around it. When unconnected, tile 0 (and all tiles) are sampled normally. | |
| bug_patchopt | DICT | Layout-specific special handling. Currently: output of 'MMH3 Last Quadrant Patch' (4_quadrants_expand only) replaces the noise mask of any tile enabled on that node (four per-tile toggles) with explicit center-seam/edge-seam frozen+fade widths. Ignored (console note) on other layouts. Leave unconnected for default behavior. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| latent | LATENT | The tiles merged into one MiniMax H3 AV latent. The audio channel is tile 0's generated audio. |
| latent_tile_0 | LATENT | Tile 0's own result as a standalone MiniMax H3 AV latent. When the latent_tile_0 INPUT is connected it is passed through unchanged (bypass - the exact input object); with ref_mode 'use_overlap' it is the anchor sampled early in Phase 5.5; otherwise it is tile 0's freshly sampled video+audio before merging. |