# Remotion Rule: Extract Frames > Remotion skill rule: Extract frames from videos at specific timestamps using Mediabunny. Part of the official Remotion Agent Skill for programmatic video in React. ## Install Save the content below to `.claude/skills/` or append to your `CLAUDE.md`: ## Quick Use ```bash npx skills add remotion-dev/skills ``` This rule activates automatically when working with extract frames in a Remotion project. --- ## Intro Extract frames from videos at specific timestamps using Mediabunny. Part of the [Remotion AI Skill](https://tokrepo.com/en/workflows/57997ead-c8fa-409c-916f-28bbc0adc8d9) — the official Agent Skill for programmatic video creation in React. **Best for**: Developers using Remotion for extract frames **Works with**: Claude Code, OpenAI Codex, Cursor --- ## Rule Content # Extracting frames from videos Use Mediabunny to extract frames from videos at specific timestamps. This is useful for generating thumbnails, filmstrips, or processing individual frames. ## The `extractFrames()` function This function can be copy-pasted into any project. ```tsx import { ALL_FORMATS, Input, UrlSource, VideoSample, VideoSampleSink, } from "mediabunny"; type Options = { track: { width: number; height: number }; container: string; durationInSeconds: number | null; }; export type ExtractFramesTimestampsInSecondsFn = ( options: Options, ) => Promise | number[]; export type ExtractFramesProps = { src: string; timestampsInSeconds: number[] | ExtractFramesTimestampsInSecondsFn; onVideoSample: (sample: VideoSample) => void; signal?: AbortSignal; }; export async function extractFrames({ src, timestampsInSeconds, onVideoSample, signal, }: ExtractFramesProps): Promise { using input = new Input({ formats: ALL_FORMATS, source: new UrlSource(src), }); const [durationInSeconds, format, videoTrack] = await Promise.all([ input.computeDuration(), input.getFormat(), input.getPrimaryVideoTrack(), ]); if (!videoTrack) { throw new Error("No video track found in the input"); } if (signal?.aborted) { throw new Error("Aborted"); } const timestamps = typeof timestampsInSeconds === "function" ? await timestampsInSeconds({ track: { width: videoTrack.displayWidth, height: videoTrack.displayHeight, }, container: format.name, durationInSeconds, }) : timestampsInSeconds; if (timestamps.length === 0) { return; } if (signal?.aborted) { throw new Error("Aborted"); } const sink = new VideoSampleSink(videoTrack); for await (using videoSample of sink.samplesAtTimestamps(timestamps)) { if (signal?.aborted) { break; } if (!videoSample) { continue; } onVideoSample(videoSample); } } ``` ## Basic usage Extract frames at specific timestamps: ```tsx await extractFrames({ src: "https://remotion.media/video.mp4", timestampsInSeconds: [0, 1, 2, 3, 4], onVideoSample: (sample) => { const canvas = document.createElement("canvas"); canvas.width = sample.displayWidth; canvas.height = sample.displayHeight; const ctx = canvas.getContext("2d"); sample.draw(ctx!, 0, 0); }, }); ``` ## Creating a filmstrip Use a callback function to dynamically calculate timestamps based on video metadata: ```tsx const canvasWidth = 500; const canvasHeight = 80; const fromSeconds = 0; const toSeconds = 10; await extractFrames({ src: "https://remotion.media/video.mp4", timestampsInSeconds: async ({ track, durationInSeconds }) => { const aspectRatio = track.width / track.height; const amountOfFramesFit = Math.ceil( canvasWidth / (canvasHeight * aspectRatio), ); const segmentDuration = toSeconds - fromSeconds; const timestamps: number[] = []; for (let i = 0; i < amountOfFramesFit; i++) { timestamps.push( fromSeconds + (segmentDuration / amountOfFramesFit) * (i + 0.5), ); } return timestamps; }, onVideoSample: (sample) => { console.log(`Frame at ${sample.timestamp}s`); const canvas = document.createElement("canvas"); canvas.width = sample.displayWidth; canvas.height = sample.displayHeight; const ctx = canvas.getContext("2d"); sample.draw(ctx!, 0, 0); }, }); ``` ## Cancellation with AbortSignal Cancel frame extraction after a timeout: ```tsx const controller = new AbortController(); setTimeout(() => controller.abort(), 5000); try { await extractFrames({ src: "https://remotion.media/video.mp4", timestampsInSeconds: [0, 1, 2, 3, 4], onVideoSample: (sample) => { using frame = sample; const canvas = document.createElement("canvas"); canvas.width = frame.displayWidth; canvas --- ## Source & Thanks > Created by [Remotion](https://github.com/remotion-dev). Licensed under MIT. > [remotion-dev/skills](https://github.com/remotion-dev/skills) — Rule: `extract-frames` Part of the [Remotion AI Skill](https://tokrepo.com/en/workflows/57997ead-c8fa-409c-916f-28bbc0adc8d9) collection on TokRepo. --- Source: https://tokrepo.com/en/workflows/79efb497-1545-4cdd-8db6-b492cdd5e23f Author: Skill Factory