---
title: "Rainbow Journey : du piano MIDI à une vallée qui écoute"
subtitle: "Comment une idée de jeu d'improvisation est devenue un premier jalon jouable en React, Web MIDI et Three.js"
date: 2026-09-12
time: "18:15"
author: VFG / Rainbow Journey
publishedBy: idea
status: pub
tags: ["rainbowjourney", "midi", "three-js", "react", "musique", "devblog"]
---

![Rainbow Journey : du piano MIDI à une vallée qui écoute](../images/covers/2026-09-12-rainbow-journey-du-piano-a-la-vallee-lumineuse.png)


Un piano ne produit pas seulement des notes. Il produit des hésitations, des répétitions, des silences, des élans et parfois une idée que l'on n'avait pas prévue.

Rainbow Journey est né d'une question assez simple : **peut-on transformer ce qui se passe sous les doigts en un espace que l'on comprend et que l'on a envie d'explorer ?**

Le projet est aujourd'hui un premier jalon jouable. On branche un piano MIDI, on joue, et chaque note devient à la fois une information musicale et un événement dans une vallée en trois dimensions : clavier arc-en-ciel, suggestions de directions, rubans lumineux, fontaine de particules et rivière réactive. Sans piano, un mode Demo permet de visiter le monde à la souris ou au toucher.

Ce n'est pas encore le grand jeu d'apprentissage imaginé au départ. C'est mieux décrit comme une fondation qui a déjà une boucle de jeu : **jouer, observer, choisir une suite possible, puis revoir son parcours**.

## Le point de départ : apprendre sans transformer la musique en examen

L'intention initiale n'était pas de fabriquer un professeur qui distribue des bonnes et des mauvaises notes. L'objectif était de soutenir l'improvisation : montrer ce que l'on joue, rendre visibles les relations entre les notes et proposer quelques portes entrouvertes.

Une suggestion peut dire « revenir au centre », « prendre appui » ou « changer de perspective ». Ce vocabulaire est personnalisable. Il décrit une intention de jeu, pas une vérité scientifique sur l'émotion d'un intervalle. Une tierce n'est pas déclarée joyeuse par décret, et une dissonance n'est pas une faute.

Cette décision a guidé toute l'interface. La gamme reste visible, mais les notes proposées sont des directions, pas des ordres. L'application peut afficher un intervalle, un degré, un nom d'accord ou un mot personnel selon le langage choisi. Les faits théoriques et les évocations sensibles restent séparés.

## Avant le code : clarifier le vrai piano

Le matériel a imposé une précision importante. Le piano utilisé est un Bird DP1 de 88 touches, relié par son MIDI OUT DIN à l'entrée MIDI d'une interface USB, puis à l'ordinateur.

Ce branchement est à sens unique. Le son local du piano continue donc de sortir du piano ; Rainbow Journey ne peut pas le couper ni le filtrer. Le filtrage MIDI concerne uniquement une sortie optionnelle vers une autre destination, par exemple un port virtuel ou un DAW. Cette nuance paraît technique, mais elle change complètement la promesse faite à l'utilisateur. Le logiciel ne devait pas prétendre contrôler un son qu'il ne reçoit jamais.

Autre choix fondateur : pas de compte, pas de serveur applicatif, pas de modèle distant pour inventer des suggestions. Le premier jalon devait fonctionner en local dans Chrome ou Edge, avec Web MIDI et l'accélération graphique du navigateur. Le mode Demo devait rester disponible même sans instrument.

## Une architecture à deux rythmes

Le projet utilise React, TypeScript et Vite pour l'application, les panneaux et les contrôles. La scène est rendue par Three.js dans un canvas plein écran. Tonal fournit les briques de théorie musicale et Zod valide les réglages et les fichiers importés.

Le coeur du système est un runtime singleton. Il possède l'accès Web MIDI, l'horloge logique, les notes actives, le journal de session et la relecture. React l'observe avec `useSyncExternalStore`, mais les particules et les rubans ne sont pas rangés dans l'état React : ils vivent dans la boucle de rendu de Three.js.

Deux horloges collaborent. Une horloge logique avance toutes les 25 millisecondes et avance aussi dès qu'une note MIDI arrive. La boucle graphique utilise `setAnimationLoop` pour dessiner. Ainsi, une note ne dépend pas du fait qu'une frame ait eu le temps d'être affichée. Cela a corrigé un problème subtil : dans une première version, les dates des notes étaient liées au dernier rendu. Une machine ralentie pouvait donc donner une chronologie musicale fausse.

