SVGからLottie JSONアニメーションを生成するスキル。After Effects依存を減らし、AI主導でモーション設計と出力を行える。
Motion that is looked at before it ships.
A Claude Code skill for Lottie. Twenty-two recipes that re-skin in nine palettes, a converter that does the bezier arithmetic, a linter for the defects that render blank, and a renderer that makes the agent look at every frame it calls done.
Gallery · Palettes · What to ask for · Tools · Install · Live page
A broken Lottie still parses. Leave the os off a polystar, put an easing handle one
level too high, forget ip/op on a layer — the JSON is valid, the player raises
nothing, and the canvas stays empty. A validator that checks json.load() calls it fine.
And a Lottie that renders can still be bad: linear, bouncy, crowded, purple. Generated motion drifts to all four by default.
This skill answers both. The tools catch the silent failures and render every file, so the agent has to look at what it made; the recipes, the motion tokens and the palettes make the first draft quiet, precise and on-palette instead of generic.
| Say to Claude Code | What it does |
|---|---|
| "A success animation for our checkout, in the forest palette" | Starts from success-check, re-skins it, lints, renders, and shows you the filmstrip |
| "Animate this SVG logo with a quiet entrance" | Converts the SVG, cascades its parts, reads the onion skin to check the easing |
| "A loader that works in dark mode" | spinner-arc in night, with the loop seam checked frame against frame |
| "Make this LottieFiles animation match our brand colours" | recolor.py --list, then maps each colour by its role |
| "Why does this Lottie render blank?" | Lints it, names the defect (say KF012), fixes it, renders the proof |
Every GIF below is the JSON in examples/ rendered by
scripts/make-gifs.mjs in a real lottie-web — the same player
the skill uses to check its own work. Each one is written by the .py beside it; open it
to see the brief, the beats and the tokens.
![]() success-checkDisc settles, check writes itself, one quiet ripple. forest · one-shot · 1.6 s
|
![]() error-shakeDisc settles, X writes itself, then one crisp decaying shake. mono · one-shot · 1.5 s
|
![]() heart-likePress, pop, burst: the one playful example, labelled as such. berry · one-shot · 1.67 s
|
![]() notification-bellOne ring that swings itself still, the clapper a beat behind. citrus · loop · 2 s
|
![]() toggle-switchOff, on, off: the knob and both colours move on one curve. night · loop · 2 s
|
![]() lock-unlockThe shackle lifts free and the colour follows; then it locks again. sky · loop · 2 s
|
![]() play-pauseEach half of the play triangle squares off into a pause bar, and back. dusk · loop · 2 s
|
![]() shape-morphA squircle softens into a circle and back, a quarter turn per cycle. sky · loop · 2 s
|
![]() spinner-arcHead leads, tail follows, the ring turns: a seamless indeterminate spinner. mono · loop · 4 s
|
![]() typing-dotsA three-dot wave with a rest, so it reads as typing. harbor · loop · 1.33 s
|
![]() panda-loaderA panda chews bamboo inside a slow, breathing loading ring. paper · loop · 2 s
|
![]() download-progressArrow into tray, a ring that fills at an uneven pace, then a check. harbor · one-shot · 3.73 s
|
![]() battery-chargeCharge climbs in eased steps, the bolt settles, then it drains. forest · loop · 4 s
|
![]() equalizerFive bars, each on its own tempo, bass slow and treble quick. night · loop · 2 s
|
![]() location-pingA still pin; rings spread across the ground, thin and fade. dusk · loop · 2 s
|
![]() weather-sunRays turn slowly while a cloud drifts across the sun and back. sky · loop · 4 s
|
![]() paper-planeA paper plane cruises in on a curve, turning with it, and leaves a dashed trail. paper · one-shot · 1.53 s
|
![]() confetti-burstPlayful but restrained: one pop, fourteen pieces on ballistic arcs. berry · loop · 2 s
|
![]() card-stackThree cards settle back to front; the content writes in; the chip lands last. mono · one-shot · 2 s
|
![]() logo-draw-onAn SVG mark writes its lines; the sun rises out of the horizon, last. paper · one-shot · 2.17 s
|
![]() rocket-launchAn SVG icon, converted part by part, assembles and lifts off its own axis. paper · one-shot · 2.5 s
|
![]() bouncing-ballPlayful: squash and stretch at constant volume, spaced by gravity. citrus · loop · 1 s
|

