Journal de bord : comment LUMO a été fabriqué
JavaScript vanilla, zéro dépendance, zéro build
LUMO est un jeu de plateforme de 67 niveaux qui tient dans trois fichiers JavaScript et une page HTML. Aucune bibliothèque, aucun moteur de jeu, aucune étape de compilation. On ouvre le fichier dans un navigateur et ça tourne. Cette page raconte les choix techniques derrière ce parti pris, et ce qu’ils ont coûté.
Pourquoi refuser les dépendances
Le choix n’était pas idéologique au départ, mais pratique : un jeu web qui doit démarrer instantanément sur un téléphone d’entrée de gamme ne peut pas se permettre de télécharger plusieurs mégaoctets de moteur avant d’afficher quoi que ce soit. LUMO se charge en quelques dizaines de kilo-octets et démarre immédiatement.
L’effet secondaire est plus intéressant que prévu : sans build, le cycle de développement devient trivial. On modifie un fichier, on rafraîchit la page, c’est à l’écran. Pas d’attente de compilation, pas de configuration qui casse après une mise à jour, et un projet qui fonctionnera encore dans dix ans parce qu’il ne dépend de rien qui puisse être abandonné.
Le prix à payer est réel : tout est à écrire soi-même. La physique, les collisions, la caméra, le son, les particules, le rendu — il n’y a pas de fonction toute faite pour « faire rebondir une balle sur une pente ».
La physique : un pas de temps fixe
Le piège classique d’un jeu web est de faire avancer la simulation au rythme de l’affichage. Un écran à 144 Hz produit alors un jeu différent d’un écran à 60 Hz : les sauts ne portent pas pareil, les collisions ne se produisent pas au même endroit.
LUMO sépare les deux. La physique tourne à 120 pas par seconde, quoi qu’il arrive, dans une boucle qui rattrape le temps écoulé ; seuls le rendu et les effets visuels suivent la cadence de l’écran. Résultat : la portée d’un saut est identique partout, ce qui est la condition pour qu’un niveau conçu sur une machine soit jouable sur une autre.
Le contrat de déterminisme
Tous les dangers cycliques — lames, pistons, plateformes intermittentes, marées — calculent leur position à partir du temps de jeu écoulé, jamais à partir de l’horloge réelle. C’est la règle la plus importante du code, et la source de presque tous les bugs subtils quand on l’oublie.
La raison : le code qui dessine un piège et le code qui teste s’il te touche sont deux endroits différents. S’ils ne calculent pas la position exactement de la même façon, le joueur meurt sur une lame qui, à l’écran, est ailleurs. Ce genre de bug est invisible en lecture de code et insupportable à jouer.
Des niveaux mi-écrits, mi-générés
Écrire 67 niveaux à la main est trop long ; les générer entièrement donne des niveaux sans intention. LUMO fait les deux.
Chaque niveau est d’abord écrit à la main dans un langage de description maison : on pose un tremplin, une rangée de pointes, un gouffre, un enchaînement d’ennemis. Les distances y sont exprimées en proportion de la vitesse du niveau, et non en pixels — un motif reste ainsi franchissable quelle que soit l’allure, du monde 1 à 320 au monde 19 à 610.
Ensuite, cinq passes automatiques enrichissent ce squelette : elles installent des scènes composées, sculptent le relief pour que le sol ne soit jamais plat, sèment des dangers et des ennemis dans les espaces restants, et rééquilibrent enfin chaque monde pour que le troisième niveau soit toujours plus dense que le premier. Chaque passe respecte les zones réservées : rien n’est jamais posé sur un point de passage ni sur une zone d’atterrissage.
L’équité comme contrainte de conception
La règle absolue du jeu : tout danger doit être franchissable quel que soit le moment où le joueur arrive dessus. Soit on roule dessous, soit on saute par-dessus. Aucun obstacle n’a le droit de bloquer le passage en attendant un cycle précis.
Un validateur automatique vérifie cette promesse à chaque chargement : il signale les gouffres trop larges pour être franchis, les dangers suspendus au-dessus du vide, les murs infranchissables, et les pièges à longue portée trop proches d’un point de passage. Plusieurs règles du jeu sont nées de morts jugées injustes pendant les tests, puis inscrites en dur pour ne jamais réapparaître.
Un exemple parlant : chez la Reine des Spores, l’espacement des nuages est calculé pour qu’un saut par-dessus l’un retombe toujours dans un intervalle sûr avant le suivant. Sans cette contrainte, le champ de spores devenait un mur infranchissable — ce qui était le cas dans une première version.
Sept boss, sept jeux de coups distincts
La tentation, quand un patron de boss fonctionne, est de le repeindre. LUMO s’est imposé l’inverse : chaque gardien possède ses propres attaques. Deux d’entre eux demandent même des réponses opposées — le Rapace apprend à sauter les dangers rampants, le Dévoreur exige de rester au sol sous son arche.
Cette contrainte coûte cher en développement, mais c’est ce qui fait qu’un boss se retient. Un joueur ne mémorise pas « le troisième boss », il mémorise « celui où il faut lire la hauteur du rayon ».
Le son : rien n’est enregistré
Il n’y a pas un seul fichier audio dans LUMO. Tous les bruitages et toute la musique sont synthétisés en direct par le navigateur : des oscillateurs, du bruit filtré, des enveloppes. Chaque monde a sa tonalité et sa gamme, et la musique est générée note par note pendant que tu joues.
Le bénéfice est le même que pour le reste : zéro octet à télécharger, et une bande-son qui ne se répète jamais exactement.
Une leçon apprise à la dure
Le Monde Renversé, le dernier, a dû être entièrement reconstruit. Sa première version enchaînait les couloirs d’inversion presque bout à bout. Comme chaque passe automatique réserve une marge de sécurité autour d’un couloir, l’espace restant était trop réduit pour accueiller quoi que ce soit : les niveaux se sont retrouvés avec zéro ennemi. Un testeur a résumé le problème en une phrase : « le dernier monde est très très facile, pratiquement pas d’ennemi ».
La deuxième version alterne délibérément des segments de combat et des couloirs d’inversion, avec assez d’espace entre eux. La leçon vaut au-delà de ce jeu : quand un système automatique ne produit rien, ce n’est pas toujours le système qu’il faut corriger — c’est parfois la place qu’on lui a laissée.
En chiffres
| Élément | Quantité |
|---|---|
| Mondes | 20 |
| Niveaux | 67, dont 7 arènes de boss |
| Étoiles à gagner | 201 |
| Lucioles | 2 203 |
| Ennemis placés | 951 |
| Durée de référence totale | environ 65 minutes |
| Dépendances externes | 0 |
| Étapes de compilation | 0 |
| Fichiers audio | 0 |