Les deux systèmes partagent toutefois le thread principal. Découpler la logique du rendu ne crée pas magiquement un second processeur. Les workers et l'architecture native restent des options à mesurer plus tard, pas des décorations ajoutées pour faire joli dans un schéma.

## Le MIDI : la partie invisible qui doit être fiable

Une démonstration visuelle peut pardonner une approximation. Un piano, beaucoup moins. Une note tenue qui ne s'éteint pas est immédiatement perceptible.

Le routeur MIDI traite séparément les entrées et les sorties. La sortie est désarmée par défaut et ne s'active qu'après le choix explicite d'un port et une action de l'utilisateur. Les attaques sont filtrées par une gamme ou un masque chromatique de douze notes, sans transposition automatique : une note exclue est bloquée, elle n'est pas déplacée vers une autre hauteur.

Le détail le plus important est la file FIFO des attaques. La décision prise au Note On est conservée jusqu'au Note Off. Si l'on joue un Do, que l'on change de gamme alors que la touche est encore enfoncée, puis que l'on relâche la touche, le relâchement suit la décision originale. Le système ne réévalue pas l'histoire à partir du réglage courant.

Les répétitions de la même hauteur sont elles aussi distinguées. Une note jouée avec la souris et une note MIDI de même hauteur ne doivent pas se couper mutuellement. La source fait donc partie de l'identité du cycle de note.

Enfin, Panic coupe les notes émises, relâche la pédale et désarme la sortie. Un changement de port, un onglet masqué, une déconnexion ou le passage en relecture nettoient également l'état. Ce sont des détails peu spectaculaires, mais ce sont eux qui empêchent un instrument de rester bloqué dans une note fantôme.

## Donner des repères sans fabriquer une fausse intelligence

Le moteur musical commence avec une tonique, une gamme et la note jouée récemment. Il peut travailler en majeur, mineur naturel, pentatonique majeure, pentatonique mineure ou masque libre. Il regarde aussi une petite fenêtre d'attaques récentes pour estimer une continuité mélodique.

À partir de là, il classe des candidats proches de la note courante, dans les deux directions, avec des rôles différents : repos, appui, ouverture, suspension, élan, octave ou surprise. L'interface affiche de deux à quatre pistes. Elle ne promet ni la meilleure note universelle, ni une composition automatique.

Quand plusieurs notes sont jouées ensemble, l'écran bascule vers l'harmonie. Les accords sont nommés quand l'analyse est suffisamment claire et les renversements sont conservés. Deux notes ambiguës ne deviennent pas artificiellement un accord certain simplement parce qu'un libellé est plus joli.

Les couleurs suivent trois modes : identité des notes, rôles dans la tonalité ou expression. Les palettes sont modifiables, et un comparateur permet de voir les mêmes touches sous Do majeur et La mineur. La couleur aide à comparer ; elle ne remplace pas le nom de la note.

## La vallée : rendre le temps visible

Le monde 3D est une vallée originale, volontairement entre fantasy et science-fiction. Une rivière lumineuse traverse le paysage. Les notes lancent des jets depuis le clavier, puis des rubans continuent leur trajectoire avant de se dissiper. Les particules, les halos, la brume, le bloom et l'eau donnent une sensation de mouvement même pendant les silences.

La rivière utilise un shader : sa surface se déforme, ses filaments pulsent et les énergies des douze classes de notes modifient leur présence. Le terrain est généré avec du bruit procédural, la végétation et les cristaux sont instanciés, et les effets sont bornés pour éviter de créer une géométrie par note pendant toute la session.

Le monde n'est pas une simple animation décorative. La vélocité agit sur l'énergie visuelle. La durée d'une note influence la persistance du jet. Les paramètres de la fontaine, des particules, de l'eau, de la caméra et de l'atmosphère sont réglables. Vingt-huit valeurs numériques et six interrupteurs d'effets sont exposés dans l'éditeur.

Le voyage avance aussi quand on ne joue pas. C'était un choix pédagogique et esthétique : le silence n'est pas un écran vide, mais un moment où l'on peut regarder ce que la phrase précédente a laissé derrière elle.

## Enregistrer une improvisation sans la transformer en fichier opaque

Le bouton Enregistrer lance un parcours explicite. Les notes, contrôles, changements de réglages, sources et décisions de routage sont conservés dans un JSON versionné. IndexedDB garde la bibliothèque locale ; localStorage conserve les réglages courants.

