BNA

BNA UI
24

Audio Waveform

A customizable audio waveform visualization component with playback progress and interactive seeking capabilities.

Installation

pnpm dlx bna-ui add audio-waveform

Usage

import { AudioWaveform } from '@/components/ui/audio-waveform';

Basic Usage

function MyComponent() {
  const [isPlaying, setIsPlaying] = useState(false);
  const [progress, setProgress] = useState(0);
 
  return (
    <AudioWaveform
      isPlaying={isPlaying}
      progress={progress}
      showProgress={true}
      height={60}
    />
  );
}

With Custom Data

function MyComponent() {
  const audioData = [0.2, 0.5, 0.8, 0.3, 0.6, 0.9, 0.4, 0.7, 0.1, 0.5];
 
  return (
    <AudioWaveform
      data={audioData}
      isPlaying={isPlaying}
      progress={progress}
      showProgress={true}
      onSeek={(position) => setProgress(position)}
      interactive={true}
    />
  );
}

Interactive Seeking

function AudioPlayer() {
  const [progress, setProgress] = useState(0);
  const [isPlaying, setIsPlaying] = useState(false);
  const [isSeeking, setIsSeeking] = useState(false);
 
  const handleSeek = (position: number) => {
    setProgress(position);
    // Update your audio player position here
  };
 
  return (
    <AudioWaveform
      isPlaying={isPlaying}
      progress={progress}
      showProgress={true}
      interactive={true}
      onSeek={handleSeek}
      onSeekStart={() => setIsSeeking(true)}
      onSeekEnd={() => setIsSeeking(false)}
      activeColor='#007AFF'
      inactiveColor='#E5E5E7'
    />
  );
}

Examples

Default

Recording Mode

Interactive Seeking

Custom Styling

Real-time Data

Compact Size

API Reference

AudioWaveform

The main AudioWaveform component.

PropTypeDefaultDescription
datanumber[]-Audio amplitude data (0-1 range). Auto-generated if not provided.
isPlayingbooleanfalseWhether the audio is currently playing.
progressnumber0Current playback progress (0-100).
onSeek(position: number) => void-Callback fired when user seeks to a position.
onSeekStart() => void-Callback fired when seeking starts.
onSeekEnd() => void-Callback fired when seeking ends.
styleViewStyle-Additional styles for the container.
heightnumber60Height of the waveform in pixels.
barCountnumber50Number of bars in the waveform.
barWidthnumber3Width of each bar in pixels.
barGapnumber2Gap between bars in pixels.
activeColorstringtheme colorColor for played/active portion.
inactiveColorstringtheme colorColor for unplayed/inactive portion.
animatedbooleantrueWhether to animate the waveform during playback.
showProgressbooleanfalseWhether to show progress indicator.
interactivebooleanfalseWhether to enable touch-based seeking.

Usage Patterns

Audio Player Integration

function AudioPlayer({ audioUrl }: { audioUrl: string }) {
  const [isPlaying, setIsPlaying] = useState(false);
  const [progress, setProgress] = useState(0);
  const [duration, setDuration] = useState(0);
  const [audioData, setAudioData] = useState<number[]>([]);
 
  // Load audio data from file
  useEffect(() => {
    loadAudioWaveform(audioUrl).then(setAudioData);
  }, [audioUrl]);
 
  const handleSeek = (position: number) => {
    const newTime = (position / 100) * duration;
    // Seek audio to newTime
    setProgress(position);
  };
 
  return (
    <View>
      <AudioWaveform
        data={audioData}
        isPlaying={isPlaying}
        progress={progress}
        showProgress={true}
        interactive={true}
        onSeek={handleSeek}
        height={80}
        activeColor='#007AFF'
      />
      <Button onPress={() => setIsPlaying(!isPlaying)}>
        {isPlaying ? 'Pause' : 'Play'}
      </Button>
    </View>
  );
}

Voice Message

function VoiceMessage({ duration, audioData }: VoiceMessageProps) {
  const [isPlaying, setIsPlaying] = useState(false);
  const [progress, setProgress] = useState(0);
 
  return (
    <View style={styles.voiceMessage}>
      <TouchableOpacity onPress={() => setIsPlaying(!isPlaying)}>
        <Icon name={isPlaying ? 'pause' : 'play'} />
      </TouchableOpacity>
 
      <AudioWaveform
        data={audioData}
        isPlaying={isPlaying}
        progress={progress}
        showProgress={true}
        interactive={true}
        height={32}
        barCount={30}
        barWidth={2}
        barGap={1}
        style={{ flex: 1, marginHorizontal: 12 }}
      />
 
      <Text>{formatDuration(duration)}</Text>
    </View>
  );
}

Recording Visualization

function RecordingView() {
  const [isRecording, setIsRecording] = useState(false);
  const [recordingData, setRecordingData] = useState<number[]>([]);
 
  // Update waveform with real-time audio levels
  useEffect(() => {
    if (isRecording) {
      const interval = setInterval(() => {
        const newLevel = getCurrentAudioLevel(); // Your audio level function
        setRecordingData((prev) => [...prev.slice(-49), newLevel]);
      }, 100);
 
      return () => clearInterval(interval);
    }
  }, [isRecording]);
 
  return (
    <View>
      <AudioWaveform
        data={recordingData}
        isPlaying={isRecording}
        animated={true}
        showProgress={false}
        height={60}
        barCount={50}
        activeColor='#FF3B30'
      />
      <Button onPress={() => setIsRecording(!isRecording)}>
        {isRecording ? 'Stop' : 'Record'}
      </Button>
    </View>
  );
}

Accessibility

The AudioWaveform component follows accessibility best practices:

  • Proper touch target sizes for interactive elements
  • Screen reader support for progress information
  • Respects system accessibility settings
  • Keyboard navigation support where applicable

Performance

The component is optimized for performance:

  • Uses react-native-reanimated for smooth native animations
  • Efficient rendering with minimal re-renders
  • Configurable bar count to balance detail vs performance
  • Native driver animations where possible

Theming

The AudioWaveform automatically adapts to your app's theme:

  • Uses theme colors by default for active/inactive states
  • Supports both light and dark modes
  • Customizable colors through props
  • Respects theme opacity values

Data Format

Audio data should be provided as an array of numbers between 0 and 1:

const audioData = [
  0.1, // Very quiet
  0.3, // Quiet
  0.5, // Medium
  0.8, // Loud
  1.0, // Maximum
];

If no data is provided, the component generates realistic sample data automatically.