How Claude Video Calculates Auto-FPS for Frame Extraction
Claude Video calculates extraction rates dynamically using duration-aware heuristics in 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 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:
- ≤ 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, this function implements the MAX_FPS = 2.0 constant to prevent excessive extraction rates that could overwhelm downstream Claude token limits.
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:
- Minimum coverage: Always returns at least 1 frame via
max(1, ...) - Budget compliance: Caps total frames at
max_frames(default 100) regardless of calculated FPS
Pipeline Integration in watch.py
The orchestration layer in 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:
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:
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:
>>> 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:
$ 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_fpsandauto_fps_focusinskills/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_fpsenforces theMAX_FPS = 2.0limit and ensures the final frame count stays within the user-defined budget (default 100)- The orchestration logic in
skills/watch/scripts/watch.pyautomatically selects the appropriate calculation path based on whether focus windows are specified - Manual
--fpsoverrides 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. 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. The 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →