Escáner QR HTML5
El escáner que funciona y la explicación de la API para quien lo va a construir.
La cámara solo se pide al pulsar el botón. Los fotogramas se descifran en este dispositivo y se descartan.
Un escáner HTML5 son tres API del navegador apiladas: getUserMedia para obtener el flujo de cámara, canvas para muestrear fotogramas y un decodificador. El decodificador es el BarcodeDetector nativo si existe, y si no, el decodificador JavaScript incluido. No hay plugins ni código nativo.
Esta página sirve como herramienta y como explicación porque mucha gente que busca esto va a construir el suyo y quiere saber dónde está la parte crítica.
Cómo se usa
- Si solo quieres leer un código, usa el escáner de arriba.
- Si vas a implementarlo, lee el orden de las API más abajo.
- Antes de elegir decodificador para producción, revisa las notas de compatibilidad.
El orden de implementación
getUserMedia({ video: { facingMode: 'environment' } }) para obtener el flujo, conectarlo a un elemento video y añadir playsinline: sin eso, Safari en iOS abre el vídeo a pantalla completa. Después se dibuja en un canvas a unas 8-10 fps y se pasa el canvas al decodificador. Al acertar, se detienen las pistas.
La trampa está en la resolución: pasar fotogramas a 1080p es lento y no mejora la precisión frente a 640 px de ancho. Reduce antes y decodifica a intervalos, no en cada requestAnimationFrame.
Revisado el 2026-10-21. Esta herramienta funciona por completo en tu navegador: sin servidor, sin cuenta y sin guardar nada de lo que generas.
Preguntas frecuentes
¿Hace falta una librería como html5-qrcode?
No es obligatorio. Una librería asume las diferencias entre navegadores y la interfaz; vale la pena si necesitas muchos formatos, pero para QR solo, BarcodeDetector con respaldo jsQR son unas decenas de líneas y mucho menos peso.
¿Por qué getUserMedia lanza una excepción en mi sitio?
Necesita un contexto seguro (HTTPS o localhost). En HTTP plano, navigator.mediaDevices es undefined, y esa es la causa más habitual.
¿Cómo se apaga la cámara al terminar?
Con stream.getTracks().forEach(t => t.stop()). Si el usuario puede salir a mitad, escucha pagehide y libera ahí también.
Herramientas relacionadas
Más de Códigos QR.