javascript

Comandos de voz en la web con JavaScript y Annyang

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:

https://unpkg.com/annyang

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

Página web para demostrar reconocimiento de voz y comandos de voz en la web con JavaScript

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í.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

1 semana hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

2 semanas hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

2 semanas hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

2 semanas hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

2 semanas hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

2 semanas hace

Esta web usa cookies.