Simple audio waveform with Wavesurfer.js and React
One of the recent features I had to build required me to explore controlling audio in the browser and the Web Audio API. In a nutshell, this is the system that developers use to control audio files in the browser and provide users with specific interactions such as play, pause and skip. Out of the box, this is a fairly standard API to use, but if you’ve ever crossed its path before you know that the standard interface is very plain! This means developers are faced with the issue of building their own interfaces, which can turn a very simple task into a complex one.
One of the solutions is to use a package called Wavesurfer.js; a library built using the Web Audio API and HTML5 Canvas to bring you super customisable audio visualisation. However, after a bit of time exploring their website, you will struggle to find any decent examples with React and likely turn to Google to search for “React wavesurfer.js” like I did. You will then likely discover that the very useful react-wavesurfer module that uses the main library is no longer maintained on Github and then begin wondering where to turn next.
Well, look no further! Below I will outline how you can get this Wavesurfer.js up and running in your React app in a few short steps, as well as some help understanding some key elements of the API. I’m assuming you have a pretty good understanding of React, and for this example I will be using Styled Components. If you are unfamiliar with these, you can find out more here. They work in a very similar way to CSS, so you should be able to translate the minimal styling I use into CSS very easily.
Base Component:
In this example, I am using Class-based composition, as the Wavesurfer.js library works best when being bound to a JavaScript class and we have access to the this keyword — if you want to find out more about this you can read one of these two great articles here and here. I’ve gone for the very basic Waveform, but you can call it whatever you wish:
Component UI:
Next, it is time to add the main elements to the component; this will consist of a container, a button to play and pause the track, a div element to mount the waveform on, and an audio element.
You may be wondering why we have imported the Wave component; this is the element we will append Wavesurfer.js to and as you will see later, is the reason why we have ids added to bother Wave component and audio element. Along with the WaveContainer, the Wave component is a styled component thats is composed of a <div />
.
Adding Wavesurfer.js:
Now it is time to import Wavesurfer.js and initialise it in out components. You can find instructions on how to import it on their website; I am using yarn to add my packages to my app with yarn add wavesurfer.js
. Once this is done, we can create our waveform in our componentDidMount
method, which initialises the waveform using the Wavesurfer.create()
method and appends it to our React class as this.waveform
.
The .create()
method takes an options argument, which is an object that can be inputed with a range of different key:value pairs. These primarily consist of styling options but also some important configuration settings such as container
and backend
. As I mentioned above, the container
key states which DOM element the waveform will be appended to, whilst the backend
describes which browser media API that Wavesurfer.js uses.
Once this is done, we load the <audio id="track" />
element into the waveform after finding it in the DOM and assigning it to the track
variable, where the audio file will be accessed. I’ve also added a url
variable inside the render method with the file I wish to be played and passed it as a prop to the audio
element. This is temporary and in the final version I would pass this file as a prop to the Waveform component. If you save and refresh the page, you will see the waveform has been rendered on the page.
If you check out the documentation, you can play around with the styling to make the bars wider, change their colour, or even add a tracking cursor. There are lots of options available so play around until you’re happy.
Play & Pause Functionality:
Finally, we can add play/pause functionality. To do this, we just need to add a handlePlay
method to our component and some local state. This will control the waveform and update the button UI. Wavesurfer.js uses a single waveform.playPause()
method to toggle play and pause, so we can mirror this with our method.
As you can see, we use the updated playing
state to change the UI of the button whenever the track is playing. If you want to add more functionality such as skipping, you can find plenty of other method in the documentation. You should end up with something like this:
EDIT: Due to multiple requests, here is the Styled Components file
The WaveformContianer, Wave, PlayButton are all exported from the Wavesurfer.styled.js file. They are Styled Components and would look like this:
Waveform Container:
export const WaveformContianer = styled.div
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
height: 100px; width: 100%;
background: transparent;
`;
Wave:
export const Wave = styled.div`
width: 100%;
height: 90px;
`;
PlayButton:
export const PlayButton = styled.button`
display: flex;
justify-content: center;
align-items: center;
width: 60px;
height: 60px;
background: #EFEFEF;
border-radius: 50%;
border: none;
outline: none;
cursor: pointer;
padding-bottom: 3px; &:hover {
background: #DDD;
}
`;
I also used play and pause icons in the middle of the PlayButton that change when the state is either playing or paused.