H3 Clock Remap (any model, presets)
De-rope any model, not just MiniMax H3
- hold_map
- length
- report
- profile_json
Here's the cleanest idea in this pack's experimental corner: the de-rope pipeline works by holding fast-motion frames to give the model more temporal room, then dropping the holds. That logic shouldn't care which model did the generating - but it does, because every video model has its own latent clock. H3's frames snap to a 17k+5 grid with 5 latent tokens per 17-frame group. LTX-2.5 runs an 8k+1 grid. A hold map built for one is garbage for the other.
H3 Clock Remap is the adapter: it takes any hold map (from the H3 Jerk Oracle, H3 Manual Hold Map, or H3 Drawn Plan) and retimes it onto another model's clock, so you can run the de-rope trick on a regenerating model that isn't MiniMax H3. This ships on a branch (generic-derope) and it's marked alpha - the LTX-2.5 graphs behind the author's derope demo page are the proof it can work, not the promise that it works everywhere.
How it works
The node knows each model's "profile": frames per latent time block, hold scale, legal length grid, fps. For the selected profile it scales holds of 2+ by the profile's hold_scale, quantizes them to whole latent time blocks, and carries the target's legal frame grid through the output map. Holds of 1 stay 1 - a plain single-frame hold is universal. The output feeds the rest of the pipeline unchanged: H3 Time Smear pads to the target's grid (LTX-2.5's 8k+1 instead of H3's 17k+5), and H3 Exact Recover / H3 Audio Recover invert the remapped map without knowing it wasn't native.
model_profile defaults to ltx-2.5, with minimax-h3 as the identity arm (the map passes through untouched - that's the sanity check). wan-2.2 (unmeasured) is in the dropdown with an honest label: no measurement behind it yet.
The custom path
custom exposes the raw fields - custom_block (frames per latent time block), custom_hold_scale (world frames one hold unit buys), custom_legal_step/custom_legal_offset (the legal length formula step*k + offset), custom_fps, and custom_cap_seconds (longest single pass before the retime needs splitting). That last one matters: the report "says when the profile is unmeasured or the retime needs more than one pass," which is the node's way of telling you the price before you pay it.
If you get a working custom row, you can save it as a preset by adding it to <user dir>/mainodes_models.json - the details are in the pack's DEROPE_ANY_MODEL.md. Outputs: hold_map (retimed), length (target-legal frame count), report (the price tag), and profile_json (the resolved profile for debugging).
Where people get burned
The obvious one: pick the wrong profile and the quantization silently shifts where your holds land. The less obvious one: this only fixes the timing grid - the model that regenerates still needs to be capable of the de-rope behavior, and a hold map retimed onto a model that hasn't been validated for it is an experiment, not a recipe. Treat the (unmeasured) label with respect.
Installing it
Same pack, pure Python:
cd ComfyUI/custom_nodes
git clone https://github.com/matlowai/ComfyUI-MAINodes
Restart, look under image/minimax/motion. Example graphs want ComfyUI-KJNodes; the nodes don't. And note the generic-de-rope arm currently lives on a branch - if the node isn't in your menu after a plain clone, that's why. Check the repo's branch list before assuming you installed wrong.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| hold_map | STRING | per-frame integer holds, oracle format | |
| model_profile | COMBO | ltx-2.5 | 4 options: minimax-h3, ltx-2.5, wan-2.2 (unmeasured), custom (use the fields below) |
| custom_blockopt | INT | 81–64 | custom: frames per latent time block |
| custom_hold_scaleopt | INT | 41–32 | custom: world frames one oracle hold unit buys |
| custom_legal_stepopt | INT | 81–64 | custom: legal lengths are step*k + offset |
| custom_legal_offsetopt | INT | 10–64 | — |
| custom_fpsopt | FLOAT | 25.0001–120 | — |
| custom_cap_secondsopt | FLOAT | 0.00–600 | custom: longest single pass in seconds; 0 = no cap |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| hold_map | STRING | — |
| length | INT | — |
| report | STRING | — |
| profile_json | STRING | — |