Modern browsers include sophisticated media handling capabilities for audio and video media. This article aims to show you how to use the MediaRecorder to record short audio clips, and includes a simple audio recorder with playback.

The basics

Audio processing revolves around the concept of a stream. A stream can have a number of sources, but for this article we'll be using the system microphone.

System peripherals are necessarily subject to strict security. Nobody wants to have their private conversations secretly recorded by a rogue web site. Hence, the microphone and camera can only be used if the script that uses them is hosted on the same machine, or a secure web site served over HTTPS://

Similarly, the recorded media must be saved to the hosting server, or downloaded to the local computer through the familiar download dialog box.

Accessing the microphone

Browsers provide the navigator.mediaDevices object which Javascript can use to request access to system devices. You can check for its availability with (if (navigator.mediDevices){...}

A call to navigator.mediaDevices.getUserMedia() returns a promise that resolves to a media stream that we can use to record Audio. It's this call that raises the prompt that the user sees asking for permission to use the microphone or camera. A constraints object controls what permissions are requested.

The use of await allows us to run the code as if it is synchronous, and wait for the user to grant permission.

let constraints = {audio:true, video:false};
let stream = await navigator.mediaDevices.getUserMedia(constraints);

Setting up the recorder

Now that we have a stream, we can record it. Just call the MediaRecorder constructor with the stream as the first parameter, and supply any further options in an options object as the second parameter.

let  mediaOptions = {
    mimetype:'audio/mpeg'
};
let mRec = new MediaRecorder(stream, mediaOptions);

Recording

We've set up our MediaRecorder. Now we need to start it and save the data it records. The demonstration code I've included uses the dataavailable event to save the recording periodically, before assembling it into a complete Blob when the recording finishes. The frequency with which this happens is set when the recorder is started.

the data is provided to the event handler in the data property of the event object. We can just push that into an array for later handling.

let chunks = [];
  mRec.addEventListener('dataavailable', (e)=>{
            console.log('Data available');
            chunks.push(e.data);
        });
  

At the end of the recording

When the recording stops the MediaRecorder fires the stop event, where we can assemble the saved chunks into a completed Blob, and offer the user a version to save

mRec.addEventListener('stop', ()=>{
    console.log('Recording stopped');
    recordedSound = new Blob(chunks, {type:mRec.mimeType});

    let dURL = URL.createObjectURL(recordedSound);
    let downloadLink = document.createElement('a');
    downloadLink.href = dURL;
    downloadLink.download = "demo.mp3";
    downloadLink.style.display= "none";
    document.querySelector('body').append(downloadLink);
    downloadLink.click();

});

The complete code

To make all this work we need to add a button to start recording, and wrap everything in an immediately-invoke function expression (IIFE) to prevent any pollution of the global workspace.

<!DOCTYPE html>
<html lang="en">
<head
    <meta charset="UTF-8"
    <title>Demo audio recorder</title>
</head>
<body>
<input type="button" id="recordButton" value="Record">
<script>
    (function(){
        "use strict"
        let recordedSound;
        let chunks = [];
        let mRec;
        let stream;

        // Wait for a click on the record button
        document.getElementById('recordButton').addEventListener('click',async function(){
            console.log("Record button clicked");
            // If this is the first time in, set up the Media Recorder.
            if (!mRec) {
                // Check that the MediaRecorder interface is available/
                if (!navigator.mediaDevices || !MediaRecorder) {
                    alert('mediaDevices not supported.');
                    return;
                }
                console.log("Media recorder available");
                stream = await navigator.mediaDevices.getUserMedia({audio: true, video: false});
                mRec = new MediaRecorder(stream);

                // Handle the dataavailable event - store the available data in an array
                mRec.addEventListener('dataavailable', (e) => {
                    console.log('Data available');
                    chunks.push(e.data);
                });

                /** Handle the stop event.
                 *    Reset the record button,
                 *    Assemble the stored audio data into blob
                 *    Create an (invisible) <a> element to action the download
                 *    Click the <a> element to initiate the download.
                 */
                mRec.addEventListener('stop', () => {
                    console.log('Recording stopped');
                    document.getElementById("recordButton").value = "Record";

                    // Assemble the stored chunks into a blob and clear the saved data
                    recordedSound = new Blob(chunks);
                    chunks = [];

                    // Package up the blob and present it for download
                    let dURL = URL.createObjectURL(recordedSound);
                    let downloadLink = document.createElement('a');
                    downloadLink.href = dURL;
                    downloadLink.download = "demo.webm";
                    downloadLink.style.display = "none";
                    document.querySelector('body').append(downloadLink);
                    downloadLink.click();
                });
            }
                // change the button label
                this.value = 'Recording';
                // set a five second timer to limit the recording length
                setTimeout(()=>{mRec.stop()}, 5000 );
                // Start recording in 100ms chunks
                mRec.start(100);

            });
    })();

</script>
</body>
</html>

Try it!

Click the button to record five seconds of audio and download it

Get the code!

The download includes this demo along with a more complete demonstration with Start, Stop, Play and Save functionality. The code was developed and tested in current versions of Firefox, Chrome and Edge. Internet Explorer does not support the Media APIs

Questions or comments?

Contact me!