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
  • -ss before -i is 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 -ss after -i decodes everything before the time and discards it.

  • -frames:v 1 stops after one video frame.

  • -q:v 2 sets 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 -autorotate or 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.

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:

  1. Fixed fraction of the duration. Take the frame at 10 percent of the video. Cheap, and avoids intros.

  2. The thumbnail filter. The thumbnail filter 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
  3. Scene detection. The select filter can pick frames where the content changes, using the scene score:

    ffmpeg -i input.mp4 -vf "select=gt(scene\,0.4),scale=480:-2" -fps_mode vfr scene_%03d.jpg

    The backslash escapes the comma inside the filter expression. Scene frames are a good source of candidate covers.

  4. 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.

  5. 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.