Skip to main content

Glow Wave Bars

A skyline of bars breathing under two offset sine waves and an arch envelope: the center runs tall, the edges stay low, and every bar shares one bottom-to-top gradient that fades to transparent so the crowns glow like light through mist. Single hue family by design — the wave is the show, not a rainbow.

HeroReactMotionTailwind CSS
CSSTailwind

Manual

Create a file and paste the following code into it.

glow-wave-bars.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
"use client";

import { useRef, useState } from "react";
import { motion, useAnimationFrame } from "motion/react";
import { cn } from "@/lib/cn";

/* ------------------------------------------------------------------ */
/*  GlowWaveBars                                                       */
/* ------------------------------------------------------------------ */

interface GlowWaveBarsProps {
  /** Number of bars [Optional, default: 24] */
  barCount?: number;
  /** Gradient stops, bottom to top — keep ONE hue family [Optional] */
  colors?: string[];
  /** Max bar height as a fraction of the container [Optional, default: 0.92] */
  maxHeightRatio?: number;
  minHeightRatio?: number;
  /** Undulation speed [Optional, default: 0.5] */
  speed?: number;
  gap?: number;
  /** Per-bar blur in px for a soft glow [Optional, default: 0] */
  blur?: number;
  background?: string;
  className?: string;
}

/** Two offset sine waves under an arch envelope — organic, never twitchy. */
function barHeight(
  index: number,
  total: number,
  time: number,
  minH: number,
  maxH: number,
): number {
  const norm = index / (total - 1);
  const arch = Math.sin(norm * Math.PI);
  const phase1 = (index / total) * Math.PI * 2;
  const phase2 = (index / total) * Math.PI * 5.3;
  const wave =
    0.5 + 0.25 * Math.sin(time * 1.1 + phase1) + 0.25 * Math.sin(time * 0.7 + phase2);
  const blended = arch * 0.65 + wave * 0.35;
  return minH + blended * (maxH - minH);
}

/**
 * A skyline of bars breathing under two offset sine waves and an arch
 * envelope: the center runs tall, the edges stay low, and every bar shares
 * one bottom-to-top gradient that fades to transparent so the crowns glow
 * like light through mist. One hue family — the wave is the show, not a
 * rainbow.
 * @param {number} barCount - Bars across the container [Optional, default: 24]
 * @param {string[]} colors - Gradient stops bottom to top [Optional]
 */
export function GlowWaveBars({
  barCount = 24,
  colors = ["#dbeafe", "#93c5fd", "#3b82f6", "#1d4ed8", "#00000000"],
  maxHeightRatio = 0.92,
  minHeightRatio = 0.18,
  speed = 0.5,
  gap = 3,
  blur = 0,
  background = "#050608",
  className,
}: GlowWaveBarsProps) {
  const [heights, setHeights] = useState<number[]>(() =>
    Array.from({ length: barCount }, (_, i) =>
      barHeight(i, barCount, 0, minHeightRatio, maxHeightRatio),
    ),
  );
  const timeRef = useRef(0);

  useAnimationFrame((_, delta) => {
    timeRef.current += (delta / 1000) * speed;
    const t = timeRef.current;
    setHeights(
      Array.from({ length: barCount }, (_, i) =>
        barHeight(i, barCount, t, minHeightRatio, maxHeightRatio),
      ),
    );
  });

  const gradientStops = colors
    .map((c, i) => `${c} ${Math.round((i / (colors.length - 1)) * 100)}%`)
    .join(", ");
  const gradient = `linear-gradient(to top, ${gradientStops})`;

  return (
    <div
      className={cn("relative h-full w-full overflow-hidden", className)}
      style={{ background }}
    >
      <div className="absolute inset-0 flex items-end">
        {heights.map((fraction, i) => (
          <div
            key={i}
            className="flex h-full flex-1 items-end"
            style={{ padding: `0 ${gap / 2}px` }}
          >
            <motion.div
              className="w-full rounded-t-full"
              style={{
                height: `${fraction * 100}%`,
                background: gradient,
                filter: blur > 0 ? `blur(${blur}px)` : undefined,
              }}
            />
          </div>
        ))}
      </div>
    </div>
  );
}

Update the import paths to match your project setup.

Similar components

Starfall Field

Sine Flow Background

Max

Grain Concierge Landing

Max

Pixel Atelier Landing

Install via CLI

Resource details

PublishedJuly 17, 2026
CategoryHero
ReactMotionTailwind CSS