Why MAX_FPS is Capped at 2.0 in claude-video and How It Interacts with max_frames
The MAX_FPS constant is hard-coded at 2.0 frames per second to control token costs and prevent over-sampling, working in tandem with max_frames to bound the total extraction budget via the _clamp_fps helper function.
The claude-video repository by bradautomates implements intelligent frame extraction to balance visual detail against API token consumption. Understanding why MAX_FPS is capped at 2.0 and how it interacts with max_frames is essential for optimizing video processing pipelines. This analysis examines the source code in skills/watch/scripts/frames.py to explain the architectural decisions behind these limits.
Why the Frame Rate is Capped at 2.0 FPS
Controlling Token Costs
Claude's API charges tokens per extracted frame. By enforcing a hard ceiling of 2.0 FPS, the _clamp_fps function ensures that short videos maintain dense sampling while longer videos are automatically throttled. This design prevents unexpected token explosions when processing extended content, keeping costs predictable regardless of video length.
Preventing Over-Sampling
Visual content rarely changes significantly faster than two frames per second. Sampling at higher rates would generate near-duplicate images that add minimal semantic value while substantially increasing token usage and processing time. The 2.0 FPS cap represents a pragmatic balance between temporal resolution and information density.
How MAX_FPS Interacts with max_frames
The coordination between these two limits occurs in the private helper _clamp_fps defined in skills/watch/scripts/frames.py:
def _clamp_fps(fps: float, duration_seconds: float, max_frames: int) -> tuple[float, int]:
fps = min(fps, MAX_FPS) # ← enforce the 2 fps ceiling
target = min(max_frames, max(1, int(round(fps * duration_seconds))))
return fps, target
MAX_FPS is defined at the top of the file on line 19. Every automatic FPS calculation—whether from auto_fps (full video) or auto_fps_focus (specified range)—ultimately passes through this clamping mechanism.
The Four-Step Workflow
- Determine effective duration for the analysis window (full video or user-specified range).
- Calculate provisional fps using
auto_fpsorauto_fps_focusbased on content length. - Clamp the fps to
MAX_FPS(2.0) via_clamp_fps. - Compute final frame target as
min(max_frames, round(fps * duration)).
Budget Scenarios
The interaction creates predictable extraction budgets:
- Short clips: A 10-second video with
max_frames=100yieldsmin(100, 2×10) = 20frames. - Long clips: A 30-minute (1800s) video with
max_frames=100yieldsmin(100, 2×1800) = 100frames (capped by max_frames). - Uncapped detail: With
max_frames=None, the 2.0 FPS ceiling still limits output to2 × duration_secondsframes.
Because the FPS is never allowed to exceed 2.0, the maximum possible frame count is effectively 2.0 × duration_seconds. If max_frames is lower than that product, the max_frames value wins and extraction stops early via ffmpeg's -frames:v flag.
Configuring Frame Extraction via CLI
The max_frames parameter is parsed from --max-frames in skills/watch/scripts/watch.py (lines 31-34).
# Default: automatic fps (capped at 2.0) with 100-frame budget
python -m skills.watch.scripts.watch video.mp4
# Reduce frame budget to 40 (still capped at 2.0 fps)
python -m skills.watch.scripts.watch video.mp4 --max-frames 40
# Override fps cap entirely (requires explicit --fps)
python -m skills.watch.scripts.watch video.mp4 --fps 5 --max-frames 200
Explicit --fps arguments bypass the MAX_FPS ceiling, but --max-frames still constrains the total count, providing a hard upper bound on token consumption.
Summary
MAX_FPSis hard-coded at 2.0 inskills/watch/scripts/frames.pyto limit token costs and prevent redundant frame extraction.- The
_clamp_fpsfunction enforces this ceiling before calculating the final frame count. max_framesacts as a secondary, user-configurable hard limit on the absolute number of frames extracted.- The effective frame count is always
min(max_frames, 2.0 × duration_seconds)when using automatic FPS calculations. - Explicit
--fpsCLI arguments can override the 2.0 cap, butmax_framesremains the final safeguard against excessive token usage.
Frequently Asked Questions
What happens if max_frames is higher than 2.0 FPS × video duration?
If the product of the 2.0 FPS cap and video duration exceeds max_frames, extraction stops at max_frames. For example, a 30-minute video could theoretically generate 3,600 frames at 2 FPS, but with max_frames=100, the pipeline stops after extracting 100 frames, preserving the token budget.
Can I override the 2.0 FPS cap in claude-video?
Yes. Passing an explicit --fps value via the CLI bypasses the MAX_FPS ceiling enforced by _clamp_fps. However, the max_frames limit still applies to the final output count, ensuring you maintain control over the absolute frame budget even when increasing temporal resolution.
Where is the MAX_FPS constant defined?
The constant is defined on line 19 of skills/watch/scripts/frames.py as MAX_FPS = 2.0. This value is imported and referenced throughout the frame extraction logic.
How do auto_fps and auto_fps_focus interact with the cap?
Both functions calculate a provisional frames-per-second value based on video duration and analysis scope. Before extraction begins, _clamp_fps passes these provisional values through min(fps, MAX_FPS), ensuring the final rate never exceeds 2.0 FPS unless the user explicitly overrides it via command-line arguments.
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 →