Riadh Mnasri
← Retour au blog
3 min de lecture

Faire tourner Stockfish dans le navigateur : ce que WebAssembly change

Sur OpeningBook, chaque position est analysée par Stockfish directement dans le navigateur, sans appel serveur. WebAssembly rend ça possible, mais le buzzword cache des contraintes concrètes qui changent la façon de concevoir l'application autour.

Ce que WASM apporte réellement

Stockfish est écrit en C++, optimisé depuis des années pour la performance brute. Compiler ce code en WebAssembly permet de l'exécuter dans le navigateur à une vitesse proche du natif, sans réécrire le moteur dans un autre langage. C'est la différence entre porter vingt ans d'optimisations d'un moteur d'échecs, et réimplémenter un évaluateur de position depuis zéro en JavaScript, avec toutes les régressions de performance que ça impliquerait.

La contrainte qu'on découvre vite : le threading

Le vrai piège n'est pas la compilation elle-même, mais l'exécution multi-thread. Stockfish exploite plusieurs cœurs pour paralléliser la recherche, mais WebAssembly multi-thread dans un navigateur nécessite SharedArrayBuffer, qui exige à son tour des en-têtes de sécurité HTTP précis (Cross-Origin-Opener-Policy, Cross-Origin-Embedder-Policy). Sans ces en-têtes correctement configurés côté serveur (ou côté Vercel dans mon cas), le moteur retombe silencieusement en mode single-thread, avec une perte de performance qui n'est pas évidente à diagnostiquer si on ne sait pas où chercher.

Navigateur
┌────────────────────────────────────────────────────────┐
│  Page OpeningBook                                       │
│    │                                                     │
│    ▼                                                     │
│  Web Worker ──▶ Stockfish.wasm ──▶ résultat d'analyse    │
│    ▲                                                     │
│    │ nécessite SharedArrayBuffer pour le multi-thread    │
│    │                                                     │
│  En-têtes HTTP requis sur la réponse du serveur :        │
│  Cross-Origin-Opener-Policy: same-origin                 │
│  Cross-Origin-Embedder-Policy: require-corp              │
└────────────────────────────────────────────────────────┘

Sans ces deux en-têtes, le navigateur refuse silencieusement d'allouer un SharedArrayBuffer partagé entre threads, et Stockfish continue de fonctionner, mais en mode single-thread, sans erreur visible dans la console qui pointe vers la cause réelle.

Le compromis architecture qui en découle

Faire tourner l'analyse côté client plutôt que côté serveur change la répartition des responsabilités : plus besoin d'API d'analyse à maintenir, pas de coût serveur qui grandit avec le nombre d'utilisateurs simultanés, mais en échange, chaque appareil doit être capable de faire tourner un moteur d'échecs correctement optimisé. Sur un ordinateur récent, la différence est invisible. Sur un appareil plus ancien, l'expérience se dégrade, et il faut prévoir un mode de repli (profondeur d'analyse réduite, ou UI qui indique clairement que le calcul prend du temps).

Client vs serveur, le compromis résumé

AspectAnalyse côté client (WASM)Analyse côté serveur
Coût d'infrastructureNul, indépendant du nombre d'utilisateursCroît avec le nombre de calculs simultanés
Latence perçueDépend de l'appareil de l'utilisateurConstante, contrôlée par le serveur
Cohérence entre utilisateursPeut varier selon le matériel (profondeur, vitesse)Identique pour tout le monde
Confidentialité des positionsNe quitte jamais le navigateurTransite par le serveur
Complexité de mise en placeEn-têtes COOP/COEP, gestion du Web WorkerUne API HTTP classique

La ligne confidentialité n'est pas un détail secondaire pour un outil familial : une position d'échecs analysée par un enfant n'a aucune raison de transiter par un serveur, et le choix WASM la garde entièrement locale par construction, pas par configuration additionnelle.

Pourquoi ce choix, malgré la complexité

Pour un outil familial comme OpeningBook, où chaque analyse de position est consultée ponctuellement plutôt qu'en continu, l'absence de coût serveur récurrent l'emporte largement sur la complexité de mise en place initiale. C'est un arbitrage qui ne se généralise pas : sur un produit avec des milliers d'utilisateurs simultanés et des exigences de cohérence d'analyse, un moteur centralisé côté serveur resterait probablement le meilleur choix. Le bon choix d'architecture dépend toujours du profil d'usage réel, pas d'une préférence technologique a priori pour le « tout côté client ».