Create Stunning Waveforms for your Audio Files with Javascript

Create Stunning Waveforms for your Audio Files with Javascript
4 months ago - 7 min read

Are you looking to create stunning waveforms for your audio files to show your users when that special moment happens in your clip? Or just want to allow better visualization of what your audio looks like?

Following on from my day of development that included, writing and creating the article on Tension Builders SFX, I developed a custom element that incorporates the fantastic library wavesurfer.js. I implemented this on filmstorm.net which runs on the open-source cms ghost.org.

You don't need to use the Ghost platform to benefit from this integration as it requires you to point to just a basic <audio> element.

Firstly, let's look at the script file. To insert this you need access and edit your page HTML or if you're limited to not editing HTML, look for code injection preferably in the footer, most site builders have this. If you're building the site yourself you most definitely have access to the HTML or any other language that is rendering your page (if you're doing it this way you should have the know-how on how to edit this code to work correctly on your own platform).

Before we get started, let's have a look at the finished result:

audio-thumbnail
Tension Builder 2
0:00
/0:06

The Completed Waveform Result

My Custom Wavesurfer.js Integration Script

<script src="https://unpkg.com/wavesurfer.js"></script>
<audio class="audio-element" src="https://yourAudioFile.com"></audio>

<h2>Preview Audio Tracks</h2>
<div id="audioList"></div>

<style>
   .audio-element {
     display: none;  
    }
    
    .playbackAudio {
    display: block;
    margin-bottom: 10px;
    height: 70px;
    background: #f7f7f7;
    border-radius: 0px;
}
    
   .audioWave {
    width: calc(100% - 80px);
    float: left;
}
    
    .playbackBtn {
    width: 70px;
    height: 70px;
    float: left;
    background: #efefef;
   
    color: #2e2e2e;
    font-family: 'Inter';
    margin-right: 10px;
}
    
  
    
    wave {
     height: 70px !important; 
        overflow: hidden !important;
    }
    
    
</style>

<script>
    var parentAudioList = document.getElementById("audioList")
    var audioEls = document.getElementsByTagName('audio');

    function createAudio(url) {
        var playbackAudio = document.createElement("div");
        playbackAudio.setAttribute("class", "playbackAudio");
        var playbackBtn = document.createElement("button");
        playbackBtn.innerText = "Play"
        playbackBtn.setAttribute("class", "playbackBtn");
        
        
        var audioWave = document.createElement("div");
	    audioWave.setAttribute("class", "audioWave");
        var wavesurfer = WaveSurfer.create({
            container: audioWave,
            backend: 'MediaElement',
            responsive: true
        });
        //attach children to playbackAudio
        playbackAudio.appendChild(playbackBtn);
        playbackAudio.appendChild(audioWave);
        
        parentAudioList.appendChild(playbackAudio);
        wavesurfer.load(url);
       
        playbackBtn.addEventListener("click", function() {
  		wavesurfer.playPause();
        if(this.innerText == "Play") {
        	this.innerText = "Stop";
        } else {
        	this.innerText = "Play";
        }
            
        wavesurfer.on('finish', function () {
        playbackBtn.innerText = "Play";
        });
});
        

    }
    
   Array.from(audioEls).forEach(function (audioEl, i) {
        createAudio(audioEl.src)
        
    });

</script>

Breaking Down the Code

<script src="https://unpkg.com/wavesurfer.js"></script>

To get started, on the first line, we import the script that holds wavesurfer.js, this allows us to reference and set up the functions needed to make this all work.

<audio class="audio-element" src="https://yourAudioFile.com"></audio>

To hold your audio, on line 2 we create an <audio> element with the src url set to your custom audio file. This is where you tell your page what you are loading to play, i.e your audio .mp3, .wav, etc.

<div id="audioList"></div>

The next part is an HTML document element that we create on line 5 this is a <div> with a style id of "audioList" - it's very important to set this id as it will allow us to reference it later on, this is the element that will append all of our wavesurfer.js renderers to.

<style>
.audio-element {
    display: none;  
}
.playbackAudio {
    display: block;
    margin-bottom: 10px;
    height: 70px;
    background: #f7f7f7;
    border-radius: 0px;
}
.audioWave {
    width: calc(100% - 80px);
    float: left;
}
.playbackBtn {
    width: 70px;
    height: 70px;
    float: left;
    background: #efefef;
    color: #2e2e2e;
    margin-right: 10px;
}   
wave {
    height: 70px !important; 
    overflow: hidden !important;
}
</style>

