Record and save audio from web – Javascript, Node JS, PHP

In this article, we are going to teach you, how you can record and save audio from web using Javascript. For saving audio files, we will teach you how you can do that with PHP and Node JS.

Let’s get started

First, we will create a folder named “record-audio-from-web”. Inside this folder, create a file named “index.html” and include fontawesome CDN in it. We will be displaying a microphone icon to start recording.

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />

Then we will create a microphone icon.

  1. Give it a unique ID so we can get it in Javascript.
  2. Attach an “onclick” listener that will be called whenever that icon is clicked.
  3. And set the cursor to pointer so user will know that this is clickable.
<i class="fa fa-microphone" id="icon-record-audio" onclick="recordAudio()" style="cursor: pointer;"></i>

Now in Javascript, we first have to create an instance of recorder.

let recorder = null

Get microphone permission

Then we will create that function that will be called when the microphone icon is clicked. This function will:

  1. Get the permission to access laptop’s microphone.
  2. Initialize a media recorder object.
  3. Start recording audio from microphone.
// function to record audio
async function recordAudio() {
    // get permission to access microphone
	navigator.permissions.query({name: 'microphone'})
		.then(function (permissionObj) {
			console.log(permissionObj.state)				
		})
		.catch(function (error) {
			console.log('Got error :', error);
		})

    // get recorder object
	recorder = await doRecordAudio()

    // start audio
	recorder.start()
}

Record audio

Then we will create “doRecordAudio” function that will handle all the recording and stoping mechanism.

function doRecordAudio() {
    return new Promise(function (resolve) {
        // get user audio media
        navigator.mediaDevices.getUserMedia({
            audio: true
        })
        .then(function (stream) {
            // [media recorder goes here]
        })
    })
}

After that, we need to create a media recorder object. Following code goes in the [media recorder goes here] section:

// create media recorder object
const mediaRecorder = new MediaRecorder(stream)

// save audio chunks in an array
const audioChunks = []
mediaRecorder.addEventListener("dataavailable", function (event) {
    audioChunks.push(event.data)
})

// [start and stop listener goes here]

This will create a media recorder object from stream and save all audio chunks (when available) in an array.

Then write the following code in [start and stop listener goes here] section to create start and stop listeners:

// create a start function
const start = function () {
    // [start listener goes here]
}

// create a stop function
const stop = function () {
    return new Promise(function (resolve) {
        // [stop listener goes here]
    })
}

// send the values back to promise
resolve({
    start,
    stop
})

This “start” function gets called from “recordAudio()” function.

Start recording audio

When the start function is called, we need to:

  1. Change the microphone icon to stop icon.
  2. Change onclick listener, so next time the icon is clicked, we will stop the recording.
  3. And play the audio.
  4. Tell the media to start recording.

Following code goes in the [start listener goes here] section:

// when recording starts, set the icon to stop
document.getElementById("icon-record-audio").className = "fa fa-stop-circle"

// on icon clicked
document.getElementById("icon-record-audio").onclick = async function() {
    // stop the recorder
    if (recorder != null) {
        const audio = await recorder.stop()

        // [play audio]

        // [get audio base64 string]
    }
}

// start media recorder
mediaRecorder.start()

If you test now, you will be able to record the audio. The microphone icon will be changed as well. But now you need to find a way to stop the recording.

Stop the recording

When you are done recording the audio, you can click the icon again to stop it. Following code goes in the [stop listener goes here] section. This will:

  1. Attach a listener that will be called when the recording is stopped.
  2. Change the icon back to microphone.
  3. Reset the onclick listener to its initial state.
  4. Convert the audio chunks array into a blob.
  5. Create URL object from blob.
  6. Create an audio object to play when required.
  7. And stop the media recorder.
