stream/ui
Browse the library

Equalizer

An animated now-playing meter whose bars move independently, and stop when paused or when motion is reduced.

stableEqualizer.tsx

Examples

Sizes and tones

equalizer/basic.tsx
"use client";

import { useState } from "react";
import { Pause, Play } from "lucide-react";
import { Button, Equalizer } from "@streamuilib/react";

export default function EqualizerBasic() {
  const [playing, setPlaying] = useState(true);

  return (
    <div className="flex items-center gap-6">
      <Equalizer playing={playing} bars={3} tone="lime" />
      <Equalizer playing={playing} bars={5} tone="pink" className="h-8" />
      <Equalizer playing={playing} bars={7} tone="blue" className="h-12 gap-1 [&>span]:w-2" />
      <Button size="sm" variant="secondary" onClick={() => setPlaying(!playing)}>
        {playing ? <Pause aria-hidden /> : <Play aria-hidden />}
        {playing ? "Pause" : "Play"}
      </Button>
    </div>
  );
}

Installation

bun add @streamuilib/react
usage.tsx
import { Equalizer } from "@streamuilib/react";

<Equalizer playing={isPlaying} bars={4} tone="pink" />

API

PropTypeDefaultDescription
playingbooleantrueAnimate the bars; when false they rest low.
barsnumber4How many bars.
tone"lime" | "pink" | "blue" | "violet" | "orange""lime"Bar color.
classNamestring—Size it with height classes, e.g. h-8.

Accessibility

  • Purely decorative and aria-hidden; pair it with text that says what's playing.
  • Under prefers-reduced-motion the bars hold still at a fixed height.

Source

packages/react/src/Equalizer.tsx
import type { ComponentProps, CSSProperties } from "react";
import { cn } from "./cn";
import { toneBg, type Tone } from "./tones";

export type EqualizerProps = ComponentProps<"span"> & {
  /** Animate the bars; when false they rest low. */
  playing?: boolean;
  bars?: number;
  tone?: Tone;
};

/**
 * A decorative "now playing" meter. Each bar gets its own speed and delay so
 * the motion never looks like one block. Hidden from assistive tech, and
 * still under prefers-reduced-motion (styles in theme.css).
 */
export function Equalizer({ playing = true, bars = 4, tone = "lime", className, ...props }: EqualizerProps) {
  return (
    <span
      aria-hidden
      data-playing={playing || undefined}
      className={cn("sui-eq inline-flex h-4 items-end gap-0.5", className)}
      {...props}
    >
      {Array.from({ length: bars }, (_, i) => (
        <span
          key={i}
          className={cn("sui-eq-bar block h-full w-1 border border-ink", toneBg[tone])}
          style={{ "--sui-eq-i": i } as CSSProperties}
        />
      ))}
    </span>
  );
}