smart-zoom
Apply AI-powered Ken Burns zoom effects with automatic focus detection and subject tracking.
SKILL.mdAutoAnimation/.claude/skills/smart-zoom/SKILL.md
GitHubPurpose
Automatically generate camera zoom keyframes at emphasis points in a video, creating dynamic Ken Burns-style zoom effects. Uses a combination of rule-based heuristics (sentence starts, punctuation, topic transitions) and optional AI analysis (Gemini-based semantic emphasis detection) to find the best moments to zoom.
Steps
Transcribe the audio. Smart zoom requires a transcript with word-level timestamps. Use Whisper/Deepgram/AssemblyAI via
transcribeAudio()fromwhisperTranscript.tsto getWhisperSegment[]andWhisperWord[].Configure zoom settings. Create a
SmartZoomConfigobject:style: zoom animation style -- 'smooth' (ease-in-out), 'crash' (instant jump), 'expo' (accelerating), or 'linear'frequency: how many zooms -- 'conservative' (top 20%), 'moderate' (top 50%), 'aggressive' (top 80%)intensity: global intensity multiplier (0-1)maxZoom: maximum zoom level (e.g., 1.3 = 130%)useAI: enable Gemini-based semantic analysis (optional)includeEmphasis,includePunctuation,includeTopicTransitions,includeSpeakerChanges: toggle heuristic detectors
Analyze for zoom points. Call
getSmartZoomService().analyzeForZoom(segments, words, config, fps). This runs heuristic detection then optionally AI detection:Heuristic detectors (
smartZoomHeuristics.ts):detectSentenceStarts()-- zoom at segment boundaries (intensity 0.5)detectExclamations()-- zoom on!marks (intensity 0.9)detectQuestions()-- zoom on?marks (intensity 0.6)detectKeywords()-- zoom on configurable keyword list (intensity 0.7)detectTopicTransitions()-- zoom when gap > 2s between segments (intensity 0.6)detectSpeakerChanges()-- zoom on speaker changes (intensity 0.3)
AI detector (
smartZoomAI.ts):- Sends transcript text to Gemini asking for 5-10 most dramatic moments
- Returns timestamps with intensity (0.5-1.0) and reasons
- Merges with heuristic points, deduplicates within 1 second
Post-processing:
- Deduplicates points within 1 second (keeps higher intensity)
- Enforces minimum 2-second spacing between zooms
- Filters by frequency setting (keeps top N% by intensity)
Generate camera keyframes. Call
service.generateCameraKeyframes(zoomPoints, config, fps, totalFrames). Each zoom point produces keyframes based on the selected style:- smooth: 500ms ease-in-out zoom, 330ms hold, 660ms ease-in-out return
- crash: 1-frame instant jump, 170ms hold, 500ms ease-out return
- expo: 330ms accelerating zoom, 170ms hold, 660ms ease-out return
- linear: 660ms linear zoom, 330ms hold, 660ms linear return
- Each zoom includes a subtle random pan offset (+-3%) for visual interest
Apply to camera store. Write the generated
CameraKeyframe[]touseCameraStore. Keyframes includeframe,zoom,panX,panY,rotation,easing, and are tagged with'smart-zoom'for easy identification and removal.
Key Files
src/services/smartZoom.ts-- SmartZoomService class, analyzeForZoom(), generateCameraKeyframes(), zoom style keyframe generationsrc/services/smartZoomAI.ts-- detectEmphasisWithAI(), Gemini-based semantic analysissrc/services/smartZoomHeuristics.ts-- detectAllHeuristic(), sentence/punctuation/keyword/topic/speaker detection functionssrc/types/smartZoom.ts-- ZoomPoint, SmartZoomConfig, ZoomStyle, ZoomTriggerType typessrc/stores/useCameraStore.ts-- Camera keyframe state, CameraKeyframe typesrc/components/panels/SmartZoomPanel.tsx-- Smart zoom configuration UI
Common Issues
- Too many zooms: Use 'conservative' frequency or increase minimum spacing. Max 1 zoom per 2 seconds is enforced by default.
- AI detection fails: Falls back silently to heuristics only. Requires
VITE_GEMINI_API_KEY. The catch block logs a warning but does not throw. - Zoom too intense: Lower
config.intensity(0-1 multiplier) or reduceconfig.maxZoom(1.0 = no zoom). The actual zoom is calculated as1 + (maxZoom - 1) * point.intensity * config.intensity. - Crash zoom too jarring: Switch to 'smooth' or 'expo' style. Crash zooms are 1-frame instant and work best for high-energy content.
- No transcript available: Smart zoom requires word-level timestamps. Transcribe the audio first using the transcript service.
Examples
import { getSmartZoomService } from '@/services/smartZoom'
const service = getSmartZoomService()
// Analyze transcript for zoom points
const zoomPoints = await service.analyzeForZoom(segments, words, {
style: 'smooth',
frequency: 'moderate',
intensity: 0.7,
maxZoom: 1.25,
useAI: true,
includeEmphasis: true,
includePunctuation: true,
includeTopicTransitions: true,
includeSpeakerChanges: false,
}, 30)
// Generate camera keyframes
const keyframes = service.generateCameraKeyframes(zoomPoints, config, 30, totalFrames)
// Apply to camera store