Capturing User Audio: From File Input to Live Microphone Access
Modern browsers offer a spectrum of options for capturing audio from users, ranging from simple file uploads to live, in-page microphone access. The right choice depends on the target platform and the desired user experience.
The File Input Approach: Broad Compatibility
The most universally compatible method is to request a pre-recorded file. A standard file input element with an accept filter restricted to audio files and a capture attribute hinting at microphone use handles this across all platforms.
<input type="file" accept="audio/*" capture />
Behavior varies by environment: on desktop, this prompts a standard file selection dialog; on iOS Safari, it launches the microphone app for recording before returning the file to the page; on Android, users choose from available recording apps.
Retrieving the recorded data requires an onchange event handler that reads the files property from the event object.
<input type="file" accept="audio/*" capture id="recorder" /> <audio id="player" controls></audio> <script> const recorder = document.getElementById('recorder'); const player = document.getElementById('player'); recorder.addEventListener('change', function (e) { const file = e.target.files[0]; const url = URL.createObjectURL(file); // Do something with the audio file. player.src = url; }); </script> </audio>
Once a file object is available, it can be used in several ways: attached directly to an <audio> element for playback, downloaded to the device, uploaded to a server via XMLHttpRequest, or processed through the Web Audio API for effects and analysis.
Interactive Microphone Access with getUserMedia
For experiences that keep users in the browser, the getUserMedia() API from the WebRTC specification provides a direct line to the microphone. The API prompts users for permission and returns a Stream object containing the audio data.
Setting audio: true in the constraints object requests microphone input.
<audio id="player" controls></audio> <script> const player = document.getElementById('player'); const handleSuccess = function (stream) { if (window.URL) { player.srcObject = stream; } else { player.src = stream; } }; navigator.mediaDevices .getUserMedia({audio: true, video: false}) .then(handleSuccess); </script>
To select a specific device, enumerate the available microphones first, then pass the desired deviceId in the constraints when calling getUserMedia.
navigator.mediaDevices.enumerateDevices().then((devices) => { devices = devices.filter((d) => d.kind === 'audioinput'); });
navigator.mediaDevices.getUserMedia({ audio: { deviceId: devices[0].deviceId, }, });
Processing Raw Audio Data
For raw data access, connect the stream from getUserMedia() to the Web Audio API. An AudioWorkletNode provides low-level custom audio processing through the process() callback method in an AudioWorkletProcessor.
<script> const handleSuccess = async function(stream) { const context = new AudioContext(); const source = context.createMediaStreamSource(stream); await context.audioWorklet.addModule("processor.js"); const worklet = new AudioWorkletNode(context, "worklet-processor"); source.connect(worklet); worklet.connect(context.destination); }; navigator.mediaDevices.getUserMedia({ audio: true, video: false }) .then(handleSuccess); </script>
// processor.js class WorkletProcessor extends AudioWorkletProcessor { process(inputs, outputs, parameters) { // Do something with the data, e.g. convert it to WAV console.log(inputs); return true; } } registerProcessor("worklet-processor", WorkletProcessor);
The buffers contain the raw microphone data, which can be uploaded to a server, stored locally, or converted to a file format like WAV for storage.
Saving Recorded Audio
The MediaRecorder API offers the most straightforward method for saving live microphone audio. It takes the stream from getUserMedia and progressively saves the data to a destination of your choice.
<a id="download">Download</a> <button id="stop">Stop</button> <script> const downloadLink = document.getElementById('download'); const stopButton = document.getElementById('stop'); const handleSuccess = function(stream) { const options = {mimeType: 'audio/webm'}; const recordedChunks = []; const mediaRecorder = new MediaRecorder(stream, options); mediaRecorder.addEventListener('dataavailable', function(e) { if (e.data.size > 0) recordedChunks.push(e.data); }); mediaRecorder.addEventListener('stop', function() { downloadLink.href = URL.createObjectURL(new Blob(recordedChunks)); downloadLink.download = 'acetest.wav'; }); stopButton.addEventListener('click', function() { mediaRecorder.stop(); }); mediaRecorder.start(); }; navigator.mediaDevices.getUserMedia({ audio: true, video: false }) .then(handleSuccess); </script>
This example accumulates data into an array, which can later be converted into a Blob for server upload or local device storage.
Managing Permission Requests
Calling getUserMedia() triggers a permission prompt in the browser if none has been granted previously. Users often ignore or block these requests when the context is unclear. Best practice is to request microphone access only at the moment it is actually needed. Once granted, access is permanent; subsequent denials cannot be re-prompted.
Checking Permission Status with the Permissions API
The getUserMedia() API itself does not reveal whether access was already granted, which complicates UI design for permission requests. The Permission API solves this by letting you query access state without triggering a prompt. Passing {name: 'microphone'} to the query method returns one of three states:
granted— prior access has been establishedprompt— access will be requested whengetUserMediais calleddenied— access has been explicitly blocked and cannot be obtained
navigator.permissions.query({name: 'microphone'}).then(function (result) { if (result.state == 'granted') { } else if (result.state == 'prompt') { } else if (result.state == 'denied') { } result.onchange = function () {}; });
This allows the user interface to adapt proactively, explaining context when a prompt will appear or providing guidance when access is already established.