Every example is written against colour roles — ground, surface, ink, muted, accent, on-accent, support — never hex values, so any of them renders in any palette:
python3 examples/heart-like.py --palette dusk -o heart-dusk.json
| Palette | Character |
|---|---|
paper | warm paper, ink and clay — the house default |
night | warm charcoal with an amber accent, for dark mode |
dusk | navy and tungsten |
harbor | sea glass with a deep teal |
citrus | cream, tangerine and saffron |
berry | blush, raspberry and apricot |
forest | sage, pine and mustard |
mono | greys and one signal red |
sky | pale blue, cobalt and sun |
The tests hold each palette to its contrast ratios — ink at 7:1 or more on its ground,
accent and muted at 3:1 — so nothing drawn against the roles disappears. For a brand, add
a palette with the same seven roles. For a file made elsewhere, recolor.py lists its
colours and maps them.
The Python tools are dependency-free; the renderer needs Node and a local Chrome.
motion.py — the house style as codeEasing tokens (strong ease-out, zero overshoot; playful exists for briefs that ask for
bounce), the palettes, and builders for every shape, paint, modifier, layer and
composition. A generator never types a handle, a hex value or a required property, so the
defects below cannot be written in the first place.
from motion import *
def build(c): # c is a palette, by role
disc = layer("Disc", group("Disc", ellipse(128), fill(c["accent"])),
p=(120, 120),
s=track((0, [92, 92], "out"), (22, [100, 100])),
o=track((0, 0, "out"), (10, 100)))
return comp("Disc", 240, 240, [disc], op=90, bg=c["bg"])
main(build, __file__, palette="paper") # --palette night -o out.json
svg2lottie.py — SVG to Lottie shape layersConverts paths, rects, circles, ellipses, lines, polygons, polylines, groups, transforms, strokes, and linear/radial gradients. Full path grammar including elliptical arcs, smooth curves, and relative commands. Each element becomes its own named layer, anchored at its own centre so scale and rotation pivot where you expect.
python3 scripts/svg2lottie.py icon.svg -o icon.json --size 512 --current-color "#1E1B18"
lottie_lint.py — the defects that render blankStructural checks, plus the ones that actually bite: per-shape required properties, easing handles on the wrong object, keyframes without handles that freeze the player, layers outside the composition range, permanently transparent layers, geometry with no paint, parent cycles, loops that do not close, and groups painted underneath an opaque sibling. Two taste notes name the commonest tells of generated motion — something growing from a point, easing that overshoots — without failing a build.
python3 scripts/lottie_lint.py examples/ # a directory
python3 scripts/lottie_lint.py a.json --strict # warnings fail too
python3 scripts/lottie_lint.py a.json --json # machine-readable
render.mjs — see itLoads the file in real lottie-web, samples the timeline on the ground it was designed
for, and writes PNG frames plus a contact sheet. Flags empty frames (a layer faded to 0
counts as empty), content off canvas, and content clipped by the edge — measured on the
drawn outline, so a spinning ring is not mistaken for a clipped one.
node scripts/render.mjs a.json --at 0,25,50,75,100
node scripts/render.mjs a.json --frames 0,12,24 --bg "#171513"
node scripts/render.mjs a.json --onion # frames stacked: spacing is the easing, the path is the arc
recolor.py — any Lottie, any paletteLists every colour in a file with its uses and layers, then maps them — static and animated fills and strokes, gradient stops, text and solid layers.
python3 scripts/recolor.py downloaded.json --list
python3 scripts/recolor.py downloaded.json --map "#FF5A5F=#C8522B" --map "#222222=#1E1B18" -o ours.json
recipe or SVG → build → lint → render → LOOK → revise
Before touching a keyframe it decides the motion — feeling, register, one hero property, timing, easing, palette. It starts from the nearest recipe or from real SVG geometry, never from hand-typed vertices, then lints, renders, and reads the filmstrip and the onion skin against an eight-question taste pass. It is told not to claim visual quality it has not seen.
References live beside the skill: motion taste · recipes · structure · SVG → Lottie · shape modifiers · Disney principles · techniques · GSAP
/plugin marketplace add obeskay/lottie-animator-skill
/plugin install lottie-animator
Clone the repository and link skills/lottie-animator/ into your skills directory, so
its scripts/ and examples/ links resolve; run npm install once in the clone for the
renderer. Copied without them, the skill degrades to guidance only and says so.
python3 -m unittest discover -s tests -v # 134 tests, stdlib only
python3 scripts/lottie_lint.py examples/ --strict
npm install && node scripts/render.mjs examples/success-check.json
node scripts/make-gifs.mjs # every GIF, the hero, the palette board
The tests rebuild every example from its generator and fail if the committed JSON has drifted, lint each one in all nine palettes, and hold the palettes to their contrast ratios. Path arithmetic is pinned against hand-computed coordinates, and arc direction was verified against Chrome's own rendering of the same path data.
To browse the examples live, serve the repository and open the landing page, which plays every example and re-skins them in any palette:
python3 -m http.server 8000 # then http://localhost:8000/docs/
See CONTRIBUTING.md.
MIT — see LICENSE.
互換性
トピック