Free · Fast · Privacy-first

CSS Keyframes Generator

The @keyframes rule is the core of every CSS animation, but building one with multiple stops, varied properties, and correct percentages by hand requires constant trial-and-error and a lot of editor-to-browser context switching.

Add keyframe stops at any percentage from 0 to 100

🔒

Set multiple CSS properties per stop

Live preview updates as stops are added or modified

Outputs a named @keyframes block ready for any animation property

Cost
Free forever
Sign-up
Not required
Processing
In your browser
Privacy
Files stay local
FreeNo signupWhite-label

Add this Animation Builder to your website

Drop the Animation Builder into any page — blog post, product docs, intranet, school portal — with a single line of HTML. Your visitors get the full tool, processed entirely in their browser. No backend, no uploads, no signup.

  • Files stay 100% in the visitor's browser
  • Responsive — adapts to any container width
  • Free forever, no API key needed

Embed code

<iframe
  src="https://www.fixtools.io/css-tool/animation-builder?embed=1"
  width="100%"
  height="780"
  frameborder="0"
  style="border:0;border-radius:16px;max-width:900px;"
  title="Animation Builder by FixTools"
  loading="lazy"
  allow="clipboard-write"
></iframe>

Attribution-friendly: a small "Powered by FixTools" link appears in the embed footer.

The Anatomy of a @keyframes Rule and How Multiple Stops Enable Complex Motion

A @keyframes rule consists of the keyword @keyframes followed by an animation name, then a block containing one or more keyframe selectors. Each selector is either the from keyword (equivalent to 0%), the to keyword (equivalent to 100%), or an explicit percentage between those endpoints. The properties declared inside each selector define what the element looks like at that point in the animation timeline, and the browser handles the rest by interpolating the property values smoothly between consecutive stops using the active easing function declared in the animation shorthand. A two-stop animation using from and to is the simplest case: the element transitions from one state to another over the full duration with a single easing curve shaping the entire motion. Most real-world animations need more stops because the kinds of motion that feel natural to human perception (a bounce that overshoots and settles, a shake that alternates direction, a progress fill that pauses partway through) cannot be expressed as a single straight interpolation between two endpoints. The keyframes generator lets you add as many stops as the animation needs while keeping the syntax correct and the preview accurate to what the browser will render.

Multiple keyframe stops let you describe complex motion paths that a two-stop animation cannot express in any number of cubic-bezier tweaks. A bounce animation might travel down to its target position at 60%, overshoot to -10px at 75%, recover to -5px at 85%, and settle at 0 at 100% so the eye reads the motion as something with weight and momentum. A shake animation alternates between translateX(-8px) and translateX(8px) across six stops spaced 10% apart to suggest a vibrating object or an emphatic form validation error. A progress indicator might hold at 30% opacity for the stops between 40% and 60% to create a visible pause mid-animation that emphasizes a specific moment in the sequence. Each additional stop adds a control point that shapes the overall motion, giving you fine-grained control over how the animation unfolds without ever leaving the declarative CSS layer or pulling in a JavaScript animation library. The generator exposes a stop list you can reorder, duplicate, or delete, which makes it easy to experiment with different rhythms (three stops, then five, then seven) and pick the one that reads best at the duration your design requires.

A common pitfall with @keyframes is how the browser handles properties that are not declared at every stop. If a property appears at 0% and 100% but not at 50%, the browser interpolates smoothly through 50% as expected. But if a property appears only at 50% and not at 0% or 100%, the browser interpolates from the element's computed style to the 50% value, then back to the computed style. This means the animation depends on the element's base styles, which can produce unexpected results if the base styles change later or differ across themes and breakpoints. Declaring the property explicitly at 0% and 100% makes the animation self-contained and predictable regardless of the surrounding CSS, and the generator follows this convention automatically when you add properties to intermediate stops without first defining them at the endpoints.

