Icône de micro avec un visuel de code
Frontend
13 minutes

API Web Speech : de la reconnaissance vocale dans ton projet

Publié le

Intègre facilement de la reconnaissance vocale à ton application web grâce à l’API Web Speech : transforme la parole en texte en quelques lignes de JavaScript.

Dans le dernier article, nous avons découvert comment intégrer de la synthèse vocale à nos applications web grâce à l'API Web Speech. Aujourd’hui, on s’attaque à l’autre versant de cette API : la reconnaissance vocale.

Bien que son support soit plus limité que celui de la synthèse vocale, l’API SpeechRecognition ouvre la voie à des interactions plus fluides et naturelles entre l’utilisateur et ton application.

Dans cet article, nous allons voir comment implémenter cette fonctionnalité, explorer ses cas d’usage dans le monde réel, et identifier les limites techniques à connaître avant de se lancer.

Tu veux intégrer le contrôle vocal pour enrichir l’expérience utilisateur ? Alors, c’est parti !

Retour sur l'API Web Speech

L’API Web Speech est un standard du W3C qui offre deux fonctionnalités principales pour donner une dimension vocale aux applications web :

  • La synthèse vocale avec SpeechSynthesis
  • La reconnaissance vocale avec SpeechRecognition

Alors que la synthèse vocale est aujourd’hui largement supportée par les navigateurs, la reconnaissance vocale reste une fonctionnalité plus expérimentale : son support est principalement assuré par Chrome et quelques navigateurs basés dessus, tandis que d’autres navigateurs limitent ou n’intègrent pas encore cette interface.

Malgré ces limitations, SpeechRecognition offre des possibilités intéressantes : elle peut détecter plusieurs langues, gérer des sessions d’écoute continues ou ponctuelles, et même reconnaître des commandes vocales spécifiques, ce qui la rend très utile pour créer des interfaces vocales intuitives.

Mettre en place la reconnaissance vocale

Pour découvrir comment fonctionne cette API, nous allons mettre en place deux exemples simples mais représentatifs de nombreux cas d’usage.

Transcription en temps réel

Pour ce premier exemple, nous allons nous contenter de transcrire mot à mot les paroles de l'utilisateur dans un paragraphe.

Commençons par vérifier que le navigateur de l'utilisateur prend en charge la reconnaissance vocale :

const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;

if (SpeechRecognition) {
    const speechRecognition = new SpeechRecognition();
} else {
    console.log('SpeechRecognition is not supported');
}

Ensuite, nous allons configurer trois options pour adapter le comportement de la reconnaissance à notre besoin :

  • lang : définit la langue de reconnaissance, au format BCP 47 (fr-FR, en-US, etc.).
  • continuous : permet de maintenir l’écoute active même après une pause dans la dictée.
  • interimResults : active les résultats intermédiaires pour une transcription plus réactive.
const speechRecognition = new SpeechRecognition();
speechRecognition.lang = 'fr-FR';
speechRecognition.continuous = true;
speechRecognition.interimResults = true;

Nous devons maintenant écouter l'événement result pour traiter les résultats de la reconnaissance en temps réel :

const paragraphElement = document.getElementById('result');
speechRecognition.addEventListener('result', e => {
    let transcription = '';

    for (const result of e.results) {
        transcription += result[0].transcript;
    }

    paragraphElement.innerText = transcription;
});

À chaque fois que l’événement result est déclenché, on parcourt les résultats pour reconstruire l’ensemble de la transcription depuis le début, et on l’affiche dans un paragraphe HTML.

💡 Pour des questions de performance, nous pourrions être tenté de n’ajouter que le dernier résultat à la transcription. Cependant, avec interimResults: true, les segments sont souvent corrigés a posteriori. Il est donc nécessaire de reconstruire la transcription complète à chaque mise à jour. Tu peux désactiver interimResults, mais la transcription sera beaucoup moins fluide, car seuls les résultats finaux seront transmis.

Il ne reste plus qu'à déclencher la reconnaissance en réaction à un événement utilisateur :

const buttonElement = document.getElementById('trigger');
buttonElement.addEventListener('click', () => {
    speechRecognition.start();
});

