Echoborn : pincez pour jouer

Un instrument dont on joue à la main, dans un onglet de navigateur. Suivi des mains, synthèse et visuels, sans compte, sans backend et sans qu'aucune donnée ne quitte l'appareil.

Client
Nuits
Année
2026
Statut
En ligne
Rôle
Fondateur solo, Design d'interaction, Ingénierie, Programmation audio
Stack
TanStack Start, MediaPipe, Tone.js, react-three-fiber, WebGL
Echoborn : pincez pour jouer

Le contexte

Echoborn est un instrument dont vous jouez avec les mains, dans un onglet de navigateur, avec la caméra que vous avez déjà. Vous pincez pour jouer. Bougez les mains et le son bouge avec elles.

Il n’y a pas de compte, pas d’inscription et pas de backend d’aucune sorte. La page se charge, la caméra s’allume localement, et rien de vous ne quitte la machine, parce qu’il n’y a nulle part où aller.

Le problème

Les instruments dans le navigateur finissent presque toujours en clavier de piano dessiné en HTML, joué à la souris, une note à la fois. Ce n’est pas un instrument. C’est le schéma d’un instrument.

La musique au geste a le problème inverse. Les démos ont l’air extraordinaires dans une vidéo de trente secondes et sont injouables en pratique, parce qu’elles sont construites comme des démonstrations technologiques plutôt que comme des instruments. La latence y est traitée comme un chiffre à annoncer plutôt que comme ce qui détermine si l’objet est musical tout court. Au-delà d’environ vingt ou trente millisecondes entre un geste physique et le son qu’il provoque, votre corps cesse de croire que les deux sont liés, et vous ne jouez plus de rien. Vous manœuvrez un pupitre de commande qui fait du bruit.

Je voulais savoir si le navigateur pouvait passer sous cette barre honnêtement, sans app native et sans matériel.

Mon rôle

Seul, comme pour le reste de Nuits. Design d’interaction, ingénierie, audio, visuels.

Le travail intéressant n’était dans aucun de ces domaines pris isolément. Il était dans l’arbitrage entre eux : le suivi des mains, la synthèse et le rendu 3D veulent tous le thread principal, veulent tous le GPU, et tous dégradent l’instrument si on les affame. Décider ce qui a le droit de coûter cher, c’était tout le travail.

L’approche

Le pincement est devenu le geste parce qu’il a un état binaire sans ambiguïté. La plupart des gestes de la main vivent sur un continuum, ce qui veut dire un seuil, ce qui veut dire du jitter au seuil, ce qui veut dire des notes qui hoquettent alors que votre main ne bouge pas. La pointe du pouce à la pointe de l’index, c’est une distance qui se referme franchement et se rouvre franchement, et c’est un mouvement que les gens comprennent déjà sans qu’on l’explique.

Tout le reste se mappe de façon continue. Position, écartement et orientation pilotent la hauteur, le filtre et l’espace. Cette répartition vous donne les deux choses dont un instrument a besoin : une attaque nette que vous pouvez placer dans le temps, et un contrôle expressif que vous pouvez façonner après l’attaque.

Il n’y a pas de tutoriel. Vous levez la main et il se passe quelque chose.

La construction

TanStack Start le sert. C’est toute l’histoire côté serveur, parce qu’il n’y a pas de logique serveur. L’ensemble est une page statique avec beaucoup de choses qui se passent dedans.

Le suivi des mains, c’est MediaPipe, et plus précisément MediaPipe avec le fichier de modèle hand_landmarker.task hébergé sur mon propre domaine plutôt que tiré du CDN de Google, ce que fait pourtant chaque exemple de l’écosystème. Trois raisons, par ordre d’importance à mes yeux. Le modèle pèse plusieurs mégaoctets et il est sur le chemin critique de l’existence même de l’instrument, donc il ne devrait pas dépendre de la disponibilité d’un tiers ni de sa décision de déplacer une URL. Le récupérer depuis un hôte externe signale à cet hôte que quelqu’un sur mon site est sur le point d’activer une caméra, ce qui est une fuite de confidentialité que je devrais ensuite déclarer. Et le servir depuis le même domaine veut dire qu’il se met en cache sous mon contrôle, et que la deuxième visite est instantanée.

L’audio, c’est Tone.js par-dessus la Web Audio API. Web Audio fait tourner son graphe sur un thread audio dédié, ce qui est la seule raison pour laquelle ça marche : la boucle de suivi peut perdre une image sans que le son bafouille. La règle que j’ai tenue, c’est que les points de repère sont lus sur l’image vidéo et écrits directement dans des changements de paramètres audio, sans état React intermédiaire. Faire passer un geste par un re-render de composant ajoute une image de latence et n’apporte rien, puisque rien dans le DOM n’a besoin de le savoir.

Les visuels, c’est react-three-fiber, et ils sont délibérément la première chose à se dégrader. Le renderer tourne sur le GPU, le suivi tourne sur son propre rythme d’inférence, et si l’appareil ne peut pas soutenir les deux, ce sont les visuels qui perdent. Une particule perdue est invisible. Une note en retard, c’est la fin de l’instrument.

Les modes de défaillance, dont plusieurs sont bien réels. La faible lumière détruit la confiance des points de repère, et des points de repère peu fiables produisent des notes que vous n’avez pas jouées, donc en dessous d’un seuil de confiance le suivi conserve le dernier état stable au lieu de deviner. Deux mains qui se croisent provoquent des échanges d’identité entre elles, ce qui s’entend comme votre main gauche qui se met soudain à jouer la partie de la droite. Le contre-jour, une fenêtre derrière vous, est la raison la plus fréquente pour laquelle ça paraît cassé, et ce n’est pas quelque chose que je peux corriger en logiciel.

Le résultat

C’est en ligne sur echoborn.app et ça ne me coûte rien à faire tourner, parce qu’une page statique sans backend n’a pas de coût marginal. Les gens en jouent, ferment l’onglet, et je n’apprends jamais rien là-dessus, ce qui est bien l’arrangement.

Ce que je ferais autrement

J’ai réglé le seuil de pincement sur mes propres mains, dans ma propre cuisine, sous mon propre éclairage. C’est un échantillon de un, et la taille des mains varie plus que je ne le supposais. Un court calibrage au premier chargement réglerait la plupart des remarques que je reçois.

J’aurais aussi enregistré et rejoué des séquences de points de repère comme banc de test. J’ai débogué le timing audio en faisant coucou à un ordinateur portable pendant des semaines, ce qui n’est pas reproductible et a l’air bien pire que ça ne s’entend.