A well-built keyframes generator becomes a teaching tool as well as a productivity tool because every adjustment you make to the visual controls maps one-to-one onto a line of generated CSS you can read in the output panel. Beginners learning the animation specification can add a stop, toggle a property, and immediately see exactly which percentage selector and which property declaration the change produces, building an intuitive model of how the cascade between declarative timing and rendered motion actually works. Experienced engineers use the same tool to skip the typing and jump directly to the experimental loop, trying three different five-stop bounces in the time it would take to write one of them in a code editor. Either audience benefits from never having to debug a malformed @keyframes block, since the generator guarantees the output is syntactically valid CSS that the browser will parse correctly the moment you paste it into your stylesheet.

How to use this tool

💡

Click Add Stop to insert a keyframe at any percentage. Set the CSS properties for that stop, add more stops as needed, then preview the sequence. Copy the @keyframes block when the motion is correct.

How It Works

Step-by-step guide to css keyframes generator:

  1. 1

    Name the animation

    Enter an animation name in the name field at the top of the controls panel. Choose a descriptive name like bounce-in, shake-error, or pulse-badge that reflects what the animation does rather than what properties it animates. The name you pick is what your CSS selectors will reference in their animation-name property, so a clear functional name pays off every time you read or refactor the stylesheet later.

  2. 2

    Add keyframe stops

    Click Add Stop and enter the percentage for each point in the timeline. Add the 0% stop first to define the starting state, then add intermediate stops at the percentages where the motion needs to change direction, hold a value, or hit a milestone, then finish with the 100% stop. The generator lets you reorder stops by dragging and delete any stop with a single click, so you can experiment with different rhythms before locking in the final sequence.

  3. 3

    Set properties at each stop

    For each stop, enter the CSS property and value pairs that describe the element at that point in the animation. Use transform for position, rotation, and scale changes, opacity for fade states, and color or background-color for color transitions. Declare the same set of properties at every stop (even when the value does not change) to make the animation self-contained and predictable regardless of the element's surrounding base styles.

  4. 4

    Copy the generated code

    Click Copy Code to copy the complete @keyframes block plus the matching animation shorthand declaration to your clipboard, then paste both into your CSS file. Place the @keyframes rule at the top level of your stylesheet (or inside a media query if it should only run at certain breakpoints) and attach the animation shorthand to the selector for the element you want to animate. The generated output is unprefixed standard CSS that works in every modern browser without any post-processing.

Real-world examples

Common situations where this approach makes a real difference:

Multi-step notification shake

A developer builds a form validation error effect using six keyframe stops that alternate translateX between -8px and 8px at 10% intervals from 0% to 60%, then return to 0 at 100%. The shake runs once (iteration-count: 1) when an error class is added by the validation handler, then the class is removed after the animation finishes so the effect can be retriggered on the next failed submit. The generator handles the six-stop keyframe block in under a minute, which would take several minutes to write, debug, and visually verify manually, especially when fine-tuning the amplitude and timing to feel emphatic without crossing into annoying territory.

Progress bar with hold states

A UI engineer creates a fake progress bar that advances to 30% quickly, holds for two-thirds of the duration (using duplicate width values at the 30% and 70% stops), then advances to 100%. The generator places stops at 0%, 30%, 70%, and 100% with matching width values at the hold stops so the browser interpolates correctly between segments. The animation runs once with a 3-second duration, giving a convincing progress simulation for a backend process that the team has not yet wired up to a real streaming progress event. The pattern is easy to maintain because the hold percentages can be adjusted independently of the start and end values whenever the perceived pacing needs tweaking.

Logo reveal animation

A brand agency developer creates a 5-stop keyframe animation for a logo reveal: starts at opacity 0 and scale(0.8), eases to opacity 1 at 50%, scales to 1.05 at 70%, drops to 0.97 at 85%, and settles at scale(1) and opacity 1 at 100%. The slight overshoot and settle gives the logo a premium spring quality that pure ease-out interpolation cannot match, and pairing it with a 900ms duration on the hero section creates a memorable load-in moment that becomes part of the brand experience. The generator produces the exact transform values for each stop and writes the matching animation shorthand, leaving no ambiguity about timing during the developer handoff.

Staggered list item entrance

A React developer generates a slide-up-fade keyframe block with three stops: 0% at translateY(20px) opacity 0, 60% at translateY(-4px) opacity 1, and 100% at translateY(0) opacity 1. The 60% stop with a small upward overshoot adds a hint of bounce that makes the entrance feel responsive rather than mechanical. The same @keyframes block is applied to each list item with animation-delay values of 0s, 0.1s, 0.2s, and 0.3s to create the staggered cascade effect common in dashboard interfaces and email clients. Because the keyframe definition is shared, changing the motion later (say, increasing the upward overshoot from -4px to -6px) updates every list item in the application at once.