// on recording stop listener
mediaRecorder.addEventListener("stop", function () {

    // change the icon back to microphone
    document.getElementById("icon-record-audio").className = "fa fa-microphone"

    // reset the onclick listener so when again clicked, it will record a new audio
    document.getElementById("icon-record-audio").onclick = async function() {
        recordAudio()
    }

    // convert the audio chunks array into blob
    const audioBlob = new Blob(audioChunks)

    // create URL object from URL
    const audioUrl = URL.createObjectURL(audioBlob)

    // create an audio object to play
    const audio = new Audio(audioUrl)
    const play = function () {
        audio.play()
    }

    // send the values back to the promise
    resolve({
        audioBlob,
        play
    })
})

// stop the media recorder
mediaRecorder.stop()

If you test now, you can record the audio and stop the audio. The icon will change from microphone to stop and then from stop to microphone.

Play the audio after recording

In order to play the audio you just recorded, write the following line in the [play audio] section:

audio.play()

“stop” function will return the promise containing the “new Audio” object and calling “play()” will play the audio.

Converting audio to base64

We are already sending audioBlob from “stop” function, so we can create base64 string from it. Write the following code in your [get audio base64 string] section:

// get audio stream
const reader = new FileReader()
reader.readAsDataURL(audio.audioBlob)
reader.onloadend = function() {
    // get base64
    let base64 = reader.result

    // get only base64 data
    base64 = base64.split(',')[1]

    // send base64 to server to save
    sendVoiceNote(base64)
}

This will create a base64 string fom audio.

Sending audio base64 to server

Now we will create a function that will call an AJAX to save this base64 string as audio.

function sendVoiceNote(base64) {
        // create an instance for AJAX
        var ajax = new XMLHttpRequest()

        // set request method as POST, set URL and set asynchronous to true
        ajax.open("POST", "http://localhost:8888/tutorials/record-audio-from-web/sendVoiceNote.php", true)

        // send base64 string to server
        const formData = new FormData()
        formData.append("base64", base64)
        ajax.send(formData)
}

Note: In your server, create a folder named “audios” where all audio files will be saved.

Saving audio file on server – PHP

First, we will teach you how can you save it using PHP. Create a file named “sendVoiceNote.php” and write the following code in it:

<?php

$base64 = $_POST["base64"];

$file_name = "audios/" . time() . ".webm";
file_put_contents($file_name, base64_decode($base64));

echo $file_name;

Try recording an audio again. When you stop the audio, you will see that a new file will be saved in your “audios” folder. You can listen to your audio in your favorite media player (VLC etc).

Saving audio file on server – Node JS

Now we will teach you, how you can save the file using Node JS. Create a Node JS project by running the following commands one-by-one:

> npm install express http express-formidable fs cors
> npm install -g nodemon
> nodemon server.js

Create a file named “server.js” and write the following code in it:

const express = require("express")
const app = express()

const expressFormidable = require("express-formidable")
app.use(expressFormidable())

const cors = require("cors")
app.use(cors())

const fileSystem = require("fs")
const http = require("http").createServer(app)

http.listen(3000, function () {
	app.post("/sendVoiceNote", async function (request, result) {
		const base64 = request.fields.base64

        const buffer = Buffer.from(base64, "base64")
        const voiceNote = "audios/" + new Date().getTime() + ".webm"
        await fileSystem.writeFileSync(voiceNote, buffer)

        result.send(voiceNote)
	})
})

In your client side, change the AJAX “open” function call from:

ajax.open("POST", "http://localhost/record-audio-from-web/sendVoiceNote.php", true)

To:

ajax.open("POST", "http://localhost:3000/sendVoiceNote", true)

So that’s it. That’s how you can record audio from web and save it in your server using Node JS or PHP. If you face any problem in following this, kindly do let me know.

Here is the complete code of all files:

index.html

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
<i class="fa fa-microphone" id="icon-record-audio" onclick="recordAudio()" style="cursor: pointer;"></i>

