Nodes/From Mesh to Motion/Keyframe Sheet
ComfyUI Node

Keyframe Sheet

The labelled contact sheet you hand a rigger

By meryboth·Created 25 days ago·Updated 23 days ago· 1
Keyframe Sheet
  • view_a
  • view_b
  • view_c
  • sheet
◄namesfront,side,back►
◄timings►
◄thumb256►

Everything before this node is machinery. This is the artefact: 16 rows of poses, three columns of orthographic views, every row stamped with the time it happens. It's the thing you look at to decide whether the run is any good, and it's the thing the auto-rigger gets.

The row count is whatever the three view batches agree on. The layout is a grid: a header row with the view names, a label column down the left, thumbnails in the cells, 8px between cells, white background.

How it works

For each keyframe it writes the three panel crops out to a temp PNG, then composes the sheet from those files with Pillow. timings (the JSON list from Sample Keyframes) gives each row its label - k03 and, if the timing is there, 1.25s underneath it. No timing, and the row label degrades to just the index, which is a visible symptom worth chasing: it means the timings wire is missing somewhere upstream.

The thumbnail size is handled with thumbnail(), so each panel is scaled to fit inside the square without being stretched. That keeps the shared ortho scale readable: a limb at a given height in the front column is at that same height in the side column, and that property is the entire value of the sheet. The tools are segoeui.ttf → DejaVuSans.ttf → Arial.ttf, falling back to Pillow's built-in bitmap font if none exist - on a stripped Linux box the labels will look like 1980s console text, which is cosmetic only.

Why labels appear here and nowhere earlier. Text baked into an image that gets handed to a video or edit model comes back garbled and then regenerated as convincing nonsense. So the canvas, the clip and the panels all stay text-free, and the sheet - the last step, going to a human and a rigger - pays for the typography.

Inputs and outputs

view_a, view_b, view_c - the three batches from Triptych Split. names - the header labels; wire Split's names output rather than retyping, so the header can't contradict the columns. timings - Sample Keyframes' second output; optional in the sense that the node still runs without it, but without it you lose the seconds. thumb - 96 to 768, default 256. 256 is about a phone-screen sheet; 512–768 is what you want if you're actually inspecting poses, and it costs nothing to re-run since nothing upstream needs to change (the sheet is cached on its inputs).

One output: sheet (IMAGE). Send it to Save Image, and also feed it into Save Keyframe Manifest's optional sheet input so the same file lands in the deliverable folder as keyframe_sheet.png instead of living only in output/. One image, two destinations, no duplicate render.

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. If you see font warnings in the console, that's the fallback talking, not a failure.

Where people get burned

Reading the sheet as the truth about pose - and nothing else. It is authoritative on pose. It is not a quality check on the mesh, and it cannot tell you whether the character will deform from this rest pose. Generated meshes are triangle soup with machine UVs and no rig; if the body plan is going to break under a rig, the sheet will look fine right up until it doesn't. This pipeline's job is to give the rigger unambiguous poses, not to fix the geometry underneath.

Only looking at the front column. The pack's own workflow step 9 says it plainly: check the side column yourself, because the automated QA measures colour and a thin or awkward profile can pass the gate while looking wrong to a person.

A sheet that's suddenly 64 rows. count on Sample Keyframes, most likely.

Judging a looping motion on the last row. With loop set, the final keyframe is deliberately dropped so it doesn't duplicate the first. The sheet ends one pose short of the clip you watched, by design.

Categoryfrom-mesh-to-motion

Inputs (6)

NameTypeDefaultDescription
view_aIMAGE—
view_bIMAGE—
view_cIMAGE—
namesSTRINGfront,side,back—
timingsSTRING—
thumbINT25696–768—

Outputs (1)

NameTypeDescription
sheetIMAGE—