When to use this guide

Use this when you need a multi-step CSS animation with more than two states, such as a bounce, a shake, or a progress sequence with pauses.

Pro tips

Get better results with these expert suggestions:

1

Declare the 0% stop explicitly for predictable behavior

Omitting the 0% stop means the animation starts from the element's computed style, which is fine until a parent component applies a conflicting style, a theme variant overrides the base values, or a developer refactoring the stylesheet six months from now changes the element's default transform without realizing an animation depends on it. Always declare the 0% stop with the starting values you intend, making the animation self-contained and resistant to styling changes elsewhere in the stylesheet. The handful of extra bytes pays for itself the first time a refactor would have silently broken the motion.

2

Use transform for all positional keyframes

Animating left, top, margin, padding, or width through keyframes triggers layout recalculation on every frame, which forces the browser off the compositor thread and can cause visible jank, particularly on lower-end mobile devices or when several elements animate simultaneously. Replace all positional changes with translateX() and translateY() in your keyframes, and replace size changes with scale() when the visual effect allows it. The visual result is nearly identical for entrance and exit motion, but the animation runs at the device's native refresh rate on the compositor thread, leaving the main JavaScript thread free for everything else the page needs to do.

3

Name keyframes by function, not by property

Name your @keyframes blocks after what the animation does (bounce-in, shake-error, pulse-badge, slide-up-fade) rather than what properties it animates (transform-scale, opacity-fade, translate-x-shake). Functional names remain meaningful when you refactor the keyframe values, are easier to search across a stylesheet of any size, and communicate intent to other developers (or your future self) who read the code months later without context. A name like bounce-in still describes the animation accurately if you later decide to swap the transform for a clip-path effect; a name like transform-scale becomes a lie the moment the implementation changes.

4

Test the animation at animation-duration: 3s while building

Set a very long duration (3 to 5 seconds) while designing the keyframe stops. This makes individual stops easy to distinguish visually and reveals whether intermediate states look correct in their own right or only work because they pass by quickly at the intended speed. Once the stops are right, reduce the duration to the intended value (typically 200 to 600 milliseconds for entrance and exit animations) and verify the motion still reads correctly. Building at full speed hides issues at intermediate stops that only become obvious when the animation runs slowly, and slow-building is one of the few habits that consistently separates production-quality motion from animation that feels off without anyone being able to say why.

FAQ

Frequently asked questions

