---
title: Frame-level data
label: Frame Data
description: Map video timestamps and transcript windows to YouTube storyboard sheets, heatmaps, and chapters.
group: Features
order: 4
---

YouTube publishes low-resolution storyboard sheets for most non-live videos.
They are useful for choosing a moment from the timeline, but they are not
finished thumbnail assets: frames are commonly only 160×90 or 320×180.

The SDK returns the image URLs and grid geometry. It does not download video,
decode pixels, crop frames, or upscale images.

## Find a frame at a timestamp

```ts
import { storyboardFrameAt } from 'just-yt';

const storyboards = await yt.storyboards('dQw4w9WgXcQ');
const frame = storyboardFrameAt(storyboards, 86.5);

if (frame) {
  frame.sheetUrl; // public sprite-sheet URL
  frame.atSeconds; // timestamp after interval rounding
  frame.x; // crop origin within the sheet
  frame.y;
  frame.width; // actual published frame width
  frame.height;
}
```

The helper uses `recommendedLevel` when that value names a published level,
then falls back to the highest-resolution level. Pass a level number explicitly
when you need a specific size:

```ts
const highest = storyboards.levels.at(-1);
const frame = storyboardFrameAt(storyboards, 86.5, highest?.level);
```

An invalid timestamp, an unavailable level, or a timestamp beyond the final
frame returns `undefined`. A level with an interval of zero cannot be mapped to
the timeline and also returns `undefined`.

## Fetch only the sheets around a transcript window

Read a segmented transcript first, choose a narrow window, then map that window
to its distinct sheets:

```ts
import { storyboardSheetsFor } from 'just-yt';

const transcript = await yt.transcript(videoId, { segmented: true });
// Suppose the relevant transcript beat is around 14:20.
const storyboards = await yt.storyboards(videoId);
const sheets = storyboardSheetsFor(storyboards, {
  fromSeconds: 850,
  toSeconds: 880,
});
```

Each result contains one sheet URL and only the cells covering that inclusive
window. This keeps image requests and vision-model input scoped to the relevant
part of the video.

Videos without storyboards return `{ levels: [] }`. That is not an error. Live,
upcoming, and very short videos commonly have no timeline-mappable level.

## Use heatmap and chapter context

A full `video()` lookup can also include two optional `/next` enrichments:

```ts
const video = await yt.video(videoId);

video.heatmap?.[0];
// { startMs, durationMs, score }

video.chapters?.[0];
// { title, startMs, thumbnails }
```

Heatmap scores are normalized within one video and are not comparable between
videos. Chapters and heatmaps are absent when YouTube does not publish them,
and they are also absent in `{ basic: true }` mode because that mode skips
`/next`.

Use `largestThumbnail(video.thumbnails)` or the same helper on a chapter's
thumbnail list to select the largest URL YouTube actually supplied. The helper
does not guess filenames or make a verification request.

## Effect API

The Effect service exposes the same operation:

```ts
const program = Effect.gen(function* () {
  const yt = yield* YouTubeApi;
  return yield* yt.storyboards(videoId);
});
```
