Wave Distortion
The letters of a text rise, lean, sink and lean the other way one after another: a slow swell, 3 seconds per wave, that rolls through the word from left to right.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Distortion has 31 effects, including 5 free. Explore the category →
3 usage examples



How it works
You write the text in .wvd-text. On load, the script copies it into a .wvd-sr reserved for screen readers, then replaces what is shown with .wvd-letter elements grouped by word in .wvd-word, all under aria-hidden="true". Each letter carries its position in the --wvd-i variable. Every .fx-0255-root on the page is handled, each with its own text.
The wvdWave animation lasts 3 s: at a quarter, the letter has risen 0.125 em and leans −5°; at three quarters, it has sunk by the same amount and leans +5°; it ends upright, in place. Each letter starts 0.15 s after the previous one: with the 10 letters of DISTORTION, the last starts 1.35 s after the first and the wave sweeps the word from left to right.
Everything is set with variables on the root: size (--wvd-size, 2.5rem), colour (--wvd-color, white), swell height (--wvd-rise, in em so it follows the size), slant (--wvd-skew), duration and stagger. The block keeps overflow: hidden: a swell too tall for the line height gets clipped, so raise line-height or add a little padding.
The root spans the full width, at least 300 px tall on a #0a0a0f background. With --wvd-height: auto, --wvd-bg: transparent and width: auto, it slips into a heading, a widget or a button.
Accessibility
- Screen readers: the text in
.wvd-sris announced normally, the split letters are ignored. Measured in Chromium, Firefox and WebKit: "D I S T O R T I O N" spelled out before the repair, "DISTORTION" after. - Reduced motion: with
prefers-reduced-motion: reduce, the letters stop moving and stay upright; the text is whole and readable. - Readability: the ±5° slant and the 0.125 em swell keep the word readable at every size tested (12 to 80 px). On a 12 px button label, raise
--wvd-riseto 0.2em so the motion shows. - Flashing: none; the colour never changes, only the position moves (frame-by-frame measurement: 0 cells above the threshold).
- Role: the effect makes nothing clickable. Placed in a
<button>or an<h1>, it keeps the meaning of the element that hosts it.
Browser compatibility
CSS (1.9 KB) and 1 KB of dependency-free JavaScript: CSS variables, calc() in keyframes and delay, skewX(), Array.from. Measured in Chromium 151, Firefox 153 and WebKit 26.5: pixel-identical to the previous version at 0, 40, 75 and 500 ms; two copies with two different texts each split correctly; 0 errors.
If the script does not run, the text stays in one piece, in the chosen colour, with no swell. Without CSS variables, the text shows with inherited styles, still.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0255-root">
<div class="wvd-text">Your text</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Text (HTML — content of .wvd-text) |
DISTORTION | The text shown and read; it may contain spaces, accents and digits. |
--wvd-size · --wvd-color |
2.5rem · #fff | Size and colour of the letters. |
--wvd-rise |
.125em | Height of the swell, up then down (5 px at 2.5rem). 0.2em on a small label. |
--wvd-skew |
5deg | Slant of the letters at the top and bottom of the wave. 0deg for a swell with no tilt. |
--wvd-speed · --wvd-delay |
3s · .15s | Length of one wave and gap between two letters. A shorter gap (0.08s) makes a tighter wave on long text. |
--wvd-bg · --wvd-height |
#0a0a0f · 300px | Root background and minimum height; transparent and auto to blend it in. |
FAQ
.wvd-sr. When copying with the mouse, the selection takes the visible letters; the copy kept for screen readers has user-select: none and is not pasted twice. Measured in all three engines: the pasted text is exact, spaces included..wvd-text block has overflow: hidden, as in the original code. If the swell goes beyond the line height (large --wvd-rise, tight line-height), add a few pixels of vertical padding to .wvd-text, as in the examples.hidden), next to the normal label: the script splits it on load. When the action starts, swap the two with hidden, then switch back at the end, like the messaging button.