Poppet is a cast of fourteen mascots — a sprout, a fox, a whale, a snail, a moon — each one a single inline SVG brought to life with nothing but CSS keyframes. No Lottie, no After Effects, no framework. This case study is the field guide: the handful of habits that make an SVG character feel alive, built and recoloured live with Claude Code, and playable right here on the page.
These are the real SVGs running live, not recordings. Every character idles on its own — bobbing, blinking, swaying — and springs into a cheer the moment you tap it, complete with a little burst of confetti.
Every wiggle, blink and bounce is a CSS @keyframes rule acting on a plain SVG shape. That constraint is the lesson: when the whole toolkit is "shapes plus keyframes," the technique is transferable to any character you'll ever draw — and the file stays tiny, portable and future-proof.
Each character was chosen to show off one animation habit. Together they cover the whole vocabulary — squash and stretch, secondary motion, staggered timing, anticipation, easing. Every card opens the scene it lives in.
Bobs on a soft scaleY so it feels weighted; leaps on a bouncy overshoot easing when tapped.
The body bounces, but the tail wags on its own faster loop — motion that follows the main motion.
Wings beat on a 0.18s loop while the body drifts slowly — two clocks running at once sells "hovering."
Before the jump it dips and squashes down — the tiny wind-up that makes the leap read as effort.
Sways from the base of its stem, not its centre — a lesson in getting transform-origin right.
Glides on a gentle ease-in-out; the shell spiral breathes on a slower beat. Slow is a choice, too.
Rays spin on a slow linear loop while the face pulses — steady rotation layered over a soft breath.
A halo behind the crescent scales and fades in sync with the drift — light as an animated layer.
Rocks and swells on a single loop; three mini-stars twinkle on offset delays around it.
Ears twitch and eyes blink on different, rarely-firing loops so the face never feels robotic.
Hops with a compress-then-stretch; the tail sways on its own hinge the whole time.
Rocks with a paired translate-and-rotate; the spout shimmers and the fin flaps off-beat.
Tail flicks fast against a slow swim; a tap sends it into a full barrel-roll cheer.
Five raindrops fall forever on staggered delays — a self-contained loop that never needs a trigger.
None of this is hard — it's a small set of disciplines, applied consistently. Give these to Claude Code as house rules and every new character comes out of the oven already breathing.
Define the palette as :root variables first, then draw against --mint, --pink, --ink. Restyling the whole cast becomes a token swap, not a redraw — which is exactly how a second, boldly-outlined set got folded into this one.
Keep every shape in the SVG and every motion in a CSS @keyframes. They never fight, and you can retune the timing of a wave or a blink without touching a single path point.
A wagging tail or flapping ear has to pivot where it joins the body. Set transform-origin in the SVG's own coordinate space — get it wrong and the part swings from the corner of the canvas.
A slow infinite loop (bob, blink, sway) gives life at rest; a one-shot .cheer class gives a reward on tap. Re-trigger the one-shot with the void el.offsetWidth reflow so it can fire again and again.
Offset the animation-delay on the second eye, ear or wing by a fraction of a second. Perfect unison reads as mechanical; a few milliseconds of drift reads as alive.
Keep eyes open for 92–95% of the loop, then shut them in one fast keyframe. A blink you could set a metronome to is worse than no blink at all.
Three cheap tricks sell warmth: a pink cheek that pulses, a white highlight parked in each eye, and a ground-shadow that shrinks as the character rises. Together they cost a dozen lines and do half the work.
Inline SVG in a single HTML file — no build, drops straight into an <iframe>. Then wrap the loops so one prefers-reduced-motion query can still them. Motion is a garnish, not the meal.
Poppet started as two unrelated sets: a soft pastel squad, and a bold, black-outlined "block crew" in loud saturated colours. Rather than redraw the second group, we treated colour as data — pulling the pastel set's swatches into :root tokens, then asking Claude Code to remap the block crew's fills onto them and drop the heavy outlines for the pastel style's soft belly-shading and thin #4A4458 ink features. Same shapes, same animations — a completely different, unified cast. That's the whole argument for tokens: a coat of paint should never mean a rebuild.