Poisson Engine — Bilan : ce que ça donne quand on prend l'audit au sérieux
Six mois après le premier commit, Poisson est passé du POC qui crashe à un moteur de simulation auditable : 859 tests verts, zéro vulnérabilité XSS, lint qui enforce l'architecture, et deux plongées techniques publiées. Tour d'horizon — avec tous les liens pour creuser.
Cet article décortique le projetPoissonSix mois plus tard
Poisson a commencé comme un défi personnel : prouver que le web peut simuler 100 000 agents
autonomes à 60 FPS, alors que la doxa dit "non, va sur Unity". Premier commit en juin 2025.
Aujourd'hui, le moteur est publié sur npm sous @poisson/engine, ouvert sur GitHub, et
deux pages techniques exhaustives détaillent chaque décision d'architecture.
Cet article est le point d'entrée. Tout ce qui suit pointe vers du contenu plus profond si tu veux creuser.
Le pitch en trois lignes
- Ce que ça fait : simule 100 000 entités autonomes (flocking, génétique, écosystème prédateur/proie) à 60 FPS stables dans un navigateur, sans framework.
- Comment : pipeline GPU à 8 passes batchées, partitionnement spatial CSR, zéro allocation pendant la boucle (le GC ne se déclenche jamais en jeu).
- Pour qui : si tu construis un produit qui doit afficher des milliers d'éléments vivants dans un navigateur (carte temps réel, dashboard, jeu, simulation métier), c'est l'expertise rare qu'il faut. Très peu de devs JS maîtrisent les compute shaders WebGPU.
Les chiffres qui comptent
| Mesure | Valeur | Pourquoi c'est important |
|---|---|---|
| Entités simultanées | 100 000 | À 60 FPS stables, mesuré sur MacBook M1 |
| Speedup vs CPU | 60× | Même algorithme, même hardware — juste GPU |
| Modules JavaScript | 355 | Tous vanilla JS, aucune dépendance front |
| Dépendances circulaires | 0 | Enforced par ESLint (no-restricted-imports) |
| Tests unitaires | 859 verts | + 49 tests browser sur Playwright |
| Allocations / frame | 0 | Zero-GC en boucle de simulation |
| Vulnérabilités XSS | 0 | Après audit complet (janvier 2026) |
| Renderers | 3 | WebGPU → WebGL → Canvas2D, dégradation auto |
Les plongées techniques
Deux pages dédiées sur le site officiel, accessibles à deux niveaux (Non-Tech / Tech) selon le profil du lecteur :
-
🏗️ Architecture & Performance — les 4 couches (Engine → Shared → Game → Client), le pipeline GPU à 8 passes, le système zero-GC, et le pipeline graphique WebGL instancié + LOD procédural en 5 niveaux.
-
🧠 Simulation & Algorithmes — flocking multi-agent (15 forces Reynolds étendues), champs de potentiel pour la navigation, génétique paramétrique avec spéciation dynamique, et l'écosystème prédateur/proie auto-régulé.
Chaque page est un essai technique long — entre 80 KB et 160 KB de HTML, avec diagrammes, équations et exemples de code. Pensées pour être lues comme un article de revue, pas comme une doc.
Le parcours technique en articles courts
Si tu préfères piocher, voici les billets de blog publiés au fil du chemin :
- WebGPU compute shaders en JS — pourquoi le SoA (Structure-of-Arrays) change tout, et le pipeline GPU à 8 passes en détail.
- Quand les algos CPU deviennent les pires sur GPU — l'histoire de mon premier compute shader, et pourquoi le BVH parallèle académique m'a battu à plates coutures.
- LOD à 5 niveaux pour 100K entités — comment rendre 100 000 poissons en sachant qu'à 99% d'entre eux, deux pixels suffisent.
L'audit de janvier 2026 — ce qui a changé
Le moteur tournait. La question d'après : est-ce qu'il résiste à un vrai audit ? Réponse : pas complètement. Voici ce qui est sorti et ce qui a été corrigé.
Sécurité
Trois vulnérabilités XSS corrigées. Toutes liées au même pattern : du contenu généré
par LLM injecté via innerHTML sans échappement.
// ❌ Avant
zone.innerHTML = `<div class="sp-ai-lore">${result}</div>`;
// ✅ Après
const lore = document.createElement('div');
lore.className = 'sp-ai-lore';
lore.textContent = result; // pas d'interpretation HTML
zone.appendChild(lore);
Quand un LLM peut renvoyer <img src=x onerror=...> dans une biographie, tu ne peux pas
faire confiance. Toutes les insertions issues d'IA passent désormais par textContent +
createElement, ou par un sanitizer explicite avec liste blanche.
Architecture enforced via le lint
L'architecture en 4 couches (Engine → Shared → Game → Client) n'a de valeur que si elle est respectée. ESLint le vérifie maintenant à chaque commit :
// eslint.config.js
{
files: ['js/engine/**/*.js'],
rules: {
'no-restricted-imports': ['error', {
patterns: [{
group: ['*/poisson/*', '*/client/*', '*/shared/*'],
message: 'Engine layer must not import from game, client, or shared layers.',
}],
}],
},
}
Résultat : 0 dépendance circulaire, et il devient impossible de glisser un import sale sans que le CI hurle.
Tests browser remis d'aplomb
Sur 4 fichiers de tests browser, 2 étaient cassés depuis des mois (sélecteurs DOM stale
après refactoring). 16 tests rouges. La cause : un test qui cherchait #energy-toggle-btn
sur un HTML qui ne contenait plus cet ID. Réécrits en smoke tests minimaux, le test passe
à 49/49 verts.
Accessibilité et SEO
- Touch targets 44×44 px (WCAG 2.5.5) via
@media (pointer: coarse). - Tous les sliders ont retrouvé leur
<label for=>(axe critique). og:image,twitter:card,canonical, et JSON-LD structured data sur les 3 pages publiques.sitemap.xml+robots.txtajoutés.- Les
<div class="scenario-card" role="button">qui contenaient un<button>(anti-patternnested-interactive) ont été ré-architecturés en un seul élément interactif.
Ce qui reste à faire
Pas tout est fini. Backlog public :
- Bundle split :
index-*.jsest encore monolithique (633 KB). Une stratégie de chunking par sous-système (engine-compute, client-renderer, sim-shared) ferait gagner beaucoup en cache hit-rate après mise à jour. - Réduire l'inline
onclick: il en reste sur les fragments pillars pour la compatibilité CSP stricte. Migration vers event delegation à terminer. - Mode multijoueur : le serveur Fastify + WebSocket binaire tourne, mais le matchmaking et la persistance d'état entre sessions sont au stade alpha.
Si l'une de ces zones t'intéresse, le repo est ouvert aux contributions.
Tester soi-même
Trois façons d'interagir :
- Démo live — la page d'accueil avec la simulation intégrée et les pillars adaptatifs (Non-Tech / Tech).
- Sandbox interactive — contrôle complet (sliders, modes, scénarios). C'est là que tu peux pousser à 100 000 entités et regarder les FPS tenir.
- Installer le package :
npm install @poisson/engine
La doc de l'API publique est sur le repo GitHub.import { SimulationBase, SpatialGrid } from '@poisson/engine';
Ce que le projet m'a appris
Plus que les algos GPU eux-mêmes, ce qui ressort de six mois sur Poisson, c'est la discipline du code en production sans utilisateur. Personne ne crie quand le moteur crashe à 50 000 entités. Donc il faut crier soi-même : tests automatisés, lint qui refuse les commits sales, audit régulier, métriques de bundle, et — surtout — un plan de documentation aussi soigné que le code.
Les deux pages de plongée technique (/deep/architecture/ et /deep/simulation/) sont
écrites avec autant de soin que le moteur. Parce qu'à terme, ce qui reste d'un projet
solo, c'est ce qu'on peut en montrer.
→ Lance la simulation · Lis l'architecture · Lis les algorithmes · Voir le repo
Florian Sola
Lead Technique · Haute performance temps réel · 9 ans d'expérience