DEV Community

ahmet gedik
ahmet gedik

Posted on

Automated Video Thumbnail Optimization Techniques

Thumbnails are the most important visual element on a video platform. On TopVideoHub, every page displays 20-50 thumbnails. Optimizing them has a massive impact on page load time and Core Web Vitals.

Here's the complete thumbnail optimization strategy I use.

YouTube Thumbnail Sizes

YouTube provides thumbnails at multiple sizes:

Quality File Dimensions Typical Size
default default.jpg 120x90 ~3 KB
medium mqdefault.jpg 320x180 ~12 KB
high hqdefault.jpg 480x360 ~25 KB
standard sddefault.jpg 640x480 ~40 KB
maxres maxresdefault.jpg 1280x720 ~120 KB

The URL pattern is:

https://i.ytimg.com/vi/{VIDEO_ID}/{QUALITY}.jpg
Enter fullscreen mode Exit fullscreen mode

Choosing the Right Size

The key principle: serve the smallest thumbnail that looks sharp at the display size.

On a video grid, cards are typically 280-320px wide. Serving maxresdefault.jpg (1280px) for a 300px card wastes 90% of the data.

class ThumbnailHelper {
    public static function url(
        string $videoId,
        string $context = 'grid'
    ): string {
        $quality = match($context) {
            'hero'    => 'maxresdefault',  // Full-width hero: needs max quality
            'grid'    => 'mqdefault',      // Grid card: 320px is enough
            'sidebar' => 'default',         // Sidebar: tiny, minimal quality
            'og'      => 'hqdefault',       // Open Graph meta: medium quality
            default   => 'mqdefault',
        };

        return "https://i.ytimg.com/vi/{$videoId}/{$quality}.jpg";
    }

    public static function srcset(string $videoId): string {
        return sprintf(
            '%s 320w, %s 480w, %s 640w',
            self::url($videoId, 'grid'),
            "https://i.ytimg.com/vi/{$videoId}/hqdefault.jpg",
            "https://i.ytimg.com/vi/{$videoId}/sddefault.jpg"
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

Responsive Images with srcset

Use srcset and sizes to let the browser choose the optimal thumbnail:

<img src="<?= ThumbnailHelper::url($video['id'], 'grid') ?>"
     srcset="<?= ThumbnailHelper::srcset($video['id']) ?>"
     sizes="(max-width: 480px) 100vw, (max-width: 768px) 50vw, 320px"
     alt="<?= htmlspecialchars($video['title']) ?>"
     width="320" height="180"
     loading="lazy"
     decoding="async">
Enter fullscreen mode Exit fullscreen mode

Key attributes:

  • sizes tells the browser the display width at each breakpoint
  • loading="lazy" defers off-screen images
  • decoding="async" prevents thumbnails from blocking rendering
  • width and height prevent layout shift (CLS)

WebP Detection and Fallback

YouTube's CDN also serves WebP format, which is 25-35% smaller than JPEG:

https://i.ytimg.com/vi_webp/{VIDEO_ID}/mqdefault.webp
Enter fullscreen mode Exit fullscreen mode

Use <picture> for WebP with JPEG fallback:

<picture>
    <source type="image/webp"
            srcset="https://i.ytimg.com/vi_webp/<?= $videoId ?>/mqdefault.webp">
    <img src="https://i.ytimg.com/vi/<?= $videoId ?>/mqdefault.jpg"
         alt="<?= htmlspecialchars($title) ?>"
         width="320" height="180"
         loading="lazy"
         decoding="async">
</picture>
Enter fullscreen mode Exit fullscreen mode

Handling Missing Thumbnails

Not all videos have maxresdefault. YouTube returns a placeholder for missing resolutions:

class ThumbnailValidator {
    private static array $cache = [];

    public static function bestAvailable(string $videoId): string {
        if (isset(self::$cache[$videoId])) {
            return self::$cache[$videoId];
        }

        // Try maxres first, fall back to lower qualities
        $qualities = ['maxresdefault', 'sddefault', 'hqdefault', 'mqdefault'];

        foreach ($qualities as $q) {
            $url = "https://i.ytimg.com/vi/{$videoId}/{$q}.jpg";
            $headers = @get_headers($url, true);
            if ($headers && str_contains($headers[0], '200')) {
                self::$cache[$videoId] = $url;
                return $url;
            }
        }

        // Fallback to medium quality (always exists)
        $fallback = "https://i.ytimg.com/vi/{$videoId}/mqdefault.jpg";
        self::$cache[$videoId] = $fallback;
        return $fallback;
    }
}
Enter fullscreen mode Exit fullscreen mode

Note: Don't call this on every page load. Run it during the fetch cron and store the best quality in the database.

Placeholder and Blur-Up

For perceived performance, show a tiny blurred placeholder before the real thumbnail loads:

.video-thumb {
    background-color: #1a1a2e;
    background-size: cover;
    transition: filter 0.3s;
}

.video-thumb[data-loaded="false"] {
    filter: blur(10px);
}

.video-thumb[data-loaded="true"] {
    filter: none;
}
Enter fullscreen mode Exit fullscreen mode
document.querySelectorAll('.video-thumb img').forEach(img => {
    if (img.complete) {
        img.closest('.video-thumb').dataset.loaded = 'true';
    } else {
        img.addEventListener('load', () => {
            img.closest('.video-thumb').dataset.loaded = 'true';
        });
    }
});
Enter fullscreen mode Exit fullscreen mode

Impact on TopVideoHub

After implementing all these optimizations on TopVideoHub:

  • Page weight for thumbnails dropped by ~60%
  • LCP improved from 2.1s to 1.1s
  • CLS dropped to near zero
  • Mobile PageSpeed score increased from 71 to 85+

Top comments (0)