Nodes/ComfyUI-PainterNodes/Painter MiniMax FL2VA 2
ComfyUI Node

Painter MiniMax FL2VA 2

Pinning the first and last frame of an H3 clip without softening them

By princepainter·Created 9 months ago·Updated 11 days ago· 205
Painter MiniMax FL2VA 2
  • clip
  • vae
  • audio_vae
  • first_frame
  • last_frame
  • ref_audios
  • positive
  • upscale_positive
  • LATENT
  • upscale_width
  • upscale_height
◄prompt—►
◄width1344►
◄height768►
◄length124►
◄upscale_width1504►
◄upscale_height864►

MiniMax H3 is the model that changed the local video conversation in 2026: an omni-modal generator that emits a clip and a synchronised stereo soundtrack in one pass, instead of bolting a voice pass onto a silent renderer afterwards. If you're running the local weights in ComfyUI, this node is where the awkward part lives - telling the model exactly which frame the clip starts on, which frame it ends on, and whose voice it should be using.

Painter MiniMax FL2VA 2 builds that conditioning. "FL2VA" is the task name (first/last → video + audio), and the 2 is not a version bump - the pack keeps v1, v2 and v3 side by side because they're different shapes of the same idea. This one is the two-stage shape: a low-res pass, then a refine pass on a bigger canvas, with a second conditioning built specifically for the refine.

What it outputs, and why two conditionings

Five outputs: positive, upscale_positive, LATENT, upscale_width, upscale_height.

positive is your normal conditioning at width x height. upscale_positive is the same text and audio reference, but with the first/last frame anchors re-encoded at upscale_width x upscale_height, and the two INT outputs echo those canvas values so you can wire them straight into your upscaler without retyping them (the author's tooltip points at the pack's own latent upscaler for the refine pass).

The reason that second conditioning exists is the mechanism worth understanding. H3's anchor frames become conditioning rows, and those rows are encoded from image pixels at a fixed canvas. The layout derives its row count from that latent's spatial shape, and the model's forward pass compares the layout against the latent it's actually sampling. Feed low-res anchor latents into a high-res pass and the DiT resamples your anchor pixels - so the first and last frames come back soft, like the model only saw a thumbnail of them. Re-encoding the anchors at the upscale canvas gives the refine pass crisp hard anchors at the resolution it samples at. If the endpoint frames come back mushy after a refine, this is almost always why.

How it actually builds the conditioning

It builds an empty audio-video latent first: a 24-channel video latent plus a 32-channel stereo audio latent, bundled in a nested tensor. Then the encoding, in an order that matters. The first frame is resized with cropping disabled (you get the whole frame) and the last frame with a centre crop, both through vae. Reference audio is re-sampled to the audio VAE's rate and encoded through audio_vae. The prompt is tokenised once - text is canvas-independent - and both conditionings reuse those tokens.

Which brings us to the interesting part: the node patches two bugs in ComfyUI's own H3 implementation at import time. The first is a shared cond_video_latents field - the keyframe and reference branches both write it, and a reference with no video latent overwrites your anchors with an empty list, so the anchors just vanish. The second is a time-axis alignment bug: the conditioning segment's position on the packed timeline ignored the reference offset, so the anchors sat at RoPE positions that didn't line up with the video segment and attention couldn't hard-anchor them at all. The author's shorthand for that symptom is that anchoring degrades to "identity only" - the frame looks vaguely like your image instead of being your image. Both patches are idempotent and stay in memory, so having several FL2VA nodes installed side by side is fine.

If the patch can't apply because your ComfyUI build has no MiniMaxH3 in comfy.model_base, you don't get an error - you get a warning line in the console and soft anchors.

The inputs to set

clip, vae and audio_vae are the H3 text encoder, video VAE and audio VAE. prompt is multiline and is where the script goes - the voice follows the timbre of your reference audio, the words come from the prompt. width / height default to 1344x768 and snap to multiples of 32. length defaults to 124 frames, which is a touch over 5 seconds at 24fps; it steps in 17s and the node will round a manually typed value up to the next legal 17k+5 count for you (141 ≈ 5.9s, 158 ≈ 6.6s).

Optional: first_frame and last_frame (the hard anchors), and ref_audios, an autogrow input that expands into up to three reference-audio slots. Then the canvas pair, upscale_width / upscale_height, defaulting to 1504x864.

Reference the anchors in the prompt by tag or they mean nothing: <Picture 1> is the first frame, <Picture 2> the last (it becomes <Picture 1> when there's no first frame), <Audio 1> is the reference voice.

Install

Search PainterNodes in ComfyUI Manager, or:

cd ComfyUI/custom_nodes
git clone https://github.com/princepainter/ComfyUI-PainterNodes
cd ComfyUI-PainterNodes && pip install -r requirements.txt

Restart after. H3's weights are the heavy part (a 33B model with its own video and audio VAEs); the node itself imports torchaudio, which isn't in the pack's requirements but normally comes with ComfyUI's environment.

Gotchas

Check the licence before you build on this. The H3 weights ship under the MiniMax H3 Community License, whose grant is scoped to a territory excluding the US, the EU, the UK and South Korea - outputs included. That's a real constraint, not a formality.

The version numbers are not upgrades. FL2VA 2 and FL2VA 3 are siblings, not a ladder. If you want the refine pass, you want this one; FL2VA 3 has no upscale conditioning at all.

A module that fails to import doesn't crash the pack. The pack's __init__.py wraps every node import in a try/except and prints Failed to import PainterMiniMaxFL2VA2: <error> at startup instead. If your node is missing from the menu, read the console before reinstalling anything - grep that line.

Commit-pin your install. This is a fast-moving personal pack - nodes get added, renamed and deleted between releases, and one changelog entry removes sixteen nodes at once.

Categorymodel/conditioning/minimax

Inputs (12)

NameTypeDefaultDescription
clipCLIP—
vaeVAE—
audio_vaeVAE—
promptSTRING—
widthINT134432–16384—
heightINT76832–16384—
lengthINT1245–3600—
upscale_widthINT150432–16384Canvas width of the upscale_positive conditioning (snapped to 32). Feed the same value to PainterMinimaxH3LatentUpscaler for the refine pass.
upscale_heightINT86432–16384Canvas height of the upscale_positive conditioning (snapped to 32). Feed the same value to PainterMinimaxH3LatentUpscaler for the refine pass.
first_frameoptIMAGEFirst frame hard anchor (<Picture 1>)
last_frameoptIMAGELast frame hard anchor (<Picture 2>; becomes <Picture 1> when no first frame)
ref_audiosoptCOMFY_AUTOGROW_V3—

Outputs (5)

NameTypeDescription
positiveCONDITIONING—
upscale_positiveCONDITIONING—
LATENTLATENT—
upscale_widthINT—
upscale_heightINT—