Rassemblons tous les morceaux pour un exemple complet :

const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;

if (SpeechRecognition) {
    const speechRecognition = new SpeechRecognition();
    speechRecognition.lang = 'fr-FR';
    speechRecognition.continuous = true;
    speechRecognition.interimResults = true;

    const paragraphElement = document.getElementById('result');
    speechRecognition.addEventListener('result', e => {
        let transcription = '';

        for (const result of e.results) {
            transcription += result[0].transcript;
        }


        paragraphElement.innerText = transcription;
    });

    const buttonElement = document.getElementById('trigger');
    buttonElement.addEventListener('click', () => {
        speechRecognition.start();
    });
} else {
    console.log('SpeechRecognition is not supported');
}

Et voilà, notre application est capable de transcrire notre logorrhée ! 🎉

📝 Le comportement de la transcription peut varier significativement d’une plateforme à l’autre.

Reconnaissance de commandes vocales

Nous allons maintenant construire un système capable de reconnaître des commandes spécifiques et d'exécuter des actions en réponse.

Pour illustrer cet usage, nous allons créer un système de contrôle vocal d'une boîte : cela nous permettra de déplacer la boîte dans les quatre directions sur une grille prédéfinie.

Pour commencer, nous pouvons reprendre une partie de l'exemple précédent en l'adaptant légèrement :

const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;

if (SpeechRecognition) {
    const speechRecognition = new SpeechRecognition();
    speechRecognition.lang = 'fr-FR';
    speechRecognition.continuous = true;

    speechRecognition.addEventListener('result', e => {
        // Parse the spoken command and move the box accordingly
    });

    const buttonElement = document.getElementById('trigger');
    buttonElement.addEventListener('click', () => {
        speechRecognition.start();
    });
} else {
    console.log('SpeechRecognition is not supported');
}

Deux changements sont à noter :

  • Le paramètre interimResults n’est plus activé : comme nous cherchons à reconnaître des commandes précises, il est préférable d’attendre les résultats finaux de la transcription. Cela permet de limiter les erreurs d’interprétation, même si cela introduit une légère latence dans l’exécution des commandes.
  • Naturellement, le traitement des résultats sera très différent de l’implémentation précédente, puisque nous allons désormais interpréter des instructions plutôt que simplement afficher la transcription.

Intéressons-nous maintenant à la détection de commandes prédéfinies. L’API propose un objet SpeechGrammar, qui permet de définir des mots-clés ou des motifs linguistiques au format JSpeech Grammar Format (JSGF). Cette fonctionnalité est censée faciliter grandement la détection des commandes que nous souhaitons implémenter ... Enfin, en théorie. Plot twist : en pratique, aucun des principaux navigateurs n’exploite réellement cette capacité. Certains implémentent bien les interfaces mais l’ajout d’une grammaire ne change en rien le comportement du moteur de reconnaissance. C’est un peu décevant, avouons-le.

Nous allons donc devoir détecter nos quatre commandes manuellement. Pour cela, nous allons mettre en place un système assez rudimentaire mais plutôt efficace. Cependant, dans des contextes plus complexes, impliquant une grande variété de commandes ou de formulations, il faudra se racler un peu plus la soupière pour concevoir un système de détection plus robuste.

speechRecognition.addEventListener('result', e => {
    const directions = ['haut', 'droite', 'bas', 'gauche'];
    const text = e.results[e.results.length - 1][0].transcript.toLowerCase().trim();

    for (const direction of directions) {
        if (!text.includes(direction)) {
            continue;
        }

        // Move the box
    }
});
  • On commence par définir un tableau avec nos quatre mot-clés.
  • On récupère la transcirption du dernier résultat, qu'on passe en minuscule, en retirant les espaces superflus.
  • Puis on vérifie la présence de chacun des mot-clés dans la transcription.

Construisons maintenant notre interface, pour cela, nous utilisons une grille CSS sur un conteneur et plaçons notre boîte à l’aide des propriétés grid-row et grid-column, chacune étant liée à une variable CSS (--row et --column) :

