optimisation core web vitals 7 min

La leçon des capteurs sismiques IoT pour vos Core Web Vitals

Un dashboard de monitoring sismique doit s'afficher en moins de 500 ms. On a passé au crible son architecture web : LCP, INP, edge computing, gestion d'état. Voici ce qui fait la différence.

Par Julien Morel
Partager

On a hébergé un fork du dashboard open source d’un petit réseau de capteurs sismiques citoyens, un projet né dans une communauté de makers lyonnais. L’interface semblait simple : une carte, un flux de données en temps réel, une poignée de jauges. Sur le papier, rien d’extraordinaire. Après un premier audit Lighthouse, on a pourtant relevé un LCP à 3,8 secondes, un INP à 320 millisecondes, et des timeouts réseau qui effaçaient une alerte sur trois avant même son affichage. Le système physique, lui, réagissait en moins de 200 millisecondes. Le maillon faible était l’interface web.

C’est là que le sujet bascule. On se dit souvent que les Core Web Vitals sont une métrique pour les sites e-commerce ou les blogs, parce que leur classement en dépend. Mais quand une alerte sismique doit être interprétée en moins d’une seconde pour déclencher une décision humaine ou automatisée, un LCP poussif ou un INP instable sort du cadre « performance SEO ». Il entre dans le registre de la sécurité et de la fiabilité industrielle. Et les remèdes qu’on a appliqués ne valent pas que pour les dashboards IoT : ils s’appliquent à n’importe quelle application web où la réactivité conditionne l’usage, voire la confiance.

La physique des ondes sismiques ne négocie pas avec votre backend

Les ondes sismiques se déplacent à quelques kilomètres par seconde dans la croûte terrestre. Un réseau de capteurs bien maillé peut détecter un séisme et en estimer la localisation en une à deux secondes. Chaque milliseconde gagnée signifie potentiellement des kilomètres de couverture utiles pour une alerte précoce. Si le backend met 400 millisecondes à répondre, on a perdu une fenêtre que la physique ne rattrapera pas. C’est une contrainte de fond : la latence réseau n’est pas une variable d’ajustement, c’est la contrainte de départ.

L’architecture du projet initial reposait sur un serveur central en région parisienne, des capteurs disséminés en zone alpine, et des websockets sans logique de filtrage côté client. Le temps d’aller-retour réseau absorbait la moitié de la fenêtre utile.

Un LCP à 3,8 secondes, c’est la secousse avant l’alerte

Ouvre les DevTools, fais un audit avec un throttling réseau « Fast 3G », et tu verras l’effet domino. Le bundle JavaScript principal dépassait les 2 Mo. Il embarquait l’intégralité de Leaflet, D3.js et une bibliothèque de visualisation temps réel, le tout chargé dans le même chunk, sans code splitting. Le LCP était retardé par le parse et l’exécution de ce bloc, alors que l’élément le plus visible (la carte) n’avait besoin que d’un sous-ensemble des données.

On a corrigé en trois étapes. D’abord, un code splitting par route et composant, pour ne charger que le strict nécessaire au premier rendu. Ensuite, le lazy-loading des modules de visualisation lourds, déclenché uniquement après l’interaction avec la carte. Enfin, un préchargement des polices et des icônes critiques via link rel="preload". Le passage d’un TTFB supérieur à 500 ms à moins de 80 ms a nécessité de déplacer la logique de prétraitement des flux de données sur un edge worker, pour que le serveur d’API ne renvoie que la vue déjà filtrée.

Ce type de pathologies, on les rencontre sur bien d’autres projets, y compris des fiches produits e-commerce. Quand on les traque, les métriques des Core Web Vitals racontent la même histoire : chaque kilooctet superflu retarde le moment où l’utilisateur peut interagir.

Les logs réseau montraient aussi des abandons de connexion TCP sur des clients mobiles en zone rurale, là où les sismologues amateurs consultent souvent le dashboard. La solution a été de basculer les flux temps réel sur un transport WebSocket avec un heartbeat toutes les 10 secondes, et un fallback en Server-Sent Events pour les réseaux instables. En parallèle, on a implémenté un mécanisme de cache HTTP avec stale-while-revalidate pour les tuiles de carte, afin que l’interface reste utilisable même en micro-coupure. L’INP a retrouvé une stabilité sous les 100 ms parce que le navigateur ne bloquait plus sur des requêtes en échec.

Gestion d’état : pourquoi le passage à Zustand a éliminé les saccades sur la grille de capteurs

Le dashboard affiche une grille pouvant contenir jusqu’à 200 capteurs, mis à jour plusieurs fois par seconde. La première version s’appuyait sur un store Redux classique. À chaque nouvelle mesure, un dispatch modifiait le tableau global des capteurs, ce qui forçait le re-rendu intégral de la grille et de tous ses enfants. L’effet était immédiat : dès que l’activité sismique augmentait, l’interface devenait saccadée. Le thread principal était saturé par les opérations de reconciliation React.

On a migré vers Zustand. La différence a porté sur un point précis : la possibilité de souscrire à des tranches atomiques de l’état sans déclencher de re-rendu sur le reste de l’arborescence. Chaque tuile de capteur écoute uniquement la portion du store qui la concerne, via un sélecteur stable. L’écriture dans le store se fait par lots, avec un batch automatique évitant les rendus en cascade.

Un extrait simplifié de l’approche retenue :

const useSensor = (id) =>
  useStore((state) => state.sensors[id]);

const SensorTile = ({ id }) => {
  const sensor = useSensor(id);
  return <div data-value={sensor.value}>…</div>;
};

