CSS Animation Generator
Pick a preset animation - fade, slide, bounce, spin, pulse, shake - tune duration, easing, delay, iteration count, direction, and fill mode, watch a live preview, and copy the full @keyframes CSS.
- Free, no account
- No watermark
- No usage limit
About the CSS Animation Generator
Most CSS animation generators will happily hand you code that looks flawless on your laptop and then stutters on a two-year-old phone. This one can't, and that constraint is the whole point of it. Every preset here moves exactly two properties, transform and opacity, and nothing else. Those are the only two a browser can animate on the GPU without re-solving your page layout on every frame, so whatever you copy out holds a smooth 60fps on the cheap Android your users actually carry. Try to generate a janky animation here and you'll fail, the safety is built into the presets.
The other thing worth knowing: the box animating at the top isn't a mockup. It's running the exact CSS sitting in the output below it, character for character. Drag the duration, swap the easing, flip the direction, and it reacts on the spot. No gap between what you preview and what lands in your stylesheet.
The names it writes are prefixed too. You get tb-spin, not plain spin, and that is deliberate. Keyframe names sit in one global pot for the whole document and the last definition wins, so a bare @keyframes spin dropped into a Tailwind project quietly takes over the spin Tailwind already ships, and every loader on the site starts running your rotation instead. Animate.css owns flip, wobble, pulse and bounce the same way. There's a name field if you want your own word, and it tells you when the one you typed already belongs to somebody.
And motion is a feel thing, you genuinely can't judge it by reading code. Two seconds too slow for this? Does the spring curve beat plain ease-out on that button? Nudge a slider and you know in a second, no reload, no guessing. Everything runs locally in your browser, nothing you build gets uploaded anywhere.
How to use
- Pick an animation. Tap any preset (fade, slide, zoom, bounce, spin, pulse, shake, flip, wobble) and the preview starts playing it immediately, so you can judge the raw motion first.
- Set the duration. Drag the slider for how long one cycle takes. Snappy interface feedback wants something short, around 0.3s, while a looping loader is happy at one to two seconds.
- Choose the easing. The timing function, how the motion speeds up and slows down.
easeis a fine default,linearis the right call for anything that spins forever, and the "back / overshoot" and "spring" curves add a real bounce (more below). - Add a delay if you want it to wait before firing. Handy when you stagger several elements so they don't all move at once.
- Set the iterations. Type a number to play it that many times, or tick infinite to loop forever, what spinners and pulses want.
- Tune direction and fill mode.
alternateplays forward then backward for a smooth there-and-back. Fill modeforwardsholds the final frame so the element doesn't snap back when it finishes. - Copy it in. Hit Replay to watch from the top, then Copy CSS and paste the whole block into your stylesheet. Give your element
class="element"(or rename that selector) and it's live.
Why every preset sticks to transform and opacity
When a browser draws a frame it works in stages that get cheaper as it goes: layout (where every box sits), then paint (filling the pixels), then composite (stacking those into the finished picture). And transform and opacity get handled entirely in that last, cheapest stage. The browser hands them to the GPU and runs them on a separate thread, with no layout and no repaint. A translate, a scale, a rotate, a fade all stay smooth even while your main thread is busy with something else.
Now the trap most generators walk you into. Animate left, top, width, height, margin, or padding, and you force the browser to recompute the layout of the whole page, sixty times a second, for the length of the animation. Your development machine is fast enough to hide it. Drop the same animation onto the mid-range phone most of your users are actually holding and it starts dropping frames and juddering.
The fix is a one-for-one swap. To move something across the screen, use transform: translateX() instead of left. Scaling swaps width and height for transform: scale(), and a fade is just opacity. Every preset here already does this, so you can copy any of them and paste with no second thought about performance. That was the goal, take the one decision people get wrong and make it impossible to get wrong here.
Easing is what decides whether it feels alive
The timing function is the setting most people skip, and it's the one that decides whether your motion reads as real or just lifeless. Nothing physical moves at a constant speed. A dropped ball accelerates, a door slows as it reaches the frame. linear throws all of that away and runs at one flat speed, which is correct for a spinner (a steady rotation should look steady) and wrong for almost everything else. Of the keywords, ease-out starts fast and slows to a stop, which is the best default for anything arriving on screen. ease-in is the one for something on its way out.
This is where the tool pulls ahead of the free crowd. Most generators give you the four named keywords and stop there. The genuinely good curves, the ones with real overshoot, they just leave on the table. cubic-bezier() lets you draw the timing curve yourself, and the trick is that its values are allowed to dip below 0 or push past 1. That out-of-bounds range is what produces a springy feel. The "back / overshoot" preset pushes past its target and snaps back, so a zoom-in lands with a little momentum instead of stopping flat, and "spring" winds under the start before it overshoots and settles. Try a zoom-in on plain ease, then swap to "back / overshoot" and run it again, you'll feel the gap right away.
There's also steps(), which behaves nothing like the rest. Instead of a smooth curve it jumps between a fixed count of frozen states with no blending. steps(6) snaps through six, which is how you drive a sprite sheet or get a chunky, typewriter kind of motion.
Frequently asked questions
Why isn't my animation running when I paste the code in?
Almost always one of three things. The animation-name doesn't exactly match the @keyframes name, and it's case-sensitive, so Spin will never find spin. Or there's no duration set, and with a zero duration the whole thing finishes in no time and you see nothing. Or your element isn't carrying the class the rule is attached to. Paste the full block this tool gives you, both the @keyframes and the .element rule, put that class on the element, and it fires.
Can I just call it `spin` like everyone else does?
You can. Type whatever you want into the name field and the CSS follows you. Just know what you're picking. Inside one document @keyframes has no scoping of its own, so two blocks sharing a name aren't two animations, they're one, and the later one wins for every element on the page. Between them, Tailwind and Animate.css have already taken spin, pulse, bounce, flip and wobble. Paste a bare one of those into a project running either library and you've replaced theirs sitewide, with no error and nothing in the console, and the thing that visibly breaks is usually a loading spinner in a component you never opened. The field flags the names we know are spoken for. On a project with no CSS framework at all, take the short name.
My element flashes or snaps back the moment the animation ends. What's wrong?
Fill mode. By default it's none, so the element reverts to its normal styles the instant the animation stops, and a fade-in pops back to invisible, reading as a flash. Set forwards to hold the last frame after it ends. Set backwards to apply the first frame during any delay, so something that starts off-screen sits off-screen while it waits instead of flashing into view first, and both applies the first frame before and the last frame after.
What's the difference between a CSS animation and a transition?
A transition needs a trigger and only moves between two states, like a color shift on :hover. One move, no loops, no middle stops. An animation runs on its own the moment the page loads, and can pass through many keyframe stages, repeat, reverse, and alternate. Loose rule: transitions for simple hover and focus effects, animations for anything that auto-plays or loops.
What is a negative animation-delay actually for?
A positive delay makes the animation wait before it starts. A negative one starts it right away, but as if it had already been running for that long. With a two-second duration, animation-delay: -1s begins the element halfway into its first cycle. Why bother? To offset several copies of the same loop so they don't move in lockstep. Give a row of pulsing dots each a slightly different negative delay and they ripple instead of blinking in unison.
Should I do anything for people who get motion sickness?
Yes, and it's only a few lines. Some people feel dizzy or queasy from on-screen motion and set a system preference to cut it down. CSS gives you a hook, the prefers-reduced-motion media query. Wrap your animation rule in @media (prefers-reduced-motion: no-preference) so that anyone who has turned reduced motion on simply gets a static element. This matters most for the loops that never stop on their own, the infinite pulses and floating background effects.
Can I animate colors or box-shadow too, not only transform and opacity?
You can. background-color, color, border-color, box-shadow, and plenty more tween fine. The catch is cost. A color or shadow change forces a repaint every frame, heavier than the composite-only transform and opacity. A one-off color fade on a single element won't hurt anything. For something looping nonstop or across dozens of elements, keep leaning on transform and opacity, and test any color animation on a real budget phone first. And you don't need vendor prefixes like -webkit- anymore, every browser people actually use handles unprefixed animations, so the code here pastes as is.