Nodes/Polyhedron Suite/⬡ Polyhedron MiniMax Keyframes
ComfyUI Node

⬡ Polyhedron MiniMax Keyframes

Pinning the first and last frame of an H3 clip

By PolyhedronAI·Created 5 months ago·Updated a day ago· 7
⬡ Polyhedron MiniMax Keyframes
  • clip
  • vae
  • latent
  • first_frame
  • last_frame
  • positive
  • latent
  • info
  • prompt_text
◄prompt►
◄frame_fitcore (first: stretch, last: cover)►
◄tags►

First-frame/last-frame conditioning is the most useful trick in video generation and the most misused. You give the model two pictures and it paints the way between them. ⬡ Polyhedron MiniMax Keyframes does that for H3 - and unlike the core node, it takes the latent from a wire instead of building one under the hood, so the size and length come from whatever you already decided upstream.

What "from a wire" buys you

Core's MiniMaxH3ImageToVideo makes its own latent. This one reads width, height and frame count out of the joint H3 AV latent you hand it and pins the first frame at pixel frame 0 and the last at frame_count - 1 of exactly that latent. So when you build 121 frames at 832×480 in your Empty Latent, or continue a scene with a specific carried head, your keyframes land where you meant them to instead of on a latent core silently re-derived.

Inputs are clip, vae, latent, prompt, frame_fit - plus optional first_frame, last_frame and tags. That clip pin is the one to respect: the keyframes are woven into the tokens by the H3 text encoder in fl2va presentation. This is not an ordinary text encode, and moving it upstream of the latent decisions breaks it.

No image wired means no keyframes, which means a plain text run - H3's t2va and fl2va are the same model, so the node degrades gracefully rather than erroring.

frame_fit, and the aspect report

frame_fit has three settings, and the default is core (first: stretch, last: cover): the first frame is stretched to the canvas because it's the geometry anchor, the last is cover-cropped. stretch both and cover both are there when you want one rule for both.

Whichever you pick, the info output names every frame whose aspect doesn't match the canvas. That's the thing you actually want to read before a run: a stretched first frame is a squashed face, and you'll notice it 90 seconds into a render if you didn't notice it in the info string.

The prompt, and the tag trick

The official H3 guide expects an alignment line for a pinned first frame - at 0.00 seconds ... <Picture 1> is fully referenced. The node's info output tells you which <Picture n> is live, because the numbering shifts: the last frame is <Picture 2> only when a first frame is wired, otherwise it's <Picture 1>.

tags is the quality-of-life fix for that numbering. One per line:

start = first_frame
goal = last_frame

Then write @start and @goal in the prompt and the node substitutes the live <Picture n> before the encode. An unknown or unwired tag stops the run by name instead of silently producing a prompt that refers to a picture that isn't there. @first_frame and @last_frame work without aliases if you don't want to bother.

Outputs: positive, latent (passed through), info, and prompt_text - the resolved prompt, so you can print exactly what was encoded instead of trusting it.

The trap nobody warns you about

If your two keyframes don't share camera, light and background, H3 doesn't move the subject from A to B. It crossfades. You get a two-second dissolve wearing a keyframe workflow's clothes.

The fix is in the pictures, not the node: make the last frame from the first - an image edit of it - and describe the way rather than the two states (first state → what visibly changes → last state), all in one shot. If you need motion that follows something real, add a pose video through Control Map + Apply Control. And if you want to know whether the run moved or dissolved, Transition Check measures it after the decode.

Install

Manager → "Polyhedron Suite", or:

cd ComfyUI/custom_nodes
git clone https://github.com/PolyhedronAI/ComfyUI-PolyhedronLoRAStack.git
# restart ComfyUI

No dependencies required. You need the H3 text encoder as clip, the video VAE (each keyframe is encoded with it and pinned into the conditioning, never denoised), and an AV latent from Polyhedron Empty Latent or Continue.

Common issues

The Media Loader placeholder. With nothing loaded, the loader emits a checkerboard; this node refuses it by name rather than pinning a checkerboard as <Picture 1>.

Frames that look squashed. Read info before you queue. frame_fit is doing what you told it to.

A last frame that renders a quarter-second early. In H3's 17k+5 grid the last pixel frame isn't where people assume it is; the node pins at frame_count - 1 and reports, so if you wanted an anchor rather than an endpoint, that's a Continue anchor, not a keyframe.

CategoryPolyhedron/Conditioning

Inputs (8)

NameTypeDefaultDescription
clipCLIPThe MiniMax H3 text encoder. The keyframes are woven into the TOKENS here (fl2va presentation) -- this is not an ordinary text encode and cannot be moved upstream.
vaeVAEThe VIDEO vae. Each keyframe is encoded with it and pinned into the conditioning, never denoised.
latentLATENTA joint MiniMax H3 AV latent -- from Polyhedron Empty Latent with latent_type 'MiniMax H3 AV'. Width, height and frame count are READ from it; the last frame is pinned at frame_count - 1 of exactly this latent.
promptSTRINGThe H3 prompt. With a first frame the official guide expects the alignment line 'at 0.00 seconds ... <Picture 1> is fully referenced'; the info output tells you which <Picture n> is live. No image wired = a plain text run.
frame_fitCOMBOcore (first: stretch, last: cover)How a keyframe meets the canvas. 'core' is Core's rule: first frame stretched (geometry anchor), last frame cover-cropped. A still whose aspect differs from the latent is squashed by 'stretch' and trimmed by 'cover' -- the info output names the offenders.
first_frameoptIMAGEPinned at pixel frame 0. Becomes <Picture 1> in the prompt.
last_frameoptIMAGEPinned at the latent's last pixel frame. Becomes <Picture 2> when a first frame is wired, else <Picture 1>.
tagsoptSTRINGSpeaking names for the two frames, one per line: 'start = first_frame', 'goal = last_frame'. In the prompt write @start -- the node puts in the LIVE <Picture n> (the last frame is <Picture 2> only when a first frame is wired). @first_frame / @last_frame work without an alias. An unknown or unwired tag stops the run by name.

Outputs (4)

NameTypeDescription
positiveCONDITIONING—
latentLATENT—
infoSTRING—
prompt_textSTRING—