Nodes/From Mesh to Motion/Triptych Compose
ComfyUI Node

Triptych Compose

Glue the three renders into one canvas before the video model sees them

By meryboth·Created 25 days ago·Updated 23 days ago· 1
Triptych Compose
  • view_a
  • view_b
  • view_c
  • triptych
  • layout
◄namesfront,side,back►
◄gutter20►
◄pad20►
◄background#E4E4E6►
◄target_ratio21:9,16:9,4:3,1:1►
◄crop_margin0.08►

Generate a front view, then a side view, then a back view and you get three drawings of three slightly different characters in three slightly different poses. By keyframe 12 the drift isn't subtle. This node exists to make that impossible: it takes the three orthographic renders and concatenates them onto one 21:9-ish canvas, so the video model isn't animating three characters, it's animating one picture that happens to contain three panels.

The other half of the trick is upstream of this node and not optional: those three renders have to come off one Blender ortho camera pass, rotated object and fixed camera, so every view shares one scale and one pivot to the pixel. A limb at a given height in the front view is at that same height in the side view. Nothing downstream can restore that property if it isn't true at the start. (Image-to-3D gives you an impressive silhouette over topology nobody would rig by hand. That's fine here - the anchors are rendered from your mesh, not generated, and the mesh only has to be rigid and readable from every side.)

How it works

It concatenates. It never fits. If the three views differ in size it raises with The three views differ in size ... re-render them together, and that's on purpose - a quiet rescale here is invisible and poisons every pose anyone reads off the sheet.

Then it does two things worth knowing about:

A shared crop. Because ComfyUI hands IMAGE tensors around without an alpha channel, it can't use transparency; it finds the subject as "everything that isn't the flat background colour" and builds one crop window that contains the subject in all three views. Taking the union rather than each view's own box is the whole point - the side view of a character with a backpack is wider than its front, so cropping each to its own content would silently rescale them relative to each other. The window is re-centred on the canvas midline so the character's own axis stays meaningful, and the result is snapped to a multiple of 8.

Aspect-ratio padding. Partner video APIs take a fixed menu of ratios. Hand one an off-menu canvas and it centre-crops or letterboxes, and a centre-cropped triptych loses its outer panels outright. So the canvas gets padded to whichever ratio in the menu is closest - padding only ever adds background, so panel pixels and the shared ortho scale survive. Which ratio is closest depends on the subject: three panels of a squat mascot land near 21:9, three of a tall thin character near 16:9.

Inputs and outputs

view_a, view_b, view_c - left to right, in that order. names - labels, front,side,back; they travel with the layout so the split step and the sheet can't disagree about which column is which.

gutter (px between panels, 20) and pad (px of margin, 20) are the two you'll touch for framing. background defaults to #E4E4E6 - pick a flat colour your character never contains. crop_margin (0.08) is the breathing room around the union content box; 0 crops tight.

target_ratio is a STRING despite looking like a dropdown - 21:9,16:9,4:3,1:1 is just the default menu. If you'd rather not pad at all, typing none skips the ratio step entirely, which is handy when you're feeding a local video model that doesn't crop.

Outputs are triptych (the canvas - feed your video node's first-frame input, and Save Image it because you'll need that exact file back at the register step) and layout. That second one is a JSON string under the custom FMM_LAYOUT type: canvas width/height, gutter, pad, background, and the x/y/w/h of each panel. One wire carries all of it, it survives a save/reload of the graph, and it's readable JSON when a run looks wrong. The context-bus pattern, except genuinely load-bearing here: the split step reads the geometry back instead of re-deriving it, which is why a resized video still cuts on the right pixel columns.

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 only; no model downloads, no GPU. Needs a reasonably current ComfyUI (the pack asks for ≥ 0.3.40) because the graph uses Get Video Components and the MiniMax partner node.

Where people get burned

The size mismatch error is the big one, and it's a workflow error, not a node bug: render all three views in one Blender pass with one --size. Loading three PNGs someone rendered separately will trip it. And background must be the colour your renders are actually on, because everything downstream finds the character by looking for pixels that differ from it.

Second: if a view is entirely background - an empty or blown-out render - the crop falls back to the full frame instead of guessing, so your strip quietly stays wide and gets letterboxed downstream. No error. Check the canvas before you spend a paid video call on it.

Categoryfrom-mesh-to-motion

Inputs (9)

NameTypeDefaultDescription
view_aIMAGE—
view_bIMAGE—
view_cIMAGE—
namesSTRINGfront,side,back—
gutterINT200–256—
padINT200–256—
backgroundSTRING#E4E4E6—
target_ratioSTRING21:9,16:9,4:3,1:1Menu of aspect ratios; the closest to the strip is used and the canvas is padded to it. Partner video APIs centre-crop an off-menu canvas, which eats the outer panels.
crop_marginFLOAT0.080–0.5—

Outputs (2)

NameTypeDescription
triptychIMAGE—
layoutFMM_LAYOUT—