Video Covers and Thumbnails
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. See the section bibliography for the reference material consulted while preparing these pages. |
Baseline date: 2026-10-07. A video needs at least one still image: the poster shown before playback starts, a cover in lists and grids, and often a strip of sprites for hover previews. All three come from frames of the video, produced once, by a worker, after the upload. They are then handled as images, see image formats and responsive variants.
Extracting one frame
The FFmpeg commands on this page were run against FFmpeg 6.1.
# Fast and accurate: -ss before -i seeks to the previous keyframe, then decodes to the exact time.
ffmpeg -ss 00:00:05 -i input.mp4 -frames:v 1 -q:v 2 cover.jpg
# Slow: -ss after -i decodes and discards everything before the time. Rarely needed.
ffmpeg -i input.mp4 -ss 00:00:05 -frames:v 1 -q:v 2 cover.jpg
-
-ssbefore-iis an input option. FFmpeg seeks to the nearest keyframe before the time and then decodes to the exact point, which is fast and accurate in current FFmpeg versions. Placing-ssafter-idecodes everything before the time and discards it. -
-frames:v 1stops after one video frame. -
-q:v 2sets JPEG quality (lower is better, 2 to 5 is the useful range). For WebP or AVIF output, give the encoder’s own quality options instead. -
Apply the rotation: FFmpeg autorotates by default when it re-encodes using the display matrix of the stream, and you can force the issue with
-autorotateor check with ffprobe (-show_entries stream_side_data=rotation). Phones record landscape pixels and flag the video as rotated, so a cover that ignores this one is sideways. -
Scale in the same command (
-vf scale=720:-2) rather than producing a full-size frame and resizing it later.
References: FFmpeg main options and the FFmpeg seeking guide.
Choosing a representative frame
A frame at a fixed time is often black (fade-in), blurry (motion) or a title card. Options, from cheapest to best:
-
Fixed fraction of the duration. Take the frame at 10 percent of the video. Cheap, and avoids intros.
-
The thumbnail filter. The
thumbnailfilter looks at a batch of consecutive frames (100 by default) and picks the one that is the most typical, which means the closest to the batch’s colour histogram average. It avoids black frames and transitions most of the time.ffmpeg -ss 00:00:03 -i input.mp4 -vf "thumbnail=n=300,scale=720:-2" -frames:v 1 cover.jpg -
Scene detection. The
selectfilter can pick frames where the content changes, using thescenescore:ffmpeg -i input.mp4 -vf "select=gt(scene\,0.4),scale=480:-2" -fps_mode vfr scene_%03d.jpgThe backslash escapes the comma inside the filter expression. Scene frames are a good source of candidate covers.
-
Let the user choose. Extract eight to twelve candidates, show them in the uploader, and store the choice. A user-chosen frame beats any heuristic for editorial content.
-
Analysis. Score candidates for sharpness (variance of the Laplacian), faces and brightness in the worker, or use a managed analysis service such as Azure AI Video Indexer or a vision model.
Remember to skip frames at the very end of the video and to fall back to a fixed time when a clip is shorter than the
thumbnail batch.
Sprite sheets for scrubbing previews
A sprite sheet is one image holding a grid of small frames sampled at regular intervals. A player shows the right tile when the user hovers or drags on the progress bar, with one request for the whole strip.
# One frame every 10 seconds, 160 px wide, laid out as a grid of 10 columns by 10 rows.
ffmpeg -i input.mp4 -vf "fps=1/10,scale=160:-2,tile=10x10" -frames:v 1 sprites.jpg
Use a second small file, usually a WebVTT track with #xywh= fragments (the format Video.js and Shaka Player
thumbnails plugins expect), to map time ranges to tiles. Cap the number of tiles, and split into several sheets for
very long videos.
Some services produce sprite sheets for you, for example the Google Cloud Transcoder API (see the Google Cloud page).
BlurHash on the cover
Treat the chosen cover like an uploaded image: downscale it to about 64 pixels, compute the BlurHash and store it next to the cover metadata, see BlurHash. The frontend shows the blurred cover in the video card and as the poster while the poster image loads, see video playback on the web.
The same operations in managed cloud services
| Service | How to get covers and thumbnails |
|---|---|
AWS Elemental MediaConvert |
Add a frame capture output to the job. It writes JPEG frames at a chosen rate or count. See frame capture and thumbnails. |
Google Cloud Transcoder API |
Add a sprite sheet or a set of frames to the job configuration. See the Google Cloud page. |
Azure |
Media Services is retired. Run FFmpeg in a Container Apps job, or analyse with Video Indexer. See the Azure page. |