Swan Resize H3 Keyframes (Conditioning)
Split your H3 graph and your keyframes end up on the wrong grid
- conditioning
- reference_latent
- conditioning
This one's plumbing. It touches no pixels, generates nothing, and if you run MiniMax H3 in a single piece you'll never need it. It exists for exactly one situation: you took a fused two-stage H3 sampler apart into two SamplerCustom nodes, and now the low-res stage is being handed reference images at the target resolution instead of its own.
The problem it solves
H3 conditioning can carry keyframe or reference latents - the stills that anchor what the shot should look like - and those latents are built at the resolution you intend to end up at. Right for the high-res stage, wrong for the low-res one sampling a grid half that size.
The upstream SelfLift implementation resizes them internally, because it owns both stages at once. Split the graph - which is what the Swan_Bits two-stage wiring does - and nothing performs that resize any more. The low-res pass quietly receives full-resolution reference latents, and depending on your graph you get a shape mismatch, a memory spike on the stage that was supposed to be the cheap one, or references that don't line up with what's being denoised.
How it works
It walks the conditioning entries looking for minimax_keyframes, and for every keyframe latent it finds, resamples the spatial dims with trilinear interpolation. H3 video latents are 5D, so the frame axis is left alone and only height and width move. Entries with no keyframes pass straight through, which is why it's safe to bolt onto any conditioning.
It picks the target size two ways. Connect reference_latent and it locates the video stream inside that latent (whichever stream has 4 or 5 dims) and uses its grid exactly. Leave it off and it multiplies the keyframe's latent height and width by scale, snapping the result to even numbers - even because H3 packs in 2x2 patches, and an odd latent dim is a bad time.
That's the whole node. Source-code small, and it either works invisibly or produces a confusing error three nodes downstream.
Inputs and the output
conditioning is the conditioning carrying your keyframes. scale is the latent width/height factor applied to them; the default 0.5 is what you leave alone for a 2x lift, because halving the latent is what doubling the pixels looks like. It's ignored the moment reference_latent is connected.
reference_latent is optional and you should still connect it: it's the latent of the stage that will consume this conditioning - the low-res Empty H3 AV Latent. Using its grid directly avoids the rounding drift you get from scaling and snapping, and on odd-sized grids that drift is real.
The output is conditioning (CONDITIONING), the same kind of thing you fed in with the keyframes moved onto the right grid. It goes into the low-res sampler's positive input.
keyframe conditioning ──→ Swan Resize H3 Keyframes ──→ low-res SamplerCustom.positive
Empty H3 AV Latent (low) ──→ reference_latent
Don't put it on the high-res path
The high-res stage wants keyframes on the target grid - that's where they were built, and the tiling node slices them per tile on its own. Resizing them down for the high stage, or feeding it low-res references, gets you tiled H3 keyframes must match the target latent height and width. Low stage only.
Install
It ships in ComfyUI_Swan_Bits, category SwanBits/H3. Manager → search the pack title, or:
cd ComfyUI/custom_nodes
git clone https://github.com/swan7-py/ComfyUI_Swan_Bits
# restart ComfyUI
No requirements.txt, no pip install, nothing to download - but it imports from comfy.ldm.minimax, so you need a ComfyUI that ships H3 support (August 2026 or later). On anything older the pack won't import at all.
While you're here: this is a personal node pack (version 2.2.0, "a small personal collection") that ports code from facok/comfyui-SelfLift and slmonker/selflift-Avatar. Unusually for the custom-node ecosystem, it keeps a PROVENANCE.json recording the upstream commits and file hashes it came from. More diligence than most packs bother with - it's still unreviewed code like everything else in that folder.
Troubleshooting
Nothing changes. Your conditioning isn't carrying minimax_keyframes. Either the keyframe path isn't producing them, or the data you're thinking of lives elsewhere in the conditioning dict - this node only rewrites the keyframes and lets everything else ride along.
You scaled the wrong way. scale is a latent factor and the low-res stage is smaller. A low-stage latent at half the target grid is 0.5, not 2. Get it backwards and the low stage gets references bigger than its own latent, which is the problem you installed this to fix.
Odd-dimension weirdness. Scaling snaps to even dims, so on grids that don't divide cleanly the result can sit a couple of latent cells off what the sampler uses. Connect reference_latent and the argument disappears.
It didn't fix an OOM. It isn't meant to - this node only shrinks reference latents. If the stage is still heavy, the resolution dial is elsewhere.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| conditioning | CONDITIONING | — | |
| scale | FLOAT | 0.500.05–4 | Latent width/height factor applied to keyframe latents (e.g. 0.5 for a 2x lift). Ignored when reference_latent is connected. |
| reference_latentopt | LATENT | Latent of the stage that will consume this conditioning (e.g. the low-res Empty MiniMax H3 AV Latent). Its grid is used exactly, avoiding rounding drift. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| conditioning | CONDITIONING | — |