# How Claude Video Calculates Auto-FPS for Frame Extraction

> Discover how Claude Video's auto FPS calculation dynamically extracts frames. Learn about duration-aware heuristics, output capping, and frame budget management for optimal detail.

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

---

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

The `bradautomates/claude-video` repository implements an intelligent frame extraction system that avoids fixed sampling rates. Instead of extracting frames at a constant interval regardless of content length, the auto-FPS calculation adapts to video duration, ensuring short videos retain visual density while long videos stay within token budget constraints.

## The Two-Path Strategy for Frame Sampling

The core logic resides in [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py) and branches based on whether the user analyzes a full video or a specific time window. This dual-path approach ensures optimal frame distribution for both comprehensive scans and targeted analysis.

### Full-Video Scans with `auto_fps`

For complete video analysis, the `auto_fps` function selects frame targets using duration-based tiers defined in [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py):

- **≤ 30 seconds**: Returns `max(12, round(duration_seconds))` frames, ensuring even 10-second clips capture at least 12 frames
- **≤ 60 seconds**: Fixed target of **40 frames**
- **≤ 180 seconds**: Fixed target of **60 frames**
- **≤ 600 seconds**: Fixed target of **80 frames**
- **> 600 seconds**: Capped at `max_frames` (default **100**)

After calculating the target frame count, the function derives the FPS value and passes it through the safety clamping mechanism.

### Focused Windows with `auto_fps_focus`

When users specify `--start` and `--end` flags for targeted analysis, `auto_fps_focus` applies more aggressive sampling to short durations while still respecting the global budget:

- **≤ 5 seconds**: Up to **6× duration** (e.g., 5 seconds → 30 frames)
- **≤ 15 seconds**: Up to **4× duration** (e.g., 10 seconds → 40 frames)
- **≤ 30 seconds**: Fixed target of **60 frames**
- **≤ 60 seconds**: Fixed target of **80 frames**
- **≤ 180 seconds**: Capped at `max_frames` (100)
- **> 180 seconds**: Capped at `max_frames` (100)

This function prioritizes temporal density for brief clips while preventing frame overflow on longer segments.

## Safety Enforcement via `_clamp_fps`

Both auto-calculation paths rely on the `_clamp_fps` helper function to enforce hard limits. Located in [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py), this function implements the `MAX_FPS = 2.0` constant to prevent excessive extraction rates that could overwhelm downstream Claude token limits.

```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

```

The clamping logic guarantees two invariants:
1. **Minimum coverage**: Always returns at least 1 frame via `max(1, ...)`
2. **Budget compliance**: Caps total frames at `max_frames` (default 100) regardless of calculated FPS

## Pipeline Integration in [`watch.py`](https://github.com/bradautomates/claude-video/blob/main/watch.py)

The orchestration layer in [`skills/watch/scripts/watch.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/watch.py) determines which calculation path to invoke based on user input. After parsing the video duration and checking for focus window flags, it executes the appropriate helper:

```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)

```

When users provide an explicit `--fps` argument, the system overrides the auto-calculation but maintains safety constraints:

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

```

This ensures manual overrides still respect the 2 FPS ceiling and frame budget.

## Practical Calculation Examples

The following examples demonstrate the auto-FPS logic using the actual implementation from [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py):

```python
>>> from skills.watch.scripts.frames import auto_fps, auto_fps_focus

# 45-second full video (tier: ≤60s → 40 frames target)

>>> auto_fps(45)
(0.89, 40)          # 0.89 fps yields 40 frames

# 8-second focused window (tier: ≤15s → 4× duration)

>>> auto_fps_focus(8)
(2.0, 16)           # Would be 32 frames, but clamped to MAX_FPS 2.0 → 16 frames

# Manual override of 1.5 FPS on 120-second video with 100-frame cap

>>> from skills.watch.scripts.frames import _clamp_fps
>>> _clamp_fps(1.5, 120, max_frames=100)
(1.5, 100)          # 180 frames calculated, but capped at budget limit

```

Running the CLI demonstrates the calculation in action:

```bash
$ python -m skills.watch.scripts.watch "https://youtu.be/xyz" --detail balanced
[watch] extracting scene-aware frames over full 180.0s (target 60, cap 60) …

```

The output shows `auto_fps` selecting **60 frames** for the 180-second duration based on its tier logic.

## Summary

- Claude Video implements **adaptive frame targeting** through `auto_fps` and `auto_fps_focus` in [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py)
- **Duration-based tiers** determine target frame counts: short videos (≤30s) get 12+ frames, while videos >600s cap at 100 frames
- **`_clamp_fps`** enforces the `MAX_FPS = 2.0` limit and ensures the final frame count stays within the user-defined budget (default 100)
- The orchestration logic in [`skills/watch/scripts/watch.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/watch.py) automatically selects the appropriate calculation path based on whether focus windows are specified
- Manual `--fps` overrides are permitted but still constrained by the 2 FPS ceiling and frame budget

## Frequently Asked Questions

### What is the maximum FPS value Claude Video allows?

The system enforces a hard cap 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 calculations suggest higher rates or users specify explicit values exceeding 2.0, the `_clamp_fps` helper reduces the rate to 2.0 FPS to prevent token limit exhaustion.

### How does focused window extraction differ from full-video scanning?

Focused windows use `auto_fps_focus`, which applies more aggressive multipliers for clips under 30 seconds (up to 6× duration for ≤5s clips) compared to `auto_fps` used for full-video scans. Both functions ultimately pass their results through `_clamp_fps` to respect the global frame budget and 2 FPS limit.

### Can I override the automatic FPS calculation?

Yes. Providing the `--fps` argument to the watch command bypasses the `auto_fps` logic. However, the override still passes through safety checks: the value is clamped to `MAX_FPS` (2.0), and the resulting frame count is capped at your specified `max_frames` budget.

### Where is the frame budget configured?

The default frame budget of **100 frames** is defined as the `max_frames` parameter in `auto_fps` and `auto_fps_focus` function signatures within [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py). The [`watch.py`](https://github.com/bradautomates/claude-video/blob/main/watch.py) orchestrator passes this value (or a user-specified cap) to these functions as `budget_cap`, which `_clamp_fps` uses to enforce the ceiling.