SVG✨ Premium

Pulse / Heartbeat

An SVG heart beating in a double pulse, surrounded by two rings that widen and fade — pure CSS, with a setting to stop and restart it.

CSSPulse

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. SVG has 30 effects, including 3 free. Explore the category →

3 usage examples

"Support" button that starts beating — football club fundraiser — Pulse / Heartbeat example 1

① "Support" button that starts beating — football club fundraiser

WhenThe fundraising page of an amateur club buying a minibus: progress bar, number of supporters, latest donors. The white heart in the button is still; on click it starts beating, the button turns into "Thank you for your support!" and the counter gains one supporter.
WhyThe beat answers the gesture: users instantly see their support was registered, with no confirmation dialog.
Settings--hrtb-size:30px, --hrtb-color:#ffffff on the red button, transparent background; data-hrtb-idle present at first, removed by the click (real <button aria-pressed>).
"Staff pick" badge on a cover — online bookshop — Pulse / Heartbeat example 2

② "Staff pick" badge on a cover — online bookshop

WhenA novel's page in an independent bookshop: the white badge on the corner of the cover holds a small heart beating continuously, next to the bookseller's note, the price and the basket button.
WhyA discreet beat draws the eye to the human recommendation without blinking; it is the only thing moving on the page.
Settings--hrtb-size:22px, --hrtb-color:#e11d48, transparent background, default pace (1.2 s); the badge is a plain block of the scene.
Large beating heart — blood donation association appeal — Pulse / Heartbeat example 3

③ Large beating heart — blood donation association appeal

WhenThe hero of a local association: a 200 px heart beats on the left, its rings widening over the pink background; on the right, the date of the next blood drive and the "Book a slot" button.
WhyAt large size, the beat becomes the subject: it says "life" before the text is read. The text sits beside it, never on the rings.
Settings--hrtb-size:200px, --hrtb-color:#dc2626, transparent background, default pace.

How it works

The drawing fits in an 80 × 80 unit SVG: a red #ef4444 <path class="hrtb-heart"> and two .hrtb-ring circles of radius 20, unfilled, with a 2-unit stroke, sharing the same center.

The heart follows @keyframes hrtbBeat: it grows to 115% on the first beat (15% of the cycle), settles, then to 110% on the second beat (45%), a "lub-dub", and rests until the end of the 1.2 s cycle. Each ring follows hrtbRing: it starts at 80% of its size and 80% opacity and grows to 180% while fading out; the second ring starts a quarter cycle after the first. Both animations derive from a single variable, --hrtb-speed: changing the pace keeps heart and rings in sync.

Add the data-hrtb-idle attribute on .fx-0606-root to stop the beat (still heart, rings hidden); remove it to start again, for instance after a click. No JavaScript is shipped: animation and class names are prefixed hrtb, so they don't mix with another beating heart on the page.

Accessibility

  • Decorative: the SVG has aria-hidden="true" and focusable="false". The meaning (a support recorded, a staff pick) must be in visible text, like the button label in the first example.
  • Reduced motion: under prefers-reduced-motion: reduce, the heart stops beating and a single ring stays visible, still and faint (35% opacity, 130% size): the drawing keeps its halo with nothing pulsing.
  • Safe pace: one beat every 1.2 s (50 per minute), far from risky flashing rates; no sudden brightness change.
  • Interaction is up to you: the effect captures neither clicks nor keys. For a "Like", put it inside a real <button aria-pressed> and toggle data-hrtb-idle on click.

Browser compatibility

CSS animations (transform: scale on SVG elements), CSS variables inside animation and calc() on a duration, prefers-reduced-motion. Measured in Chromium, Firefox and WebKit (Playwright): 0 errors, three animations named hrtbBeat / hrtbRing, no animation with data-hrtb-idle, frozen rendering under reduced motion, two independent copies.

Chrome 74+✓ Full (measured)
Firefox 63+✓ Full (measured)
Safari 10.1+✓ Full (measured in WebKit)
Edge 79+✓ Full (Chromium engine)
Safari iOS 10.3+✓ WebKit engine, not verified on a real device
Chrome Android 74+✓ Full (Chromium engine)

Without CSS animations, the red heart stays visible and still, and the rings stay invisible.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0606-root">
  <div class="hrtb-stage">
    <svg class="hrtb-svg" viewBox="0 0 80 80" aria-hidden="true" focusable="false">
      <circle class="hrtb-ring" cx="40" cy="40" r="20"/>
      <circle class="hrtb-ring" cx="40" cy="40" r="20"/>
      <path class="hrtb-heart" d="…"/>
    </svg>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Size (CSS — --hrtb-size) 80px Side of the drawing; 100% follows the parent's width. At their largest the rings slightly exceed the heart: leave a little room around it.
Color (CSS — --hrtb-color) #ef4444 Heart and rings together (white on a colored button, for instance).
Pace (CSS — --hrtb-speed) 1.2s Length of one full beat; 60 / length = beats per minute (0.8s ≈ 75 per minute). The second ring's offset follows automatically.
Stop / restart (HTML — data-hrtb-idle attribute on .fx-0606-root) absent (the heart beats) Present: still heart, rings hidden. Toggle it in JavaScript to make the heart beat only after an action.
Block background and height (CSS — --hrtb-page, --hrtb-root-h) #0a0a0f / 300px transparent and 0px to slip the heart into a button, a badge or a heading.

FAQ

Put data-hrtb-idle on the root in the HTML, then remove it on click: root.removeAttribute('data-hrtb-idle'). To stop it again, root.setAttribute('data-hrtb-idle', ''). CSS does the rest; no script is shipped.
Yes: one cycle = one beat. For 90 beats per minute, --hrtb-speed: calc(60s / 90) (about 0.67 s). You can change it live from your data with root.style.setProperty('--hrtb-speed', …).
No. The animations are named hrtbBeat and hrtbRing, and every class is prefixed hrtb: nothing carries a generic name such as heartbeat or .heart that could clash with another effect.