⬡ Polyhedron Transition Check
Did your clip move, or did it just fade?
- images
- first_frame
- last_frame
- info
- score
- report
Every first-to-last-frame workflow has the same silent failure mode. You gave the model two pictures, it gave you a clip, and what it actually did was dissolve one into the other over four seconds. There's no error, no warning, and if you're watching at 24 fps on a small canvas it can take a second viewing to notice. ⬡ Polyhedron Transition Check measures it instead of eyeballing it.
It works in two directions
Before a run, wire first_frame and last_frame only. It reports how much of the picture differs between the two, and whether the light differs - because little common ground between the keyframes is exactly the setup that produces a crossfade. That's a five-second sanity check that saves a five-minute render. Make the last picture from the first, as the pack's own docs keep saying, and the check agrees with you.
After a run, wire the decoded clip into images. Now it judges the passage from the clip's own first frame to its own last frame and classifies it:
- MOTION - something moved through; the in-between frames are not mixtures of the two ends. This is what you wanted.
- DISSOLVE - the in-between frames are mixes of A and B. The model faded.
- SNAP - the picture holds and then jumps at the end, reported with the second it happened. No in-between at all.
With images wired and the keyframes wired, it also tells you how close the clip's ends actually came to the keyframes you asked for - which is the difference between "the model moved between my frames" and "the model moved between something vaguely near my frames".
The one knob
pixel_level (0.08 default, 0.01–0.5) is how much a pixel has to change between first and last to count as changing, averaged over colour channels on a 0–1 scale. Higher and only the big structural changes get judged; lower and noise counts as motion. If your clip is a slow dolly in dim light, drop it. If it's a busy crowd scene, raise it or the motion score is meaningless.
Outputs
info is the verdict in words, with the measurements behind it. score is the number to watch: the median residual of the in-between frames. Near 0 means the middle frames are blends of the ends - a fade. High means they're something else entirely - a motion. -1 means it declined to judge: no clip wired, or a snap, or a still, or too short to say anything honest.
report is an IMAGE: first | middle | last frame, the pixels that change, and a small graph of progress (white) and residual (orange) over time, with the fade and motion thresholds dotted. Save it, look at it, then delete it. It's diagnostic apparatus, not a deliverable.
One structural note: this is an output node, so it executes on queue whether or not anything downstream needs it. Free, in the scheme of things, but don't wire report into a Save unless you want a PNG per render.
Why it earns its place
Ask anyone who's spent a year chaining video models and they'll tell you the same thing: the failures that hurt are the ones that look fine. Motion drift across chunk boundaries, a face quietly changing, a run that technically satisfies the prompt by crossfading. All of those are measurable with a residual curve and invisible in a contact sheet.
ControlNet gets a strength dial and start/end fractions so you can ask for 40% of a pose. Keyframe workflows have had nothing equivalent - the model either moved or it didn't, and you found out by watching. A number that says "this was a dissolve" belongs in the graph next to the render.
MiniMax Keyframes → Sampler → VAE Decode → Transition Check
│ first_frame/last_frame ───────────────┘
Transition Check.report → Save (when you want the picture)
Install
Manager, search "Polyhedron Suite", or:
cd ComfyUI/custom_nodes
git clone https://github.com/PolyhedronAI/ComfyUI-PolyhedronLoRAStack.git
# restart ComfyUI
No dependencies required - the pack's requirements.txt is a comment block saying as much. No model file either: this is arithmetic on tensors you already have, which is why it's the cheapest node in the pack to try.
Where it goes wrong
Judging a clip with a cut in it. A two-shot clip with a hard edit in the middle reads as a snap or a nonsense motion score. One shot per check.
Very short clips. Four frames isn't a passage; you get -1 and a shrug, correctly.
Interpreting score as quality. It isn't. A high score means "not a fade". Whether the motion is good is still your eyes' job - this just stops you shipping a dissolve you didn't ask for.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| pixel_level | FLOAT | 0.080.01–0.5 | How much a pixel must change between the first and the last picture to count as changing (mean of the colour channels, 0..1). Higher = only the big changes are judged. |
| imagesopt | IMAGE | The rendered clip (after the VAE decode). Judged as a passage from its own first to its own last frame: motion, dissolve or snap. | |
| first_frameopt | IMAGE | The first keyframe. Without images: the pair is checked before a run. With images: how close the clip's first frame came. | |
| last_frameopt | IMAGE | The last keyframe, the same way. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| info | STRING | What was measured and the verdict. |
| score | FLOAT | Median residual of the in-between frames: near 0 = a fade, high = a motion. -1 = not judged (no clip, a snap, a still or too short). |
| report | IMAGE | First | middle | last frame | the pixels that change, and the graph: progress (white) and residual (orange) over time, the fade and the motion threshold dotted. |