Audio Recorder
This example shows the implementation of a web application for recording audio from your device's microphone. The full cycle of the application is described below.
- The user clicks the 🎤 → button to start recording
- During recording:
- The button turns red with animation
- The timer is running
- Audio data is being collected
 
- When stopping:
- An audio
 file is formed - A player and a download button appear
- Downloading creates the file "recording-{date}.wav"
A detailed analysis of the audio recorder code is provided below. The code starts with a basic HTML structure and CSS styles for a beautiful interface.:
<div class="container">
    <div class="title">Audio Recorder</div>
    <div class="timer" id="timer">00:00</div>
    <button id="recordBtn">🎤</button>
    <button class="download-btn hidden" id="downloadBtn" title="Download recording">↓</button>
    <div id="status">Press 🎤 to start recording</div>
    <div class="audio-container" id="audioContainer">
        <audio id="audioPlayer" controls></audio>
    </div>
</div>
Key elements:
- recordBtn– round record button,
- downloadBtn– download button (initially hidden),
- timer– displays the recording time,
- audioPlayer– an element for playing recorded audio.
Next comes the initialization of variables
let mediaRecorder;
let audioChunks = [];
let audioBlob;
let audioUrl;
let startTime;
let timerInterval;
let stream;
let isRecording = false;
Variables store the application state:
- mediaRecorder– an object for recording media,
- audioChunks– an array for storing recorded data,
- audioBlob/- audioUrl– final audio,
- timerInterval– to update the timer,
- stream– data stream from the microphone.
After that, auxiliary functions are declared.
function formatTime(seconds) {
    const mins = Math.floor(seconds / 60).toString().padStart(2, '0');
    const secs = Math.floor(seconds % 60).toString().padStart(2, '0');
    return `${mins}:${secs}`;
}
function updateTimer() {
    const elapsed = Math.floor((Date.now() - startTime) / 1000);
    timerElement.textContent = formatTime(elapsed);
}
formatTime converts seconds to the "MM:SS" format, and updateTimer updates the timer display.
Next comes the recording startup function.
function startRecording() {
    navigator.mediaDevices.getUserMedia({ audio: true })
        .then(function(s) {
            stream = s;
            mediaRecorder = new MediaRecorder(stream);
            audioChunks = [];
            
            mediaRecorder.ondataavailable = function(e) {
                if (e.data.size > 0) {
                    audioChunks.push(e.data);
                }
            };
            
            mediaRecorder.onstop = function() {
                audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
                audioUrl = URL.createObjectURL(audioBlob);
                audioPlayer.src = audioUrl;
                downloadBtn.classList.remove('hidden');
                statusElement.textContent = "Recording saved! Click 🎤 to record again";
            };
            
            mediaRecorder.start(100);
            startTime = Date.now();
            timerInterval = setInterval(updateTimer, 1000);
            updateTimer();
            isRecording = true;
            recordBtn.classList.add('recording');
            recordBtn.textContent = "⏹️";
            statusElement.textContent = "Recording... Click ⏹️ to stop";
        })
        .catch(function(err) {
            console.error('Error accessing microphone:', err);
            statusElement.textContent = "Error accessing microphone";
        });
}
The logic of the operation is described below.
- Request access to the microphone via getUserMedia
- Create MediaRecorderto record a stream
- Setting up handlers:
- ondataavailable– collects pieces of audio,
- onstop– creates the final file and shows the download button.
 
- Start the timer and change the interface
After that, the recording stop is implemented.
function stopRecording() {
    if (mediaRecorder && mediaRecorder.state !== 'inactive') {
        mediaRecorder.stop();
        clearInterval(timerInterval);
        stream.getTracks().forEach(track => track.stop());
        isRecording = false;
        recordBtn.classList.remove('recording');
        recordBtn.textContent = "🎤";
    }
}
Stops recording, timer, and releases microphone resources.
And downloading the recording:
function downloadRecording() {
    if (audioUrl) {
        const a = document.createElement('a');
        a.href = audioUrl;
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        a.download = `recording-${timestamp}.wav`;
        a.click();
        statusElement.textContent = "Download started...";
    }
}
Creates a temporary download link for a file with a unique name.
There is also one event handler that allows the recording buttons to work as a start/stop switch, and also makes it possible to link the entire interface and its update to the audio file recording event.
recordBtn.addEventListener('click', function() {
    if (isRecording) {
        stopRecording();
    } else {
        startRecording();
    }
});
downloadBtn.addEventListener('click', downloadRecording);
Now let's launch the app itself and see how it works.
display("text/html", read("audio_recorder.html", String))
Conclusion
This example and the tool itself can be useful for generating test data samples for DSP.