webdevpuneet.com

Wave Text — Free HTML CSS JS Animation Snippet

Wave Text Animation · Animations · Plain HTML, CSS & JS · Live preview

CategoryAnimations

Copy & paste

Source Code

<div class="scene">
  <div class="wave-wrap" id="wave1"></div>
  <div class="wave-wrap rainbow" id="wave2"></div>
  <div class="controls">
    <button class="btn" onclick="setWord('WAVE EFFECT')">WAVE EFFECT</button>
    <button class="btn" onclick="setWord('HELLO WORLD')">HELLO WORLD</button>
    <button class="btn" onclick="setWord('LOADING...')">LOADING...</button>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

buildWave() wraps each character in a <span> with unique animation-delay
animation-delay: i * 0.05s staggers the wave from left to right
translateY(-12px) at 50% keyframe creates the vertical wave peak
Non-breaking spaces (\u00A0) preserve word spacing in split characters
Hover re-triggers animation by removing and re-adding the animated class
Multiple wave rows with different phrases and speeds
CSS animation-timing-function: ease controls the character rise/fall curve
Export as HTML file, React JSX, or React + Tailwind CSS
Mobile (375px), Tablet (768px), Desktop device preview buttons
Live split-pane editor — preview updates as you type

About this UI Snippet

Wave Text — buildWave() Character Wrapping, animation-delay Stagger

Screenshot of the Wave Text Animation snippet rendered live

Wave text animates each character in a heading individually — characters rise and fall in a sequential wave pattern. Used on interactive headings, loading messages, and any text that benefits from playful motion — see also split text reveals, a typewriter effect, and gradient text.

The character-splitting technique

buildWave(id, text) splits the text string into individual characters via .split(''). Each character is wrapped in a <span> with a unique animation-delay: i * 0.05s — the delay increases by 50ms per character, creating the left-to-right wave timing. Spaces are replaced with non-breaking spaces ( ) to preserve spacing.

The CSS wave animation

Each span has a CSS @keyframes wave animation that runs translateY(-12px) at 50% and returns to translateY(0) at 100%. With staggered delays, the upward peak travels from character 0 to the last character, creating the wave.

Hover re-trigger

Hovering the wave element triggers the animation again by briefly removing and re-adding the animation class — creating an on-demand wave that plays each time the user hovers.

The staggered animation-delay technique

Wave text works by applying the same keyframe animation to every character span, but with a progressively increasing animation-delay. Character 0 starts immediately; character 1 waits 0.05s; character N waits N×0.05s. The keyframe itself is identical — translateY(-12px) at 50% and translateY(0) at 0%/100%. The delay offset is all that creates the wave appearance.

Character splitting with JavaScript

The text content is split into individual characters, each wrapped in a span: [...text].forEach((char, i) => { const span = document.createElement('span'); span.textContent = char === ' ' ? ' ' : char; span.style.animationDelay = i * 0.05 + 's'; el.appendChild(span); }). The non-breaking space ( ) replaces regular spaces so they preserve their width in the inline span layout.

Controlling the wave properties

Change the delay multiplier (0.05s) to control wave speed — smaller values create a faster wave, larger values create a slower, more dramatic roll. Change the translateY value in the keyframe to control wave height. Add translateX for a sideways sway. Use a sine-based delay function for a smoother wave curve: Math.sin(i * 0.4) * 0.1 + 'delay'.

Accessibility and prefers-reduced-motion

The animation can be distracting for users sensitive to motion. Add @media (prefers-reduced-motion: reduce) { .wave-char { animation: none; } } to freeze all characters for users who have enabled the reduce motion accessibility setting. The text remains fully readable — only the decorative motion is removed.

The staggered animation-delay technique

Wave text works by applying the same keyframe animation to every character span, but with a progressively increasing animation-delay. Character 0 starts immediately; character 1 waits 0.05s; character N waits N×0.05s. The keyframe itself is identical — translateY(-12px) at 50% and translateY(0) at 0%/100%. The delay offset is all that creates the wave appearance.

Character splitting with JavaScript

The text content is split into individual characters, each wrapped in a span: [...text].forEach((char, i) => { const span = document.createElement('span'); span.textContent = char === ' ' ? ' ' : char; span.style.animationDelay = i * 0.05 + 's'; el.appendChild(span); }). The non-breaking space ( ) replaces regular spaces so they preserve their width in the inline span layout.

Controlling the wave properties

Change the delay multiplier (0.05s) to control wave speed — smaller values create a faster wave, larger values create a slower, more dramatic roll. Change the translateY value in the keyframe to control wave height. Add translateX for a sideways sway. Use a sine-based delay function for a smoother wave curve: Math.sin(i * 0.4) * 0.1 + 'delay'.

Accessibility and prefers-reduced-motion

The animation can be distracting for users sensitive to motion. Add @media (prefers-reduced-motion: reduce) { .wave-char { animation: none; } } to freeze all characters for users who have enabled the reduce motion accessibility setting. The text remains fully readable — only the decorative motion is removed.

Step by step

How to Use

  1. 1
    Watch the wave animateThe wave cycles through the text continuously. Characters rise in sequence from left to right.
  2. 2
    Hover to replayMove the cursor over the wave text to trigger the animation on demand.
  3. 3
    Update the textIn the JS panel, update the text arguments in the buildWave() calls to your own phrases.
  4. 4
    Change animation delayUpdate i * 0.05 in the JS animation-delay calculation. Smaller values (0.03) create faster waves; larger (0.08) create slower ones.
  5. 5
    Change wave heightUpdate the translateY(-12px) value in the CSS wave keyframe.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.

Real-world uses

Common Use Cases

Hero headline wave animations
Apply to a key phrase in a hero heading for a playful, attention-grabbing entrance animation.
Loading and processing messages
Animate "Loading...", "Please wait...", or "Processing..." text to make wait states feel active rather than frozen.
Learn CSS animation-delay stagger patterns
Edit the i * 0.05 delay multiplier to understand how per-element delay creates sequential animations from a shared keyframe.
Interactive text on hover
Trigger the wave on mouseenter for any heading. Users discover the animation by hovering, turning a passive heading into an interactive element.
Game score and achievement displays
Animate score numbers or achievement text in a wave pattern as a celebratory reveal.
Combine with typewriter effect
Build the wave after the typewriter finishes writing a word — the wave plays on each new word as it finishes typing.

Got questions?

Frequently Asked Questions

buildWave() assigns animation-delay: i * 0.05s to each span, where i is the character index. Character 0 starts at 0s, character 1 at 0.05s, character 2 at 0.1s. The translateY peak travels left to right as each character reaches its 50% keyframe at a slightly later time.

When text is split into individual characters, regular spaces between words are rendered as empty spans with zero width. Non-breaking space (\u00A0) has a visible width, preserving the gap between words.

Replace translateY(-12px) with translateX(12px) for a horizontal wave, or use a combination: transform: translateY(-12px) rotate(10deg) for a rotation wave.

Wrap the wave trigger in an IntersectionObserver. When the element enters the viewport, call buildWave() (which re-creates all spans and restarts animations from delay 0).

Yes. Click "JSX" for a React component. Split the text string into characters, map each to a span with style={{ animationDelay: i * 0.05 + "s" }}. Increment a key on hover to re-trigger by remounting the spans.

Add a colour array and assign span.style.color = colors[i % colors.length] in buildWave(). Combine with the delay stagger for a rainbow wave effect.