Back to library

buttons

Visualizer Button

An outline button that doubles as an audio player — five randomized bars animate while an <audio> element plays.

Inspired by ruixen.ui · View source

Preview

Copy for AI

Copy for Lovable is the fastest way to move this component into another Lovable project — it copies the full prompt with dependencies, files, and a usage example. Copy all files gives you the raw source if you want to paste it manually.

Installation

npx shadcn@latest add https://21st.dev/r/ruixen.ui/visualizer-button

Paste into a terminal — or paste into a Lovable chat and I'll run it for you.

Source files

components/ui/visualizer-button.tsx
"use client"

import * as React from "react"
import { Button } from "@/components/ui/button"
import { cn } from "@/lib/utils"

interface VisualizerButtonProps {
  audioSrc: string
  width?: number
  height?: number
  className?: string
}

export default function VisualizerButton({
  audioSrc,
  width = 60,
  height = 30,
  className,
}: VisualizerButtonProps) {
  const audioRef = React.useRef<HTMLAudioElement | null>(null)
  React.useEffect(() => {
    audioRef.current = new Audio(audioSrc)
    return () => {
      audioRef.current?.pause()
      audioRef.current = null
    }
  }, [audioSrc])
  const [isPlaying, setIsPlaying] = React.useState(false)
  const [levels, setLevels] = React.useState<number[]>(Array(5).fill(0))

  React.useEffect(() => {
    let interval: ReturnType<typeof setInterval> | undefined
    if (isPlaying) {
      interval = setInterval(() => {
        setLevels(levels.map(() => Math.random() * height))
      }, 150)
    } else {
      setLevels(Array(5).fill(0))
    }
    return () => clearInterval(interval)
  }, [isPlaying, height])

  const togglePlay = () => {
    const audio = audioRef.current
    if (!audio) return
    if (isPlaying) audio.pause()
    else void audio.play()
    setIsPlaying(!isPlaying)
  }

  return (
    <Button
      className={cn(
        "relative flex items-end justify-between px-2 py-1",
        className
      )}
      onClick={togglePlay}
      variant="outline"
      style={{ width, height }}
    >
      {levels.map((lvl, idx) => (
        <div
          key={idx}
          className="bg-blue-500 dark:bg-white rounded-sm transition-all duration-150"
          style={{ width: 4, height: `${lvl}px` }}
        />
      ))}
    </Button>
  )
}

Dependencies

@/components/ui/button