Thumbnail Generation Guide
Learn how to generate high-quality, representative thumbnails from your videos using VideoIntel's intelligent frame scoring algorithm.
Overview
VideoIntel uses advanced computer vision techniques to analyze every frame and select the most visually appealing and representative moments for thumbnails. Unlike simple time-based sampling, our algorithm considers:
- Visual Quality: Sharpness, contrast, and clarity
- Content Diversity: Avoids similar-looking frames
- Face Detection: Prefers frames with visible faces (coming soon)
- Color Distribution: Balanced and vibrant colors
- Motion Blur: Avoids blurry frames
How It Works
The thumbnail generation process follows a sophisticated 7-step pipeline:
Step 1: Extraction Strategy
Calculate which frames to extract as candidates. Extracts 3x more candidates than needed (e.g., 15 candidates for 5 thumbnails) and skips 5% margins at start/end to avoid fade-in/fade-out transitions.
Step 2: Frame Extraction
Extract candidate frames at calculated timestamps. Frames are extracted in chronological order for optimal performance with minimum 2-second spacing between candidates.
Step 3: Quality Scoring
Score each frame using a composite algorithm that evaluates: sharpness (Laplacian variance), brightness (luminance formula), contrast (min-max range), and color variance (RGB diversity). Scores are normalized to 0-1 range.
Step 4: Frame Filtering
Filter out unusable frames: black frames (<5% brightness), white/overexposed frames (>95% brightness), and blurry frames (<5% sharpness). Typically filters 20-30% of candidates.
Step 5: Diversity Filter
Apply temporal diversity to spread thumbnails across video timeline. Always selects highest-scoring frame, then adds frames that are at least 5 seconds apart for comprehensive video coverage.
Step 6: Thumbnail Generation
Convert selected frames to final thumbnails with specified format (JPEG/PNG), quality (0-1), and dimensions. Maintains aspect ratio automatically if only one dimension specified.
Step 7: Memory Cleanup
Clean up temporary canvas elements to prevent memory leaks. Essential for processing multiple videos or generating many thumbnails.
Quality Scoring Algorithm
Each frame receives a composite quality score based on four metrics:
// Simplified scoring algorithm
function scoreFrame(imageData: ImageData) {
// 1. Sharpness (0-1): Laplacian variance edge detection
// Sharp images have strong edges and high variance
const sharpness = calculateLaplacianVariance(imageData);
// 2. Brightness (0-1): Perceptual luminance
// Y = 0.299R + 0.587G + 0.114B (weighted for human perception)
const brightness = calculatePerceptualBrightness(imageData);
// 3. Contrast (0-1): Dynamic range
// High contrast = wide range of brightness values
const contrast = (maxLuminance - minLuminance) / 255;
// 4. Color Variance (0-1): RGB diversity
// Colorful images have high variance across channels
const colorVariance = calculateRGBVariance(imageData);
// Composite score with weighted average
const score = (
sharpness * 0.4 + // 40% weight (most important)
brightness * 0.2 + // 20% weight
contrast * 0.2 + // 20% weight
colorVariance * 0.2 // 20% weight
);
return score;
}Frame Filtering Thresholds
VideoIntel uses conservative thresholds to filter unusable frames:
| Check | Threshold | Purpose |
|---|---|---|
| Black Frame | < 5% brightness | Filter fade-to-black transitions |
| White Frame | > 95% brightness | Filter overexposed/fade-to-white |
| Blurry Frame | < 5% sharpness | Filter out-of-focus or motion blur |
import videoIntel from 'videointel';
// Generate 10 high-quality thumbnails
const thumbnails = await videoIntel.getThumbnails(videoFile, {
count: 10,
quality: 0.9,
width: 1280,
height: 720,
});
// Thumbnails are automatically sorted by quality (best first)
const bestThumbnail = thumbnails[0];
console.log(`Best thumbnail at ${bestThumbnail.timestamp}s`);
console.log(`Quality score: ${bestThumbnail.quality}`);Best Practices
Choose the Right Count
The optimal number of thumbnails depends on your use case:
// For video previews (single thumbnail)
const thumbnails = await videoIntel.getThumbnails(file, { count: 1 });
// For video galleries (3-5 thumbnails)
const thumbnails = await videoIntel.getThumbnails(file, { count: 5 });
// For video seekbar/timeline (10-20 thumbnails)
const thumbnails = await videoIntel.getThumbnails(file, { count: 15 });
// For comprehensive analysis (30+ thumbnails)
const thumbnails = await videoIntel.getThumbnails(file, { count: 50 });💡 Performance Tip
More thumbnails = longer processing time. For a 10-second video: 5 thumbnails ≈ 2s, 20 thumbnails ≈ 5s. Balance quality with user experience.
Set Quality Appropriately
The quality parameter affects both file size and visual fidelity:
// Low quality - Small file size, good for previews (0.5-0.6)
const lowQuality = await videoIntel.getThumbnails(file, {
quality: 0.6,
width: 640,
height: 360
});
// Medium quality - Balanced (0.7-0.8) ⭐ Recommended
const mediumQuality = await videoIntel.getThumbnails(file, {
quality: 0.8,
width: 1280,
height: 720
});
// High quality - Large files, best visual quality (0.9-1.0)
const highQuality = await videoIntel.getThumbnails(file, {
quality: 0.95,
width: 1920,
height: 1080
});Quality Optimization
Resize for Target Display
Always resize thumbnails to match your display size. This reduces memory usage and improves performance:
// For mobile thumbnails
const mobileThumbs = await videoIntel.getThumbnails(file, {
width: 320,
height: 180,
quality: 0.7,
});
// For desktop gallery
const desktopThumbs = await videoIntel.getThumbnails(file, {
width: 640,
height: 360,
quality: 0.8,
});
// For full-screen hero images
const heroThumbs = await videoIntel.getThumbnails(file, {
width: 1920,
height: 1080,
quality: 0.9,
});Maintain Aspect Ratio
VideoIntel automatically maintains the source video's aspect ratio:
// Specify only width - height calculated automatically
const thumbnails = await videoIntel.getThumbnails(file, {
width: 1280,
// height will be calculated based on video aspect ratio
});
// Or specify both for exact dimensions (may crop or pad)
const thumbnails = await videoIntel.getThumbnails(file, {
width: 1280,
height: 720, // Forces 16:9 aspect ratio
});Performance Tips
Use Appropriate Sample Sizes
For long videos, you don't need to analyze every second:
// For short videos (< 1 min) - High frequency sampling
const shortVideo = await videoIntel.getThumbnails(file, {
count: 10, // Dense sampling for detailed analysis
});
// For medium videos (1-10 min) - Medium frequency
const mediumVideo = await videoIntel.getThumbnails(file, {
count: 15, // Good balance
});
// For long videos (> 10 min) - Strategic sampling
const longVideo = await videoIntel.getThumbnails(file, {
count: 20, // Sufficient coverage without overhead
});Process in Batches
When generating thumbnails for multiple videos, process them sequentially to avoid memory issues:
async function processManyVideos(files: File[]) {
const allThumbnails = [];
for (const file of files) {
const thumbnails = await videoIntel.getThumbnails(file, {
count: 5,
quality: 0.8,
});
allThumbnails.push({ file: file.name, thumbnails });
// Optional: Small delay to prevent overwhelming the browser
await new Promise(resolve => setTimeout(resolve, 100));
}
return allThumbnails;
}
// Usage
const results = await processManyVideos(videoFiles);Common Use Cases
Video Gallery Thumbnail
Generate a single, high-quality representative thumbnail:
async function createGalleryThumbnail(videoFile: File) {
const thumbnails = await videoIntel.getThumbnails(videoFile, {
count: 1,
quality: 0.85,
width: 640,
height: 360,
});
// Use the best thumbnail
const thumbnail = thumbnails[0];
// Create img element
const img = document.createElement('img');
img.src = thumbnail.dataUrl;
img.alt = `Video thumbnail at ${thumbnail.timestamp}s`;
img.className = 'rounded-lg shadow-lg';
return img;
}Video Timeline/Scrubber
Create preview thumbnails for video timeline navigation:
async function createTimelineThumbnails(videoFile: File, videoElement: HTMLVideoElement) {
const duration = videoElement.duration;
const thumbnails = await videoIntel.getThumbnails(videoFile, {
count: 20,
quality: 0.7,
width: 160,
height: 90,
});
// Create hover preview
const previewContainer = document.createElement('div');
previewContainer.className = 'timeline-preview';
videoElement.addEventListener('mousemove', (e) => {
const rect = videoElement.getBoundingClientRect();
const position = (e.clientX - rect.left) / rect.width;
const time = position * duration;
// Find closest thumbnail
const closest = thumbnails.reduce((prev, curr) =>
Math.abs(curr.timestamp - time) < Math.abs(prev.timestamp - time)
? curr
: prev
);
// Show preview
previewContainer.innerHTML = `
<img src="${closest.dataUrl}" alt="Preview">
<span>${formatTime(closest.timestamp)}</span>
`;
});
}Chapter Selection
Generate thumbnails for video chapters or sections:
async function createChapterThumbnails(videoFile: File) {
// First detect scenes (natural chapter breaks)
const scenes = await videoIntel.detectScenes(videoFile);
// Then generate thumbnails
const thumbnails = await videoIntel.getThumbnails(videoFile, {
count: scenes.length,
quality: 0.85,
});
// Match thumbnails to scenes
const chapters = scenes.map((scene, i) => ({
title: `Chapter ${i + 1}`,
timestamp: scene.timestamp,
thumbnail: thumbnails[i].dataUrl,
}));
return chapters;
}🚀 Next Steps
- • Learn about Scene Detection
- • Explore Color Extraction
- • Try the Interactive Playground
- • View Complete API Reference