Generate Thumbnails
Extract frames from video as images.
Generate Thumbnails
Generating thumbnails from video is essential for preview images, video galleries, and content management systems. FFmpeg can extract single frames, periodic snapshots, or create contact sheet grids from any video.
Single frame at a timestamp
Extract one frame at the 5-second mark and save it as an image.
ffmpeg -i {{in_video}} -ss 00:00:05 -frames:v 1 {{out_thumbnail}}-ss 00:00:05seeks to 5 seconds into the video-frames:v 1tells FFmpeg to output exactly one video frame
Multiple frames at intervals
Extract one frame every 10 seconds throughout the entire video. This generates multiple output files with sequential numbering.
ffmpeg -i {{in_video}} -vf "fps=1/10" {{out_frame}}_%03d.pngfps=1/10outputs 1 frame every 10 seconds%03dadds zero-padded numbering (001, 002, 003, etc.)
Note that this produces multiple files. The output alias {{out_frame}} serves as the filename prefix, and FFmpeg appends the sequential number.
Contact sheet / grid
Create a single image containing a grid of frames from throughout the video. This gives a visual overview of the entire video at a glance.
ffmpeg -i {{in_video}} -vf "select=not(mod(n\,100)),scale=320:180,tile=4x4" -frames:v 1 {{out_grid}}select=not(mod(n\,100))picks every 100th framescale=320:180resizes each frame to 320x180tile=4x4arranges 16 frames in a 4-by-4 grid-frames:v 1outputs only the first complete grid
Adjust the frame interval and grid size based on your video length. For a 10-minute video at 30fps (18,000 frames), selecting every 1000th frame gives you 18 frames, enough to fill a 4x5 grid.
Full API example
Extract a thumbnail from the 5-second mark of a video.
curl -X POST https://renderio.dev/api/v1/run-ffmpeg-command \
-H "Content-Type: application/json" \
-H "X-API-KEY: ffsk_your_api_key_here" \
-d '{
"input_files": {
"in_video": "https://example.com/source.mp4"
},
"output_files": {
"out_thumbnail": "thumbnail.jpg"
},
"ffmpeg_command": "ffmpeg -ss 00:00:05 -i {{in_video}} -frames:v 1 {{out_thumbnail}}"
}'import requests
response = requests.post(
"https://renderio.dev/api/v1/run-ffmpeg-command",
headers={
"Content-Type": "application/json",
"X-API-KEY": "ffsk_your_api_key_here",
},
json={
"input_files": {
"in_video": "https://example.com/source.mp4",
},
"output_files": {
"out_thumbnail": "thumbnail.jpg",
},
"ffmpeg_command": "ffmpeg -ss 00:00:05 -i {{in_video}} -frames:v 1 {{out_thumbnail}}",
},
)
data = response.json()
print("Command ID:", data["command_id"])interface RunCommandResponse {
command_id: string;
}
const response = await fetch("https://renderio.dev/api/v1/run-ffmpeg-command", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": "ffsk_your_api_key_here",
},
body: JSON.stringify({
input_files: {
in_video: "https://example.com/source.mp4",
},
output_files: {
out_thumbnail: "thumbnail.jpg",
},
ffmpeg_command: "ffmpeg -ss 00:00:05 -i {{in_video}} -frames:v 1 {{out_thumbnail}}",
}),
});
const { command_id } = (await response.json()) as RunCommandResponse;
console.log("Command ID:", command_id);const response = await fetch("https://renderio.dev/api/v1/run-ffmpeg-command", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": "ffsk_your_api_key_here",
},
body: JSON.stringify({
input_files: {
in_video: "https://example.com/source.mp4",
},
output_files: {
out_thumbnail: "thumbnail.jpg",
},
ffmpeg_command: "ffmpeg -ss 00:00:05 -i {{in_video}} -frames:v 1 {{out_thumbnail}}",
}),
});
const { command_id } = await response.json();
console.log("Command ID:", command_id);<?php
$ch = curl_init("https://renderio.dev/api/v1/run-ffmpeg-command");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
"X-API-KEY: ffsk_your_api_key_here",
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
"input_files" => [
"in_video" => "https://example.com/source.mp4",
],
"output_files" => [
"out_thumbnail" => "thumbnail.jpg",
],
"ffmpeg_command" => "ffmpeg -ss 00:00:05 -i {{in_video}} -frames:v 1 {{out_thumbnail}}",
]));
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
echo "Command ID: " . $data["command_id"] . "\n";Note that -ss is placed before -i in this example. When -ss comes before the input, FFmpeg seeks directly to that position without decoding every frame from the beginning. This is significantly faster, especially for long videos.
The API returns a command_id immediately. Poll GET /api/v1/commands/:commandId to check when the extraction is complete, then download the output from the storage_url in the response.
Tips and variations
-ssplacement matters: Place-ssbefore-ifor fast (but slightly less accurate) seeking. Place-ssafter-ifor frame-accurate seeking at the cost of decoding from the start. For thumbnails, the speed gain of pre-input seeking is almost always worth it.- Output format: The output format is determined by the filename extension. Use
.jpgfor smaller files,.pngfor lossless quality and transparency support, or.webpfor modern web use. - JPEG quality: Control JPEG compression with
-q:v 2(range 2-31, lower is better quality). - Best frame selection: To pick the most visually interesting frame near a timestamp, use the
thumbnailfilter:-vf "thumbnail=100"which analyzes 100 frames and picks the most representative one. - Specific resolution: Add a scale filter to generate thumbnails at a specific size:
-vf "scale=640:360"or-vf "scale=640:-2"to maintain aspect ratio.
Related guides
- Chained Workflow -- transcode video and extract thumbnails in one pipeline
- Batch Processing -- generate thumbnails from multiple videos at once