agentleFS
drinkspillerdrinkspiller/skills/gif_creation/SKILL.md

gif-creation

Skill1 starsChanged 6 days agoUnlicenseView on GitHub

Create optimized GIFs from videos, image sequences, or WebGL shader captures using 2-pass palette generation. Use when users ask to create GIF, video to GIF, optimize GIF, animated GIF, GIF from images, shader to GIF, or reduce GIF file size. Supports quality presets, custom timing, transparency, and headless WebGL capture.

  • Installs packages

The file

We split this skill into the rules it gives an agent

9 rules. About 0 are common, 3 are distinctive, and 6 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Create high-quality, optimized GIFs from videos, image sequences, or WebGL shader renders using FFmpeg's 2-pass palette generation, …

    line 15
  2. Very specific:

    Use alpha_threshold=128 in FFmpeg or ImageMagick's palette handling.

    line 213
  3. Very specific:

    Use the size optimisation preset.

    line 299
  4. Very specific:

    Use floyd_steinberg dithering for photographic content.

    line 308
  5. Very specific:

    Always use -dispose Background with ImageMagick.

    line 317
  6. Distinctive:

    Prefer ImageMagick over FFmpeg for transparent output — more reliable disposal.

    line 318
  7. Distinctive:

    Test the output in multiple viewers.

    line 319
  8. Very specific:

    Use deterministic u_time stepping — never record real-time playback.

    line 323
  9. Very specific:

    Use gl.readPixels + offscreen canvas — never rely on canvas.toDataURL directly (WebGL clears the drawing buffer between frames).

    line 325

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md
<!-- Based on https://mcpmarket.com/tools/skills/gif-creation-optimization, extended with Playwright + WebGL shader capture pipeline from dithering-gif. -->

GIF Creation

Create high-quality, optimized GIFs from videos, image sequences, or WebGL shader renders using FFmpeg's 2-pass palette generation, ImageMagick, or Playwright-based headless capture pipelines.


Quick Start

From video

ffmpeg -i video.mp4 \
  -vf "fps=15,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
  -loop 0 output.gif

From image sequence

magick -delay 7 -loop 0 -dispose Background frames/frame_*.png output.gif

From WebGL shader (interactive)

cd <project-dir>
pnpm dev
# → http://localhost:3000  — tweak params, click ⬇ Save GIF

Source Detection

Automatically detect the source type and choose the appropriate pipeline:

InputDetectionMethod
Video file (video.mp4)Video sourceFFmpeg 2-pass with palette
Image folder (./frames/)Image sequenceFFmpeg or ImageMagick
Glob pattern (frame_*.png)Image sequenceFFmpeg or ImageMagick
WebGL shader (shader.html)Shader sourcePlaywright capture → ImageMagick

Pipeline 1: Video → GIF (FFmpeg 2-Pass)

The gold standard for video-to-GIF conversion. Two passes produce better colour fidelity and smaller files than single-pass approaches.

Pass 1 — Generate palette

ffmpeg -ss <start> -t <duration> -i video.mp4 \
  -vf "fps=<FPS>,scale=<WIDTH>:-1:flags=lanczos,palettegen=stats_mode=diff" \
  -y /tmp/palette.png

Pass 2 — Encode GIF

ffmpeg -ss <start> -t <duration> -i video.mp4 -i /tmp/palette.png \
  -lavfi "fps=<FPS>,scale=<WIDTH>:-1:flags=lanczos[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=5" \
  -loop 0 \
  output.gif

Optimization Presets

Presetstats_modeDitherColoursBest For
qualityfullfloyd_steinberg256Photographs, gradients
balanceddiffbayer:5256General use (default)
sizesinglenone128Smallest files, simple graphics

Pipeline 2: Image Sequence → GIF

With ImageMagick (recommended for transparency)

magick -delay <DELAY> -loop 0 -dispose Background "frames/frame_*.png" output.gif

