Nodes/comfyui-obvpm-timeline/H3 MCtx Pin Spec (obvpm)
ComfyUI Node

H3 MCtx Pin Spec (obvpm)

How much of the old clip the new one has to keep

By chanon·Created 21 days ago·Updated 2 days ago· 33
H3 MCtx Pin Spec (obvpm)
  • mctx
  • pin_specs
  • pin_specs
window39
take_fromtail
take_from_frame0
placebefore
place_at_frame0
audio_window0
modemasked
mask_ramp_frames0
mask_ramp_edge0.00
mask_hold0.00

If you're extending clips with the loaders' convenience pins, you may never need this node. It's the manual transmission: pure data, no tensor work, describing which part of a source clip to carry into the next generation and where it sits in the target. Apply does the slicing later. Chain several to pin from several sources.

You reach for it when you want an explicit cut point rather than "the tail", an audio window, or a stack of pins from more than one source. The pack's own note: the loaders' create_pins collapses Load → PinSpec → Apply to Load → Apply for the two everyday cases, and everything fancier is this node's job.

The fields you'll actually touch

mctx is the source clip's bundle, from a loader. window is how many frames of it to pin, and only these lengths are whole latent steps: 5 is barely fluid, 22 nearly seamless, 39 and up pin more motion but cost you more of the new clip. 1 is reserved for a future keyframe path.

take_from picks the part of the source: tail for extend, head for prepend, or at_frame - the window ending at take_from_frame, which is how you extend from a timeline cut. That last one is where the grid bites: latent-grade cuts must land on H3's 17-frame grid, and misaligned cuts are refused with the nearest valid frames suggested. The convenience loaders auto-shift instead; this node makes you decide.

place is where the pinned run sits in the target: before for context leading in (extend), after for context leading out (prepend). at_frame - kept interior content, i.e. repainting a middle section - is declared but not implemented yet; Apply refuses it with a pointer rather than half-doing it.

audio_window is frames of tail audio to pin, end-aligned with the video window. 0 follows the video window, which is what you want unless you're deliberately offsetting.

The mode switch, and why it's the interesting part

mode has three values and they're not cosmetic:

  • guide - the window rides as native H3 keyframe conditioning rows and the model re-renders it, which then gets trimmed. Works with any ladder window. Measured caveat: video pins reproduce, audio pins don't.
  • masked - the window's latents are written into the target and preserved with a denoise mask. Exactly one rendering of that window exists, so the level-step and post-overlap flicker mechanisms of the guide route can't occur, and pinned audio is kept verbatim rather than asked for. This is the default, and it's what makes a masked extend seamless. It needs ComfyUI 2026-08-18 or later (core PR #15375 added per-stream H3 denoise masks) and a window on the shared AV grid - 39, 90, 141, 192 - where the video run is legal and the audio ends on whole 40 Hz ticks. A 22-frame window is a legal guide pin but not a legal masked one.
  • both - the same slice as both. The keyframe rows give the model something to steer toward; the mask makes the arrival exact. They agree by construction because it's one slice from one source, so they reinforce instead of fighting. The cost is the extra conditioning tokens.

Then the mask ramps, which control how much of the destination is already there. The mask is a strength, not a flag: core runs the row at sigma = mask × sigma, so 0 preserves verbatim and 1 generates freely. mask_ramp_frames softens the hold over N frames measured back from the join; mask_ramp_edge is the value at the join (a landing strip for the model); mask_hold is the value through the rest of the window.

The one piece of empirical advice in the tooltip is worth repeating verbatim in spirit: prefer a ramp landing one frame past a latent step offset - 6, 10, 14, 19, 23 - because a ramp landing exactly on an offset (5, 9, 13, 17, 22) zeroes that step and loses it from the run's runway.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/chanon/comfyui-obvpm-timeline

Restart ComfyUI. No pip installs - the pack's pyproject is deliberately empty since ComfyUI already provides torch, safetensors and av. You want ComfyUI 2026-08-13+ for core H3, and 2026-08-18+ if you intend to use masked pins. Manager: search comfyui-obvpm-timeline, or obvpm in the node menu.

Output: pin_specs, your upstream stack (via the optional pin_specs input) plus this pin's spec. Wire it to H3 MCtx Apply Pins; Trim and Save read what Apply resolved.

