Ahosting Logo
Knowledge Base

How to Create Video Thumbnails and Previews with FFmpeg

There are three different jobs hiding behind the word thumbnail, and they need different commands. Pulling one still image from a video. Producing a strip of images at regular intervals. And building the sprite sheet that a player uses to show previews when someone drags along the seek bar.

All three are fast, because none of them require encoding video. Getting them wrong usually means a black frame, a wrong aspect ratio, or a seek that took minutes when it should have taken a second.

One thumbnail at a specific time

ffmpeg -ss 00:00:05 -i input.mp4 -frames:v 1 -q:v 2 thumb.jpg

-ss before -i is what makes this instant. FFmpeg seeks to the timestamp and starts there. Placed after -i, it decodes the whole file up to that point, on a long video that is the difference between a second and several minutes.

-frames:v 1 takes exactly one frame. -q:v 2 sets JPEG quality on a scale from 2 to 31 where lower is better; 2 is near-maximum and 5 is usually indistinguishable at thumbnail size.

Do not take the frame at zero seconds. Videos frequently open on black, a fade-in, or a title card, and a library full of black thumbnails is the most common outcome of an automated pipeline that grabs frame one. Five to ten seconds in is a safer default.

Where the seek option goes decides the speedBefore minus i· FFmpeg jumps directly to the timestamp· decoding almost nothing· fast, and accurate to the nearest keyframeAfter minus i· FFmpeg decodes from the beginning· discarding everything up to that point· exact, and slow on a long fileFor a thumbnailPut it before. For a frame that must be the exact one, put it after and accept the time.

Let FFmpeg pick a representative frame

If you would rather not guess a timestamp, the thumbnail filter examines a batch of frames and selects the most representative one.

ffmpeg -i input.mp4 -vf "thumbnail" -frames:v 1 thumb.jpg

It considers the first hundred frames by default. To choose from a wider window, raise the number: at the cost of decoding that many frames:

ffmpeg -i input.mp4 -vf "thumbnail=300" -frames:v 1 thumb.jpg

This avoids black frames without hard-coding a timestamp, which makes it a good default for a pipeline handling videos of unknown content.

Thumbnails at a fixed size

Add a scale filter to produce a consistent size for your layout:

ffmpeg -ss 00:00:05 -i input.mp4 -frames:v 1 -vf "scale=320:-2" -q:v 3 thumb.jpg

-2 preserves the aspect ratio and rounds to an even number. Using -1 can produce an odd height, which some encoders reject.

If the layout needs an exact box regardless of the source aspect ratio, crop to fill rather than stretching:

ffmpeg -ss 00:00:05 -i input.mp4 -frames:v 1 \
 -vf "scale=320:180:force_original_aspect_ratio=increase,crop=320:180" thumb.jpg

That scales until the frame covers the box, then trims the overflow. The alternative: forcing both dimensions, distorts the picture, and distorted thumbnails look wrong in a way viewers notice without being able to say why.

A series of thumbnails

For a contact sheet or a set of preview images, output one frame at a fixed interval. This filter takes one frame per second:

ffmpeg -i input.mp4 -vf "fps=1" -q:v 3 thumb_%04d.jpg

One frame every ten seconds:

ffmpeg -i input.mp4 -vf "fps=1/10" -q:v 3 thumb_%04d.jpg

The %04d in the filename produces zero-padded numbers: thumb_0001.jpg and so on. Zero padding matters, because without it file listings sort thumb_10 before thumb_2.

Be careful with the interval on long files. One frame per second across a two-hour video is 7,200 images.

Sprite sheets for seek bar previews

Players that show a preview while you drag the seek bar use a single image containing many small frames in a grid, plus a file describing where each one sits. Producing the grid is one command.

ffmpeg -i input.mp4 -vf "fps=1/10,scale=160:-2,tile=5x5" -q:v 3 sprite_%03d.jpg

That takes a frame every ten seconds, scales each to 160 pixels wide, and packs them into 5×5 grids, 25 frames per sheet, starting a new sheet when full.

Two constraints are worth planning around. Every tile must be the same size, which the scale filter handles. And the interval and grid size together determine how many sheets you get, so pick them against the video length rather than copying values from an example.

Your player will also need a WebVTT file mapping timestamps to regions of the sprite. FFmpeg does not generate that; it is produced alongside, from the same interval and tile dimensions you used here.

An animated preview

For a short looping preview instead of a still, take a few seconds and produce a GIF or a small silent MP4.

# Short muted MP4 preview - smaller and better quality than GIF
ffmpeg -ss 00:00:10 -i input.mp4 -t 3 -an -vf "scale=320:-2" -c:v libx264 -crf 30 preview.mp4

An MP4 is dramatically smaller than a GIF at the same visual quality, and every browser plays it. Prefer it unless something specifically requires GIF.

-an drops the audio, which a silent autoplaying preview does not need and which browsers frequently block anyway.

Extracting from a stream you cannot seek

Some sources, live inputs, certain streamed files, do not support seeking. There, -ss before -i has nothing to seek with, and you have to read forward:

ffmpeg -i input.m3u8 -frames:v 1 -q:v 2 thumb.jpg

This takes the first available frame. For a live stream that is the current frame, which is usually what you want.

When the output is not what you expected

A black image. The timestamp fell on black. Move it later, or use the thumbnail filter.

The command hangs for minutes. -ss is after -i. Move it before.

Only one file when you expected many. The filename has no %04d pattern, so each frame overwrote the last.

Height must be an even number. Use -2 rather than -1 in the scale filter.

The thumbnail is stretched. Both dimensions were forced. Use one dimension with -2, or scale and crop.

For the wider set of error messages, troubleshooting common FFmpeg errors goes into what they mean. And if you are generating thumbnails as part of a streaming pipeline, setting up HLS streaming explains the delivery side.

For putting a logo or a caption over the video rather than extracting a frame, How to Add Watermarks and Overlays with FFmpeg goes into the filters.

Picking a frame at a fixed time regularly lands on a blur or a cut. Choosing the first real scene change gives a usable image far more often. For that, see How to Detect Scenes, Silence and Black Frames with FFmpeg.

The strip that appears when a viewer drags the seek bar is one grid image rather than many files. How to Generate Preview Sprites for a Video Player walks through producing it.