Free · Fast · Privacy-first

CSS Pulse Animation

A pulse animation cycles an element between two visual states to draw the user's eye to something that needs attention.

Scale pulse for a breathing or heartbeat feel

🔒

Opacity pulse for notification badge flashing

Control rhythm speed from slow ambient to fast urgent

Generates infinite-loop CSS with alternate direction option

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.

What a Pulse Animation Achieves and the Two CSS Approaches for Building One

A pulse animation achieves one goal: drawing the user's eye to a specific element without requiring them to interact with it. Unlike an entrance animation that plays once, a pulse animation repeats continuously, creating an ambient signal that persists until the user takes action or the condition changes. The visual mechanism is cycling between two states: the element appears to breathe or beat because the transition between states is smooth in both directions. The design choice is which property to cycle. Scale cycling from 1.0 to 1.05 creates a gentle breathing effect visible on medium and large elements. Opacity cycling from 1.0 to 0.6 creates a flashing effect suitable for small badges and counts where scale changes would be too subtle.

Two CSS implementations produce pulse animations. The first uses animation-direction: alternate with two keyframe stops: 0% at the base state and 100% at the pulsed state. The alternate direction plays the keyframes forward on odd iterations and backward on even iterations, creating a smooth cycle without requiring a return keyframe. This produces a gentle, even pulse. The second approach uses four explicit keyframe stops: 0% at base, 50% at the pulsed state, and 100% back at base, without using alternate. This gives more control over the hold time at each state, which is useful when you want the element to briefly hold its pulsed state before returning. The four-stop approach can also include a hold at 50% by adding a stop at 60% with identical values.

Choosing between scale and opacity pulses depends on the element and the urgency of the signal. Scale pulses work for notification dots, online status indicators, and CTA buttons in empty states, where a subtle breathing motion signals that the element is active without alarming the user. Opacity pulses work for unread count badges, live data feed indicators, and error banners that need to remain visible to users even as the page content updates. For genuinely urgent signals, a combined scale and opacity pulse at a short duration (under 0.8s) creates a more insistent effect, but this should be reserved for actual errors or time-sensitive states, not ambient activity indicators.

Pulse animation density across an interface determines whether the technique feels purposeful or chaotic. A single pulsing element on a page reliably attracts attention because it is the only thing in motion. Two or three pulsing elements at the same time split the user's focus and reduce the effectiveness of each. Four or more simultaneous pulses produce a noisy interface where nothing reads as urgent and the user begins to ignore the motion entirely. Treat pulse animations as a finite attention budget: each one costs a unit of focus that other interface elements cannot reclaim. When introducing a new pulsing indicator, audit the page to confirm no other elements are already pulsing in the same view, and consider replacing or pausing existing pulses if the new signal is higher priority.

How to use this tool

💡

Choose between scale pulse and opacity pulse. Set the minimum and maximum values for each cycle. Adjust duration to match the urgency: 1.5s for calm, 0.6s for urgent. Enable alternate direction for smooth reversal. Copy the code when the rhythm is right.

How It Works

Step-by-step guide to css pulse animation:

  1. 1

    Choose scale or opacity pulse

    Select scale pulse for a breathing effect on icons and buttons, or opacity pulse for a flashing effect on badges and status indicators.

  2. 2

    Set the pulse range

    For scale: set the base scale (1.0) and the peak scale (1.04 to 1.08 for subtle, 1.15 to 1.2 for obvious). For opacity: set base (1.0) and minimum (0.5 to 0.7 for subtle, 0.2 to 0.4 for sharp).

  3. 3

    Set duration and direction

    Set duration to 0.6s for urgent, 1s for standard, 1.5s to 2s for calm ambient. Enable alternate direction for smooth cycling without a jump at the end of each iteration.

  4. 4

    Copy and add a reduced-motion override

    Click Copy Code. After pasting, add a @media (prefers-reduced-motion: reduce) block that sets animation: none for the pulse class.

Real-world examples

Common situations where this approach makes a real difference:

Live data feed indicator

A financial dashboard shows a green dot next to each ticker that is receiving live price updates. The dot uses a slow 1.5-second opacity pulse from 1.0 to 0.5 to signal that data is streaming. The pulse pauses when the WebSocket disconnects and a reconnecting class replaces it with a faster 0.7-second pulse to signal the different state.

Unread notification badge

A messaging app sidebar icon shows an unread count badge that uses a 1-second scale pulse from 1.0 to 1.15 when new messages arrive. The pulse starts when the count increments via a class added in the message handler, and stops automatically after three iterations using animation-iteration-count: 3. This draws attention at the moment of arrival without running the animation indefinitely.

CTA button in an empty state

A project management app's empty project list shows a Create Project button with a slow 2-second breathing scale pulse from scale(1) to scale(1.04). The subtle pulse draws the eye to the primary action without feeling urgent. The animation runs only when no projects exist; once a project is created, the pulse class is removed permanently for that user session.

Error state requiring immediate attention

A form shows an error banner when a payment fails. The banner uses a fast 0.6-second opacity pulse between 1.0 and 0.7 combined with a red background. The combined urgency of the colour and fast pulse signals that user action is required. The pulse runs for five iterations (animation-iteration-count: 5) then stops, leaving the static red banner visible without the ongoing distraction of continued flashing.

When to use this guide

Use this for notification badges, live data indicators, error states that need ongoing attention, and any element that should signal activity or urgency without interrupting the user's current task.

Pro tips

Get better results with these expert suggestions:

1

Use animation-iteration-count to limit pulses to a specific count