Delay calculation: delay = 100 / FPS (centiseconds). E.g. 15 FPS → delay 7.

ImageMagick's -dispose Background is essential for transparent GIFs — it clears each frame before drawing the next, preventing ghosting artefacts.

With FFmpeg

ffmpeg -framerate <FPS> -pattern_type glob -i "frames/*.png" \
  -vf "scale=<WIDTH>:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
  -loop 0 \
  output.gif

Pipeline 3: WebGL Shader → GIF (Playwright + ImageMagick)

For generating GIFs from programmatic WebGL/shader sources — no screen recording needed. This pipeline was developed in the dithering-gif project.

Architecture

shader.html  →  Playwright (headless)  →  PNG frames  →  ImageMagick  →  GIF
                     │                         │
                     └─ page.evaluate()        └─ temp dir (cleaned up)
                        drives u_time
                        deterministically

Key Concepts

  1. Deterministic capture: Drive the shader's u_time uniform manually (one value per frame) rather than recording real-time playback. This gives pixel-perfect, timing-independent frames and guarantees seamless loops.
  2. gl.readPixels for reliable export: WebGL clears its drawing buffer after each frame swap. Use gl.readPixels + an offscreen 2D canvas to bypass this and get reliable PNG data.
  3. Row-flip: WebGL is bottom-left origin; PNG/2D canvas is top-left. Flip rows during the readback copy.
  4. Dynamic sizing: The shader's canvas.width/canvas.height and the gl.viewport must be updated together when changing output resolution. The readPixels buffer must be reallocated to match.

Shader Page Contract

The shader HTML page must expose these globals for headless capture:

window.__ready      = true;                      // signals page is loaded
window.__setParams  = (params) => { /* ... */ };  // apply uniform overrides
window.__render     = (uTime) => { /* ... */ };   // render one frame at time t
window.__getDataUrl = () => canvas.toDataURL();   // return PNG as data URL

Capture Script Pattern (Node.js)

import { chromium } from 'playwright';

const LOOP_PERIOD = 8 * Math.PI;  // exact loop period for the animation
const FRAME_COUNT = 90;
const step        = LOOP_PERIOD / FRAME_COUNT;

const browser = await chromium.launch({ args: ['--enable-webgl', '--use-gl=egl'] });
const page    = await (await browser.newContext({
  viewport: { width: 480, height: 480 },
})).newPage();

await page.goto('http://localhost:3000/shader.html');
await page.waitForFunction(() => window.__ready === true);

// Push params before capture
await page.evaluate((p) => window.__setParams(p), params);

for (let i = 0; i < FRAME_COUNT; i++) {
  await page.evaluate((t) => window.__render(t), i * step);
  const b64 = await page.evaluate(() => window.__getDataUrl().split(',')[1]);
  writeFileSync(`frame_${String(i).padStart(4, '0')}.png`, Buffer.from(b64, 'base64'));
}

await browser.close();

Encode with ImageMagick

magick -delay <DELAY> -loop 0 -dispose Background "frames/frame_*.png" output.gif

Seamless Loop Calculation

To find the exact loop period for sinusoidal animations:

  1. Identify all time-dependent terms: e.g. cos(a × t) and sin(b × t).
  2. Each term's period in t is 2π / coefficient.
  3. The animation's period is the LCM of all individual periods.
  4. Drive u_time from 0 to that LCM over exactly FRAME_COUNT frames.

Example: cos(0.75t) has period 8π/3; sin(0.625t) has period 16π/5. LCM = 8π. So 90 frames over 8π gives a mathematically exact loop.


Transparency

In GIFs (general)

GIF supports only 1-bit transparency (fully transparent or fully opaque per pixel). Use alpha_threshold=128 in FFmpeg or ImageMagick's palette handling.

With ImageMagick (recommended)

magick -delay 7 -loop 0 -dispose Background frames/frame_*.png output.gif

