You are the entry point for SVG animation requests in this repository.
The repository (svg-animation-studio) is a compounding system: every animation
reuses primitives from lib/primitives/, every new primitive earns the right
to be added through /svg-add-primitive. Your job is to translate one
sentence of natural language into a working output/<slug>.svg that visually
delights — without breaking that compounding discipline.
Why this skill exists
LLMs that template-literal SVG strings end up with subtly broken markup,
inconsistent IDs, and no way to evolve. This skill funnels everything through
lib/composer.js so the system stays composable, snapshot-testable, and
reusable across sessions. If you find yourself wanting to write <svg ...>
inline anywhere, stop — composer.composeSVG exists for exactly that need.
Step 1 — Understand what's being asked
- Read any user-provided reference from
ref/(sketches, sample SVGs, screenshots). - Identify the asset class: character mascot, logo mark, loader/spinner, notification badge, ambient background, or icon.
- Identify the motion verbs: jumping, bobbing, pulsing, spinning, waving, talking, glowing, drifting, flickering.
- Identify the style cues: pixel-art, gradient-smooth, flat, neon, comic.
If the request is vague ("make it cool"), ask one clarifying question — pick the highest-leverage one (usually: "what shape/character?" or "what motion?"). Don't grill the user.
Step 2 — Read the catalog before composing
Open lib/presets/index.js to see what already exists. Look at each
candidate's compose() (~30 lines each) to learn the patterns. Decide one of:
- Fork — closest existing preset is 80%+ right. Copy its file to a new slug, adjust params/colors.
- Compose from primitives — no existing preset fits, but the needed
primitives all exist in
lib/primitives/. Build a new preset. - Stop and request a primitive — a needed motion / shape / filter doesn't
exist. Output a one-line summary plus:
"I need a
<primitive-name>primitive inlib/primitives/<category>.js. Please run/svg-add-primitive <name>." Do NOT add primitives inline. Seedocs/extension-protocol.mdrule 1.
For inspiration mapping ("user wants X → reach for Y"), open
references/preset-cookbook.md.
For the full lib API (signatures + defaults), open references/lib-api.md.
For any branded / multi-layer asset (cover, banner, logo, loader), use the modern composition path (it's how the quality bar is reproduced):
- Pull a palette with
getPalette(opts.palette || '<default>')fromlib/palettes.jsand reference accents by index (pal.accents[0]) — never hardcode hex. - Build the body with
composeScene({ layers, defs, style })fromlib/scene.js: it orders layers back-to-front and auto-nests a layer's statictransform+ animatedclassName(so the CSS transform can't clobber positioning — Gotcha 2). - Then fork the closest family template:
brandCover.js/gradientCover.js(covers),heroStrip.js/repoHero.js(banners),orbitLogo.js/pulseMonoLogo.js/spinBadge.js(logos/badges),dotLoader.js/rippleLoader.js/bounceChar.js(loaders/characters).chan-coveris the FROZEN reference — forkbrand-coverinstead.
For why timing / squash / staggering matter, open
references/animation-principles.md. The 5-point quality checklist there is
the same one /svg-verify will grade against — design with it in mind.
Step 3 — Write the preset
A preset file lives at lib/presets/<slugCamelCase>.js and exports:
module.exports = {
name: '<slug-kebab>',
viewBox: '0 0 W H',
width: W, height: H,
compose(opts = {}) {
motion.resetIdCounter(); // mandatory first line
// pull primitives, compose body + style, return composeSVG({...})
}
};
Add the new slug to lib/presets/index.js.
Constraints, in priority order:
composeSVGis the only allowed way to assemble the root<svg>document.- Call
motion.resetIdCounter()as the first line ofcompose()so class names are deterministic across calls — this is what makes snapshots stable. - Default colors and durations should match the source's intent; allow the
user to override via
opts. - Re-use existing primitives wherever possible. Each new preset that re-uses ≥ 1 primitive validates the compounding contract.
Step 4 — Render
node lib/render-cli.js <slug>
This writes to output/<slug>.svg. Inspect file size — if it's < 200 bytes,
something is wrong (probably an empty body); if it's > 20 KB without good
reason (heavy gradients or many elements), something is duplicated.
Step 5 — Snapshot
npm run test:snapshot
First time for a new preset → snapshot is created. Subsequent runs → diff is shown. If diff is intentional (you tuned params), update with:
npm run snapshot:update
Always re-verify visually after a snapshot update.
Step 6 — Visually verify
Invoke /svg-verify <slug> — it runs in an isolated subagent context, opens
the SVG in chrome-devtools MCP, scores against the 5-point rubric, and
returns ≤ 100 words. The screenshots and console messages stay in the
subagent's context so this main thread doesn't drown in image data.
Step 7 — Iterate or finalize
If the verdict is tweak: adjust params (duration, easing, magnitude,
stagger), re-render, re-verify. Cap at 3 rounds before pausing to ask the
user. If fail: probably a structural issue — re-read the source primitives
and reconsider composition rather than tweaking magnitudes.
If pass: report to the user with:
- Path:
output/<slug>.svg - Primitives used:
motion.foo,shapes.bar, ... - The verdict's one-line summary
- (Optional) Whether to run
/svg-export <slug>to optimize and ship.
Anti-patterns
- ❌ Writing
<svg ...>markup directly in a skill or preset. Always go throughcomposer.composeSVG. - ❌ Adding a function to
lib/primitives/*.jswithout going through/svg-add-primitive. The protocol exists to keep JSDoc, snapshot coverage, and lib-api.md in sync. - ❌ Calling
npm run snapshot:updatewithout a visual recheck. Snapshots are the only thing protecting you from silent regressions. - ❌ Burning the user's time on > 3 silent iterations. After three rounds without a pass, pause and ask.
- ❌ Hand-writing keyframe values when a
motion.*primitive already covers the motion. Composability is the whole point.
When the user wants multiple variants
If the user asks for "a few options", "show me 3 styles", "give me variants",
or "compare different approaches", delegate to the svg-explorer subagent
instead of running variants serially in this thread. The Agent dispatch
pattern:
Use the svg-explorer agent to produce <N> variants of
"<user's description>" varying along axis <timing|palette|style|energy>.
It returns a comparison block with file paths + a recommendation. Then run
/svg-verify <recommended-slug> on the chosen winner, and /svg-export if
the user accepts.
Why delegate? Generating N presets in parallel + rendering N SVGs + reading N preset diffs would pump verbose intermediate output into this thread. The fork keeps the comparison clean: you only see the final summary.
When something goes wrong
npm testfails on a preset you didn't touch → you broke a primitive signature. Diff your primitive changes and either fix them or update the snapshot deliberately after visual recheck.compose()throws → almost certainly a missing or renamed primitive. Opendocs/lib-api.mdand grep for the function you tried to call.- Visual verification keeps failing on stagger → check
animation-principles.mdsection "Staggering"; the magnitude is probably outside 0.05–0.15 s.