For event-triggered pulses, set animation-iteration-count: 3 to 5 rather than infinite. The element pulses a defined number of times to signal the new event, then stops cleanly. This is less distracting than infinite looping for events that should attract initial attention but not demand ongoing focus.

2

Pair pulse with a box-shadow glow only on small elements

Adding an animated box-shadow alongside a scale pulse creates a glow effect that reinforces the pulse visually. Keep this to elements under 40px in diameter. On larger elements, an animated box-shadow triggers paint on every frame and causes performance issues. For large elements, stick to transform and opacity only.

3

Prefer opacity pulse over scale pulse for elements inside flex or grid containers

A scale pulse on an element inside a flex row or grid cell can cause visual collision with adjacent elements when the element grows. Opacity pulses have no layout effect and are always safe inside any container type. Use opacity for badges, counts, and indicators; reserve scale for standalone elements with clear surrounding space.

4

Stop the pulse immediately when the underlying condition clears

A notification badge should stop pulsing the moment the user views the notification, not on the next page reload. Listen for the state change in your component and remove the pulsing class at the same time as clearing the unread count. Leaving a pulse running after the condition has cleared confuses users about whether new activity has occurred.

FAQ

Frequently asked questions

A scale pulse animates transform: scale() between two values, making the element appear to grow and shrink rhythmically. This is visible on elements of any size but requires that the element has room to expand without overlapping adjacent content. An opacity pulse animates the opacity property between two values, making the element appear to flash or fade in and out. Opacity pulses work for any element size and do not affect surrounding layout. Use scale for breathing effects on standalone elements and opacity for compact badges or inline indicators.
Use animation-direction: alternate, which plays the keyframes forward then backward on successive iterations. This means the element smoothly returns to its starting state at the end of each forward cycle without needing a return keyframe. The alternative, declaring the return to base explicitly at 100% with a two-stop @keyframes, works equally well but requires the 0% and 100% values to match exactly. Any discrepancy between the two will produce a visible jump at the loop point.
Duration maps directly to perceived urgency. A 0.5 to 0.7 second cycle is fast and alarming, appropriate for error states or time-sensitive actions. A 1 to 1.2 second cycle is the standard notification badge speed that feels active without anxious. A 1.5 to 2 second cycle feels calm and ambient, like a breathing indicator for an idle but connected state. A 3-second or slower cycle is barely noticeable as animation and works for very subtle live indicators on background elements.
Yes, if the animation only uses transform and opacity. Both properties run entirely on the compositor thread, meaning they do not trigger layout or paint recalculations on any frame. An infinite opacity or scale pulse on a single element has no measurable performance cost on modern hardware. The risk emerges when pulsing many elements simultaneously (more than 30 to 50) or when pulsing properties like box-shadow, filter, or background-color, which trigger paint on each frame. Keep infinite pulses limited to transform and opacity. On battery-powered devices the same animation is essentially free while the tab is visible, and it pauses automatically when the tab is in the background on most modern browsers.
Define the @keyframes and animation property in a CSS class that is not applied by default. Add the class to the element via JavaScript when the event occurs: document.getElementById("badge").classList.add("pulsing"). Remove the class when the condition clears. If the element should pulse only once when a new event arrives, add the class, then remove and re-add it after a short delay to re-trigger the animation: element.classList.remove("pulsing"); requestAnimationFrame(() => element.classList.add("pulsing")).
Use a CSS animation for a pulse because transitions require a state change trigger and cannot loop automatically. A transition interpolates between two states when a CSS property value changes; once the transition completes, no further animation plays. A CSS animation with animation-iteration-count: infinite loops automatically without any JavaScript event needed. For on-demand pulsing triggered by a user action, a JavaScript-toggled animation class is more appropriate than a transition, since the animation can loop independently of further user interaction.
Add a click event listener that removes the pulsing CSS class from the element. The animation stops immediately when the class is removed. If you want the element to finish its current cycle before stopping, listen for the animationiteration event instead: when the event fires, remove the class. The animationiteration event fires at the end of each complete iteration, so the element will complete the current pulse and stop cleanly at its base state rather than halting mid-pulse.
For a pulsing button, a scale range of 1.0 to 1.03 is barely noticeable and suitable for a subtle "live" signal; 1.0 to 1.06 is clearly visible and good for an active CTA in an empty state. For a notification badge (typically 16 to 24px), a range of 1.0 to 1.2 is appropriate because the small element size means the 20% increase is only a few pixels in absolute terms. At that small size, subtle ranges like 1.0 to 1.04 produce no visible motion at all. Always test the chosen range at the actual rendered size in the target context, since the same numeric scale value reads very differently on a 16px badge than on a 200px hero CTA, and what looks subtle in isolation may look excessive when surrounded by static content.
A glow effect typically uses box-shadow, which triggers paint on every frame when animated and can cause jank on long-running infinite pulses. The compositor-safe alternative is to layer a second element behind the pulsed element, give it a static box-shadow or radial-gradient background that produces the glow appearance, then animate only the opacity of that backing element in sync with the foreground pulse. This keeps the animation on the compositor thread while still producing the visual glow. For small elements like 16 to 24px badges, animating box-shadow directly is acceptable since the painted area is tiny, but for larger elements always prefer the layered opacity approach.
Yes, using JavaScript to add and remove the pulse class in response to the event. In the WebSocket onmessage handler, add the pulsing class to the element representing the affected item. To re-trigger the pulse for repeated events, remove the class, force a reflow with void element.offsetWidth, then re-add the class. This restart pattern is necessary because adding a class that is already present does not restart the CSS animation. For events arriving faster than the pulse duration, debounce the trigger or accept that rapid events may not show distinct pulses and instead increment a count badge that pulses on each change.

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