Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Convert video clips to optimized GIFs with speed control, cropping, text overlays, and file size optimization. Create perfect GIFs for social media, documentation, and presentations.
.claude/skills/nicepkg-video-to-gif/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 75% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 54% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 175% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 91% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 134% | 0% |
Transform video clips into high-quality, optimized GIFs perfect for social media, documentation, tutorials, and presentations. Features precise timing control, text overlays, effects, and intelligent file size optimization.
pythonfrom scripts.gif_workshop import GifWorkshop # Basic conversion workshop = GifWorkshop("video.mp4") workshop.to_gif("output.gif") # With options workshop = GifWorkshop("video.mp4") workshop.clip(start=5, end=10) # 5-10 seconds workshop.resize(width=480) # Resize to 480px wide workshop.set_fps(15) # 15 frames per second workshop.optimize(max_size_kb=500) # Max 500KB workshop.to_gif("output.gif")
pythonfrom scripts.gif_workshop import GifWorkshop # From file workshop = GifWorkshop("video.mp4") # With initial settings workshop = GifWorkshop("video.mp4", fps=15, width=480)
python# By time (seconds) workshop.clip(start=5, end=15) # 5s to 15s # By time string workshop.clip(start="00:01:30", end="00:01:45") # 1:30 to 1:45 # From start or to end workshop.clip(start=10) # From 10s to end workshop.clip(end=5) # First 5 seconds # Multiple clips workshop.clip_multi([ (0, 3), (10, 15), (20, 25) ]) # Concatenates clips
python# Speed up workshop.speed(2.0) # 2x faster # Slow motion workshop.speed(0.5) # Half speed # Reverse workshop.reverse() # Boomerang effect (forward then reverse) workshop.boomerang()
python# Resize by width (maintain aspect) workshop.resize(width=480) # Resize by height workshop.resize(height=360) # Exact dimensions workshop.resize(width=480, height=360) # Crop to region workshop.crop(x=100, y=50, width=400, height=300) # Crop to aspect ratio workshop.crop_to_aspect(16, 9) # 16:9 workshop.crop_to_aspect(1, 1) # Square
python# Simple text overlay workshop.add_text( "Hello World!", position='bottom', fontsize=24, color='white' ) # Text with timing workshop.add_text( "Watch this!", position='top', start_time=0, end_time=3 # Show for first 3 seconds ) # Multiple text overlays workshop.add_text("Step 1", position='top-left', start_time=0, end_time=2) workshop.add_text("Step 2", position='top-left', start_time=2, end_time=4) # Caption bar workshop.add_caption_bar( "This is a caption", position='bottom', background='black', padding=10 )
python# Color filters workshop.filter('grayscale') workshop.filter('sepia') # Adjustments workshop.adjust(brightness=0.1, contrast=0.2) # Fade in/out workshop.fade_in(duration=0.5) workshop.fade_out(duration=0.5) # Blur workshop.blur(intensity=2)
python# Target file size workshop.optimize(max_size_kb=500) # Quality settings workshop.optimize( quality='medium', # 'low', 'medium', 'high' colors=128 # Color palette size (2-256) ) # Manual FPS control workshop.set_fps(10) # Lower FPS = smaller file # Lossy compression workshop.optimize(lossy=80) # 0-100, higher = more compression
python# Basic export workshop.to_gif("output.gif") # With options workshop.to_gif( "output.gif", optimize=True, colors=256, loop=0 # 0 = infinite loop, 1+ = loop count ) # Export as video (for comparison) workshop.to_video("output.mp4") # Export frames workshop.export_frames("frames/", format='png')
python# Social media presets workshop.preset('twitter') # 512px wide, 5MB max workshop.preset('discord') # 256px, 8MB max workshop.preset('slack') # 480px, 5MB max workshop.preset('reddit') # 720px, optimized # Quality presets workshop.preset('high') # High quality, larger file workshop.preset('medium') # Balanced workshop.preset('low') # Small file, lower quality # Special presets workshop.preset('thumbnail') # Small preview GIF workshop.preset('reaction') # Reaction GIF (small, fast)
| Position | Description | |----------|-------------| | 'top' | Top center | | 'bottom' | Bottom center | | 'center' | Center of frame | | 'top-left' | Top left corner | | 'top-right' | Top right corner | | 'bottom-left' | Bottom left corner | | 'bottom-right' | Bottom right corner |
python# Get best frame (thumbnail) best_frame = workshop.get_best_frame() best_frame.save("thumbnail.png") # Extract frame at time frame = workshop.get_frame_at(5.5) # Frame at 5.5 seconds frame.save("frame.png") # Extract all frames workshop.export_frames("frames/", format='png')
pythoninfo = workshop.get_info() print(f"Duration: {info['duration']} seconds") print(f"Size: {info['width']}x{info['height']}") print(f"FPS: {info['fps']}") print(f"Frames: {info['frame_count']}")
python# Apply custom function to each frame def custom_filter(frame): # frame is a PIL Image return frame.rotate(5) workshop.apply_filter(custom_filter)
python# Join multiple clips workshop.concat([ "intro.mp4", "main.mp4", "outro.mp4" ])
bash# Basic conversion python gif_workshop.py video.mp4 -o output.gif # With clip selection python gif_workshop.py video.mp4 -o output.gif --start 5 --end 15 # With options python gif_workshop.py video.mp4 -o output.gif \ --width 480 \ --fps 15 \ --speed 1.5 \ --max-size 500 # With text python gif_workshop.py video.mp4 -o output.gif \ --text "Hello World" \ --text-position bottom # Apply preset python gif_workshop.py video.mp4 -o output.gif --preset twitter
| Preset | Width | FPS | Colors | Use Case | |--------|-------|-----|--------|----------| | reaction | 256px | 10 | 64 | Quick reactions | | low | 320px | 12 | 128 | Basic sharing | | medium | 480px | 15 | 256 | General use | | high | 640px | 20 | 256 | High quality | | max | Original | 24 | 256 | Best quality |
pythonfrom scripts.gif_workshop import GifWorkshop, GifError try: workshop = GifWorkshop("video.mp4") workshop.clip(start=0, end=100) # May exceed video length workshop.to_gif("output.gif") except GifError as e: print(f"Error: {e}") except FileNotFoundError: print("Video file not found")
moviepy>=1.0.3
Pillow>=10.0.0
imageio>=2.31.0
numpy>=1.24.0| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-14 | pass→pass | 7,118 | 2,771 | -61% | 1 | 1 | 0% | 1,233 | 2,982 | +142% | 0 | 0 | — |
case-01 | fail→fail | 17,763 | 6,565 | -63% | 1 | 1 | 0% | 2,800 | 3,877 | +38% | 0 | 0 | — |
case-02 | fail→pass | 11,067 | 3,163 | -71% | 1 | 1 | 0% | 1,828 | 3,198 | +75% | 0 | 0 | — |
case-03 | fail→pass | 12,126 | 6,124 | -49% | 1 | 1 | 0% | 2,464 | 3,796 | +54% | 0 | 0 | — |
case-04 | fail→fail | 14,437 | 9,213 | -36% | 1 | 1 | 0% | 2,579 | 4,012 | +56% | 0 | 0 | — |
case-05 | pass→pass | 10,959 | 7,658 | -30% | 1 | 1 | 0% | 2,002 | 3,977 | +99% | 0 | 0 | — |
case-06 | pass→pass | 9,714 | 9,694 | -0% | 1 | 1 | 0% | 1,774 | 4,360 | +146% | 0 | 0 | — |
case-07 | fail→pass | 6,181 | 3,262 | -47% | 1 | 1 | 0% | 1,131 | 3,113 | +175% | 0 | 0 | — |
case-08 | fail→pass | 9,291 | 3,862 | -58% | 1 | 1 | 0% | 1,741 | 3,331 | +91% | 0 | 0 | — |
case-09 | fail→pass | 7,194 | 2,031 | -72% | 1 | 1 | 0% | 1,223 | 2,858 | +134% | 0 | 0 | — |
case-10 | fail→pass | 8,968 | 2,792 | -69% | 1 | 1 | 0% | 1,552 | 3,006 | +94% | 0 | 0 | — |
case-11 | fail→pass | 10,954 | 3,106 | -72% | 1 | 1 | 0% | 1,874 | 3,127 | +67% | 0 | 0 | — |
case-12 | fail→pass | 10,087 | 2,812 | -72% | 1 | 1 | 0% | 1,769 | 3,056 | +73% | 0 | 0 | — |
case-13 | fail→pass | 5,902 | 2,300 | -61% | 1 | 1 | 0% | 979 | 2,951 | +201% | 0 | 0 | — |
case-15 | fail→pass | 18,523 | 3,052 | -84% | 1 | 1 | 0% | 2,870 | 3,060 | +7% | 0 | 0 | — |
case-16 | fail→pass | 9,837 | 2,007 | -80% | 1 | 1 | 0% | 1,431 | 2,846 | +99% | 0 | 0 | — |
case-17 | fail→pass | 9,384 | 2,236 | -76% | 1 | 1 | 0% | 1,454 | 2,923 | +101% | 0 | 0 | — |
case-18 | fail→pass | 15,609 | 1,823 | -88% | 1 | 1 | 0% | 2,547 | 2,814 | +10% | 0 | 0 | — |
case-19 | fail→pass | 13,040 | 3,796 | -71% | 1 | 1 | 0% | 2,184 | 3,223 | +48% | 0 | 0 | — |
case-20 | fail→pass | 10,571 | 4,349 | -59% | 1 | 1 | 0% | 1,891 | 3,265 | +73% | 0 | 0 | — |
case-21 | pass→pass | 7,511 | 2,153 | -71% | 1 | 1 | 0% | 1,406 | 2,967 | +111% | 0 | 0 | — |
case-22 | fail→pass | 9,420 | 1,724 | -82% | 1 | 1 | 0% | 1,559 | 2,818 | +81% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +73 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.