Zack Scholl

Waveforms on the web

 / #software #tutorial #music #javascript 

Drop-in replacement for an audio tag for stylized audio playback.

In my previous post about the SuperCollider musical programming language I wanted to include some audio demos. I didn’t like the traditional way of showing an audio file and tried to make something more akin to how soundcloud renders (i.e. an abstract waveform). It turns out this is really easy to do thanks to wavesurfer.js.

This is the traditional way of making showing an audio file in HTML:

1<audio src="drone5.wav" type="audio/wav" controls></audio> 
2<audio src="drone4.wav" type="audio/wav" controls></audio> 

which looks like this:

It turns out its really easy to now get a waveform visualized playback with a drop-in replacement for the audio tag. With about 50 lines of code (below) you can convert audio elements into something much nicer and still practical.

Now the same two audio tags will instead look like this:

How does it work

I simpy wrote a little javascript as a wrapper around wavesurfer.js. The wrapper looks for all the audio selectors and converts them to waveforms. Then it adds SVG buttons for play/pause from

Here is the entire HTML you need for a drop-in replacement to convert audio tags into nice playable waveforms:

 1<script src="/js/wavesurfer.js"></script>
 3document.addEventListener('DOMContentLoaded', function() {
 4    var els = document.querySelectorAll("audio");
 5    for (var i = 0; i < els.length; i++) {
 6        console.log(els[i].src);
 7        let i_ = i;
 8        let src_ = els[i].src;
 9        let newNode = document.createElement("div")
10        newNode.innerHTML = `<table style="padding-top:16px;padding-bottom:16px;"><tr><td style="position: relative;top: 4px;width:40px;"><div class="controls" style=""> <button class="audiobtn" data-action="play" style="background: #fff; border: none; padding-left:0;padding-right:0;"> <svg class="playicon" xmlns="" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="feather feather-play-circle"><circle cx="12" cy="12" r="10"></circle><polygon points="10 8 16 12 10 16 10 8"></polygon></svg> <svg class="pauseicon" style="display:none;" xmlns="" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="feather feather-pause-circle"><circle cx="12" cy="12" r="10"></circle><line x1="10" y1="15" x2="10" y2="9"></line><line x1="14" y1="15" x2="14" y2="9"></line></svg> </button></div></td><td width="100%"><div class="wave"></div></td></tr></table>`;
11        els[i_].parentNode.insertBefore(newNode, els[i_])
12        els[i].remove();
13        let wavesurfer = WaveSurfer.create({
14            container: document.getElementsByClassName("wave")[i_],
15            waveColor: '#909090',
16            progressColor: '#443e3c',
17            cursorColor: '#ffffff',
18            backend: 'MediaElement',
19            mediaControls: false,
20            hideScrollbar: true,
21            minPxPerSec: 120,
22            normalize: true,
23            height: 64,
24        });
25        wavesurfer.once('ready', function() {
26            console.log('Using wavesurfer.js ' + WaveSurfer.VERSION);
27        });
28        wavesurfer.on('error', function(e) {
29            console.warn(e);
30        });
31        wavesurfer.on('play', function(e) {
32            document.getElementsByClassName("playicon")[i_].style.display = "none";
33            document.getElementsByClassName("pauseicon")[i_].style.display = "block";
34        });
35        wavesurfer.on('pause', function(e) {
36            document.getElementsByClassName("playicon")[i_].style.display = "block";
37            document.getElementsByClassName("pauseicon")[i_].style.display = "none";
38        });
39        newNode.querySelector('[data-action="play"]')
40            .addEventListener('click', wavesurfer.playPause.bind(wavesurfer));
41        wavesurfer.load(src_);
42    }