From gif
Convert a video or screen recording to GIF. Handles HDR recordings, macOS filenames with spaces, and ffmpeg two-pass palette compression. Do NOT use for frame extraction (use /frames instead).
npx claudepluginhub ramonclaudio/skills --plugin gifThis skill is limited to using the following tools:
ultrathink
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Performs token-optimized structural code search using tree-sitter AST parsing to discover symbols, outline files, and unfold code without reading full files.
ultrathink
Convert screen recordings to compressed GIFs using ffmpeg's two-pass palette method. Handles HDR (HDR10/PQ) recordings from macOS automatically.
which ffmpeg 2>/dev/null && ffmpeg -version 2>&1 | head -1 || echo "NOT INSTALLED - run: brew install ffmpeg"If ffmpeg is NOT INSTALLED, stop immediately and tell the user to install it. Do not attempt any conversion.
| Setting | Value | Notes |
|---|---|---|
| FPS | 10 | Good for screen recordings |
| Width | 640px | Lanczos scaling |
| Speed | 1x | No speedup |
| Palette | stats_mode=diff | Optimizes for static areas |
| Dither | bayer:bayer_scale=5 | Good quality, small file |
Parse arguments from the user's invocation:
--speed N → playback speed multiplier (default: 1). Use 2-4x for long demos.--width N → override scale width (default: 640)--fps N → override frame rate (default: 10)--full → no scaling, keep original resolution--crop → crop out macOS screen recording overlay (top bar). Probe dimensions first, then apply crop=in_w:in_h-PIXELS:0:PIXELS to remove the top PIXELS.Parse the video path from $ARGUMENTS.
IMPORTANT: File paths with spaces, timestamps, and special characters are problematic. ALWAYS use the glob+copy pattern.
Extract a unique identifier from the user's path (like a timestamp) and use glob:
/bin/cp -f /path/to/dir/*UNIQUE_PART* /tmp/video.mov && \
ffprobe -v error -select_streams v:0 \
-show_entries stream=width,height,r_frame_rate,duration \
-show_entries stream=color_transfer \
-of default=noprint_wrappers=1 /tmp/video.mov
Example for Screen Recording 2026-01-29 at 12.33.07 PM.mov:
/bin/cp -f ~/Desktop/Screen*12.33.07* /tmp/video.mov && \
ffprobe -v error -select_streams v:0 \
-show_entries stream=width,height,r_frame_rate,duration \
-show_entries stream=color_transfer \
-of default=noprint_wrappers=1 /tmp/video.mov
Check the color_transfer value:
smpte2084 → HDR recording, must convert to SDR first (Step 1b)bt709, unknown, etc.) → SDR, skip to Step 2macOS screen recordings on XDR displays use HDR10 (PQ/BT.2020/10-bit). The base Homebrew ffmpeg cannot tone-map PQ without the zimg library. Use macOS-native avconvert instead. It uses AVFoundation which handles HDR to SDR tone mapping correctly.
avconvert -s /tmp/video.mov -o /tmp/video_sdr.mov -p PresetHighestQuality --replace --progress
Then use /tmp/video_sdr.mov as input for Step 2 instead of /tmp/video.mov.
Verify conversion:
ffprobe -v error -select_streams v:0 -show_entries stream=color_transfer -of csv=p=0 /tmp/video_sdr.mov
Should output bt709 (not smpte2084).
Use /tmp/video_sdr.mov if HDR was detected, otherwise /tmp/video.mov. Substitute the input path as INPUT below.
Build the filter chain from arguments. Omit setpts when speed is 1. Omit crop when not requested.
Filter chain order: setpts → crop → fps → scale → palettegen/paletteuse
With defaults (fps=10, width=640, speed=1):
mkdir -p /tmp/gif-output && \
ffmpeg -y -v warning -i INPUT \
-vf "fps=10,scale=640:-1:flags=lanczos,palettegen=stats_mode=diff" \
-update 1 /tmp/gif-output/palette.png && \
ffmpeg -y -v warning -i INPUT -i /tmp/gif-output/palette.png \
-lavfi "fps=10,scale=640:-1:flags=lanczos[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=5" \
-loop 0 /tmp/gif-output/output.gif && \
du -h /tmp/gif-output/output.gif
With --speed 3 (3x speedup):
mkdir -p /tmp/gif-output && \
ffmpeg -y -v warning -i INPUT \
-vf "setpts=PTS/3,fps=10,scale=640:-1:flags=lanczos,palettegen=stats_mode=diff" \
-update 1 /tmp/gif-output/palette.png && \
ffmpeg -y -v warning -i INPUT -i /tmp/gif-output/palette.png \
-lavfi "setpts=PTS/3,fps=10,scale=640:-1:flags=lanczos[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=5" \
-loop 0 /tmp/gif-output/output.gif && \
du -h /tmp/gif-output/output.gif
With --full (no scaling):
mkdir -p /tmp/gif-output && \
ffmpeg -y -v warning -i INPUT \
-vf "fps=10,palettegen=stats_mode=diff" \
-update 1 /tmp/gif-output/palette.png && \
ffmpeg -y -v warning -i INPUT -i /tmp/gif-output/palette.png \
-lavfi "fps=10[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=5" \
-loop 0 /tmp/gif-output/output.gif && \
du -h /tmp/gif-output/output.gif
With --crop (remove macOS recording overlay):
Insert crop before fps. The macOS recording bar is typically ~60px on retina displays:
# crop=in_w:in_h-PIXELS:0:PIXELS removes top PIXELS from source
"setpts=PTS/3,crop=in_w:in_h-60:0:60,fps=10,scale=640:-1:flags=lanczos,palettegen=stats_mode=diff"
Report the output path and file size. Then open:
open /tmp/gif-output/output.gif # macOS
For large or long videos:
| Technique | Command modification |
|---|---|
| Speed up | setpts=PTS/3 for 3x (fewer frames = smaller file) |
| Lower FPS | fps=5 (for long videos) |
| Smaller width | scale=480:-1 or scale=320:-1 |
| Trim to range | Add -ss 00:00:05 -t 10 before -i to grab 10s starting at 5s |
| Crop region | Add crop=w:h:x:y to the filter chain |
| Remove overlay | crop=in_w:in_h-60:0:60 removes top 60px (macOS recording bar) |
avconvert is macOS-only. On Linux, HDR tone mapping requires ffmpeg built with zimg or libplacebo, which Homebrew/apt default builds lack.--full on retina screen recordings (2880x1800+) produces GIFs over 100MB. Always warn about file size when skipping the scale filter./tmp/gif-output/palette.png and /tmp/gif-output/output.gif but never cleans them up. Repeated runs overwrite, but stale files persist across sessions.scale=640:-1 fails when the source height is odd after scaling. Use scale=640:-2 to force even dimensions.--crop assumes 60px for the macOS recording bar, but this varies by display scale and macOS version. Always probe actual dimensions and let the user confirm the crop value.