La grille ne re-rend que les tuiles dont la valeur change. Le temps de traitement moyen par mise à jour est passé sous la barre des 10 ms, même sur les terminaux mobiles les plus modestes. Et contrairement à une solution à base de contextes React, il n’y a pas d’effet de baillement où un contexte qui change trop souvent force malgré tout le re-rendu de dizaines de composants.

Le point de vigilance reste la sérialisation de l’état pour la persistance locale : un grand nombre de mises à jour brutes doit être throttlé avant écriture dans localStorage ou IndexedDB, sinon le bottleneck se déplace sur les E/S. On a mis en place un buffer circulaire qui ne persiste que tous les 500 ms.

Là où l’edge computing change la donne

Le filtrage des signaux parasites, la détection de patterns simples et l’agrégation par zone peuvent être exécutés en dehors du navigateur, au plus près de l’utilisateur. Plutôt que d’expédier le flux brut de 200 capteurs au client et de lui demander de filtrer, on a déporté cette logique dans une edge function. Le navigateur ne reçoit que le résultat consolidé, un JSON de quelques kilo-octets. Le LCP s’en trouve mécaniquement réduit, et le travail du thread principal allégé.

Sur le plan architectural, on a utilisé des workers de type Cloudflare Workers pour intercepter les données au niveau du CDN et appliquer les règles de filtrage. Le cold start a été contenu en maintenant une connexion persistante vers le broker MQTT des capteurs via un tunnel WebSocket. On obtient un temps de réponse sous les 10 ms pour la plupart des utilisateurs européens, et l’INP ne subit plus les fluctuations liées aux calculs côté client.

Les dashboards IoT ne sont pas les seuls à bénéficier de ce déport : les applications e-commerce qui calculent la disponibilité de stock en temps réel ou les tableaux de bord d’analytics en direct gagnent la même stabilité quand on y applique le même principe.

Pourquoi l’indexation du dashboard public est un faux problème, jusqu’au jour où ça bloque

Les concepteurs du dashboard n’avaient pas pensé à son référencement. La page d’accueil était servie en rendu côté client intégral, sans SSR ni contenu statique détectable par les robots. Le fichier robots.txt bloquait tout crawl, héritage d’une ancienne configuration de staging. Résultat : le projet était invisible sur les moteurs de recherche, y compris pour des requêtes aussi légitimes que « dashboard sismique open source temps réel ».

La question n’est pas de ranker pour du trafic. Elle est de permettre au bon interlocuteur, un sismologue, un responsable sécurité civile, un journaliste scientifique, de trouver l’outil quand il en a besoin. On a mis en place :

  • Un rendu côté serveur pour la page d’accueil et la documentation, avec une version allégée accessible sans JavaScript.
  • Un sitemap.xml segmenté listant les pages de documentation et les endpoints de démonstration.
  • Des balises meta et JSON-LD décrivant l’application comme un WebApplication, avec la catégorie « Science & Technology ».
  • Un crawl budget protégé : les URLs dynamiques des sessions temps réel sont marquées en noindex, seules les pages statiques sont indexées.

Le crawl est redevenu propre en 48 heures. Le site est apparu en première page pour sa requête principale, sans qu’on ait dépensé un euro en netlinking. Simplement parce qu’on a cessé de le cacher.

L’outillage qui a fait gagner du temps

L’audit initial aurait pu prendre trois jours à un développeur seul. On a branché Claude Code sur le codebase pour identifier les imports circulaires et les modules dupliqués dans les bundles. L’assistant a balayé l’arbre de dépendances en une dizaine de minutes et a proposé une refonte du découpage des chunks qu’on n’aurait pas esquissée aussi vite manuellement.

Son vrai apport n’a pas été de remplacer l’analyse humaine, mais de la focaliser là où elle était nécessaire : les choix de lazy-loading, la stratégie de cache, le dimensionnement des workers. Ce qu’on a observé confirme la complémentarité décrite dans notre retour sur Claude Code et les IDE : le gain est réel quand on s’en sert comme accélérateur de diagnostic, pas comme générateur automatique de correctifs.

Questions fréquentes

Est-ce que les WebSockets restent le meilleur choix pour le transport temps réel dans un dashboard IoT ?

Tout dépend du volume et de la tolérance à la perte. Les WebSockets offrent un canal bidirectionnel persistant avec overhead minimal, mais ils peuvent poser problème derrière certains proxies d’entreprise. Dans un scénario de diffusion unidirectionnelle (le capteur vers le dashboard), les Server-Sent Events (SSE) sont plus simples à déployer et bénéficient de la compression HTTP/2. Le piège, c’est la gestion des abandons de connexion : prévoir un heartbeat et un mécanisme de reconnexion automatique est indispensable quel que soit le protocole.

Un INP correct sur un dashboard de supervision, c’est quel seuil exact ?

Google parle de « bon » sous 200 ms. Pour une interface où l’opérateur doit cliquer pour isoler une zone sismique, un délai de réponse tactile supérieur à 100 ms crée une sensation de flou qui induit des erreurs. On recommande de viser un INP inférieur à 100 ms sur les interactions critiques, quitte à reléguer les interactions secondaires (réglages, filtre avancé) en dehors des chemins temps réel. Mesurez-le avec l’extension Web Vitals en simulant un CPU throttlé 4x, sans quoi les valeurs obtenues sur une machine de développement surpuissante n’ont aucun sens.

La 5G résout-elle le problème de latence réseau pour ce type d’application ?

Elle le réduit, mais ne l’élimine pas. Le goulot d’étranglement se déplace vers le temps de traitement du serveur et la vitesse de rendu du client. Avec une latence radio de 5 ms, un TTFB de 400 ms reste inacceptable. L’edge computing ne devient pas optionnel parce que le réseau s’améliore : c’est précisément quand le réseau disparaît comme variable limitante que la lenteur du backend et du navigateur devient insupportable.

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.