Render a Motion Test as Checkable Frames, Then Make the Review Movie
Render a Motion Test as Checkable Frames, Then Make the Review Movie
A movie export is a closed door. It opens or it doesn't. When it opens, you get a file that plays, and almost nothing about whether the motion inside it is the motion you meant. For a treatment designer watching a motion study burn through render time, that is a bad bargain: the one artifact you can inspect is the one that hides the most.
The way out is to stop treating the movie as the thing you made. Render the test to an image sequence, check that sequence as a set of files, and treat the review movie as a derivative assembled from a sequence you have already verified. Four moves, in this order:
- Freeze the edition before the first frame renders — source state, frame range, sampling rate, format, output folder.
- Check the sequence three separate ways: every expected file present, every file readable, every frame from the same edition.
- Repair a gap only against an unchanged source. If the source moved, start a new edition rather than patching the old one.
- Encode with an explicit input rate, then compare the finished movie's frame count and duration against the sequence you verified.
None of that is exotic. It is the difference between a file that exists and a frame you can defend.
Freeze the edition before you render
"Edition" is a small idea with a lot of load. It means one state of the source, one set of render conditions, one output folder, and a name that says which is which. Adobe's rendering documentation keeps composition and render conditions separate from output encoding and naming, and that separation is exactly what you want to make explicit: the thing that was rendered, and the thing that came out of the render, are two decisions, and both should be written down before you start.
A manifest you can read in ten seconds is enough:
edition: E2
source: motion_test.aep — comp "Motion Test"
range: 1–6
rate: 6 fps
format: PNG, RGBA
output: motion_E2/
expected: 6 files, frame_0001.png … frame_0006.png
A production render-farm job database is overkill here. A text file beside the frames, or a note in the project, will do — as long as it lives with the output and not only in your head.
Derive the count from the actual range rather than assuming it. Frames 1 through 6 inclusive is six, not five. A range of 0–100 inclusive is 101, not 100 — an off-by-one that will follow you into every later check. Derive the duration the same way: frames divided by rate. Six frames at 6 fps is 1.000 second.
That 6 fps is a drill value chosen so the arithmetic is visible; your real motion test will declare whatever rate the motion was authored at, 24, 25, or 30, and the division works the same. What matters is that the rate is declared with the range, because it is the thing that tells the numbered frames what they mean. Frames 1–6 are a second of motion at 6 fps and a quarter second at 24. Same pictures, different claim.
One more piece of insurance worth adding to a synthetic or motion-test render: a slug in the corner of each frame carrying the edition and the frame number, and maybe a distinct background hue per edition. A version burn-in earns its keep on a motion test. If you'd rather not put anything in the picture, the edition has to live in the folder name — which works, but it means your edition check depends on where a file is, not on what it is. When the check depends on location, a copy-paste can beat it.
Three ways a sequence lies
Missing, damaged, and stale frames fail differently, and only one of them is loud.
Missing frames are the honest failure. The file isn't there. This is trivially caught by comparing what's on disk against the manifest list — and it is still missed all the time, because people scrub the movie instead of listing the folder. A short movie plays fine. It just plays less.
Damaged frames are the quiet one that the renderer's own documentation warns about. Adobe's page on rendering an image sequence across multiple machines notes that the Skip Existing Files option skips frame files that are already present, including files still in progress, and warns that a computer stopped mid-render can leave an unfinished frame (After Effects, Automated rendering and network rendering, read 2026-09-18). Read that twice, because it cuts two ways. A file can exist, be counted, be skipped on every subsequent attempt, and still not be a usable picture. And the option that skips it will keep skipping it, because skipping is decided on the filename.
Skip Existing Files is a resumption convenience, not a validator. It knows a name is taken. It does not know what is inside.
Stale frames are the reason this whole workflow exists. The file is present, it opens, it is the right format and the right dimensions. It is also from an earlier state of the source. A sequence can be complete by name, valid by readability, and still be a mix of two editions — because a render was resumed into a folder that already held output, or a frame was re-rendered and dropped in beside older ones, or an older set was copied in to "fill" a gap.
Filename continuity will not catch this. Nothing in the file's name or its metadata is obliged to be truthful about which version of the comp produced it. What catches it is looking at the frame, which is why the burned-in edition slug from the previous section is the cheapest insurance in this whole method.
The six-frame drill
Here is a paper example, not a render anyone performed. Six frames, deliberately small so every one fits in view. The checks scale unchanged to six hundred.
The setup: frames 1–6, each a synthetic image with a large numeral in the center and a corner slug reading the edition and frame — E2 · 04 — with the two editions given different background hues so a mismatch is visible at thumbnail size. Declared rate 6 fps, so a correct assembly is 6 frames and 1.000 second.
Three variants:
| Set | Files present | Each opens | One edition | What it is |
|---|---|---|---|---|
| A — complete | 6 | yes | yes | the good case |
| B — gap | 5 (03 absent) | yes | yes | a missing frame |
| C — stale | 6 | yes | no — 04 is E1 |
a stale frame |
Set A passes all three checks and encodes to 6 frames, 1.000 second. This is the only variant where the movie's properties are evidence of anything.
Set B fails the first check immediately: five files where six were expected. You do not need to encode it to know. If you encode it anyway, the result depends on how you asked for it. FFmpeg's formats documentation describes the image2 input demuxer as reading patterned, numbered images and expecting consecutive sequence numbers, with an explicit input frame rate you set yourself (FFmpeg, Formats, image2 section, read 2026-09-18) — I am reading that page, not running it. In a public issue from 2023, in an unrelated timelapse tool, a reporter describes a render stopping short when a number was missing from the sequence (moonraker-timelapse issue 126, 28 July 2023). That is one person's report in one project, not a law about every encoder; treat it as a reason to check your own build, not as a guarantee of what yours will do. The practical point holds either way: a gap can produce a short movie or an early stop, and neither is a completeness signal.
Set C is the instructive one. Six files, six names, all six open cleanly, and the encoder will exit successfully with 6 frames and 1.000 second — exactly the same numbers as the good set. Every automated property agrees. The only detector is the picture: frame 4 carries the E1 slug and the wrong hue, and it is a single instant buried in a second of motion, sitting somewhere in the middle where a hard cut or a sudden hold might pass as intentional.
That is the asymmetry worth remembering. Missing and stale frames both make the motion wrong, and they require different evidence. A count catches the first. Only a look catches the second.
Repair only against an unchanged source
Four rules, in rough order of how often people break them.
Repair against a frozen source, and only then. If the source and the render settings have not changed since the frames were rendered, a single missing or damaged frame can be re-rendered on its own into the same edition folder. The edition name is a promise that this is true. Keep it true or change the name.
Remove the bad file before you re-render it. An unfinished frame is present, and Skip Existing Files skips what is present. Render over it without deleting or moving it aside first and the skip can land on it again: the same unusable frame stays exactly where it was, while the render reports success. Nothing about that report distinguishes it from a clean pass. Delete the file, or render the repair into a clean folder and assemble from there, so the repair render has to write a new frame rather than decide it already has one.
If the source changed, start a new edition. This is the rule that matters most and gets bent most. The question is not "does frame 4 look right?" It is "are all six frames from the same state of the source as each other?" Once the comp has moved on, the surviving frames are of unknown provenance relative to the new source, and quietly re-rendering the one frame you noticed leaves the other five unverified against the version you are now calling current. New edition, new folder, whole range, and then the frame 4 mistake is impossible by construction. Keep the old set. It is the record of what you rendered before, and it is the comparison that tells you whether the change did what you meant.
Never renumber to close a gap. Renaming 4 to 3, 5 to 4, and 6 to 5 turns five frames into a set of names that looks complete and now maps to the wrong instants. The frame number is a time coordinate. Shifting it moves every later frame's moment, and it does so invisibly, which is a strictly worse failure than the gap you started with.
Encode with the rate you declared
FFmpeg's image2 demuxer distinguishes sequential-number patterns like frame_%04d.png from glob patterns like frame_*.png, and takes an explicit input frame rate (FFmpeg, Formats, image2 section, read 2026-09-18). Two things follow.
First, the input rate is a statement about how to read the pictures, and it belongs on the input side:
ffmpeg -framerate 6 -i frame_%04d.png \
-c:v libx264 -pix_fmt yuv420p -crf 18 review.mp4
The -framerate before -i tells the demuxer that the numbered files represent six pictures per second. Get it wrong and the movie plays at the wrong speed — 6 frames read at 24 fps is a quarter second, not one. Do not expect an output-side rate setting to repair a wrong input reading; at best it re-times what the input already decided, duplicating or dropping pictures along the way.
Second, the two pattern forms behave differently around a hole, and the difference is not in your favor. A sequential pattern is told to expect consecutive numbers. A glob matches whatever filenames happen to be there — so a gap may not stop the read at all, and you can get a clean exit on a movie assembled from five files. Either way, the encoder finishing tells you the encoder finished. It does not tell you how many files it read or whether the right six were among them.
If your numbering does not start at 1, say so with -start_number rather than renaming files to fit. If the motion test has sound, bring it in as its own clearly identified input and check its alignment separately:
ffmpeg -framerate 6 -i frame_%04d.png -i scratch_audio.wav \
-c:v libx264 -pix_fmt yuv420p -crf 18 -c:a aac -shortest review.mp4
Note what -shortest does: it truncates to whichever stream ends first. That is usually what you want and it is also a fine way to hide a short video. If the audio is 0.4 seconds and the video is meant to be 1.0, you will get a 0.4-second movie and no complaint from anyone.
Check the movie against the sequence, not against itself
Ask the finished file for its frame count and duration and compare them with the manifest:
ffprobe -v error -count_frames -select_streams v:0 \
-show_entries stream=nb_read_frames,duration review.mp4
For the drill, the answers to compare against are 6 and 1.000 second. Container duration can be rounded to the file's timebase, so a hair either side of 1.000 is a formatting artifact, not a missing frame. A duration of 0.833 or 0.250 is a real problem, and the ratio tells you which: five of six frames, or a rate read four times too fast.
Then watch it. Not to judge the motion — you already judged that in the comp — but to look for the one thing the count cannot see. A stale frame or a duplicated instant shows up as a jump or a hold at a single moment, and if the change between editions was subtle, it will not show up at all. That is the case the burned-in slug exists for, and the reason to check the frames before the movie rather than after.
Keep the frame master and the review movie as separate things. The movie is a derivative with one job: to be watched and reacted to. When a note comes back — the middle looks wrong — you go to the frames, not to the movie. The frames are where you can tell whether "wrong" means missing, damaged, or from last week.
The end state is modest and worth the discipline: one verified sequence, one checked movie, both traceable to a single named edition of the source. A successful export tells you the encoder reached the end of its work. It cannot tell you whether a gap or an old frame changed the motion you meant to show, and those two failures need different evidence even when both previews look almost right.
Frequently asked questions
Why render a motion test as an image sequence rather than judge the movie export?
A movie export is a closed door: it can play while hiding whether the motion is the motion you meant. An image sequence is inspectable as files, so you can check for missing, damaged and stale frames before treating the review movie as a derivative assembled from a verified sequence.
What should be frozen before rendering begins?
One edition: source state, frame range, sampling rate, format and output folder, ideally written in a manifest beside the output. Derive the expected frame count from the actual inclusive range—frames 1–6 is six, not five—and derive duration as frames divided by rate. The rate must be declared with the range because it tells the numbered frames what they mean.
What are the three ways an image sequence can lie?
Missing frames are absent, trivially caught by comparing disk against the manifest, though people miss them by scrubbing the movie instead of listing the folder. Damaged frames can exist and be counted but be unusable, which is why Skip Existing Files is a resumption convenience, not a validator. Stale frames are present, readable and correct in format but from an earlier source state; filename continuity will not catch them. A burned-in edition slug is the cheapest way to expose a stale frame.
When can a single missing or damaged frame be repaired?
Only against a frozen, unchanged source and render settings, and the bad file must be removed or moved aside before re-rendering, because Skip Existing Files skips what is present and can leave the same unusable frame while reporting success. If the source has changed, start a new edition, use a new folder and render the whole range; quietly re-rendering the one frame you noticed leaves the others unverified. Never renumber frames to close a gap, because the frame number is a time coordinate.
How should the review movie be encoded and checked against the sequence?
Set the input frame rate explicitly before the input, for example with -framerate 6 before -i frame_%04d.png, because an output-side rate setting cannot repair a wrong input reading. Then compare the finished file's frame count and duration with the manifest, using something like ffprobe with -count_frames, allowing only tiny container-duration rounding. Finally watch it for a jump or hold that the count cannot see. Encoder success only tells you the encoder reached the end, not how many files it read or whether the right six were among them.