Grabación de audio en el navegador con la Web Audio API
La mayoría de los desarrolladores asume que la Web Audio API se encarga de la grabación. No es así, al menos no directamente. La Web Audio API es un motor de procesamiento y enrutamiento. La tarea real de grabación corresponde a la MediaRecorder API. Comprender esta distinción es la forma más rápida de dejar de construir lo equivocado.
Este artículo recorre la arquitectura recomendada actualmente para la grabación de audio en el navegador: capturar la entrada del micrófono con getUserMedia(), opcionalmente enrutar el audio a través de un grafo de Web Audio, y codificarlo con MediaRecorder.
Puntos clave
- La Web Audio API procesa y enruta audio, pero
MediaRecorderes lo que realmente lo graba en un archivo. - Una canalización completa de grabación tiene tres etapas: capturar con
getUserMedia(), opcionalmente procesar con nodos de Web Audio, y codificar conMediaRecorder. getUserMedia()requiere un contexto seguro (HTTPS o localhost) y un permiso explícito del usuario para acceder al micrófono.- Usa
AudioWorkletpara trabajos de DSP personalizados;ScriptProcessorNodeestá obsoleto y provoca fallos bajo carga. - Verifica siempre el soporte del tipo MIME con
MediaRecorder.isTypeSupported()antes de grabar, ya que Safari y Chrome difieren en los formatos soportados.
Cómo funciona realmente la grabación de audio en el navegador
La canalización moderna de grabación tiene tres etapas distintas:
- Captura —
getUserMedia()solicita acceso al micrófono y devuelve unMediaStream. - Procesamiento (opcional) — La Web Audio API inspecciona o transforma el flujo a través de nodos de audio.
- Grabación —
MediaRecordercodifica el flujo en un archivo.
No necesitas la Web Audio API para una grabación básica. Pero si quieres filtrado de ruido, control de ganancia, visualización o efectos personalizados mediante AudioWorklet, encaja perfectamente en el medio de esta cadena.
Un detalle sutil pero importante: MediaRecorder graba un MediaStream directamente. Si quieres grabar la salida procesada de un grafo de Web Audio, deberás enrutar el grafo de vuelta a un flujo usando audioContext.createMediaStreamDestination() y pasar la propiedad .stream de ese nodo a MediaRecorder.
Paso 1: Solicitar acceso al micrófono con getUserMedia
getUserMedia() requiere un contexto seguro (HTTPS o localhost) y permiso explícito del usuario antes de que el navegador conceda acceso al micrófono.
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: {
echoCancellation: true,
noiseSuppression: true,
sampleRate: 44100
}
});
} catch (err) {
console.error('Microphone access failed:', err.name, err.message);
}
Envuélvelo siempre en un try/catch. Los usuarios pueden denegar el permiso, y algunos entornos (como las páginas HTTP) rechazarán la llamada directamente con un NotAllowedError o SecurityError. Ten en cuenta que restricciones como sampleRate son sugerencias: los navegadores pueden ignorarlas según el hardware subyacente.
Paso 2: Enrutar a través de la Web Audio API (opcional)
Si necesitas analizar o procesar el audio antes de grabarlo, crea un AudioContext y canaliza el flujo a través de él:
const audioContext = new AudioContext();
const source = audioContext.createMediaStreamSource(stream);
// Example: connect to an AnalyserNode for visualization
const analyser = audioContext.createAnalyser();
source.connect(analyser);
// To record the processed output, route to a MediaStreamDestination
const destination = audioContext.createMediaStreamDestination();
analyser.connect(destination);
// destination.stream is what you pass to MediaRecorder
Para procesamiento personalizado —como un noise gate o un medidor de nivel en tiempo real— usa AudioWorklet en lugar del obsoleto ScriptProcessorNode. AudioWorklet se ejecuta fuera del hilo principal, lo que significa que no bloqueará tu interfaz ni descartará muestras de audio bajo carga.
Nota: En iOS Safari,
AudioContextcomienza en estado suspendido hasta que se activa mediante una interacción directa del usuario. Créalo (o llama aaudioContext.resume()) dentro del manejador de un clic de botón, no al cargar la página.
Discover how at OpenReplay.com.
Paso 3: Grabar y exportar con MediaRecorder
MediaRecorder toma un MediaStream y lo codifica. No fijes un tipo MIME directamente; verifica primero el soporte:
function pickMimeType() {
const candidates = [
'audio/webm;codecs=opus',
'audio/ogg;codecs=opus',
'audio/mp4'
];
return candidates.find(type => MediaRecorder.isTypeSupported(type)) || '';
}
const mimeType = pickMimeType();
const recorder = new MediaRecorder(stream, mimeType ? { mimeType } : undefined);
const chunks = [];
recorder.ondataavailable = (e) => {
if (e.data.size > 0) chunks.push(e.data);
};
recorder.onstop = () => {
const blob = new Blob(chunks, {
type: recorder.mimeType || mimeType || chunks[0]?.type || 'audio/webm'
});
const url = URL.createObjectURL(blob);
document.querySelector('audio').src = url;
};
recorder.start();
WebM/Opus suele ser la mejor opción por defecto para los navegadores modernos: archivos pequeños y excelente calidad. Safari a menudo prefiere audio/mp4 en su lugar. Recurrir a una lista de tipos candidatos y dejar que el navegador escoja el primero que soporte es la estrategia más fiable. Puedes verificar el soporte actual del navegador para MediaRecorder y los formatos relacionados en Can I Use.
MediaRecorder vs Web Audio API: referencia rápida
| Necesidad | Usar |
|---|---|
| Grabación simple de micrófono | getUserMedia + MediaRecorder |
| Efectos o filtros en tiempo real | Nodos de la Web Audio API |
| Procesamiento DSP personalizado | AudioWorklet |
| Visualización de audio | AnalyserNode |
| Codificación a un archivo | MediaRecorder |
Qué evitar
ScriptProcessorNode— obsoleto, se ejecuta en el hilo principal, provoca fallos de audio bajo carga.- Tipos MIME fijos en el código — fallan silenciosamente en Safari y versiones antiguas de Firefox.
- Crear
AudioContextal cargar la página — los navegadores lo suspenden hasta que ocurre un gesto del usuario, así que reanúdalo dentro de un manejador de eventos. - Olvidar detener el flujo — llama a
stream.getTracks().forEach(t => t.stop())cuando termines, o el indicador del micrófono permanecerá activo.
Conclusión
La grabación de audio en el navegador es tarea de dos APIs: getUserMedia() y MediaRecorder se encargan de la captura y codificación, mientras que la Web Audio API maneja todo lo intermedio. Empieza con la canalización más simple que cumpla tus requisitos, añade procesamiento de Web Audio solo cuando lo necesites y verifica siempre el soporte del tipo MIME antes de configurar MediaRecorder. Esa es toda la arquitectura en una sola frase.
Preguntas frecuentes
No. Para una grabación básica, getUserMedia() y MediaRecorder son suficientes. La Web Audio API solo es necesaria cuando necesitas procesar, analizar o transformar el audio antes de grabarlo, como aplicar filtros, construir visualizaciones o ejecutar DSP personalizado a través de un AudioWorklet.
Safari y Chrome soportan distintos formatos de grabación. Safari suele preferir audio/mp4, mientras que Chrome comúnmente usa WebM/Opus. Si fijas un tipo MIME no soportado, MediaRecorder puede lanzar un error o producir una salida inutilizable. Usa siempre MediaRecorder.isTypeSupported() para detectar un formato compatible en tiempo de ejecución.
Crea un MediaStreamAudioDestinationNode usando audioContext.createMediaStreamDestination(), conecta el nodo final de tu grafo de audio a él, y pasa su propiedad .stream a MediaRecorder. Esto captura el audio post-procesado en lugar del flujo crudo del micrófono que provino de getUserMedia().
iOS Safari requiere que AudioContext se cree o reanude dentro de un gesto directo del usuario, como un evento de clic o toque. Si lo instancias durante la carga de la página, permanece suspendido. Mueve la creación del AudioContext al manejador de un botón, o llama a audioContext.resume() dentro de uno para desbloquear la reproducción y el procesamiento.
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.