# How Claude Video Calculates Auto-FPS for Frame Extraction

> Discover how Claude Video calculates auto-FPS for frame extraction. Learn about duration-based tiered logic, FPS capping, and budget considerations at 2 FPS.

- Repository: [bradautomates/claude-video](https://github.com/bradautomates/claude-video)
- Tags: internals
- Published: 2026-07-26

---

**Claude Video calculates auto-FPS using duration-based tiered logic in [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py), capping extraction at 2 FPS and respecting a configurable frame budget to balance detail against token limits.**

The `bradautomates/claude-video` repository implements an intelligent auto-FPS calculation system that dynamically adapts frame extraction rates based on video duration and user-defined focus windows. Rather than using a fixed sampling rate, the algorithm selects a target frame count from predefined tiers, then derives the appropriate FPS while enforcing safety limits. This approach ensures dense sampling for short videos and prevents token limit exhaustion for long content.

## The Auto-FPS Algorithm Overview

The auto-FPS mechanism consists of two primary strategies implemented in [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py). The system chooses between **full-video scans** and **focused range extractions** depending on whether the user specifies start/end timestamps.

Both strategies rely on a **frame budget** (defaulting to 100 frames maximum) and a **maximum FPS ceiling** of 2.0. The helper function `_clamp_fps()` enforces these constraints, ensuring the final extraction never exceeds hardware or token processing capabilities.

## Full-Video Scan Logic: `auto_fps()`

When processing an entire video without user-specified ranges, the `auto_fps()` function applies duration-based tiers to determine the target frame count:

- **≤ 30 seconds**: `max(12, round(duration))` frames
- **≤ 60 seconds**: 40 frames
- **≤ 180 seconds**: 60 frames
- **≤ 600 seconds**: 80 frames
- **> 600 seconds**: `max_frames` (default 100)

After selecting the target, the function divides by duration to calculate the raw FPS, then passes through `_clamp_fps()` for safety enforcement.

```python
from skills.watch.scripts.frames import auto_fps

# 45-second video with default 100-frame budget

fps, target = auto_fps(45)

# Returns: (0.89, 40)

# 0.89 FPS yields exactly 40 frames, respecting the ≤60s tier

```

## Focused Range Logic: `auto_fps_focus()`

For focused extractions where `--start` and `--end` flags define a specific window, the `auto_fps_focus()` function applies tighter sampling budgets to capture fine-grained detail in short segments:

- **≤ 5 seconds**: Up to 6× duration (capped by budget)
- **≤ 15 seconds**: Up to 4× duration (capped by budget)
- **≤ 30 seconds**: 60 frames
- **≤ 60 seconds**: 80 frames
- **≤ 180 seconds**: `max_frames`
- **> 180 seconds**: `max_frames`

This aggressive up-sampling for short windows ensures critical moments receive adequate visual representation without exceeding the global frame budget.

```python
from skills.watch.scripts.frames import auto_fps_focus

# 8-second focused clip

fps, target = auto_fps_focus(8)

# Returns: (2.0, 16)

# Clamped to MAX_FPS of 2.0, yielding 16 frames for the segment

```

## Safety Limits and Frame Budget Enforcement

The `_clamp_fps()` helper function in [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py) serves as the gatekeeper for extraction parameters:

```python
def _clamp_fps(fps: float, duration_seconds: float, max_frames: int) -> tuple[float, int]:
    fps = min(fps, MAX_FPS)                                   # never exceed 2 fps

    target = min(max_frames, max(1, int(round(fps * duration_seconds))))
    return fps, target

```

This implementation guarantees three critical constraints:

- **Maximum FPS**: Never exceeds 2.0 FPS to prevent overwhelming downstream token limits
- **Minimum frames**: Always returns at least 1 frame
- **Budget compliance**: Hard caps total frames at the `max_frames` parameter

## Integration in the Watch Pipeline

The [`skills/watch/scripts/watch.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/watch.py) module orchestrates the selection logic. After parsing video metadata and determining the effective duration, it selects the appropriate auto-FPS strategy:

```python
if focused:
    fps, target = auto_fps_focus(effective_duration, max_frames=budget_cap)
else:
    fps, target = auto_fps(effective_duration, max_frames=budget_cap)

```

Users can override the automatic calculation by providing an explicit `--fps` argument. When specified, the system clamps the user value to `MAX_FPS` but still applies the frame budget cap:

```python
if args.fps is not None:
    fps = min(args.fps, MAX_FPS)
    target = max(1, int(round(fps * effective_duration)))

```

## Summary

- Claude Video's auto-FPS calculation resides in [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py) within the `bradautomates/claude-video` repository
- Two distinct algorithms exist: `auto_fps()` for full videos and `auto_fps_focus()` for time-restricted ranges
- Duration-based tiers determine target frame counts, favoring denser sampling for shorter content
- The `_clamp_fps()` helper enforces a 2.0 FPS maximum and respects the configurable frame budget (default 100)
- Explicit user `--fps` values override automatic calculations but remain subject to safety limits

## Frequently Asked Questions

### What is the maximum FPS Claude Video will extract?

Claude Video enforces a hard limit of **2.0 FPS** through the `MAX_FPS` constant in [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py). Even if the duration-based tier logic suggests a higher rate, or if the user specifies an aggressive `--fps` value, the `_clamp_fps()` function caps the extraction at 2 frames per second to protect downstream token budgets.

### How does Claude Video handle very short video clips?

For videos under 30 seconds, the `auto_fps()` function uses the formula `max(12, round(duration))` to ensure adequate visual coverage. When using focused ranges under 5 seconds, `auto_fps_focus()` can sample up to 6× the duration (capped by the frame budget), extracting up to 30 frames for a 5-second clip while respecting the 2.0 FPS ceiling.

### Can I override the automatic FPS calculation?

Yes. Providing the `--fps` argument in the CLI bypasses the tiered auto-selection logic. The system applies `min(args.fps, MAX_FPS)` to respect the safety ceiling, then calculates the target frame count as `max(1, int(round(fps * effective_duration)))`. However, this manual override still respects the maximum frame budget cap to prevent system overload.

### What file contains the auto-FPS calculation logic?

The core auto-FPS functions are implemented in **[`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py)**. This module contains `auto_fps()` for full-video processing, `auto_fps_focus()` for restricted time windows, and the `_clamp_fps()` helper that enforces safety limits. The orchestration logic that selects between these strategies lives in **[`skills/watch/scripts/watch.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/watch.py)**.