Skip to main content

useVisualizerChart


The useVisualizerChart hook returns a tuple [amplitude, pending]:

  • amplitude is an array of numbers representing the inverted amplitude of the currently playing audio track, suitable for visualizing waveforms or bar charts.
  • pending is a boolean indicating whether the audio data is still being fetched and processed.

This hook accepts an optional dataPoints parameter, which defaults to 64 if not specified. This value determines the number of points in the amplitude array, effectively controlling the resolution of the visualized waveform.

The hook automatically handles:

  • Fetching and decoding the current track.
  • Calculating the average amplitude in each block.
  • Running the computation in a Web Worker to avoid blocking the main thread.
  • Cleaning up workers and aborting fetches when the track changes.
http://localhost:3000
for her chill00:00
warning
In case of any issues, try refreshing the page.
// AudioPlayer.tsx

import { Audio, useVisualizer, formatTime } from '@sina_byn/re-audio';

const VisualizerChart = () => {
const [amplitude, isPending] = useVisualizerChart(400);

return (
<div style={{ opacity: isPending ? 0.2 : 1 }} className='flex justify-center overflow-hidden'>
<div className='flex items-end justify-between h-32 w-fit gap-1'>
{amplitude.map((amp, index) => (
<div
key={index}
style={{ height: `${(1 - amp) * 100}%` }}
className='w-px min-h-2.5 bg-white/50 rounded-t-full shrink-0'
/>
))}
</div>
</div>
);
};

const AudioPlayer = () => {
return (
<Audio
playlist={[
{ id: 1, src: _1_mp3, name: 'for-her-chill' },
{ id: 2, src: _2_mp3, name: 'trap-type-beat-rap-instrumental-riff' },
{ id: 3, src: _3_mp3, name: 'whip-afro-dancehall' },
]}
>
{({
loading,
trackIndex,
playlist,
playing,
togglePlay,
duration,
currentTime,
volume,
setVolume,
prevTrack,
nextTrack,
rewindTrack,
forwardTrack,
setCurrentTime,
}) => (
<div>
<div className='flex'>
<div className='flex max-md:flex-col justify-between gap-4 w-full'>
<div className='flex items-center gap-4'>
<div className='size-[60px] bg-[#c4c4c4] rounded' />

<div className='flex flex-col'>
<span className='capitalize'>
{/* @ts-ignore */}
{playlist[trackIndex].name.split(/-/).join(' ')}
</span>
<span>{formatTime(currentTime)}</span>
</div>
</div>

<div className='flex items-center max-md:justify-center gap-x-4'>
<button type='button' onClick={rewindTrack.bind(null, 10)}>
<IconPlayerSkipBackFilled />
</button>

<button type='button' onClick={prevTrack}>
<IconPlayerTrackPrevFilled />
</button>

<button type='button' onClick={togglePlay}>
{playing ? <IconPlayerPauseFilled /> : <IconPlayerPlayFilled />}
</button>

<button type='button' onClick={nextTrack}>
<IconPlayerTrackNextFilled />
</button>

<button type='button' onClick={forwardTrack.bind(null, 10)}>
<IconPlayerSkipForwardFilled />
</button>
</div>
</div>
</div>

<div className='mt-4'>
<input
type='range'
min='0'
max={duration}
step='1'
className='w-full'
value={currentTime}
onChange={e => setCurrentTime(+e.currentTarget.value)}
/>
</div>

<div className='flex items-center justify-between gap-x-4'>
<div className='flex items-center gap-x-2 mt-4'>
<IconVolume2 />

<input
type='range'
min='0'
max='100'
className='w-full md:w-[150px]'
value={volume}
onChange={e => setVolume(+e.currentTarget.value)}
/>

<IconVolume />
</div>

{loading && <span className='max-lg:text-sm mt-2'>loading...</span>}
</div>

<div className='mt-8'>
<VisualizerChart />
</div>
</div>
)}
</Audio>
);
};

export default AudioPlayer;