Nodes/ComfyUI-H3VAE-PyOpt/MiniMax H3 VAE Load (PyTorch Optimized)
ComfyUI Node

MiniMax H3 VAE Load (PyTorch Optimized)

MiniMax H3's Video VAE, Without Building a TensorRT Engine

By fishelegs·Created 8 days ago·Updated a day ago· 3
MiniMax H3 VAE Load (PyTorch Optimized)
    • VAE
    vae_nameuse default
    dtypefp16
    decoder_tile_size256
    tile_batch2
    compile_decodertrue
    compile_encodertrue
    encoder_staged_batch4
    cudnn_benchmarktrue
    warmupnone
    warmup_frames124
    warmup_width1344
    warmup_height768
    log_callstrue
    encoder_tile_size0
    fast_linearfalse
    int8_decodefalse
    int8_encodefalse
    model_code_dir
    weights_path

    If you've run MiniMax H3 locally, you've noticed the weird part: the sampler finishes and then you sit there. On a 768×1344, 124-frame clip the VAE encode plus decode is its own coffee break, and it scales with the video. This node is a straight replacement for the stock VAE Loader that runs H3's video VAE on a tuned PyTorch stack - without a TensorRT engine build.

    What it actually is

    A loader, and only a loader. You wire its single VAE output into the stock VAE Encode / VAE Decode nodes - same workflow, one node swapped. That's deliberate: the runtime implements the same first_stage_model interface ComfyUI's comfy.sd.VAE expects from the core MiniMaxH3VideoVAE, so tiled decode, device handling and latent scaling all keep working.

    The older route to a fast H3 VAE was a TensorRT engine build at fixed tiles. This is pure PyTorch, so there's nothing to build, and the author's same-tile A/B has it beating a locally-built TRT engine at 256/256 tiles (23.4s vs 26.2s). That's one RTX PRO 5000 72GB box, not a law of physics.

    How it goes faster

    Three things stack up:

    • Decoder. The QK RMSNorm + RoPE step is fused into a Triton kernel, the whole decoder is torch.compiled (max-autotune-no-cudagraphs), and spatial tiles run in batches per forward call rather than one at a time (tile_batch, default 2).
    • Encoder. GN/SiLU/padding fusions in Triton, channels-last-3d, internal padding, plus staged clip batching (encoder_staged_batch, validated at 1/2/4 with cut=2).
    • cuDNN autotune. cudnn_benchmark is on process-wide, worth ~4% in encoder steady state - at the price of a one-time search spike on the first conv per shape.

    Nothing here is "mathematically lossless," and the author is refreshingly blunt about that - VAE encode→decode is a lossy round trip by definition. The validated baseline is FP16, not BF16; BF16 isn't wired up or benchmarked, so don't assume the FP16 numbers transfer.

    The inputs you'll actually touch

    vae_name takes the same key-compatible safetensors the stock loader uses, or leave it on use default and set H3_VAE_WEIGHTS_PATH. dtype should stay fp16. warmup (none/decode/encode/both) runs a dummy pass at load using warmup_frames/warmup_width/warmup_height, so inductor's autotune cost lands at load time instead of mid-generation - set it to the resolution you actually render at. log_calls prints per-call encode/decode latency with a running mean, which is how you A/B a setting honestly instead of by vibe.

    decoder_tile_size=256 is stock behaviour; 368 is the fast campaign config, but the tooltip warns the RoPE coordinates are tile-normalized, so output shifts by about 20% mean. Fine if you're chasing seconds, not fine if you're comparing quality. encoder_tile_size=0 picks 672 up to 672×672 and 256 above it. Both tiles change overlap blending, so any tile change deserves a re-look at your footage. fast_linear, int8_decode and int8_encode are genuinely experimental: extra dependency, changed output, no fallback.

    Output is one thing: VAE. Into VAE Decode for generation, into VAE Encode if you're feeding an image or video in.

    Install

    Via ComfyUI Manager, search ComfyUI-H3VAE-PyOpt (registry publisher @fishelegs), or by hand:

    cd ComfyUI/custom_nodes
    git clone https://github.com/fishelegs/ComfyUI-H3VAE-PyOpt.git
    cd .. && python -m pip install -r custom_nodes/ComfyUI-H3VAE-PyOpt/requirements.txt
    

    The requirements file is short - torch>=2.8, triton, safetensors. Keep your existing CUDA-matched PyTorch rather than letting pip replace it. comfy-kitchen==0.2.34 is only needed if you flip on fast_linear or the INT8 switches.

    Then point it at the model code and weights and restart:

    export H3_VAE_MODEL_CODE_DIR=/path/to/MiniMax-H3/FL2VA/video_vae
    export H3_VAE_WEIGHTS_PATH=/path/to/minimax_h3_video_vae_fp16.safetensors
    

    Weights can also sit in ComfyUI/models/vae and be picked by name - the code directory is required either way. The repo ships no model code and no weights, and those weights are MiniMax's under the H3 Community License, which excludes the US, EU, UK and South Korea. Check that before you spend the evening.

    Where people get burned

    "Set H3_VAE_MODEL_CODE_DIR or pass model_code_dir." The loader wants the FL2VA/video_vae bundle directory - the one containing config.json and the source_path subdirectory with the klvae reference code. Pointing at FL2VA, or at the parent MiniMax-H3 folder, gets you config.json not found instead.

    A weight mismatch RuntimeError with missing/unexpected keys. You loaded something that isn't the H3 video VAE, or a re-export with renamed keys. It has to be the key-compatible export the stock VAELoader accepts.

    The first generation hangs, then everything is fast. That's inductor autotune (cached afterwards) plus the cuDNN search. Warmup is the fix; without it the first request eats it.

    "No available kernel." Older builds forced Flash SDPA; this one defaults MINIMAX_H3_TORCH_SDPA_BACKEND to auto so PyTorch picks a backend your GPU and shape actually support. Only force flash to reproduce a benchmark.

    Turning the experimental switches on and expecting a speedup. int8_encode is measured slower than the FP16 encoder - don't enable it for speed. int8_decode is the one that pays (~28% off decode), and it needs SM80+, fp16, CUDA and comfy-kitchen==0.2.34, and can't be combined with fast_linear. It errors loudly rather than quietly running FP16.

    Windows. Triton is the sticking point and every benchmark here is Linux; not impossible, just unverified.

    CategoryMiniMax_H3/Acceleration

    Inputs (19)

    NameTypeDefaultDescription
    vae_nameCOMBOuse defaultSame safetensors the stock VAELoader uses (key-compatible fp16/fp32 export). 'use default' requires H3_VAE_WEIGHTS_PATH.
    dtypeCOMBOfp162 options: fp16, fp32
    decoder_tile_sizeINT256128–1024Decoder spatial tile. 256 = stock behaviour (use this for an apples-to-apples A/B). 368 = the campaign's fast configuration, but RoPE coords are tile-normalized so the output shifts (~20% mean) -- verify quality.
    tile_batchINT20–8Decoder tiles per forward call. 0 = adaptive (up to 2, limited by free GPU memory); 1 = serial; 2 = tested default.
    compile_decoderBOOLEANtrueWhole-decoder torch.compile + QK RMSNorm/RoPE Triton fusion (max-autotune-no-cudagraphs).
    compile_encoderBOOLEANtrueEncoder GN/SiLU/padding Triton fusions + whole-graph compile + staged clip batching.
    encoder_staged_batchCOMBO4Encoder clips batched in the suffix stage (validated combinations with cut=2).
    cudnn_benchmarkBOOLEANtrueProcess-wide torch.backends.cudnn.benchmark. ~4% faster encoder steady state; first conv call per shape pays a search spike (~2 GiB at 336px) -- warmup absorbs it.
    warmupCOMBOnoneRun a dummy pass at load to absorb inductor compile + cuDNN search before the first real request. Use with the warmup shape parameters below.
    warmup_framesINT1241–1000
    warmup_widthINT134464–4096
    warmup_heightINT76864–4096
    log_callsBOOLEANtrueLog per-call decode/encode latency (running mean) for A/B comparison.
    encoder_tile_sizeoptINT00–10240 = measured auto choice: 672 up to 672x672, else 256. Changing tile can change overlap blending and output quality.
    fast_linearoptBOOLEANfalseExperimental FP16 decoder linear kernels via comfy-kitchen >=0.2.34. About 2-3% faster decode on the tested GPU, but changes output; no global --fast flag required.
    int8_decodeoptBOOLEANfalseExperimental CUDA FP16 decoder-only INT8 FFN path via comfy-kitchen==0.2.34. Strict opt-in: requires SM80+; mutually exclusive with fast_linear and has no fallback.
    int8_encodeoptBOOLEANfalseExperimental CUDA FP16 mixed INT8 encoder path: exactly eight 3x3x3 convolutions in prefix stages 0/1; other encoder operations retain their original precision. Measured slower than the default encoder; changes output. No fallback; independent of int8_decode.
    model_code_diroptSTRINGFL2VA video_vae bundle dir (klvae reference code + source config).
    weights_pathoptSTRINGDirect path override for the VAE safetensors.

    Outputs (1)

    NameTypeDescription
    VAEVAE