{
  "markdown": "# compress-gif\n\nA Claude Code plugin that compresses animated GIFs to a target size\n(default 200 KB) **without** changing frame rate, dimensions, frame count,\nor visibly degrading the color profile.\n\nEvery run executes two independent encoders in parallel —\n[`gifsicle`](https://www.lcdf.org/gifsicle/) and\n[`gifski`](https://github.com/ImageOptim/gifski) — and writes both outputs\nside by side so you can pick whichever looks better. They trade off\ndifferently on different content: gifsicle wins on flat-color material\n(screen recordings, code demos), gifski wins on gradients and photographic\ncontent.\n\nIf the target can't be reached without violating the size-preservation\nconstraints, the plugin emits a best-effort output and explains the gap\nrather than silently dropping frames or resizing.\n\n---\n\n## Quick start\n\nInside a running Claude Code session, type these three slash commands:\n\n```\n/plugin marketplace add ankshvayt/compress-gif\n/plugin install compress-gif@ankshvayt-plugins\n/reload-plugins\n```\n\nThe first command registers this GitHub repo as a marketplace. The second\ninstalls the plugin from it. The third makes the new skill available\nwithout needing to restart Claude Code.\n\nThen use the skill:\n\n```\n/compress-gif:compress ~/demo.gif           # default 200 KB target\n/compress-gif:compress ~/demo.gif 150       # custom target in KB\n```\n\nThe first time you run it, the plugin checks your environment and walks\nyou through installing any missing dependencies (`gifsicle`, optionally\n`gifski` and `ffmpeg`) with your consent — no surprise `brew install`s.\n\n---\n\n## Why this plugin\n\nMost \"compress a GIF\" recipes hit small file sizes by doing things that\nquietly degrade the result:\n\n- **Dropping frames** → playback becomes stuttery.\n- **Resizing** → loses the original dimensions you cared about.\n- **Transcoding to a different palette space** → wrecks the color profile.\n\nThis plugin refuses all three. It only tunes knobs that are visually\nsafe — LZW encoding, perceptual lossy passes, and (as a last resort)\ngentle palette reduction down to a floor of 64 colors. If even the\nsafest aggressive pass can't reach the target, the plugin says so\ninstead of resorting to the destructive shortcuts above.\n\nIt also runs **two** encoders every time and shows both outputs, because\ngifsicle and gifski produce subtly different artifacts and the better\nchoice depends on the content. Running both costs a few extra seconds\nand saves you from guessing.\n\n---\n\n## How it works\n\nThe script applies passes in order, stopping the moment the target is\nmet. Whichever pass settles the result is reported as the \"recipe.\"\n\n**gifsicle backend:**\n\n1. Lossless `-O3` (structural optimization, no quality loss).\n2. `--lossy=N` ladder: 20 → 40 → 60 → 80 → 120 → 160 → 200. Perceptual\n   tweak to the LZW step — not a resolution or frame change.\n3. If still over budget at `--lossy=200`, gentle palette reduction:\n   224 → 192 → 160 → 128 → 96 → 64 colors. Stops at 64 to keep the\n   color profile recognizable.\n\n**gifski backend:**\n\n1. Read the GIF's per-frame delays via `gifsicle --info`, take the\n   mode, derive an FPS.\n2. Extract every frame as PNG via `ffmpeg` (one-to-one, no rate\n   conversion).\n3. `gifski --quality` ladder: 100 → 90 → 80 → 70 → 60 → 50 → 40 → 30\n   → 20. Stops as soon as the target is met. pngquant chooses the\n   palette internally.\n\nNeither backend ever passes `--scale`, `--resize`, `--width`, `--height`,\n`--delay`, frame slicing, or `--every-nth-frame`. Frame count and\ndimensions in the output are byte-identical to the input; per-frame\ndelays are preserved exactly by gifsicle and reconstructed at the\ndetected FPS by gifski.\n\n---\n\n## What's preserved vs. what's tuned\n\n|                                       | gifsicle | gifski |\n|---                                    |---       |---     |\n| Lossless structural optimization      | `-O3`              | (built in)        |\n| Perceptual quality dial               | `--lossy` 20 → 200 | `--quality` 100 → 20 |\n| Gentle palette reduction (last resort)| `--colors` 224 → 64 | (pngquant)       |\n| Dimensions (`--scale`/`--resize`)     | **never**          | **never**         |\n| Frame count (`--every-nth-frame`)     | **never**          | **never**         |\n| Per-frame delay / FPS                 | unchanged          | reconstructed at source FPS |\n| Format change (→ MP4, WebP, etc.)     | **never**          | **never**         |\n\n---\n\n## Requirements\n\n| Tool       | Role     | macOS                       | Linux (apt)                              | Notes                                  |\n|---         |---       |---                          |---                                       |---                                     |\n| Claude Code| required | v2.1.0+                     | v2.1.0+                                  | Needed for the `skills/` plugin layout |\n| Python     | required | 3.8+ (system Python OK)     | 3.8+ (system Python OK)                  | Standard library only                  |\n| `gifsicle` | required | `brew install gifsicle`     | `sudo apt install gifsicle`              | Always runs                            |\n| `gifski`   | optional | `brew install gifski`       | `cargo install gifski` (no apt package)  | Enables the second output              |\n| `ffmpeg`   | optional | `brew install ffmpeg`       | `sudo apt install ffmpeg`                | Required only by the gifski backend    |\n\n**Windows:** untested. The Python script itself is platform-neutral, but\nthe `gifsicle`/`gifski`/`ffmpeg` install paths in this README target\nmacOS and Debian-family Linux. If you're on Windows, WSL2 with the\nLinux instructions is the safest route.\n\nYou don't need to install these by hand. On the first run, the plugin\ndetects what's missing or broken — including the case where a tool is\non `PATH` but fails to load due to a dylib ABI mismatch — and surfaces\nthe exact install command. It **never** runs `brew install` or\n`apt install` silently; Claude proposes the command and you approve it.\n\nIf you decline an *optional* install, gifsicle still runs and you get\none output instead of two.\n\n---\n\n## Installation\n\n### From this GitHub repo (recommended)\n\nIn Claude Code, run the three slash commands shown in [Quick start](#quick-start):\n\n```\n/plugin marketplace add ankshvayt/compress-gif\n/plugin install compress-gif@ankshvayt-plugins\n/reload-plugins\n```\n\nTo update later (after a new release is pushed here):\n\n```\n/plugin marketplace update ankshvayt-plugins\n/reload-plugins\n```\n\nTo uninstall:\n\n```\n/plugin uninstall compress-gif@ankshvayt-plugins\n```\n\n### For local development\n\nClone and load the repo directly with `--plugin-dir`. Useful if you want\nto hack on the plugin source itself — changes take effect after\n`/reload-plugins`. This loads the plugin for the current session only.\n\n```bash\ngit clone https://github.com/ankshvayt/compress-gif.git\ncd compress-gif\nclaude --plugin-dir .\n```\n\n---\n\n## Usage\n\n### Inside Claude Code\n\n```\n/compress-gif:compress <path-to-gif> [target-kb]\n```\n\nExamples:\n\n```\n/compress-gif:compress ~/Desktop/demo.gif\n/compress-gif:compress ./screencast.gif 150\ncompress demo.gif under 100kb with gifski only\n```\n\nThe skill is model-invocable, so natural-language phrasing also\ntriggers it.\n\n### As a standalone CLI\n\nThe compression script works without Claude Code:\n\n```bash\n# Run the preflight without compressing anything:\npython3 skills/compress/scripts/compress_gif.py --check\n\n# Default: both backends, 200 KB target, output next to input:\npython3 skills/compress/scripts/compress_gif.py input.gif\n\n# Custom options:\npython3 skills/compress/scripts/compress_gif.py input.gif --target-kb 150\npython3 skills/compress/scripts/compress_gif.py input.gif --only gifsicle\npython3 skills/compress/scripts/compress_gif.py input.gif --output-dir ./out\n```\n\nExit codes:\n\n| Code | Meaning                                                         |\n|---   |---                                                              |\n| 0    | At least one backend produced output at or under the target.    |\n| 1    | Both backends produced output, but neither reached the target.  |\n| 2    | Preflight failed (required tool missing/broken) or bad input.   |\n\n---\n\n## Output\n\nBoth files are written next to the input (or to `--output-dir` if set):\n\n- `<stem>.gifsicle.gif`\n- `<stem>.gifski.gif`\n\nThe original input is never modified or deleted.\n\nEnd-of-run report:\n\n```\n================================================================\n  original:       2.4 MB\n  target:         under 200.0 KB\n================================================================\n  gifsicle    ✓ 187.3 KB   ( 92.4% smaller)  -O3 --lossy=120\n              → /path/to/demo.gifsicle.gif\n  gifski      ✓ 162.8 KB   ( 93.4% smaller)  --fps 10.0 --quality 70\n              → /path/to/demo.gifski.gif\n================================================================\n  recommendation: gifski  (162.8 KB)\n```\n\nThe `recommendation` line is just \"smallest output that hit the target.\"\nIt is **not** a visual-quality judgment — open both files and pick what\nlooks best for your content.\n\nA `△` flag instead of `✓` means the backend couldn't reach the target\nwithout violating constraints. The file is still written (best-effort)\nand the report explains the gap.\n\n---\n\n## Known limitations\n\n- **Variable per-frame delays** — gifski reconstructs frames at a single\n  FPS (the mode of the input's per-frame delays). If your GIF has\n  intentionally non-uniform timing, gifski may shift the perceived\n  cadence slightly. gifsicle preserves per-frame delays exactly.\n- **Single-frame \"GIFs\"** — there's nothing for gifski to gain on a\n  static image; gifsicle's lossless pass will usually still shrink it.\n- **Inputs with high motion + photographic content + tight targets**\n  may best-effort out. That's the design — the alternative is to\n  silently degrade in ways the user told us not to.\n\n---\n\n## Troubleshooting\n\n**`! gifski [optional]  dyld[…]: Library not loaded: …libx265.…dylib`**\ngifski's Homebrew bottle is linked through ffmpeg's `libavcodec`, which\nin turn links against `libx265`. When x265 bumps its ABI version\n(common in fast-moving builds), the old filename disappears and gifski\ncan't load. Fix:\n\n```bash\nbrew reinstall ffmpeg\n# or, if you use a custom ffmpeg tap:\nbrew reinstall homebrew-ffmpeg/ffmpeg/ffmpeg\n```\n\nThen re-run `python3 skills/compress/scripts/compress_gif.py --check`.\n\n**`✗ gifsicle [required]`** — required dependency. Install per the\ntable above and re-run `--check`.\n\n**Both backends best-effort (`△`)** — your input is fundamentally too\nlarge for the target at its native dimensions and frame rate. Either\nraise the target with `--target-kb N` or accept the best-effort\noutput.\n\n---\n\n## Acknowledgments\n\nThis plugin is a thin orchestrator over two excellent open-source\nencoders. All real work is theirs:\n\n- [`gifsicle`](https://www.lcdf.org/gifsicle/) — Eddie Kohler\n- [`gifski`](https://github.com/ImageOptim/gifski) — Kornel Lesiński\n- [`pngquant`](https://pngquant.org/) / `libimagequant` — the palette\n  quantization that powers gifski's quality, also by Kornel Lesiński\n\n---\n\n## License\n\n[MIT](./LICENSE)\n",
  "bytes": 11158,
  "sha": "c0ed43a56c8bfc24738f95ea50f5280335b9ac7b5022cd9cc4ac73dd5151cb6f",
  "repo_slug": "ankshvayt/compress-gif",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/plg_ankshvayt_compress_gif_compress_gif_bf68e8c3/readme"
}