Creates animated looping GIFs from CSS animations (default) or AI image-to-video. 800×800px default, 6 animation types, 4 style presets. Trigger when user says "create an animated gif", "make a looping gif", "animated banner", "CSS animation gif", "social media animation", "make this loop", "animated graphic", or "motion graphic".
npx skills add https://github.com/Varnan-Tech/opendirectory --skill graphic-gif
Generates an animated looping GIF from CSS animations or an AI image-to-video API. Output: animation.gif.
Unlike every other graphic- skill that outputs a static PNG or PDF, this skill outputs an animated .gif. Uses CSS @keyframes animations captured frame-by-frame via Playwright and the Web Animations API (Option A, default), or an AI image-to-video pipeline via Kling (Option B).
ai-generated unless explicitly requested.clamp() values computed at 800px (1vw = 8px).<style>. Font CDN <link> only external dependency.setTimeout loops, NOT animation-delay tricks.Math.floor(duration_seconds * fps) frames. The frame at t=duration_ms MUST NOT be captured — it duplicates t=0 and causes a visible stutter at the loop point.clean-slate, terminal, electric-burst, brutalist.@keyframes percentages — frame seeking handles timing.10. Commit to design direction before writing CSS. Tone, signature element, motion style, unforgettable detail — all decided before first line of code.
Required: prompt (content description AND motion brief)
Optional with defaults:
| Parameter | Default | Options |
|---|---|---|
| animation_type | css-animated | css-animated / ai-generated |
| duration | 3.0 | seconds |
| fps | 12 | frames per second |
| loop | true | true / false |
| style | clean-slate | clean-slate / terminal / electric-burst / brutalist |
| dimensions | 800x800 | WxH in pixels |
| optimization | balanced | quality / balanced / filesize |
If prompt is missing or lacks motion description, ask exactly:
> "What should the GIF show? Describe the content AND the motion (e.g., 'Stats count up: 73% of buyers read 3+ pieces of content before purchase. Typewriter effect, one character at a time. Style: terminal. 3 seconds, 12fps.')
>
> Key settings (all optional, defaults shown):
> - animation_type: css-animated (default) or ai-generated
> - duration: 3.0 seconds
> - fps: 12
> - loop: true
> - style: clean-slate (options: clean-slate / terminal / electric-burst / brutalist)
> - dimensions: 800x800
> - optimization: balanced (options: quality / balanced / filesize)"
If all required info is present → skip directly to Step 2.
For css-animated:
fade-in, slide-in, typewriter, counter, pulse, loop-scrollreferences/animation-library.md — find the chosen type's full HTML/CSS specreferences/style-presets.md — load the chosen style's CSS token blockMath.floor(duration_seconds * fps) — write this number down| Decision | Derive from |
|---|---|
| Tone | Emotional register for audience (mechanical / warm / electric / professional) |
| Signature element | ONE visual device used consistently (cursor blink, ghost number, scan-line overlay, accent border) |
| Motion style | Ease curve philosophy for this type (spring / linear / step / ease-in-out) |
| Unforgettable detail | The ONE thing a viewer will remember about this GIF |
For ai-generated:
POST https://api.klingai.com/v1/videos/image2video with image_url and prompt describing the motion # Two-pass palette for best color quality
ffmpeg -i input.mp4 -vf "fps=12,scale=800:800:flags=lanczos,palettegen=stats_mode=diff" palette.png
ffmpeg -i input.mp4 -i palette.png -vf "fps=12,scale=800:800:flags=lanczos,paletteuse=dither=bayer:bayer_scale=5" output.gif
Read references/animation-library.md and references/style-presets.md before generating.
Canvas base — required on every GIF:
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
body {
width: 800px;
height: 800px;
overflow: hidden;
background: var(--bg);
font-family: var(--font-body);
}
.canvas {
width: 800px;
height: 800px;
position: relative;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
Animation rules:
animation-fill-mode: forwards (or both) on ALL animated elementstypewriter → steps(N, end) where N = exact character countcounter → linearfade-in → cubic-bezier(0.22, 1, 0.36, 1) (ease-out)slide-in → cubic-bezier(0.34, 1.56, 0.64, 1) (spring overshoot)pulse → ease-in-out with animation-iteration-count: infiniteloop-scroll → linear with animation-iteration-count: infiniteanimation-iteration-count: 1 — looping happens at GIF levelanimation-delay — stagger is baked into @keyframes percentagesTypewriter N calculation:
Count every character including spaces, punctuation, numbers:
steps(14, end)steps(13, end)Counter CSS @property (required for counter type):
@property --num {
syntax: '<integer>';
inherits: false;
initial-value: 0;
}
.counter {
animation: countUp var(--duration) linear forwards;
counter-reset: num var(--num);
}
.counter::after { content: counter(num); }
@keyframes countUp {
from { --num: 0; }
to { --num: var(--target); }
}
Loop-scroll: content MUST be duplicated:
<!-- 4 original items + 4 duplicate items -->
<div class="ticker-track" style="--item-count: 4;">
[item1][item2][item3][item4][item1][item2][item3][item4]
</div>
translateX(0 → -50%) with linear infinite.
Design quality rules (from commit in Step 2):
#fff for dark styles — use the preset's exact --bg value::after with repeating-linear-gradient at opacity: 0.034px solid #000 or 4px solid var(--accent) on key elementCanvas:
body and .canvas exactly 800×800px (or specified dimensions)overflow: hidden on both body and .canvasAnimations:
animation-delay anywhere — stagger is in @keyframes percentagest=0 (Web Animations API will seek from there)animation-fill-mode: forwards or both on all animated elementsanimation-iteration-count: 1animation-iteration-count: infiniteType-specific checks:
steps(N, end) = exact character count of text string@property --num declared with syntax: '<integer>' and initial-value: 0counter-reset: num var(--num) and ::after { content: counter(num) }Design:
references/style-presets.md — no free-floating hex colors<link> present for chosen style's fontDetermine slug from prompt (kebab-case, ≤30 chars). Create output directory:
mkdir -p [slug]
Save HTML:
[slug]/animation.html
Open in browser for quick visual check:
open [slug]/animation.html
Run export script (replace [skill-root] with the actual path to this skill):
bash [skill-root]/scripts/export-gif.sh \
[slug]/animation.html \
[slug]/animation.gif \
--duration [duration] \
--fps [fps] \
[--no-loop if loop=false] \
--optimization [optimization] \
--width [W] \
--height [H]
The script:
gifenc, sharp (or jimp), and playwright in a temp directorycapture-and-encode.mjs — pauses animations, seeks each frame, screenshots, assembles GIFgifsicle optimization pass if availableIf export script not found at [skill-root]/scripts/export-gif.sh, check that the skill was installed with its scripts/ folder intact.
Show after successful export:
## GIF: [1-line description]
Date: [YYYY-MM-DD] | Style: [style] | Animation: [type] | [duration]s @ [fps]fps
Dimensions: [WxH] | Frames: [N] | Loop: [true/false]
Files
Source: [slug]/animation.html
Output: [slug]/animation.gif
Size: [X] KB
Checklist
- [ ] Preview loops cleanly at start/end point (no stutter)
- [ ] Text legible at intended display size
- [ ] File size appropriate: email <500KB / social <3MB
Only use when animation_type: ai-generated is explicitly specified.
Requirements:
KLING_API_KEY (66 free credits/day, no credit card for free tier)ffmpeg installed locally for video→GIF conversionWorkflow:
# Quick screenshot via Playwright
node -e "
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: W, height: H } });
await page.goto('file://[slug]/animation.html');
await page.screenshot({ path: '[slug]/base-frame.png' });
await browser.close();
})();
"
When Kling is unavailable: Fall back to css-animated with a note to the user: "AI generation requires a Kling API key (KLING_API_KEY). Falling back to css-animated. Set the key to enable AI generation."
> "Describe motion, not just content. 'Stats count up one by one' beats 'show stats'."
>
> "Keep it simple for file size. 1–3 animated elements and a solid background."
>
> "Think in loops. The animation should flow invisibly from end back to start."
>
> "Specify the animation type explicitly. typewriter and counter are the most effective for social."
>
> ✅ Good: "Create an animated GIF, css-animated, typewriter effect. Text: '73% of B2B buyers read 3+ pieces of content before contacting sales.' Each character types out one at a time. Style: terminal. 3 seconds, 12fps, loop=true."
>
> ❌ Bad: "make an animated gif of marketing tips"
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Improves the quality of images, especially screenshots, by enhancing resolution, sharpness, and clarity. Perfect for preparing images for presentations, documentation, or social media posts.
Downloads videos from YouTube and other platforms for offline viewing, editing, or archival. Handles various formats and quality options.
Lightweight WSI tile extraction and preprocessing. Use for basic slide processing tissue detection, tile extraction, stain normalization for H&E images. Best for simple pipelines, dataset preparation, quick tile-based analysis. For advanced spatial proteomics, multiplexed imaging, or deep learning pipelines use pathml.
Microscopy data management platform. Access images via Python, retrieve datasets, analyze pixels, manage ROIs/annotations, batch processing, for high-content screening and microscopy workflows.
Python library for working with DICOM (Digital Imaging and Communications in Medicine) files. Use this skill when reading, writing, or modifying medical imaging data in DICOM format, extracting pixel data from medical images (CT, MRI, X-ray, ultrasound), anonymizing DICOM files, working with DICOM metadata and tags, converting DICOM images to other formats, handling compressed DICOM data, or processing medical imaging datasets. Applies to tasks involving medical image analysis, PACS systems, radiology workflows, and healthcare imaging applications.
This skill should be used when working with pre-trained transformer models for natural language processing, computer vision, audio, or multimodal tasks. Use for text generation, classification, question answering, translation, summarization, image classification, object detection, speech recognition, and fine-tuning models on custom datasets.
Take varnan-tech/graphic-gif from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.