<script>

    let recorder = null

    function sendVoiceNote(base64) {
        // create an instance for AJAX
        var ajax = new XMLHttpRequest()

        // set request method as POST, set URL and set asynchronous to true
        // ajax.open("POST", "http://localhost:3000/sendVoiceNote", true)
        ajax.open("POST", "http://localhost/record-audio-from-web/sendVoiceNote.php", true)

        // whenever the status of request is changed
        // ajax.onreadystatechange = async function() {
        //     if (this.readyState == 4) {
        //         if (this.status == 200) {
        //             console.log(this.responseText)
        //         }

        //         if (this.status == 500) {
        //             console.log(this.responseText)
        //         }
        //     }
        // }

        // send base64 string to server
        const formData = new FormData()
        formData.append("base64", base64)
        ajax.send(formData)
    }

	function doRecordAudio() {
        return new Promise(function (resolve) {
            // get user audio media
            navigator.mediaDevices.getUserMedia({
                audio: true
            })
            .then(function (stream) {
                // create media recorder object
                const mediaRecorder = new MediaRecorder(stream)

                // save audio chunks in an array
                const audioChunks = []
                mediaRecorder.addEventListener("dataavailable", function (event) {
                    audioChunks.push(event.data)
                })

                // create a start function
                const start = function () {

                    // when recording starts, set the icon to stop
                    document.getElementById("icon-record-audio").className = "fa fa-stop-circle"

                    // on icon clicked
                    document.getElementById("icon-record-audio").onclick = async function() {
                        // stop the recorder
                        if (recorder != null) {
                            const audio = await recorder.stop()

                            // play the audio
                            audio.play()

                            // get audio stream
                            const reader = new FileReader()
                            reader.readAsDataURL(audio.audioBlob)
                            reader.onloadend = function() {
                                // get base64
                                let base64 = reader.result

                                // get only base64 data
                                base64 = base64.split(',')[1]

                                // send base64 to server to save
                                sendVoiceNote(base64)
                            }
                        }
                    }

                    // start media recorder
                    mediaRecorder.start()
                }

                // create a stop function
                const stop = function () {
                    return new Promise(function (resolve) {

                        // on recording stop listener
                        mediaRecorder.addEventListener("stop", function () {

                            // change the icon back to microphone
                            document.getElementById("icon-record-audio").className = "fa fa-microphone"

                            // reset the onclick listener so when again clicked, it will record a new audio
                            document.getElementById("icon-record-audio").onclick = async function() {
                                recordAudio()
                            }

                            // convert the audio chunks array into blob
                            const audioBlob = new Blob(audioChunks)

                            // create URL object from URL
                            const audioUrl = URL.createObjectURL(audioBlob)

                            // create an audio object to play
                            const audio = new Audio(audioUrl)
                            const play = function () {
                                audio.play()
                            }

                            // send the values back to the promise
                            resolve({
                                audioBlob,
                                play
                            })
                        })

                        // stop the media recorder
                        mediaRecorder.stop()
                    })
                }

                // send the values back to promise
                resolve({
                    start,
                    stop
                })
            })
        })
    }

    // function to record audio
	async function recordAudio() {
        // get permission to access microphone
		navigator.permissions.query({name: 'microphone'})
			.then(function (permissionObj) {
				console.log(permissionObj.state)				
			})
			.catch(function (error) {
				console.log('Got error :', error);
			})

        // get recorder object
		recorder = await doRecordAudio()

        // start audio
  		recorder.start()
	}
</script>

server.js

const express = require("express")
const app = express()

const expressFormidable = require("express-formidable")
app.use(expressFormidable())

const cors = require("cors")
app.use(cors())

const fileSystem = require("fs")
const http = require("http").createServer(app)

http.listen(3000, function () {
	app.post("/sendVoiceNote", async function (request, result) {
		const base64 = request.fields.base64

        const buffer = Buffer.from(base64, "base64")
        const voiceNote = "audios/" + new Date().getTime() + ".webm"
        await fileSystem.writeFileSync(voiceNote, buffer)

        result.send(voiceNote)
	})
})

sendVoiceNote.php

<?php

$base64 = $_POST["base64"];

$file_name = "audios/" . time() . ".webm";
file_put_contents($file_name, base64_decode($base64));

echo $file_name;