Your files never leave your device. All processing happens locally in your browser.
How do I generate CSS keyframe animation code?
The CSS Animation Generator builds a real @keyframes rule visually — add keyframe stops, set the CSS declarations inside each (transform / opacity / filter / colour…), and dial the full animation shorthand (duration, delay, an editable cubic-bezier or steps() timing-function, iteration-count, direction, fill-mode, play-state) — with a live preview the browser animates for real, an honest 60fps performance check, and copyable CSS. It runs 100% in your browser; nothing is uploaded.
What is
CSS @keyframes animation
A CSS animation runs an element through a sequence of styles defined in an @keyframes rule and attached with the animation shorthand. Each keyframe is a percentage stop (0% / from … 100% / to) holding CSS declarations, and the browser interpolates between them. The shorthand order is animation: name duration timing-function delay iteration-count direction fill-mode. The timing-function (easing) can be a keyword, an author-defined cubic-bezier(), or a steps() function, and it may also be set per-keyframe.
Yes. Your keyframes, timing values and property lines are serialised into an @keyframes rule and animation shorthand entirely on your device — nothing is uploaded, and there is no server or CDN in the processing path.
Everything is 100% client-side. The tool holds the animation as typed state — keyframe stops, each with real CSS declarations, plus the shorthand values — and a pure engine serialises the exact @keyframes block and animation shorthand right in your browser tab. The live preview is animated by your own browser from the injected keyframes; no input is ever sent to a server, and there is no CDN in the processing path. It works offline once the page is cached, and closing the tab discards your work.
Only transform and opacity. They are handled by the compositor thread without layout or paint, so they stay smooth. Animating width, height, top, left or margin forces layout (reflow) every frame and janks.
For smooth 60fps motion, animate transform (translate / scale / rotate) and opacity — the browser composites these on their own layer without re-running layout or paint. Animating box-metric or position properties (width, height, top, left, margin, padding) forces a full layout/reflow every frame, the most expensive tier, and commonly janks; properties like filter, background-color, box-shadow and clip-path trigger a repaint each frame (moderate cost). This tool classifies every animated property live and shows the worst tier so you know whether it will be smooth before you ship it.
Pick the cubic-bezier() timing-function and drag the two control points on the live graph. The X of each point must stay within 0–1; the Y can overshoot to create bounce or anticipation.
cubic-bezier(x1, y1, x2, y2) defines the speed curve of the animation with two control points. Selecting cubic-bezier in this tool shows an editable graph: adjust x1/y1 and x2/y2 and the curve redraws live. The two X values are clamped to 0–1 (the spec requirement), while the Y values may go below 0 or above 1 to create anticipation or overshoot (bounce). For a stepped, non-interpolated look — sprite frames, a ticking clock — use the steps() timing-function instead, with any jump position.
Yes — wrap non-essential motion in @media (prefers-reduced-motion: no-preference) so motion-sensitive users are respected. will-change helps paint/layout work but is pointless for transform/opacity and should never be left on.
Some people experience motion sickness, so non-essential animation should sit inside @media (prefers-reduced-motion: no-preference) — this tool can wrap the rule for you with one toggle. As for will-change: it hints the browser to promote an element to its own compositing layer ahead of time, which can smooth paint- or layout-triggering animations, but it is unnecessary for already-composited transform/opacity and costs memory if left on permanently (add it just before an animation, remove it after). The generator only emits will-change when the animated properties would actually benefit.
Detailed Explanation
⚙️Methodology
How the CSS Animation Generator Builds a Rule
A CSS animation moves an element through styles declared in an @keyframes rule and attached with the animation shorthand. This generator’s pure engine (cssAnimationEngine.ts) holds the animation as typed state — an ordered list of keyframe STOPS, each a percentage offset (0–100) carrying real CSS declarations, plus the shorthand values — and serialises the exact @keyframes block and the shorthand the spec defines. Stops are sorted by offset; 0% is emitted as “from” and 100% as “to”; a per-keyframe animation-timing-function is supported. The shorthand is written in the canonical MDN order: animation: name duration timing-function delay iteration-count direction fill-mode. play-state is toggled separately so pausing never resets the other values.
Each keyframe stop is a percentage offset holding real CSS declarations
Engine sorts stops and emits 0% as “from”, 100% as “to”
Shorthand order: name duration timing-function delay iteration-count direction fill-mode
play-state is separate from the shorthand so pausing preserves state
Pure and deterministic — the same state always emits the same CSS
🔧Technical Details
Easing: Keywords, cubic-bezier() and steps()
The timing-function controls the speed curve. The tool exposes the keywords (linear, ease, ease-in, ease-out, ease-in-out), an editable cubic-bezier(x1, y1, x2, y2) with a live graph, and a steps(n, position) function for a hard, non-interpolated look. cubic-bezier takes two control points: the X of each is clamped to 0–1 (a spec requirement), while the Y may go below 0 or above 1 to create anticipation or overshoot (bounce). steps() jumps between n discrete states — useful for sprite-sheet frames, a ticking counter, or a typewriter effect — with a selectable jump position (jump-start, jump-end, jump-none, jump-both, start, end).
cubic-bezier(x1,y1,x2,y2): X clamped to 0–1, Y free (overshoot = bounce)
steps(n, position) jumps between n discrete states, no interpolation
Easing can also be set per-keyframe via animation-timing-function inside a stop
⚠️Limitations
Not Every Property Is Cheap to Animate
The rendering pipeline is a waterfall: layout → paint → composite. Only transform and opacity skip layout AND paint — the compositor thread handles them on their own layer, so they stay smooth at 60fps. Animating box-metric or position properties (width, height, top, left, margin, padding) forces a full LAYOUT/reflow every frame, the most expensive tier, which commonly janks; filter, background-color, box-shadow and clip-path trigger a repaint each frame (moderate cost). The engine classifies every animated property into composite / paint / layout and reports the worst tier live, steering users toward transform: translate() / scale() instead of animating geometry.
transform and opacity are composited — the smooth 60fps path
width/height/top/left/margin force layout (reflow) every frame — janky
filter/background-color/box-shadow/clip-path trigger paint each frame
The tool flags the worst tier and suggests transform alternatives
📋Use Cases
will-change and Reduced Motion
will-change hints the browser to promote an element to its own compositing layer before it animates, which can smooth paint- or layout-triggering work — but it is POINTLESS for already-composited transform/opacity and costs memory if left on permanently, so it should be added just before an animation and removed after. This tool only emits will-change when the animated properties would actually benefit. Accessibility matters too: some users have vestibular disorders, so non-essential motion should sit inside @media (prefers-reduced-motion: no-preference); the tool can wrap the rule with one toggle so motion-sensitive users never see it.
will-change helps paint/layout work but not transform/opacity
Leaving will-change on permanently wastes memory — add then remove
The generator only emits will-change when it would genuinely help
Wrap non-essential motion in @media (prefers-reduced-motion: no-preference)
🔒Privacy & Security
Private by Design
Everything happens on the user’s device. The keyframes, timing values and property lines are just numbers and strings serialised into CSS locally in the browser tab — there is no server call and no CDN in the processing path. Nothing is uploaded, logged or stored; the output is plain CSS text copied to the clipboard, and the live preview is animated by the user’s own browser from the injected @keyframes. The tool works offline once loaded, and closing the tab discards all state.
Keyframes and timing values are serialised into CSS entirely in the browser
No upload, no CDN in the processing path, no server, no tracking
Output is plain CSS copied locally; the preview runs in your own browser
Works offline after first load; nothing is stored server-side
CSS animation-cost tiers: composite vs paint vs layout
Tier
Example properties
Cost per frame
Composite
transform (translate/scale/rotate), opacity
Cheapest — compositor thread, no reflow/repaint
Paint
filter, background-color, box-shadow, clip-path, color
Moderate — repaints the element each frame
Layout
width, height, top, left, margin, padding
Most expensive — forces reflow, commonly janks
will-change
Promotes an element to its own layer ahead of time
Helps paint/layout; pointless for transform/opacity
Reduced motion
@media (prefers-reduced-motion: no-preference)
Guards non-essential motion for sensitive users
CSS animations have universal modern-browser support (Chrome, Firefox, Safari, Edge). “GPU acceleration” is not one switch — compositing may use the GPU while rasterisation stays on the CPU, and results vary by device, driver and memory pressure. As of July 2026.