Thumbnails

PRESTOplay for web includes a thumbnails plugin that parses and downloads thumbnail images in a variety of formats. The plugin provides automatic format detection, seek-bar preview rendering, and advanced preloading strategies to deliver a smooth scrubbing experience.

Review Thumbnails first for shared thumbnail formats, manifest signaling, and authoring patterns.

Installation

Import the thumbnails plugin:

<script type="text/javascript" src="cl.thumbnails.js"></script>

For DASH and HLS streams with embedded thumbnails, the plugin works automatically with no additional configuration. For other formats, provide a configuration object when loading content.

Configuration

Thumbnail formats are distinguished by modes. The plugin can infer the mode from the file extension and configuration fields, or you can set it explicitly.

Single image mode

One image file per thumbnail:

player.load({
  thumbnails: {
    mode: 'SINGLE',
    url: 'https://example.com/thumbs/$index$.jpg',
    duration: 5,
    templateKey: '$index$'
  }
});

Grid image mode

Multiple thumbnails in a single sprite image, reducing the number of HTTP requests:

player.load({
  thumbnails: {
    mode: 'GRID',
    url: 'https://example.com/$index$_7.jpg',
    duration: 10,
    gridSize: '10x10',
    templateKey: '$index$'
  }
});

WebVTT mode

Thumbnails defined in a WebVTT file with cues containing URLs and timing:

player.load({
  thumbnails: {
    mode: 'WEBVTT',
    url: 'https://example.com/thumbs.vtt'
  }
});

BIF mode

Binary Image Format, commonly used for Roku devices:

player.load({
  thumbnails: {
    mode: 'BIF',
    url: 'https://example.com/thumbs.bif'
  }
});

Usage

Basic usage

Get the thumbnails plugin from the player and retrieve a thumbnail for a specific time:

const thumbPlugin = player.getPlugin(clpp.thumbnails.ThumbnailsPlugin.Id);

const thumbnail = await thumbPlugin.get(60); // Thumbnail at 60 seconds
const thumbElement = thumbnail.element(160, 90); // Scale to 160x90 pixels
document.getElementById('thumbnail-container').appendChild(thumbElement);

The element() method returns a styled div with the thumbnail as a background image, handling grid positioning and scaling automatically.

Seek-bar preview

The most common use case is displaying a thumbnail preview when the viewer hovers over the seek bar. Here’s a complete implementation:

const seekbar = document.getElementById('seekbar');
const thumbContainer = document.getElementById('thumbnail-preview');
const thumbPlugin = player.getPlugin('thumbnails');

seekbar.addEventListener('mousemove', async (event) => {
  const rect = seekbar.getBoundingClientRect();
  const percent = (event.clientX - rect.left) / rect.width;
  const position = percent * player.getDuration();

  try {
    const thumbnail = await thumbPlugin.get(position);
    const thumbElement = thumbnail.element(160, 90);

    thumbContainer.innerHTML = '';
    thumbContainer.appendChild(thumbElement);
    thumbContainer.style.left = event.clientX + 'px';
    thumbContainer.style.display = 'block';
  } catch (error) {
    thumbContainer.style.display = 'none';
  }
});

seekbar.addEventListener('mouseleave', () => {
  thumbContainer.style.display = 'none';
});

Custom styling

For more control over rendering, access the raw image element and thumbnail properties:

const thumbnail = await thumbPlugin.get(position);

await thumbnail.load();
const img = thumbnail.raw(); // Raw HTMLImageElement

// Thumbnail properties
console.log(thumbnail.width);    // Width in pixels
console.log(thumbnail.height);   // Height in pixels
console.log(thumbnail.time);     // Start time in seconds
console.log(thumbnail.duration); // Duration in seconds
console.log(thumbnail.src);      // Image source URL

// Grid-specific properties
console.log(thumbnail.x); // X coordinate in the grid image
console.log(thumbnail.y); // Y coordinate in the grid image

Preloading

Preloading downloads thumbnails in advance so they’re ready when the viewer seeks. This is especially useful for single image mode where each thumbnail requires its own HTTP request. Configure preloading as an array of step values in seconds:

player.load({
  thumbnails: {
    mode: 'SINGLE',
    url: 'https://example.com/$index$.jpg',
    duration: 5,
    preload: [300, 60, 5]
  }
});

With this configuration, the plugin creates three downloaders:

  1. Downloads a thumbnail every five minutes (300 seconds)

  2. When the first completes, downloads one every minute

  3. When the second completes, downloads one every five seconds

Each downloader pauses during video buffering to prioritize playback. To force preloading during buffering, use the object form:

player.load({
  thumbnails: {
    preload: [
      { step: 300, preloadWhileBuffering: true },
      60,
      5
    ]
  }
});

Note

Preloading during buffering may compete with video segment downloads. Use it carefully.

Preloading is automatically disabled for live content.

Platform-specific notes

Safari and HLS Image Media Playlist

HLS Image Media Playlist is not supported when using Safari’s native HLS engine. To enable support, switch to the MSE-based engine:

const player = new clpp.Player('vid', {
  preferNativeHlsOnSafari: false
});

Smart TVs and large grid images

Some smart TVs may not correctly process very large grid images. Unexpected downscaling has been observed on certain Hisense and Philips TV models for images larger than approximately 10,000 x 10,000 pixels. Splitting the grid into multiple smaller images avoids this issue.

Previous topic: Player basics