From line 7 to line 44 we have our style script. This basically styles the play/pause button, sets the button to be square, and aligns the button and waveform elements next to each other nicely. We don't handle the responsiveness of wavesurfer.js here, as you can provide the library with an option to enable responsiveness.

 var parentAudioList = document.getElementById("audioList")
    var audioEls = document.getElementsByTagName('audio');

Starting in our main script file, we have on line 47: document.getElementById("audioList") which provides us with the main element to append the created wavesurfer.js elements to.

Line 48: document.getElementsByTagName('audio')  gets all our provided audio element (this is from line 2, remember? Also you can have as many <audio> elements as you - see here: https://filmstorm.net/free-tension-builder-sound-effects/ we render multiple audio waveforms)

It's important to use getElementsByTagName() as it finds all the elements on the page and not just one. We use this later on.

  function createAudio(url) {
        var playbackAudio = document.createElement("div");
        playbackAudio.setAttribute("class", "playbackAudio");
        var playbackBtn = document.createElement("button");
        playbackBtn.innerText = "Play"
        playbackBtn.setAttribute("class", "playbackBtn");
        
        
        var audioWave = document.createElement("div");
	    audioWave.setAttribute("class", "audioWave");
        var wavesurfer = WaveSurfer.create({
            container: audioWave,
            backend: 'MediaElement',
            responsive: true
        });
        //attach children to playbackAudio
        playbackAudio.appendChild(playbackBtn);
        playbackAudio.appendChild(audioWave);
        
        parentAudioList.appendChild(playbackAudio);
        wavesurfer.load(url);
       
        playbackBtn.addEventListener("click", function() {
  		wavesurfer.playPause();
        if(this.innerText == "Play") {
        	this.innerText = "Stop";
        } else {
        	this.innerText = "Play";
        }
            
        wavesurfer.on('finish', function () {
        playbackBtn.innerText = "Play";
        });
});
        

    }

This function is fairly simple, lines 51 to 55 create <div> and <button> elements and use setAttribute to set classes and text for the play button.

var playbackAudio = document.createElement("div");
        playbackAudio.setAttribute("class", "playbackAudio");
        var playbackBtn = document.createElement("button");
        playbackBtn.innerText = "Play"
        playbackBtn.setAttribute("class", "playbackBtn");
        
        
        var audioWave = document.createElement("div");

Lines 58 to 59 create the wavesurfer.js element. This lives in a <div> as well, using setAttribute again we set the class so we can style it.

var wavesurfer = WaveSurfer.create({
            container: audioWave,
            backend: 'MediaElement',
            responsive: true
        });

Lines 60 to 64 instantiate the wavesurfer.js element and we provide options, the container (audioWave), backend (MediaElement) and we set it to be responsive (true - which means yes, be responsive.)

playbackAudio.appendChild(playbackBtn);
playbackAudio.appendChild(audioWave);
        
parentAudioList.appendChild(playbackAudio);

Lines 66, 67 & 69 we append the button and audioWave element to a parent div (so we can align everything nicely using the style code above).

wavesurfer.load(url);

Line 70 we call wavesurfer.load(url) which uses the url of the <audio> src we get from our Array of <audio> objects.

playbackBtn.addEventListener("click", function() {
  		wavesurfer.playPause();
        if(this.innerText == "Play") {
        	this.innerText = "Stop";
        } else {
        	this.innerText = "Play";
        }
            
        wavesurfer.on('finish', function () {
        playbackBtn.innerText = "Play";
        });

Almost there, Lines 72 to 82 setup our button to respond to clicking. This is a basic flip / flop which checks the buttons text if playing set text to stop and vice versa. We use a wavesurfer.js method called .playPause() which allows the audio to play and pause with each click. There's a bunch of different options available to check out on it's official site:  https://wavesurfer-js.org/docs/methods.html

That's it for the main function, but we have one last, but very important function at the bottom from lines 88 to 91.

 Array.from(audioEls).forEach(function (audioEl, i) {
        createAudio(audioEl.src)
        
    });

Remember how we found all the <audio> elements on line 48 - we use a forEach() method to iterate over each audio element and run the function we just made above createAudio(url).

The .forEach() would normally work, but if your using a straight tag like <audio> you can add Array.from(audioEls).forEach() and this will read your array correctly.

Then it's as simple as reading your <audio> elements src and calling the function createAudio(audioEl.src);

And you're done, now you should have a stunning waveform, like this:

The finished result