optimisation core web vitals 8 min

Reconnaissance vocale : l’impact réel sur vos Core Web Vitals

L’API SpeechRecognition peut faire grimper votre INP à 400 ms en 10 lignes. Mesurez, comprenez, et corrigez le coût réel de la reconnaissance vocale sur le thread principal.

Par Julien Morel
Partager

On a mis 48 heures à comprendre pourquoi un side-project de dictée en ligne, une app React toute bête, envoyait soudain des INP à 420 ms sur mobile. Le LCP restait propre, le TTFB sous les 200 ms. C’est la Search Console qui a sonné l’alerte : une chute brutale des URLs bien classées sur mobile, sans changement de code visible. Le coupable n’était pas une bundle trop lourd ni un script tiers. C’était l’activation continue de la reconnaissance vocale via l’API SpeechRecognition, ajoutée la veille pour une fonctionnalité de recherche par commande vocale. Dix lignes de JavaScript. Trois cent millisecondes de latence d’interaction. La reconnaissance vocale est utile, parfois indispensable pour l’accessibilité, mais sous Chrome et ses dérivés, elle exécute tout sur le thread principal. Si vous l’intégrez sans mesurer, vous sabotez vos métriques et, mécaniquement, votre visibilité.

Pourquoi la reconnaissance vocale sature le thread principal bien plus que vous ne le pensez

L’API SpeechRecognition, que vous l’utilisiez via webkitSpeechRecognition ou le standard, est une boucle événementielle gourmande. Chaque résultat intermédiaire (onresult), chaque fragment audio traité par le moteur de reconnaissance local ou distant, déclenche des micro-tâches sur le thread UI. Sur un desktop récent, vous ne verrez rien : les 5 à 15 ms de traitement par fragment sont absorbées sans dommage. Sur un mobile milieu de gamme (un Snapdragon 6xx ou un vieux MediaTek), le coût passe à 40-80 ms par événement, plusieurs fois par seconde quand l’utilisateur parle. Le navigateur n’a d’autre choix que de différer la réponse aux clics, aux scrolls et aux changements d’état. Vous obtenez un INP qui grimpe à 300 ou 450 ms, parfois plus si le garbage collector s’en mêle parce que vous accumulez des chaînes de transcription dans un state global non nettoyé.

Ce n’est pas une question de réseau : la reconnaissance peut s’exécuter localement sur Chrome sans appel serveur, mais le goulot reste le thread unique. L’API ne fournit aucune option de worker natif. Le navigateur interprète l’audio dans un bac à sable, puis expédie les hypothèses de transcription sur l’event loop principale. Résultat, votre page reste « janky » pendant une session de dictée. Et si vous avez pris soin de suivre les derniers ajustements sur les signaux de classement documentés pour l’optimisation des Core Web Vitals, vous savez déjà qu’un INP au-dessus du seuil de 200 ms sur mobile est un signal négatif que Google n’ignore pas.

⚠️ Attention : Sur Chrome Android, même une reconnaissance lancée en arrière-plan (par exemple via une commande « always listening » sur un bouton micro visible) continue d’émettre des événements onresult et onspeechend de façon agressive. Coupez explicitement la session avec recognition.abort() dès que l’utilisateur a fini de parler.

L’illusion de la latence zéro

Les démos de reconnaissance vocale donnent l’impression d’une magie immédiate. En réalité, le délai entre le début de la phrase et l’affichage du texte dépend du VAD (Voice Activity Detection) du navigateur, de la segmentation audio, et de la pile d’événements JavaScript. Sur un site où on vous vend une « expérience vocale fluide », le retour utilisateur est souvent à 300 ms d’inertie. Pire, l’interface reste bloquée juste après. La latence zéro n’existe pas côté client, elle est simplement masquée par du rendu optimiste.

Auditez l’INP déclenché par la voix sans quitter l’onglet performance

Ouvrez votre app dans Chrome Canary, sur un vrai mobile ou via le mode Device Toolbar avec CPU throttling 4x. Allez dans l’onglet Performance. Cochez « Disable cache » pour éviter les biais. Lancez l’enregistrement, déclenchez la reconnaissance vocale, prononcez une phrase de trois secondes, puis cliquez immédiatement sur un bouton de l’interface. Arrêtez l’enregistrement. Zoomez sur la trace principale. Cherchez les longs blocs « Function Call » qui coïncident avec vos événements onresult. Notez leur durée et surtout le moment où l’interaction utilisateur (le clic) est traitée. Si le délai entre le pointerdown et le click dépasse 50 ms, vous êtes déjà au-delà d’une interaction rapide.

Refaites la même mesure avec recognition.stop() appelé immédiatement après chaque phrase. La différence est souvent spectaculaire. Sur notre side-project, désactiver la reconnaissance dès le speechend a fait chuter l’INP de 410 ms à 170 ms en médiane mobile. Cette technique ne demande aucun changement d’architecture.

Si vous débuggez souvent ce genre de problème avec un environnement d’édition alimenté par IA, la question du choix d’outil se pose. Dans un comparatif que nous avons mené entre deux assistants de code, l’un s’est montré plus pertinent pour analyser des traces de performance et proposer des correctifs ciblés, un sujet que nous avons détaillé dans notre article Claude code vs Cursor IDE.