#container {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: repeat(4, 1fr);
    border: 2px solid var(--color-primary);
    max-width: 600px;
    width: 100%;
    aspect-ratio: 1 / 1;
}

#container #box {
    --row: 1;
    --column: 1;
    grid-row: var(--row);
    grid-column: var(--column);
    background-color: var(--color-secondary);
}

Il ne reste plus qu’à déplacer dynamiquement la boîte dans la grille, en modifiant la valeur des variables CSS :

const boxElement = document.getElementById('box');
const styles = getComputedStyle(boxElement);
let row = parseInt(styles.getPropertyValue('--row'));
let column = parseInt(styles.getPropertyValue('--column'));

switch (direction) {
    case 'haut':
        row--;
        break;
    case 'bas':
        row++;
        break;
    case 'gauche':
        column--;
        break;
    case 'droite':
        column++;
        break;
}

row = Math.min(4, Math.max(1, row));
column = Math.min(4, Math.max(1, column));

boxElement.style.setProperty('--row', row.toString());
boxElement.style.setProperty('--column', column.toString());

Et voilà, c'est un second prototype fonctionnel. Essaie par toi-même avec la démo ci-dessous.

Pour une reconnaissance optimale, il est préférable de prononcer une indication de direction complète (par exemple : Déplacement à droite ou Vers le bas).

Perspectives d'intégration

La fonctionnalité de reconnaissance vocale de nos navigateurs nous offre une myriade de possibilités :

  • Faciliter la saisie de données, en permettant à l’utilisateur de les dicter.
  • Offrir des contrôles vocaux dans les situations où l’usage des mains est limité ou peu pratique.
  • Proposer une accessibilité renforcée pour les personnes en situation de handicap.
  • Ajouter une dimension plus immersive à des jeux ou expériences interactives en ligne.
  • Proposer un mode vocal pour un chatbot conversationnel

Limitations et améliorations

Malgré des perspectives d’intégration prometteuses, cette API souffre de plusieurs limitations importantes à prendre en compte avant toute utilisation en production :

  • Compatibilité limitée : bien que cette fonctionnalité soit prise en charge par Chrome et Safari, ce qui couvre une part importante des utilisateurs, plusieurs navigateurs n’offrent actuellement aucun support pour la reconnaissance vocale. Il est donc essentiel d’adapter l’interface et l’expérience aux plateformes non compatibles.
  • Spécifications expérimentales : l’API repose sur des spécifications encore en cours de standardisation. Elle utilise un préfixe webkit, et certaines parties comme SpeechGrammar sont implémentées mais inopérantes dans les navigateurs actuels.
  • Usage d’un serveur : la page web doit être servie via un serveur HTTP pour que les fonctionnalités vocales soient actives. Ce n’est pas un réel frein en production, mais c'est un point à garder en tête lors du développement local.
  • Disponibilité partielle en mode hors-ligne : la reconnaissance vocale reposent sur des services distants. Dans un contexte où l’approche Offline First gagne en popularité, il est important d’anticiper les scénarios hors-ligne.
  • Intégration en TypeScript : l’API n’est pas entièrement typée dans les définitions DOM standards. Il peut être nécessaire de déclarer manuellement certains types.

En complément, les implémentations proposées dans cet article adoptent une approche simple et volontairement naïve. Mettre en place une reconnaissance vocale robuste et réellement fonctionnelle sur un large éventail de plateformes nécessitera de nombreuses améliorations et ajustements.

Conclusion

Tout comme la synthèse vocale, l’API de reconnaissance vocale ouvre des perspectives intéressantes pour enrichir l’interaction entre l’utilisateur et l’application. Cependant, plusieurs freins majeurs entravent encore la mise en œuvre de cette technologie, notamment dans des contextes de production où la tolérance à l’erreur est faible.

Cela dit, avec l’essor de l’IA générative et notamment des modèles locaux, on peut s’attendre à ce que cette fonctionnalité devienne rapidement plus accessible et performante.

Si tu ne l’as pas encore lu, je t’invite à découvrir la première partie de cette mini-série, consacrée à la synthèse vocale.

À bientôt sur ce blog ou en live sur Twitch 👋