La relecture est visuelle uniquement. Elle ne renvoie rien vers MIDI OUT et ne produit pas d'audio. On peut mettre en pause, déplacer le curseur, changer la vitesse et choisir entre Original, qui conserve les couleurs historiques, et Réinterprété, qui rejoue la performance avec les réglages de présentation actuels.

Cette distinction est importante : une relecture n'est pas une exportation MIDI et ne prétend pas reproduire chaque pixel d'une version future du moteur. Les parcours appartiennent au navigateur et à son origine, port localhost compris. Un export JSON reste la meilleure façon de les conserver durablement.

Le stockage actuel prend des snapshots complets toutes les cinq secondes et reconstruit la relecture depuis le début du journal. C'est suffisant pour un premier jalon, mais ce n'est pas encore une architecture de longues performances. Le journal reste en mémoire, les checkpoints manquent et la dernière tranche peut être perdue après une fermeture brutale.

## Les bugs qui ont réellement appris quelque chose

Le chantier n'a pas avancé en ligne droite. Quelques problèmes ont été particulièrement instructifs.

Le premier concernait le terrain. Une berge était générée par miroir, mais ses faces étaient orientées dans le mauvais sens. La géométrie existait bien ; elle paraissait simplement absente selon l'éclairage. Corriger les indices, les normales et l'ancrage a rappelé une règle utile en 3D : un objet invisible n'est pas forcément un objet non créé.

Le second touchait aux petits écrans. En paysage mobile, le clavier et le transport sortaient du viewport. La solution n'a pas été de réduire tout le jeu jusqu'à l'illisible, mais de traiter l'écran court comme une contrainte réelle et de conserver le transport dans la zone visible.

Le troisième était temporel : les notes semblaient recevoir l'heure du dernier rendu. Le test qui a tranché le débat était volontairement simple : injecter une note sans laisser passer de frame. L'horloge logique a permis de rendre le résultat indépendant du rendu graphique.

Enfin, Chromium en rendu logiciel tournait autour de trois images par seconde dans certains tests. Ce chiffre aurait pu conduire à une mauvaise conclusion sur la scène. Les tests avec le navigateur Chromium complet et l'accélération D3D11 ont donné le contexte correct. La leçon est générale : mesurer le chemin réellement utilisé par l'utilisateur avant de déclarer une scène 3D trop lourde.

## Ce qui est vérifié, et ce qui ne l'est pas

Le jalon a été contrôlé le 12 septembre 2026 :

- 12 tests unitaires répartis dans 4 fichiers ;
- 4 scénarios Playwright, dont des vues desktop et mobile ;
- typecheck, lint et build réussis ;
- audit npm sans vulnérabilité signalée ;
- scène non vide, animée et réactive dans le navigateur ;
- relecture sans sortie MIDI ;
- filtrage, Note Off, répétitions, persistance et imports invalides testés.

Ces résultats ne remplacent pas un essai avec le vrai DP1, l'interface USB et un DAW. La latence touche-son n'a pas été mesurée. Les sessions de dix ou trente minutes n'ont pas encore fourni de percentiles CPU, GPU et mémoire. La cible 1080p à 60 images par seconde reste une cible, pas une garantie.

Cette différence entre « testé » et « imaginé » est une partie du produit. Un devblog utile doit aussi dire où s'arrête la preuve.

## Et maintenant ?

La prochaine étape la plus concrète est l'essai accompagné avec le piano réel : ports, répétitions rapides, vélocité, pédale, Panic, changement de gamme avec une note tenue et lisibilité des suggestions pendant que les mains jouent.

Ensuite viendront le stockage par blocs et les checkpoints de relecture, puis l'éditeur plus détaillé des effets : source de modulation, gain, plage, lissage, courbes et textures. L'analyse d'accord pourra être stabilisée autour d'une courte fenêtre temporelle, avant de raffiner la projection des jets, le contraste et l'accessibilité clavier.

Le son navigateur, les défis, les objectifs de progression, l'apprentissage de la pédale et l'export MIDI restent volontairement hors de ce premier jalon. Les ajouter plus tard sera possible ; les annoncer comme déjà présents ne le serait pas.

Rainbow Journey commence donc modestement : un piano entre dans le navigateur, une note devient une couleur, puis une trace dans un paysage. La réussite du premier jalon n'est pas d'avoir construit tout le rêve. C'est d'avoir rendu la boucle assez réelle pour pouvoir maintenant jouer, regarder, mesurer et décider de la suite avec les mains sur le clavier.