Nodes/CoachBate/CoachBateShotKeyframes
ComfyUI Node

CoachBateShotKeyframes

Start and end frames, placed at the right timestamps, from the shotlist

By CoachBate·Created 3 months ago·Updated 22 days ago· 2
CoachBateShotKeyframes
  • conditioning
  • latent
  • vae
  • conditioning
  • latent
◄shot_payload—►

Anchors without anchor maths

Keyframe conditioning is the most reliable way to make a video model land a shot: give it the first frame, give it the last frame, let it fill the middle. In a shot-by-shot pipeline it's also the most annoying thing to keep straight, because the second anchor's position isn't zero - it's the end of the shot, which is duration × 24 frames, and if the shot continues from a previous one there's a context prefix in front of all of it.

CoachBateShotKeyframes does that arithmetic and hands the result to the installed H3 keyframe nodes. Inputs are conditioning, latent, vae and shot_payload; outputs are conditioning and latent. It reads start_image, end_image, start_image_strength, end_image_strength and edit_duration_seconds (falling back to duration_seconds) out of the shot record, opens the images, and places them: the start image at frame 0, the end image at context + ceil(edit_duration × 24) − 1.

If the shot has no usable anchors - no images, or a strength of 0 - it returns your inputs untouched. No images, no work, no cost.

Soft anchors or hard ones

hard_frame_anchors in the shot record chooses the mechanism, and it's a real difference:

  • Off (default): the node calls MiniMaxH3CustomKeyframes, which applies the images to the conditioning. The frames guide the model. Cheaper, more forgiving, and the model can still move away from them if the prompt argues.
  • On: it calls MiniMaxH3CustomKeyframesMasked and applies them as real masked anchors on the latent, so those frames are held. This is the "the first frame must be this frame" mode.

Hard anchors need a two-stream AV noise mask, so the node compacts Concat AV's channel-expanded masks down to a single channel per stream before handing them over - and it refuses if the mask is channel-dependent, because silently dropping channels would change values rather than just shapes.

One conflict is caught explicitly: with a continuation context present (you're continuing from a previous shot's output), a start_image isn't allowed, because the model can't start from two different first frames. The error says exactly that - "Use a source video OR a first-frame still, not both" - and it's the right call, even if you'll hit it once by accident.

Why the timestamp maths matters

Everything in this pipeline is pinned to 24fps: frame counts are duration × 24, continuation context lengths are 39/90/141 frames, and the latent grid is 17k+5. If you place an end anchor by hand at "duration seconds × 24" while a continuation prefix is in front of the clip, the anchor lands 39 frames early and H3 dutifully builds the shot to end before it should. Letting the node compute the position from the same fields the rest of the pipeline uses is the whole point; it's not a convenience wrapper, it's the thing that keeps the anchors on the grid.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/CoachBate/ComfyUI-CoachBate.git

Or ComfyUI Manager → search "CoachBate" → restart. Category CoachBate. It needs ComfyUI's H3 keyframe nodes present - MiniMaxH3CustomKeyframes and, for hard anchors, MiniMaxH3CustomKeyframesMasked - and the H3 model set (model, text encoder, video and audio VAE) in the usual folders. No pip dependencies of its own.

Where people get burned

  • The shot ignores the end frame. end_image_strength is 0, or the image path doesn't exist - which the loader already reports as 0.0 strength with a toast, so check there first.
  • "Use a source video OR a first-frame still, not both." - continuation plus a start_image. Pick one; the continuation already supplies the first frames.
  • Hard anchors error out about the noise mask. Something upstream produced a channel-expanded mask that can't be compacted; check what's feeding the latent.
  • The end frame lands a beat early. You set edit_duration_seconds shorter than duration_seconds on purpose - which is correct if that's your editorial intent, and confusing if it isn't.
CategoryCoachBate

Inputs (4)

NameTypeDefaultDescription
conditioningCONDITIONING—
latentLATENT—
vaeVAE—
shot_payloadSTRING—

Outputs (2)

NameTypeDescription
conditioningCONDITIONING—
latentLATENT—