A contact sheet seeks the nearest decodable frame, not your timestamp
Evenly sampled grids label requested times, but the seek lands on a keyframe, so the picture can be a fraction of a second off.
Evenly sampled grids label requested times, but the seek lands on a keyframe, so the picture can be a fraction of a second off. The stamp under the cell is the time the player actually landed on — use that, not the mathematical sample point.
That is the whole constraint of Video Contact Sheet. It spaces N requests across the duration, seeks the file, draws whatever decoded, and writes the landed clock. If you treat the grid as a timecode index, you will pick a shot that is not quite the beat you thought you grabbed.
The sample points are math
The tool does not hunt for cuts, motion peaks, or faces. It divides the clip into N bins and seeks to the midpoint of each bin: ((i + 0.5) / N) × duration. Twelve frames on a sixty-second file means requests at 2.5s, 7.5s, 12.5s, and so on — not at 0:00 and not at the last encoded frame. You can set N from 2 to 48 and the column count from 1 to 8. The arithmetic does not change.
Those requests are the schedule. They are not a promise about pixels. HTML video seeking is allowed to snap to a decodable frame, which in practice is usually the nearest keyframe. A closed GOP of two seconds can put the picture almost two seconds away from the midpoint you asked for. The cell still occupies the slot for that bin. The picture is whatever the decoder could show.
The grid is a JPEG on a dark canvas with a monospace stamp under each tile. It is for shot review, not a colour-accurate still.
The seek is a keyframe
Browsers do not reconstruct an arbitrary presentation timestamp on demand. They jump to a frame they can decode without walking the whole GOP, then fire seeked. The contact sheet waits for that event, reads video.currentTime, and draws. If currentTime is 6.04 when the schedule said 7.50, you get 6.04 — in the picture and in the label.
That is why two files of the same length can produce different sheets at the same N. A file with frequent keyframes lands close to the math. A file with long GOPs repeats the same I-frame across neighbouring cells, or shows a frame from the previous shot while the stamp still looks "in" the next one. Neither result is a bug in the sampler. Both are the decoder doing its job.
The video trimmer has the inverse problem: it re-encodes a played range, so the cut is near your in/out, not on a source keyframe. The contact sheet never re-encodes the movie. It only samples what a seek can decode. Different tools, same GOP physics.
HDR footage will also look washed. Canvas 2D composites into the page colour space, typically sRGB. HDR metadata is not preserved. Use the sheet to see which shot, not to grade it.
Read the stamp, not the interval
Each cell is labelled with m:ss.cc from the landed time. That stamp is the only number you should trust if you are jumping back into an editor. Do not interpolate between neighbouring stamps and assume the source has a frame at the midpoint. Do not caption a review as "frame at 0:12" because that cell is the twelfth of twelve. It is the landed time of bin 12.
If two adjacent cells share a picture, the file does not have a decodable frame between those requests. Raising N does not invent keyframes. The sheet is telling you the GOP is coarser than your sample.
When you need the exact still
A labelled grid is the wrong object if the job is "this presentation timestamp, this resolution, this colour." For a single moment you will actually use — a thumbnail, a last-frame conditioner, a still to hand a model — open Video Frame Extractor, scrub until the picture is the picture, and export a PNG at the video's own pixel size. You are still seeking, so you can still land on a keyframe, but you are looking at the frame instead of trusting an interval.
When you need several stills as hosted image URLs, that is a different surface: turn a video into still photos runs extract_video_frames and bills per extraction job. Quality is capped by the source. Pair with an upscale if the still has to print. Do not send the contact-sheet JPEG into that path and expect it to become a set of full-resolution plates — it is already a composited grid.
Use the contact sheet to see the shape of a clip in one glance. Use the extractor when the stamp has to match the picture you will keep. Use the billed extractor when the stills have to live as URLs in a generate chain.
FAQ
Why isn't the frame at the time I asked for?
The browser seeks to a decodable frame, usually the nearest keyframe, not to an arbitrary timestamp. The label is the time the element landed on. A two-second GOP can put you almost two seconds off the mathematical sample point.
Can two cells show the same picture?
Yes, when neighbouring sample points fall inside one GOP. Raising N does not invent keyframes. It only asks for more timestamps. If the decoder has one I-frame in that span, you will see it more than once.
Why does an HDR clip look washed out?
Canvas 2D draws whatever the element composited to the page, in the page's colour space. HDR metadata is not preserved. The grid is for shot review, not a colour-accurate still.
Should I use the frame extractor instead of the contact sheet?
Use the extractor when you need one full-resolution PNG of a moment you have already found. Use the contact sheet when you need a labelled overview of the whole clip. If you need hosted stills for a generate, that is the billed stills job, not either browser tool.