Text✨ Premium

Wave Animation

Each letter waves up and down in an infinite loop using staggered CSS delays — pure CSS, zero JS for the animation.

CSSLoopWave

You'll get access to the interactive demo with a free account.

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Text has 56 effects, including 4 free. Explore the category →

3 usage examples

Music site — Artist name waving on loop — Wave Animation example 1

① Music site — Artist name waving on loop

WhenThe homepage of an artist, label, or podcast displays the stage name or album title in large format, looping continuously: the letter wave mimics sonic rhythm and keeps the identity animated without any audio file.
WhyA continuous loop is naturally justified in a musical context: the wave travelling through the letters evokes sound, vibration, pulse. It reinforces sonic identity through typographic motion alone — zero media assets needed.
Settingsfont-size: 5–7 rem, letter-spacing: -0.05 em, stagger delay 0.09 s, amplitude translateY(-18px), duration 2.2 s — tempo aligned with ~130 BPM.
Dashboard — Continuously waving 'LIVE' status badge — Wave Animation example 2

② Dashboard — Continuously waving 'LIVE' status badge

WhenA dashboard shows a status indicator (live feed, active connection, ongoing analysis): the badge waves continuously to signal the system is active.
WhyThe gentle wave replaces the aggressive <code>opacity</code> blink — it draws attention without distracting from the surrounding metrics.
Settingsfont-size: 0.75–1 rem, short text (3–6 letters), amplitude translateY(-4px), duration 1.4 s — fast rhythm for a 'pulse' feel.
Section header — Online magazine or newsletter — Wave Animation example 3

③ Section header — Online magazine or newsletter

WhenThe opening section of a long article or newsletter features its theme word in large type — the word waves while the reader reads the intro paragraph.
WhySlow-speed waving brings motion to an otherwise static layout; at large size it evokes breathing and keeps the reader engaged without interrupting reading.
Settingsfont-size: 4 rem, duration 3 s (slow), amplitude translateY(-18px), delay 0.12 s, brand color applied on .demo-text.

How it works

The wave relies entirely on a @keyframes waveContinuous with three steps: 0% and 100% at translateY(0), and 50% at translateY(-15px). The ease-in-out curve gives both the rise and fall a natural, sine-like rhythm.

The animation is assigned to every child <span> via the rule .text-wave-continuous span { animation: 2s ease-in-out infinite waveContinuous }. Phase offset is injected as inline style (animation-delay: 0s, 0.1s, 0.2s…): each letter enters the wave 100 ms after its neighbour.

At load time, the HTML already contains the pre-built spans with their delays. The splitText(el, text, step) utility — referenced from within the JS IIFE — can rebuild those spans on-the-fly from a string: it loops over each character, creates a <span style="animation-delay:…">, and inserts it into the target element.

The Replay button empties the element (innerHTML = '') then calls splitText after 50 ms. This delay is needed to force the browser to reset the CSS timers — without it, animations restart immediately without honouring their animation-delay.

Accessibility

  • No prefers-reduced-motion: the stylesheet contains no @media (prefers-reduced-motion: reduce) block. On systems set to reduced motion, the animation keeps running. To disable it: @media (prefers-reduced-motion:reduce){.text-wave-continuous span{animation:none}}.
  • The full text remains readable by screen readers through the textual content of each <span> — no aria-hidden is set in the provided source. To prevent letter-by-letter reading, add aria-label="YOUR TEXT" on the parent and aria-hidden="true" on each child span.
  • Contrast: white text (#fff) on background #0a0a0f → ratio ≥ 18:1, WCAG AAA.
  • No keyboard interaction is triggered by the effect itself. The Replay button is a native <button>, inherently focusable and keyboard-activatable.
  • The effect is 100% CSS: no requestAnimationFrame loop, no continuous JS computation — minimal CPU impact on battery-powered devices.

Browser compatibility

Relies on CSS @keyframes, transform: translateY(), and animation-delay — natively supported for years across all modern browsers.

Chrome 43+✓ Full
Firefox 16+✓ Full
Safari 9+✓ Full
Edge 12+✓ Full
Mobile iOS✓ Full
Android Chrome✓ Full

Without <code>@keyframes</code> support (very old browsers), the text displays statically at its rest position — no JS errors, content remains readable.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="demo-preview">
  <span class="demo-text text-wave-continuous"
        id="waveContinuousText"
        aria-label="YOUR TEXT">
    <span aria-hidden="true">W</span>
    <span aria-hidden="true">A</span>
    <span aria-hidden="true">V</span>
    <span aria-hidden="true">E</span>
  </span>
  <button class="replay-btn" onclick="replayWave()">Replay</button>
</div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
translateY(-15px) -15px Vertical wave amplitude in @keyframes waveContinuous (the 50% step). Increase for a more pronounced wave, reduce for a subtle effect.
2s (animation duration) 2s Full cycle duration in .text-wave-continuous span { animation: 2s … }. Reduce to 1 s for a fast rhythm, increase to 4 s for slow, serene motion.
ease-in-out (timing) ease-in-out Animation timing curve. ease-in-out = natural motion; linear = mechanical oscillation; cubic-bezier(0.45,0,0.55,1) accentuates the extremes.
animation-delay step (0.1s) 0.1s Delay gap between each letter (3rd argument of splitText or inline value). Reduce to 0.06 s for long words; increase to 0.18 s for slower, more dramatic ripple.
font-size: 2.5rem (.demo-text) 2.5rem Text size. The effect is most striking at large sizes (4–8 rem). Adjust on .demo-text or your own wrapper class.
letter-spacing: -0.02em (.demo-text) -0.02em Letter spacing. Tight by default for a modern look; set to 0.05–0.15 em for an airier wave where each letter has more breathing room.

FAQ

No. The wave is 100% CSS: @keyframes waveContinuous and animation-delay handle everything. JavaScript only handles span reconstruction (the splitText utility) and the Replay button — removing the JS leaves the animation working as long as the spans are already in the HTML.
Replace the content of the <span id="waveContinuousText"> with your own <span style="animation-delay: Xs">Letter</span>, incrementing X by 0.1 s per letter. Or call splitText(el, 'YOUR TEXT', 0.1) in JS to build the spans dynamically.
CSS animation-delay timers count from when the element is inserted in the DOM. Simply replacing the spans without removing them first means the browser continues the existing timers and the animation does not restart from zero. Clearing the element + a 50 ms pause forces the browser to reset all timers.