Nodes/From Mesh to Motion/Save Keyframe Manifest
ComfyUI Node

Save Keyframe Manifest

A folder of PNGs is not a deliverable

By meryboth·Created 25 days ago·Updated 23 days ago· 1
Save Keyframe Manifest
  • view_a
  • view_b
  • view_c
  • sheet
  • manifest_path
  • handoff_text
◄namesfront,side,back►
◄timings►
◄subject_namecharacter►
◄motion_description►
◄fps24.00►
◄loopfalse►
◄output_dirfrom_mesh_to_motion►
◄fps_in—►

You've got 48 crops of a character in three views, and you're about to hand them to a rigging tool that has never seen your graph, your prompt, or your seed. Which pose happens when? Which column is front? What was the motion supposed to be? A folder answers none of that.

So this node writes the folder and the answers: keyframes.json, handoff.txt, and the per-view PNGs in a views/ subdirectory, all under ComfyUI/output/<output_dir>/.

How it works

It walks the shortest of the three batches, writes each panel to views/k00_front.png, k00_side.png, k00_back.png, k01_front.png... and builds a manifest in which every path is rewritten relative to the manifest's own folder, so you can zip the directory, mail it, and the links still resolve.

The JSON carries a schema version and a UTC timestamp, the subject name, the motion (description, loop flag, fps, computed duration, keyframe count), the view order, "projection": "orthographic", the sheet path if you wired one, a keyframe array of {index, t, frame, views}, and a provenance block naming the generator. That last one is the point of doing this in a file rather than by memory: a run you can re-read in three months and argue with.

handoff_text is the paragraph to paste next to the sheet in Astra - it states the grid, the number of views, and the property that makes the sheet usable: every view shares one camera scale and one pivot, so read the pose from all three together, not the front alone. That sentence is doing real work with a rigger that might otherwise assume the views were independent photos.

Inputs and outputs

Required: view_a, view_b, view_c (from Triptych Split), names, timings, subject_name (goes in the manifest as the subject), motion_description (paste Motion Prompt's summary output here and the record matches the prompt that was actually sent), fps, loop, and output_dir (default from_mesh_to_motion).

Optional: sheet - wire Keyframe Sheet's output and it's written into the same folder as keyframe_sheet.png. fps_in - wire Get Video Components' frame rate and it overrides the fps widget so the manifest's timings describe the clip that came back.

Outputs: manifest_path and handoff_text, both strings. It's an output node, so it runs on every queue whether or not anything reads it.

The one error it raises, on purpose

If it gets fewer timings than keyframes, it stops:

Save Keyframe Manifest got timings for 0 of 16 keyframes.
Wire Sample Keyframes' `timings` output into this node's `timings` input.

That's a deliberate refusal, and the reason is in the code comment: an earlier version guessed t = index / fps when the input was unwired, which put k01 at 0.04s instead of 0.33s and handed a rigger a sheet whose timing was eight times off - plausibly, silently. Timing is the one thing that cannot be recovered from the images, so guessing there is worse than failing.

It's also the pack's most common first-run error, and it means the workflow you loaded is missing a link. Reload the workflow file rather than hand-patching it.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/meryboth/from-mesh-to-motion
# restart ComfyUI

Or ComfyUI Manager → from-mesh-to-motion, or comfy node install from-mesh-to-motion. Pillow and numpy. The clone route is also what gives you the example assets and the write-up - the registry package deliberately leaves them out.

Where people get burned

output_dir is a folder name, not a path guard. Change it between runs and the old folder stays where it was; nothing cleans up, and it's easy to hand over last week's keyframes.json from the sibling directory.

subject_name defaults to character. Harmless until you have three of these folders.

Don't expect the metadata to travel with the PNGs. ComfyUI's saved images embed the workflow, so a single Save Image panel is self-describing - but these are crops written by the node's own Pillow path, not by Save Image, and the rigging tool will never see the graph anyway. The JSON is the metadata. Keep it next to the images.

Categoryfrom-mesh-to-motion

Inputs (12)

NameTypeDefaultDescription
view_aIMAGE—
view_bIMAGE—
view_cIMAGE—
namesSTRINGfront,side,back—
timingsSTRING—
subject_nameSTRINGcharacter—
motion_descriptionSTRING—
fpsFLOAT24.001–120—
loopBOOLEANfalse—
output_dirSTRINGfrom_mesh_to_motion—
sheetoptIMAGE—
fps_inoptFLOAT—

Outputs (2)

NameTypeDescription
manifest_pathSTRING—
handoff_textSTRING—