Recording mp3 audio in HTML5 using vmsg - a WebAssembly library based on LAME

This is the 3rd post in our new recording audio in HTML5 series. We’ve previously covered how to use Recorder.js to capture uncompressed pcm audio and WebAudioRecorder.js to capture pcm, mp3 and Vorbis audio in the browser.

We’ll now briefly look at vmsg.

vmsg is very similar to WebAudioRecorder.js in that it uses a compiled version of the LAME mp3 encoding library but they differ in the way that library was compiled:

  • Web Audio Recorder used a JavaScript library compiled from LAME’s C code to a subset of JavaScript called asm.js.
  • vmsg uses the same C based LAME mp3 encoding library but compiled to a newer WebAssembly library which, in theory, should be faster than the asm.js one and smaller (73KB vs 114 KB gzipped).
  • vmsg uses the latest version of the LAME library (3.100 from October 2017)

But encoding mp3 is very fast on modern CPUs – even when using the asm.js version of the LAME mp3 encoder – so choosing between Web Audio Recorder and vmsg comes down to ease of use, browser support, whether or not the library is still maintained and personal preference.

vmsg also differs in that:

  1. it includes a recording UI/modal with start/stop buttons + gain and pitch sliders
  2. it handles the getUserMedia() code required to access the microphone on desktop and mobile devices

Building a simple demo

Since vmsg’s 3 official demos (live demo, live coding demo, demo folder) are all React based we’re going to set up a simple JS/HTML demo.

Our vmsg mp3 audio recording demo running on Safari/iOS11

You can check out our demo live here, the code is on GitHub.

We’ll start off with a simple index.html that includes app.js as an ES6 module and vmsg.css which contains CSS classes for vmsg’s UI.

<title>Simple vmsg audio recording demo</title>
<link rel="stylesheet" type="text/css" href="vmsg.css">
<script src="app.js" type="module"></script>
<button id="recordButton">Record</button>  

Module scripts are deferred so even though app.js is included at the beginning it will not execute until the document has finished parsing thus the recordButton element will be available in app.js.

Here’s the app.js code:

import {
    record
} from "./vmsg.js";
let recordButton = document.getElementById("recordButton");
recordButton.onclick = function() {
    record({
        wasmURL: "vmsg.wasm"
    }).then(blob => {
        console.log("Recorded MP3", blob);
        var url = URL.createObjectURL(blob);
        var preview = document.createElement('audio');
        preview.controls = true;
        preview.src = url;
        document.body.appendChild(preview);
    });
};

The app.js module imports the record function from vmsg.js and sets up the onclick event function for the Record button. When the button is clicked the record() function is executed and the vmsg UI shows up, a modal that greyes out everything else:

The vmsg UI includes sliders for pitch and gain

The record() function receives the path to the vmsg.wasm WebAssembly library through the wasmURL object property. If omitted the default path is /static/js/vmsg.wasm. No other properties but the path to a webassembly polyfill (shimURL) and the pitch (pitch, values between -1 and 1) can be sent.

For this demo we’ve placed everything in the same folder:

After recording, when you click the top right checkmark, the blob is made available to the app.js code. In our code, we add it as an <audio> element in the HTML page for playback but you could do anything with it including POST-ing it to a server side upload script.

To avoid 1 HTTP call we could have placed the code in app.js inline in index.html like this:

 <script type="module"> //app.js code </script>

Important: Make sure your web server delivers .wasm files with the MIME type application/wasm like this:

Otherwise, Chrome will throw an error

> Error: TypeError: Failed to execute ‘compile’ on ‘WebAssembly’: Incorrect response MIME type. Expected ‘application/wasm’.

On Apache you can easily configure the correct MIME type for .wasm files by adding the following line to a .htaccess file located in the same folder or in the root folder of the web server.

AddType application/wasm .wasm

Since we’re using ES6 modules and WebAssembly this demo and library will only work on the latest browsers:

Desktop:

  • Edge 16
  • Firefox 60
  • Chrome 61
  • Safari 11

Mobile:

  • Safari 11 on iOS
  • Chrome 61 on Android

More details: ES6 modules, WebAssembly.

The vmsg library author also provides a wasm polyfill which will makes the library work on much older desktop browsers.

Quality wise the vmsg library is encoding mono mp3 audio at the sample rate set in your OS for your playback device (as per the spec). In practice you’ll most commonly see 44100 samples/second (44.1kHz) or 48000 samples/second (48kHz)

Personally, I would have loved to see the vmsg wasm API separated from the UI and the getUserMedia() code.

Further reading: