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.
- Give it a unique ID so we can get it in Javascript.
- Attach an “onclick” listener that will be called whenever that icon is clicked.
- 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:
- Get the permission to access laptop’s microphone.
- Initialize a media recorder object.
- 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:
- Change the microphone icon to stop icon.
- Change onclick listener, so next time the icon is clicked, we will stop the recording.
- And play the audio.
- 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:
- Attach a listener that will be called when the recording is stopped.
- Change the icon back to microphone.
- Reset the onclick listener to its initial state.
- Convert the audio chunks array into a blob.
- Create URL object from blob.
- Create an audio object to play when required.
- 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;