-dispose Background is critical — without it, transparent pixels accumulate ghost frames from previous iterations.

With FFmpeg

ffmpeg -framerate 15 -pattern_type glob -i "frames/*.png" \
  -vf "split[s0][s1];[s0]palettegen=reserve_transparent=1[p];[s1][p]paletteuse=alpha_threshold=128" \
  -gifflags +transdiff \
  -loop 0 output.gif

Configuration Defaults

OptionDefaultRange / OptionsDescription
Width480240, 320, 480, 640, 720, 800, 1024Output width in pixels
FPS1510, 12, 15, 20, 24, 30Frames per second
Durationfullfull, or secondsVideo duration to convert
Start0seconds or HH:MM:SSStart time offset
Loopinfiniteinfinite, once, <N>Loop count (-loop 0 = infinite)
Optimizebalancedquality, balanced, sizePreset (see table above)

Size Estimation

Approximate file sizes at the balanced preset:

Width5 s @ 15 fps10 s @ 15 fps5 s @ 24 fps10 s @ 24 fps
800 px8–15 MB15–30 MB12–24 MB24–48 MB
640 px5–10 MB10–20 MB8–16 MB16–32 MB
480 px3–6 MB6–12 MB5–10 MB10–20 MB
320 px1–3 MB2–6 MB2–5 MB4–10 MB
240 px0.5–2 MB1–4 MB1–3 MB2–6 MB

Factors affecting size: width, FPS, duration, scene complexity, and optimisation preset (quality > balanced > size).


Platform Limits

PlatformMax SizeRecommended Settings
Twitter / X5 MB480 px, 15 fps, 5–10 s
Discord8 MB640 px, 15 fps, 5–10 s
Slack5 MB480 px, 12 fps, 5–10 s
GitHub10 MB640 px, 15 fps, 10–15 s
Email1–2 MB320 px, 12 fps, 3–5 s

Post-Processing (Optional)

gifsicle — further compression

gifsicle -O3 --lossy=80 output.gif -o output_optimized.gif
FlagEffect
--lossy=30Minimal quality loss
--lossy=80Good balance (recommended)
--lossy=200Maximum compression

Best Practices

Reducing file size

  • Lower FPS (12–15 is sufficient for most content).
  • Smaller width (480 px or less).
  • Use the size optimisation preset.
  • Trim to the shortest necessary duration.
  • Apply gifsicle -O3 --lossy=80 as a post-processing step.

Better quality

  • Higher FPS (24–30 for smooth motion).
  • Larger width (640–800 px).
  • Use the quality optimisation preset.
  • Use floyd_steinberg dithering for photographic content.

Smooth motion

  • 24+ FPS for fast action or camera movement.
  • 12–15 FPS for simple content, UI demos, or talking heads.

Transparent GIFs

  • Always use -dispose Background with ImageMagick.
  • Prefer ImageMagick over FFmpeg for transparent output — more reliable disposal.
  • Test the output in multiple viewers; some render GIF transparency inconsistently.

Shader / WebGL GIFs

  • Use deterministic u_time stepping — never record real-time playback.
  • Calculate the exact loop period mathematically from the animation's frequency components.
  • Use gl.readPixels + offscreen canvas — never rely on canvas.toDataURL directly (WebGL clears the drawing buffer between frames).
  • Expose a __setParams API so the capture script can override uniforms without editing the shader source.

Requirements

ToolRequiredPurpose
FFmpegYes (video sources)Video decoding + 2-pass palette GIF encoding
ImageMagick 7+Yes (image sequences)Frame assembly + transparent GIF encoding
PlaywrightYes (shader sources)Headless Chromium for WebGL frame capture
gifsicleOptionalAdditional lossy compression

Install (macOS)

brew install ffmpeg imagemagick gifsicle

Verify

ffmpeg -version | head -n 1
magick -version | head -n 1
gifsicle --version | head -n 1

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.