Classic Visualizer

This component recreates the classic audio bars we all know and love. Click the button to start call and the audio bars will animate to your voice assistant. This component is animated purely with React implementation.

Preview

Code

Copy the following code to your component file for example classic.tsx.

import React, { useEffect, useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import useWebRTCAudioSession from '@/hooks/use-webrtc';
import { Button } from '@/components/ui/button';
import { MicIcon, PhoneOff } from 'lucide-react';
 
const Visualizer: React.FC = () => {
  const { currentVolume, isSessionActive, handleStartStopClick } = useWebRTCAudioSession('alloy');
  const [bars, setBars] = useState(Array(50).fill(5));
 
  console.log(currentVolume);
 
  useEffect(() => {
    if (isSessionActive) {
      updateBars(currentVolume);
    } else {
      resetBars();
    }
  }, [currentVolume, isSessionActive]);
 
  const updateBars = (volume: number) => {
    if (volume > 0.002) {
      setBars(bars.map(() => Math.random() * volume * 500));
    } else {
      setBars(Array(50).fill(5));
    }
  };
 
  const resetBars = () => {
    setBars(Array(50).fill(5));
  };
 
  const micPulseAnimation = {
    scale: [1, 1.2, 1],
    opacity: [1, 0.8, 1],
    transition: { duration: 0.8, repeat: Infinity }
  };
 
  return (
    <div className="flex flex-col items-center justify-center p-6 rounded">
      <AnimatePresence>
        {isSessionActive && (
          <motion.div
            className="flex items-center justify-center w-full h-full"
            initial={{ opacity: 0, scale: 0.8 }}
            animate={{ opacity: 1, scale: 1 }}
            exit={{ opacity: 0, scale: 0.8 }}
            transition={{ duration: 0.5 }}
          >
            <svg width="100%" height="100%" viewBox="0 0 1000 200" preserveAspectRatio="xMidYMid meet">
              {bars.map((height, index) => (
                <React.Fragment key={index}>
                  <rect
                    x={500 + index * 20 - 490}
                    y={100 - height / 2}
                    width="10"
                    height={height}
                    className={`fill-current ${isSessionActive ? 'text-black dark:text-white opacity-70' : 'text-gray-400 opacity-30'}`}
                  />
                  <rect
                    x={500 - index * 20 - 10}
                    y={100 - height / 2}
                    width="10"
                    height={height}
                    className={`fill-current ${isSessionActive ? 'text-black dark:text-white opacity-70' : 'text-gray-400 opacity-30'}`}
                  />
                </React.Fragment>
              ))}
            </svg>
          </motion.div>
        )}
      </AnimatePresence>
      <motion.div
        className="mt-4"
        animate={isSessionActive && currentVolume === 0 ? micPulseAnimation : {}}
      >
        <Button onClick={handleStartStopClick} className="flex items-center justify-center w-12 h-12 rounded-full shadow-lg">
          <AnimatePresence>
            {isSessionActive ? (
              <motion.div
                key="phone-off"
                initial={{ opacity: 0, scale: 0.8 }}
                animate={{ opacity: 1, scale: 1 }}
                exit={{ opacity: 0, scale: 0.8 }}
                transition={{ duration: 0.3 }}
              >
                <PhoneOff size={24} />
              </motion.div>
            ) : (
              <motion.div
                key="mic-icon"
                initial={{ opacity: 0, scale: 0.8 }}
                animate={{ opacity: 1, scale: 1 }}
                exit={{ opacity: 0, scale: 0.8 }}
                transition={{ duration: 0.3 }}
              >
                <MicIcon size={24} />
              </motion.div>
            )}
          </AnimatePresence>
        </Button>
      </motion.div>
    </div>
  );
};
 
export default Visualizer;

Usage

Import the component in your file and then use it in your page. Ensure the use-webrtc hook is copied over to your project.

Note: This component uses Tailwind CSS, make sure to have it installed in your project.

import Visualizer from "@/components/openai-blocks/classic";
 
export default function Home() {
  return (
    <main className="flex items-center justify-center h-screen">
      <Visualizer />
    </main>
  );
}