UI/Components/Audio waveforms/Bar Waveform

Bar Waveform

A customizable bar audio waveform visualization component.

Animate a bar waveform, often used for audio visualizations.

0:00 / 0:10

Installation

CLI

COMING SOON

Installation via npx shadcnui install clipkittt@bar-waveform isn't quite ready yet. For now, please follow the manual installation instructions.

Eventually users will be able to run the following command:

npx shadcn@latest add "https://clippkit/r/bar-waveform"

The GitHub task can be found here.

Manual

Create a new file, for example, at src/components/bar-waveform.tsx (or your preferred location) and paste the following code into it.

src/components/bar-waveform.tsx
"use client";

import React, { useEffect, useState } from "react";
import {
  MediaUtilsAudioData,
  visualizeAudioWaveform,
} from "@remotion/media-utils";
import { useCurrentFrame, useVideoConfig } from "remotion";

// Helper function to generate waveform samples
const generateWaveformSamples = (
  audioData: MediaUtilsAudioData | null | undefined,
  numberOfSamples: number,
  frame: number,
  waveSpeed: number,
  fps: number
): number[] => {
  if (audioData) {
    return visualizeAudioWaveform({
      fps,
      frame,
      audioData,
      numberOfSamples,
      windowInSeconds: 1 / fps,
    });
  }
  return Array(numberOfSamples)
    .fill(0)
    .map((_, i) => {
      return (
        Math.sin(frame / waveSpeed + (i / numberOfSamples) * 2 * Math.PI) *
          0.5 +
        0.5
      );
    });
};

interface BarWaveformProps {
  audioData?: MediaUtilsAudioData | null;
  numberOfSamples?: number;
  barColor?: string;
  barWidth?: number;
  barGap?: number;
  waveAmplitude?: number;
  waveSpeed?: number;
  containerStyle?: React.CSSProperties;
  height?: string | number;
  width?: string | number;
  barBorderRadius?: string | number;
}

export default function BarWaveform({
  audioData,
  numberOfSamples = 64,
  barColor = "var(--foreground)",
  barWidth = 5,
  barGap = 2,
  waveAmplitude = 100,
  waveSpeed = 10,
  containerStyle,
  height: propHeight,
  width: propWidth,
  barBorderRadius = 0,
}: BarWaveformProps) {
  const frame = useCurrentFrame();
  const { width: videoWidth, height: videoHeight, fps } = useVideoConfig();

  const finalWidth = typeof propWidth === "number" ? propWidth : videoWidth;
  const finalHeight = typeof propHeight === "number" ? propHeight : videoHeight;

  const [barHeights, setBarHeights] = useState<number[]>([]);

  useEffect(() => {
    const waveformData = generateWaveformSamples(
      audioData,
      numberOfSamples,
      frame,
      waveSpeed,
      fps
    );
    const newBarHeights = waveformData.map((sample) =>
      Math.max(1, sample * waveAmplitude)
    );
    setBarHeights(newBarHeights);
  }, [
    frame,
    audioData,
    numberOfSamples,
    waveAmplitude,
    waveSpeed,
    fps,
  ]);

  const totalBarWidth = numberOfSamples * barWidth;
  const totalGapWidth = (numberOfSamples - 1) * barGap;
  const waveformVisualWidth = totalBarWidth + totalGapWidth;
  const startX = (finalWidth - waveformVisualWidth) / 2;

  return (
    <div
      style={{
        width: finalWidth,
        height: finalHeight,
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        overflow: "hidden",
        backgroundColor: "transparent",
        ...containerStyle,
      }}
    >
      <svg
        viewBox={`0 0 ${finalWidth} ${finalHeight}`}
        width={finalWidth}
        height={finalHeight}
      >
        {barHeights.map((barH, i) => {
          const x = startX + i * (barWidth + barGap);
          const y = finalHeight / 2 - barH / 2;
          const calculatedBarHeight = Math.min(barH, finalHeight);

          return (
            <rect
              key={i}
              x={x}
              y={y}
              width={barWidth}
              height={calculatedBarHeight}
              fill={barColor}
              rx={barBorderRadius}
              ry={barBorderRadius}
            />
          );
        })}
      </svg>
    </div>
  );
}
Update the import paths in your Remotion compositions if you placed the file in a different location than shown in the usage examples.

Usage

Once the BarWaveform component is added to your project (either via CLI or Manually), you can integrate it into your Remotion project by importing it and defining a Composition.

Prerequisite

Ensure you have a Remotion project set up. If not, please refer to the Remotion documentation to get started.

Define a Composition:

In your Remotion project's entry file (commonly src/Root.tsx or src/index.ts), import BarWaveform and define a Composition.

src/main.tsx (or equivalent)
import { Player } from "@remotion/player";
import { Audio } from "remotion";

import BarWaveform from "@/components/bar-waveform"; // Assuming you placed it in src/components

// Example audio source
const MEDIA_SRC = "YOUR_AUDIO_SOURCE_URL_HERE"; // e.g., a public URL to an mp3 file

export default function MyVideoComposition() {
  // You would typically use useAudioData here if MEDIA_SRC is dynamic or needs processing
  // For simplicity in this example, we'll assume direct usage if BarWaveform handles it internally
  // or audioData is passed via inputProps if needed.

  const barWaveformProps = {
    numberOfSamples: 50,
    barColor: "lightgreen",
    waveAmplitude: 100,
    waveSpeed: 8,
    barWidth: 6,
    barGap: 2,
    // audioData: audioData, // Pass if useAudioData is used in this component
  };

  return (
    <Player
      component={BarWaveform} // Or a wrapper component if using <Audio> alongside
      inputProps={barWaveformProps}
      durationInFrames={300} // Adjust based on your audio duration
      compositionWidth={640}
      compositionHeight={360}
      fps={30}
      style={{
        width: "100%",
        height: "100%",
      }}
      autoPlay
      controls
      loop
    />
  );
}

API Reference

The component exported as BarWaveform (e.g., from apps/docs/registry/default/ui/bar-waveform.tsx or your project's component path) accepts the following props to customize its appearance and animation:

PropTypeDefault ValueDescription
audioDataMediaUtilsAudioDatanullAudio data processed by @remotion/media-utils.
numberOfSamplesnumber64Number of bars to render.
barColorstring"var(--foreground)"Color of the bars. Accepts any valid CSS color value.
barWidthnumber5Width of each bar in pixels.
barGapnumber2Gap between each bar in pixels.
barBorderRadiusstring | number0Border radius for the bars.
waveAmplitudenumber100Maximum height of the bars.
waveSpeednumber10Speed of the wave animation. Higher value means slower animation.
heightstring | numberVideo config heightExplicit height for the waveform container. Overrides video config height.
widthstring | numberVideo config widthExplicit width for the waveform container. Overrides video config width.
containerStyleReact.CSSProperties{}Custom CSS styles for the main container div.

On this page