# Generate thumbnail tracks for scrubbing

Create I-frame thumbnail strips for video player seek previews

Scrub previews let viewers see where they're seeking before they release the slider. "Reel" (a media platform like Vimeo) generates thumbnail images at regular intervals and maps them to the player's seek bar.

## API

Extract a frame every 5 seconds from the source video:

<CodeGroup labels={["CLI", "TypeScript", "Python", "curl"]}>
```bash
ittybit image \
  -i https://reel-app.com/videos/keynote-2025.mp4 \
  --interval 5 \
  --width 160 \
  --format jpg
```

```typescript
const task = {
  input: 'https://reel-app.com/videos/keynote-2025.mp4',
  kind: 'image',
  options: {
    interval: 5,
    width: 160,
    format: 'jpg',
  },
};

const res = await fetch('https://api.ittybit.com/jobs', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.ITTYBIT_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(task),
});
const data = await res.json();
```

```python

task = {
    "input": "https://reel-app.com/videos/keynote-2025.mp4",
    "kind": "image",
    "options": {
        "interval": 5,
        "width": 160,
        "format": "jpg",
    },
}

res = requests.post(
    "https://api.ittybit.com/jobs",
    headers={"Authorization": f"Bearer {api_key}"},
    json=task,
)
data = res.json()
```

```bash
TASK='{
  "input": "https://reel-app.com/videos/keynote-2025.mp4",
  "kind": "image",
  "options": {
    "interval": 5,
    "width": 160,
    "format": "jpg"
  }
}'

curl -X POST https://api.ittybit.com/jobs \
  -H "Authorization: Bearer $ITTYBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$TASK"
```

</CodeGroup>

The `interval` option generates one frame every N seconds across the full duration. A 10-minute video at `interval: 5` produces 120 thumbnail images.

## CLI

```bash
ittybit image \
  -i keynote-2025.mp4 \
  -o thumbs/ \
  --interval 5 \
  --width 160 \
  --format jpg
```

Output files are named sequentially (`thumb-000.jpg`, `thumb-001.jpg`, etc.).

## Choosing the interval

| Video duration | Interval | Approximate frames |
| -------------- | -------- | ------------------ |
| < 5 min        | 2s       | ~150               |
| 5-30 min       | 5s       | ~60-360            |
| 30-120 min     | 10s      | ~180-720           |
| > 2 hours      | 15-30s   | Varies             |

Shorter intervals give smoother scrubbing but produce more images. 160px wide JPEGs are typically 3-5 KB each, so even 500 frames adds under 2.5 MB total.

## Generating a VTT thumbnail manifest

Most players (Video.js, Plyr, hls.js) use a WebVTT file to map timestamps to thumbnail URLs:

```
WEBVTT

00:00:00.000 --> 00:00:05.000
https://cdn.reel-app.com/thumbs/keynote-2025/thumb-000.jpg

00:00:05.000 --> 00:00:10.000
https://cdn.reel-app.com/thumbs/keynote-2025/thumb-001.jpg

00:00:10.000 --> 00:00:15.000
https://cdn.reel-app.com/thumbs/keynote-2025/thumb-002.jpg
```

Point your player's thumbnail track at this VTT file and scrub previews work automatically.

## Sprite sheets vs individual frames

| Approach          | Pros                   | Cons                                       |
| ----------------- | ---------------------- | ------------------------------------------ |
| Individual frames | Simple, cache-friendly | More HTTP requests                         |
| Sprite sheet      | Single request         | Larger initial download, needs coordinates |

For most cases, individual frames served from a CDN with HTTP/2 are the simpler choice.