Nettoyez le state ou préparez-vous à des INP en escalier

Chaque résultat intermédiaire (interimResults: true) ajoute une chaîne de caractères dans votre state. Si vous stockez l’intégralité de l’historique de la session dans un store global sans limite, le garbage collector s’active de plus en plus fréquemment après 15 ou 20 secondes de dictée. Sur React, un state mis à jour 30 fois par seconde avec une concaténation de chaînes immuable recrée des objets, écrase le DOM virtuel et diff-toute la portion de l’interface qui affiche la transcription. Résultat, le temps passé dans React pour commiter l’arbre augmente, et avec lui le Time to Interactive local.

Le bon réflexe est de ne garder que le résultat final (isFinal: true), ou d’afficher les intermédiaires dans un buffer séparé en dehors du cycle de rendu React (une mise à jour directe du DOM via un ref). C’est là qu’une librairie de gestion d’état légère, sans boilerplate, devient critique. Quand nous avons migré le state vocal d’un useState classique vers un store atomique avec sélecteurs pour le state management React avec Zustand, le nombre de rendus superflus a chuté de 80 % et l’INP a perdu 40 ms supplémentaires, simplement parce que l’affichage du texte n’impactait plus le reste de l’arbre.

Le coût SEO d’une interface vocale non optimisée ne se limite pas à l’INP

Si votre LCP est déjà dans le vert, vous pourriez croire que la reconnaissance vocale ne touche pas au référencement. Mais sur un site où la voix est au cœur du parcours, le taux de rebond implicite augmente quand chaque interaction est ralentie. Google évalue l’expérience de page via les Core Web Vitals agrégés, et un INP dégradé sur une URL clé abaisse la note globale dans le rapport CrUX. Sur un e-commerce qui ajoute une recherche vocale en PWA, la sanction n’est pas une pénalité manuelle, c’est une perte de quelques positions sur des requêtes ultra-concurrentielles parce que le concurrent d’à côté a un INP à 180 ms et vous à 380 ms.

On ne vous propose pas d’abandonner la reconnaissance vocale. On vous propose de la considérer comme une fonctionnalité qui a un coût mesurable sur les métriques de terrain. Le même regard qu’on porte sur une iframe YouTube lazy-loadée ou un carrousel en JavaScript pur. Une stratégie d’optimisation des Core Web Vitals passe par l’examen de chaque API qui touche au thread principal.

Mesurez avant d’optimiser : le protocole qui ne ment pas

Posez un PerformanceObserver spécifique à l’INP sur votre page et loggez les événements d’interaction juste après une session de reconnaissance. Ne vous contentez pas du lab Lighthouse, qui ne capture jamais la complexité de la voix en continu. Activez le reporting CrUX si vous avez assez de trafic, ou bien instrumentez en local avec la library web-vitals en mode debug. Sur notre side-project, voici ce qu’on a traqué :

  • Durée de chaque onresult en ms via performance.now()
  • Nombre d’appels onresult par seconde de parole
  • INP via l’API web-vitals mesuré 2 secondes après le speechend
  • Évolution du TBT sur 3 sessions de 10 secondes

Les chiffres bruts ne vous trompent pas. Avant d’écrire une seule ligne d’optimisation, capturez ces quatre métriques, lancez un test à froid sur un Moto G4 simulé, et fixez un seuil d’alerte. La reconnaissance vocale est un outil puissant, à condition d’en connaître le prix en millisecondes. Ce n’est pas le concept qui coûte cher. C’est l’implémentation naïve qu’on livre le vendredi soir et qu’on oublie jusqu’au prochain rapport Search Console.

Questions fréquentes

La reconnaissance vocale a-t-elle un impact sur le LCP ?

Directement, non. L’API ne charge pas d’éléments lourds visibles. En revanche, si vous chargez un polyfill volumineux ou un script de reconnaissance tiers avant le rendu du contenu principal, il peut différer le LCP. L’appel à SpeechRecognition doit toujours être paresseux, déclenché par un geste utilisateur.

Peut-on utiliser la reconnaissance vocale sur un service worker ?

Non, le service worker n’a pas accès au DOM ni à l’API window.SpeechRecognition. Le seul chemin viable pour soulager le thread principal est d’utiliser un Web Worker pour le post-traitement du texte, en envoyant les résultats transcrits via postMessage. La capture audio brute avec AudioContext et createMediaStreamSource peut aussi être partiellement déportée, mais la reconnaissance elle-même reste liée à la window.

Est-ce que les PWA bénéficient d’une exception sur mobile ?

Non, le seuil d’INP est le même pour une PWA installée. L’avantage d’une PWA est que vous pouvez conserver un contexte d’exécution plus longtemps et gérer finement le cycle de vie de l’API via des flags internes. Cela ne dispense pas de mesurer et d’optimiser.

Articles similaires

Julien Morel

Julien Morel

Ancien dev front React passé SEO technique après une migration e-commerce qui a fait perdre 60% du trafic organique à son employeur en une nuit (fichier robots.txt oublié en staging). Depuis, il écrit pour que ça n'arrive à personne d'autre et teste sur ses propres side-projects avant de publier quoi que ce soit.

Cet article est publie a titre informatif. Faites vos propres recherches avant toute decision.