SVG✨ Premium

Sine Wave Grid

An 8 × 15 grid of dots that swell and brighten in turn: a diagonal wave keeps crossing it, from the top-left corner to the bottom-right one.

CSSJSWave

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

Flight comparison site — the grid fills the results area during the search — Sine Wave Grid example 1

① Flight comparison site — the grid fills the results area during the search

WhenAfter a click on “Search”, the results area clears and a 5 × 20 grid of blue dots ripples above the line “Looking for available flights…” until the flights arrive.
WhyA wait of a few seconds feels shorter when something is moving; the wave crossing the grid gives that feeling without promising a percentage.
Settingsdata-rows="5" data-cols="20", --swg-dot: 6px, --swg-gap: 9px, --swg-color: #2563eb on --swg-bg: #fff, --swg-height: 120px. The scene switches the area's state.
Farm weather station maker — background of the “Our sensors” section — Sine Wave Grid example 2

② Farm weather station maker — background of the “Our sensors” section

WhenThe products section appears: a 22 × 33 grid of light green dots spans the full width and the wave passes behind the S2 weather station, Soil probe and Radio gateway cards.
WhyEvenly spaced dots recall sensors laid out on a grid across a plot, and the wave suggests wind running over the field.
Settingsdata-rows="22" data-cols="33", --swg-dot: 5px, --swg-gap: 11px, --swg-color: #84cc16, --swg-bg: #0d1a0f, --swg-height: 360px, root set to position: absolute; inset: 0.
Trade fair — an animated strip at the top of the digital ticket — Sine Wave Grid example 3

③ Trade fair — an animated strip at the top of the digital ticket

WhenVisitors open their ticket on their phone: the dark top strip runs a wave of golden dots above the fair's name, the holder's name and the entry code.
WhyA living strip sets the ticket apart from a frozen screenshot and gives the fair a visual signature staff recognise at the gate.
Settingsdata-rows="5" data-cols="20", --swg-dot: 5px, --swg-gap: 8px, --swg-color: #fbbf24, --swg-bg: #1c1917, --swg-height: 96px.

How it works

The HTML ships an empty .swg-grid container; for every .fx-0614-root on the page, the script fills it with data-rows rows of data-cols dots (8 × 15 = 120 by default). Each dot gets a single value, --swg-k, the sum of its row and column numbers. All dots on the same diagonal therefore share the same --swg-k.

The swgPulse animation takes a dot from half to one and a half times its size and from 30% to 100% opacity, then back, over 2 seconds with an eased start. The delay is --swg-k × 0.1 s: diagonals light up one after another and the wave moves at a slant. Because the easing curve is symmetrical, each dot follows a motion close to a sine. Delays are made negative in CSS (- 100 × duration), so the wave is already formed when it appears, without the jump of the first seconds.

The script adds no stylesheet to the page and builds the dots only once per root (flagged data-swg-ready). Animations, classes and variables all carry the swg prefix and hang off the root, so several grids live on one page without clashing. The grid keeps a fixed size (8 px dots, 8 px gaps) centred in a 300 px tall area.

Accessibility

  • Hidden from screen readers: the grid carries aria-hidden="true". Used as a waiting state, it needs text next to it ("Looking for available flights…") in an aria-live="polite" region.
  • Reduced motion: under prefers-reduced-motion: reduce, the animations are paused; the grid shows a frozen wave, with its large bright dots and small pale ones, instead of 120 identical dots.
  • Nothing to activate: no focus, no click, no hover. The effect does not capture any event.
  • Flashing: the wave moves one diagonal every tenth of a second with a soft, continuous opacity change, far from the sudden flashes accessibility rules guard against.

Browser compatibility

Pure CSS for the motion, JavaScript only to create the dots. The gap property in a flex box (Chrome 84, Firefox 63, Safari 14.1, MDN data) sets the minimum versions. Measured in Chromium 151, Firefox 153 and WebKit 26.5: 120 dots per copy, two independent copies, no stylesheet added to the page, frozen wave under reduced motion.

Chrome 84+✓ Full (measured)
Firefox 63+✓ Full (measured)
Safari 14.1+✓ Full (measured on WebKit)
Edge 84+✓ Full (Chrome engine)
Safari iOS 14.5+✓ Not checked on a device
Chrome Android 84+✓ Full (measured at 390 px, touch)

Without JavaScript the area stays empty. A browser without flexbox gap packs the dots edge to edge: the wave still shows, only denser.

The code

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

index.html — structure
<div class="fx-0614-root" data-rows="8" data-cols="15">
  <div class="swg-grid" aria-hidden="true"></div>
</div>

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

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
data-rows / data-cols (root) 8 / 15 Grid size (1 to 60 each). The grid does not adapt to the width: use fewer columns or smaller dots on a narrow screen, anything wider is clipped.
--swg-color #6366f1 Dot colour.
--swg-dot 8px Resting dot diameter; it ranges from half to 1.5 times this value.
--swg-gap 8px Space between two dots, across and down.
--swg-speed 2s Length of one dot's pulse.
--swg-step 0.1s Delay of one diagonal behind the previous one. Larger stretches the wave; 0 pulses the whole grid together.
--swg-height / --swg-bg 300px / #0a0a0f Height and background of the area; the grid sits in its centre.

FAQ

Yes, by changing the value the script sets: replace r + c with r + (cols - 1 - c) to start from the top-right corner, or with c alone for a vertical wave sweeping left to right.
Remove or hide the root (hidden, display: none): the animations stop with it. To freeze it while keeping it visible, add animation-play-state: paused on .swg-dot.
Each dot swings along an eased, symmetrical curve very close to a sine, and its offset depends linearly on its position: together they behave like a travelling wave. No sine is computed in JavaScript.