The from keyword is identical to 0% and to is identical to 100%. Using from and to is a shorthand for the simplest case where you only need the start and end states, and it reads slightly more naturally in code reviews than the equivalent percentage selectors. Once you need any intermediate stop, you must switch to percentage syntax, as you cannot mix from/to with percentage stops in the same @keyframes rule without one of the selectors being ignored by some browsers. Most generators (this one included) use percentages consistently regardless of complexity to avoid this limitation and to make every @keyframes block in your project look the same, which is easier to scan visually when you have a stylesheet full of animations. If you prefer the from/to syntax for two-stop animations specifically, you can manually edit the generated output: change 0% to from and 100% to to with no other modifications required.
Yes. Each keyframe stop can contain any number of CSS property declarations, and animating multiple properties in one @keyframes block is usually the right approach for any non-trivial motion. You can animate transform and opacity simultaneously in the same @keyframes block, which is the canonical pattern for entrance and exit effects because both properties are handled by the browser's compositor thread and update at full frame rate without triggering layout. You can also include properties like color, background-color, border-radius, filter, box-shadow, and clip-path inside the same keyframe selectors, though these trigger paint or layout rather than composition and are noticeably less performant on lower-end devices, especially when several animated elements are visible at the same time. For high-performance UI, restrict animated properties to transform and opacity whenever possible and use the other properties sparingly for static styling.
The browser handles each property independently inside a @keyframes rule. If opacity appears at 0% and 100% but transform only appears at 50% and 100%, the browser interpolates opacity across the full duration as expected and interpolates transform from the element's computed style to the 50% value, then to the 100% value. The result depends on what computed transform the element has when the animation begins, which means a stylesheet refactor that changes the element's base transform can silently change how the animation looks. To make the animation predictable, declare every property you want to animate at both 0% and 100% even if the value is the same as the element's base style. The generator nudges you toward this pattern by pre-filling new stops with the properties you have already used elsewhere in the same @keyframes block, so you do not have to remember to copy values manually.
To create a pause at a specific point, add two consecutive keyframe stops with identical property values. For example, to pause at 40% opacity for 20% of the animation duration, add a stop at 40% with opacity: 0.4 and another stop at 60% with the same opacity: 0.4. The browser holds the value constant between those two stops because there is nothing to interpolate, then resumes interpolation toward the next non-matching stop. This duplicate-stop technique works for any property and is far more predictable than trying to achieve a pause with easing functions like steps() or aggressive cubic-bezier curves, both of which can produce visible jumps rather than the smooth hold most designs call for. The generator makes the pattern obvious because you can see in the stop list which adjacent stops share values, and you can adjust the start and end percentages of the pause without touching the values themselves.
Yes. The @keyframes rule is defined once with a name, and that name is referenced in the animation-name property (or the animation shorthand) on any element you choose to animate. Each element can use a different duration, delay, easing function, iteration count, direction, and fill mode while sharing the exact same @keyframes block. This decoupling of the motion definition from the per-element timing is one of the most powerful patterns in CSS animation, and it makes a well-named @keyframes rule a reusable animation token across an entire stylesheet. A design system can ship a small set of canonical @keyframes blocks (fade-in, slide-up, scale-in, pulse, shake) and let individual components mix and match them with their own timing values, keeping the visual language consistent without sacrificing per-component flexibility.
By default, the easing function declared in the animation shorthand applies between every consecutive pair of stops, so the same curve shapes each segment of the timeline. You can override easing for a specific segment by declaring an animation-timing-function inside a keyframe stop. For example, adding animation-timing-function: ease-in at the 0% stop makes only the first segment (from 0% to the next stop) ease in, while subsequent segments use the animation-level easing. The easing declared at a stop applies to the segment that begins at that stop, not the segment that ends at it, which is the most common source of confusion when developers first reach for per-stop easing. The generator supports per-stop easing for fine-grained control over multi-step sequences like bounces and elastic motions, where different segments need different curves to feel right (a fast ease-out into the overshoot, a slower ease-in-out into the settle).
The animation-iteration-count property controls how many times the @keyframes sequence plays from start to finish. A value of 1 plays it once and is the default. A value of 3 plays it three times in a row with no gap between iterations. The value infinite loops it continuously until the animation is paused via animation-play-state, the animation property is removed, or the element itself is removed from the DOM. Use infinite for loaders, pulsing indicators, animated backgrounds, and any other ambient effect that needs to keep moving as long as it is visible. Use a finite count (almost always 1) for entrance effects, exit effects, one-time alerts, and confirmation animations, because a looping entrance animation is distracting, drains battery on mobile devices, and is one of the most common accessibility complaints from users who experience vestibular disorders. Fractional values like 1.5 are valid and stop the animation partway through a cycle, which is occasionally useful for an entrance that ends on a non-final keyframe.
Yes. The animation-direction property controls whether the @keyframes are played forward (normal), backward (reverse), alternating forward then backward (alternate), or alternating backward then forward (alternate-reverse). With alternate, the animation plays from 0% to 100% on odd iterations and from 100% to 0% on even iterations, which means the easing function also reverses on the backward pass. This can replace the need for duplicate keyframe definitions when you want the animation to return smoothly to its start state after completion, and it pairs particularly well with iteration-count: infinite to produce smooth, symmetric loops for pulsing buttons, breathing icons, and other ambient effects. The alternate-reverse variant is useful when you want the first visible motion to go backward (for example, a card that slides out before sliding back in), which is harder to express by rewriting the keyframe percentages.

Related guides

More use-case guides for the same tool:

Ready to get started?

Open the full Animation Builder — free, no account needed, works on any device.

Open Animation Builder →

Free · No account needed · Works on any device