Build an Audio Recorder with Howler.js
This article provides a step-by-step tutorial on how to build a web-based audio recorder interface. Because Howler.js is designed exclusively for audio playback, you will learn how to combine the browser’s native MediaRecorder API to capture audio with Howler.js to manage and play back your recorded clips.
Understanding the Architecture
Howler.js does not have built-in features to access a user’s microphone or record audio input. To create an audio recorder, you must use a two-part system: 1. The MediaStream Recording API: A native browser API used to capture the audio stream from the user’s microphone and compile it into a data blob. 2. Howler.js: A robust audio library used to load the recorded data blob as a source URL and handle the playback controls (play, pause, stop, volume).
Step 1: Create the HTML Interface
First, set up a simple user interface with three buttons: one to start recording, one to stop recording, and one to play back the captured audio. Include the Howler.js library via CDN.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Audio Recorder</title>
<!-- Include Howler.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.2.3/howler.min.js"></script>
</head>
<body>
<div id="recorder-container">
<button id="record-btn">Start Recording</button>
<button id="stop-btn" disabled>Stop Recording</button>
<button id="play-btn" disabled>Play Recording</button>
</div>
<script src="app.js"></script>
</body>
</html>Step 2: Implement the JavaScript Logic
In your app.js file, you need to request microphone
access, record the incoming audio data chunks, convert those chunks into
a playable URL, and initialize Howler.js with that URL.
let mediaRecorder;
let audioChunks = [];
let playbackSound = null;
const recordBtn = document.getElementById('record-btn');
const stopBtn = document.getElementById('stop-btn');
const playBtn = document.getElementById('play-btn');
// 1. Request microphone access and start recording
recordBtn.addEventListener('click', async () => {
audioChunks = []; // Clear previous recordings
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) {
audioChunks.push(event.data);
}
};
mediaRecorder.onstop = () => {
// Convert audio chunks into a single Blob
const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
// Create a local URL pointing to the Blob
const audioUrl = URL.createObjectURL(audioBlob);
// Initialize Howler.js with the Blob URL
playbackSound = new Howl({
src: [audioUrl],
format: ['webm'],
html5: true // Forces HTML5 Audio, necessary for blob URLs in some browsers
});
playBtn.disabled = false;
};
mediaRecorder.start();
recordBtn.disabled = true;
stopBtn.disabled = false;
playBtn.disabled = true;
} catch (err) {
console.error('Microphone access denied or not supported:', err);
}
});
// 2. Stop the recording
stopBtn.addEventListener('click', () => {
mediaRecorder.stop();
// Stop tracks to release the microphone hardware
mediaRecorder.stream.getTracks().forEach(track => track.stop());
recordBtn.disabled = false;
stopBtn.disabled = true;
});
// 3. Play the recording using Howler.js
playBtn.addEventListener('click', () => {
if (playbackSound) {
playbackSound.play();
}
});Key Considerations
- HTTPS Requirement: Modern browsers restrict
microphone access (
getUserMedia) to secure contexts. Your application must be served overhttps://orlocalhostto function. - MIME Types: This implementation uses
audio/webmwhich is widely supported in modern browsers like Chrome and Firefox. For Safari compatibility, you may need to check supported MIME types (such asaudio/mp4) usingMediaRecorder.isTypeSupported(). - Memory Management: Every time a recording is made,
URL.createObjectURL()allocates memory. If your application records repeatedly in a single session, make sure to callURL.revokeObjectURL(audioUrl)on old recordings to prevent memory leaks.