Troubleshooting

  • "Misaligned cut" refusal. Your take_from_frame isn't on the 17-frame grid. Take the suggestion, or switch to a convenience pin and accept the auto-shift - but be aware the model plants its anchor where it lands, and a half-step anchor is a soft join, not an exact one.
  • Masked mode errors on a 22-frame window. Expected: 22 isn't on the shared AV grid. Use 39, or switch to guide.
  • Everything looks frozen where it should move. mask_hold above 0 with no ramp holds the whole window softly. If you wanted a fixed wall, that's mask_hold: 0; if you wanted a handover, you need the ramp.
  • at_frame placement. Not implemented. Interior repainting is a future phase.
  • Multiple sources. Chain specs through the pin_specs input rather than expecting one node to do two.

Pin Spec is where the pack's most opinionated engineering lives. If your joins are good and you never open it, that's the convenience pins working exactly as intended.

Categoryobvpm/h3

Inputs (12)

NameTypeDefaultDescription
mctxOBVPM_H3_MCTXThe source clip's latents+lineage bundle from H3LoadVideoWithMCtx.
windowCOMBO39Frames of the source to pin. Only these lengths are whole latent steps. 5 is barely fluid, 22 nearly seamless; longer windows pin more motion but cost more of the new clip. 1 is reserved for the future keyframe path.
take_fromCOMBOtailWhich part of the source supplies the window: tail = the end (extend), head = the start (for future prepends), at_frame = the window ENDING at take_from_frame (a timeline cut). Latent-grade cuts must land on the 17-frame grid; misaligned cuts are refused with the nearest valid frames.
take_from_frameINT00–4096Only used when take_from is at_frame: the delivered frame the window ends at (your cut point).
placeCOMBObeforeWhere the pinned run sits in the target: before = context leading in (extend; re-rendered at the head, trimmed). after = context leading out (prepend; generation runs toward it, re-rendered at the tail, trimmed -- typically with take_from=head). at_frame (kept interior content) is NOT YET IMPLEMENTED.
place_at_frameINT00–4096Only used when place is at_frame (not yet implemented).
audio_windowINT00–240Frames of tail audio to pin, end-aligned with the video window. 0 follows the video window.
modeCOMBOmaskedguide: the window rides as native keyframe cond rows and the model RE-RENDERS it (trimmed after; any ladder window). masked: the window's latents are written INTO the target and preserved with a denoise mask -- one rendering, no level step, audio kept verbatim. Needs ComfyUI >= 2026-08-18 and a window on the shared AV grid (39/90/141/...). both: the same window as BOTH -- the keyframe rows give the model something to steer toward, the mask makes the arrival exact. They agree by construction (one slice, one source), so they reinforce rather than fight; the cost is the extra cond tokens.
mask_ramp_framesINT00–256Masked/both only. Softens the hold over this many frames measured back from the JOIN -- the window edge facing the delivered content (an after-pin's first frame, a before-pin's last). 0 = the whole window is held at mask_hold. The mask is a strength, not a flag: core runs a row at sigma = mask * sigma, so 0 preserves verbatim, 1 generates freely, and between them the model is told how much of the destination is already there. The softened frames are DELIVERED rather than trimmed (the source enters past them), so this is the run's remaining runway to arrive in. Prefer a value one frame PAST a latent step offset -- 6/10/14/19/23 -- since a ramp landing exactly on an offset (5/9/13/17/22) zeroes that step and loses it from the runway.
mask_ramp_edgeFLOAT0.000–1The mask value AT the join, falling to mask_hold across mask_ramp_frames. Raise it to give the model room to bend into the destination (a landing strip); leave it 0 to keep the join exact. Ignored when mask_ramp_frames is 0.
mask_holdFLOAT0.000–1The mask value through the rest of the window. 0 = verbatim (the default, and what makes a masked extend seamless). Raising it with mask_ramp_frames at 0 holds the WHOLE window softly, so the pinned rows join the flow instead of standing as a fixed wall.
pin_specsoptOBVPM_H3_PINSPECSUpstream pin stack to append to.

Outputs (1)

NameTypeDescription
pin_specsOBVPM_H3_PINSPECSThe upstream stack plus this pin's spec. Feed H3MCtxApplyPins; Trim/Save ride the resolved pins wire Apply produces.