Resumen: mostrar cómo controlar elementos en una página web usando la voz; es decir, implementar el reconocimiento de voz con JS para definir comandos de voz y ejecutar acciones (enviar un correo, mostrar un reporte, mostrar una gráfica, escribir texto) dependiendo de la acción.
Para el reconocimiento de acciones de voz en la web (o mejor dicho, en el navegador con JavaScript) vamos a usar la librería annyang que internamente utiliza la API de speech recognition.
Puedes ver lo que construiremos al final en este enlace.
La librería annyang
Como lo dije, vamos a usar esta librería pues funciona perfectamente y provee un modo de definir comandos en la web. Puedes ver más sobre la misma en su repositorio.
Debido a que utiliza la API de reconocimiento de voz, necesita acceder al micrófono. Y para acceder al micrófono se necesita estar en localhost o en un dominio con https.
Comandos de voz
El uso es simple. Cargamos el script de annyang, definimos una lista de comandos e invocamos a setCommands
. Después, llamamos a start
.
Veamos un ejemplo sencillo:
let comandos = {
"hola": () => {
console.log("Dijiste hola");
}
};
annyang.addCommands(comandos);
annyang.start();
Como ves, los comandos son un diccionario en donde la clave es el comando, es decir, lo que el usuario dice. Y el valor es la función (usando la sintaxis de flecha en este caso); lo que se ejecuta cuando se detecta ese comando.
Al invocar a start
se comienza a detectar la voz y en cada resultado se compara si algo que el usuario dijo coincide con un comando.
Dentro de la función puedes hacer casi cualquier cosa que harías en otro evento, pero la diferencia es que ahora es controlado por la voz.
Conseguir librería
Para descargar la librería puedes usar unpkg. Ve al siguiente URL:
Eso te dará la última versión estable. En mi caso es:
https://unpkg.com/annyang@2.6.1/dist/annyang.min.js
Ahora hay que descargar el script e incluirlo en el head
:
<head>
<script src="annyang.min.js"></script>
</head>
Soporte
Si quieres saber si el navegador soporta el reconocimiento de voz simplemente verifica si la variable global annyang está definida, o mejor dicho, no es null
ni undefined
.
Para ello puedes hacer algo como:
if (!annyang) {
alert("Lo siento, tu navegador no soporta el reconocimiento de voz :(");
}
Recuerda incluir antes el script de la librería, pues si no lo incluyes, la variable estará igualmente no definida.
El idioma
Podemos definir el idioma que el usuario hablará, para que la detección sea mejor. Para ello invocamos a annyang.setLanguage
así:
annyang.setLanguage("es-MX");
Los idiomas puedes verlos aquí.
Callbacks
annyang soporta varios callbacks que nos informan cuando algo sucede. Por ejemplo, cuando se deniegan permisos, se conceden, etcétera.
Hay un callback interesante y es el que captura todo lo que se ha reconocido en el micrófono. Es útil para saber lo que el motor reconoce o para depurar las cosas.
El callback es result
, y lo definimos invocando a annyang.addCallback("nombre", función)
por ejemplo:
annyang.addCallback("result", frases => {
console.log("El arreglo de frases: ", frases);
});
Más comandos
Además de un simple comando estático, podemos capturar variables. Las mismas se definen con *
al definir el comando. Veamos el siguiente ejemplo:
let comandos = {
"mi nombre es *nombre y tengo *anios años": (nombre, anios) => {
console.log(`Hola ${nombre} es genial que tu edad sea ${anios} :)`);
}
}
Así estamos capturando a *nombre
y *anios
, mismas variables que recibimos en la función. De este modo agregamos todavía más potencial a nuestras aplicaciones web.
Ejemplo y demostración
Ahora que he mostrado lo básico podemos pasar a la demostración. Voy a usar los estilos de Bootstrap y definiré dos párrafos.
El primero tendrá mensajes indicando que un comando fue reconocido, y el segundo tendrá todo lo escuchado sin importar si coincide o no con un comando.
<!doctype html>
<html lang="es">
<!--
Manejar la web con comandos de voz usando JavaScript y la librería Annyang
https://parzibyte.me/blog
-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1,
shrink-to-fit=no">
<meta name="description" content="Comandos de voz en la web">
<meta name="author" content="Parzibyte">
<title>Comados de voz en la web con Annyang</title>
<link
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous">
<script src="annyang.min.js"></script>
</head>
<body>
<main role="main" class="container-fluid">
<div class="row">
<div class="col-12">
<h1>Comandos de voz en la web con JavaScript y Annyang</h1>
<a href="//parzibyte.me/blog" target="_blank">By Parzibyte</a>
<p>
<strong>Intenta con:</strong>
<br>Hola
<br>Reporte de ventas de {mes}
<br>Enviar correo a {dirección}
<br>Mi nombre es {Nombre} y tengo {años} años
</p>
</div>
<div class="col-6">
<h2>Comandos reconocidos</h2>
<div class="alert alert-success">
<p id="comandosReconocidos"></p>
</div>
</div>
<div class="col-6">
<h2>Voz escuchada, sin reconocer</h2>
<div class="alert alert-info">
<p id="vozDetectada">
</p>
</div>
</div>
</div>
</main>
<script src="script.js"></script>
</body>
</html>
En el encabezado estoy cargando annyang, y en el pie estoy cargando el script que manejará todo.
Ahora veamos el script. En el mismo espero al evento DOMContentLoaded y compruebo que annyang funcione. Después defino algunos elementos del DOM obtenidos con querySelector para loguear las acciones e indico los comandos con la respectiva acción.
Adicional a ello también agrego un callback para cuando haya resultados, los cuales también logueo (como es un arreglo, el cual convierto a cadena usando join
)
document.addEventListener("DOMContentLoaded", function() {
if (!annyang) {
return alert("Lo siento, tu navegador no soporta el reconocimiento de voz :(");
}
const $comandosReconocidos = document.querySelector("#comandosReconocidos"),
$vozDetectada = document.querySelector("#vozDetectada");
const loguearComandoReconocido = contenido => {
$comandosReconocidos.innerHTML += contenido + "<br>";
};
const loguearVozDetectada = contenido => {
$vozDetectada.innerHTML += contenido + "<br>";
};
annyang.setLanguage("es-MX");
let comandos = {
"hola": () => {
loguearComandoReconocido(`Hola mundo!`);
},
"reporte de ventas de *mes": mes => {
if ("enero,febrero,marzo,abril,mayo,junio,julio,agosto,septiembre,octubre,noviembre,diciembre".split(",").indexOf(mes.toLowerCase()) === -1) {
return;
}
loguearComandoReconocido(`Ok te muestro el reporte de ventas de ${mes}`);
},
"enviar correo a *usuario": usuario => {
let usuarioCorregido = usuario.replace(/\ /g, "").replace(/arroba/g, "@").toLowerCase();
loguearComandoReconocido(`Originalmente es ${usuario} pero creo que el correcto es ${usuarioCorregido}`);
},
"mi nombre es *nombre y tengo *anios años": (nombre, anios) => {
loguearComandoReconocido(`Hola ${nombre} es genial que tu edad sea ${anios} :)`);
}
};
annyang.addCommands(comandos);
annyang.addCallback("result", frases => {
loguearVozDetectada(`<strong>Probablemente has dicho: </strong> <br> ${frases.join("<br>")}`);
});
annyang.start();
});
Vemos dos casos especiales en la línea 28 y 22.
En el primer caso tengo un comando para enviar un correo electrónico, pero al decir arroba, el motor lo reconoce como arroba
y no como @
por lo que hago un remplazo de cadenas; del mismo modo elimino los espacios.
En el segundo caso hago una validación de que el mes del reporte de ventas sea un mes válido; ya que el mes será una variable y capturará cualquier palabra dicha.
Aunque parezca complicado, no lo es. Y con unas simples validaciones podemos armar un reconocedor potente.
Nota: annyang ya proporciona algunos modos de usar expresiones regulares, puedes ver la documentación para probarlo.
Notas finales
Recuerda que esta librería tiene las mismas restricciones que la API de reconocimiento de voz. No olvides que puedes probar la demo en este enlace, y ver el código completo aquí.
La documentación oficial está en GitHub. ¿Más sobre JavaScript moderno? click aquí.