Le jeu n’était pas un projet, c’était un cadeau d’anniversaire. Une luciole qu’on fait voler du doigt, des bougies à souffler en passant pile dans la flamme, un coffre au bout du voyage. Offert par un lien, à usage unique, et rangé.

Il tourne aujourd’hui sur la page 404 de ce site. Entre les deux, je n’ai ajouté aucune fonctionnalité. J’en ai retiré la moitié.

Ce qu’un jeu doit perdre pour vivre ailleurs

La version d’origine a un menu, deux modes de jeu, un écran de victoire, un service worker qui l’installe comme une application, un manifeste et ses icônes. Tout cela sert le cadeau : on ouvre, on choisit, on gagne, on l’emporte hors ligne dans un jardin.

Sur une page d’erreur, chacune de ces pièces devient un défaut. Un menu demande une décision à quelqu’un qui cherchait autre chose et ne trouve rien. Un écran de victoire promet une fin à qui ne fait que passer. Et un service worker déclaré à la racine détournerait toute la navigation du site, 404 comprises : il supprimerait exactement la page qui l’héberge.

La version embarquée n’a donc ni menu, ni mode, ni écran de fin, ni service worker. On tombe sur « Tape pour t’envoler ! », et c’est tout.

Un invité, pas un propriétaire

Le reste du travail consiste à rendre le jeu poli. Il ne touche pas au titre de l’onglet. Il ne prend pas le focus au chargement. Ses styles sont tous préfixés par une seule classe, et j’ai compté : trois règles injectées, aucune qui déborde sur la page. Il écrit deux clés dans le stockage local, le record et l’état du son.

Côté portfolio, rien n’a bougé non plus. Aucune dépendance ajoutée, aucune étape de build. Le bundle et ses neuf sons sont des fichiers statiques, versionnés comme des images, servis en chemin absolu. Ce détail-là n’est pas cosmétique : une 404 peut répondre sur n’importe quelle adresse, et un chemin relatif serait allé chercher les sons à côté de cette adresse-là.

La hauteur qui se mordait la queue

Le jeu calcule la taille de son canvas d’après celle de son conteneur. J’avais donné à ce conteneur une hauteur minimale, min-height: 100dvh, ce qui paraissait suffisant.

Ça ne l’était pas. La ligne de grille se réglait sur le canvas pendant que le canvas se réglait sur la ligne, et les deux ont enflé ensemble : 1241 pixels de section pour un écran de 860. Une hauteur minimale n’est pas une mesure, c’est une promesse. Ce qui doit se mesurer réclame une hauteur définie, pas un plancher.

La barre d’espace confisquée

Le jeu réserve la barre d’espace pour son frein. Phaser l’écoute sur window et annule le comportement par défaut du navigateur, sans regarder qui a le focus.

Sur la page 404, le résultat est net : impossible de taper un espace dans le formulaire de contact, et plus moyen d’activer un bouton au clavier. Le correctif tient en dix lignes, relues à chaque appui, et la règle est simple : si le focus est ailleurs que sur le jeu, la touche appartient à la page.

C’est un dégât qu’aucune capture d’écran ne montre. Un composant invité qui écoute la fenêtre entière ne reste pas dans son coin, et l’intégration n’est finie que le jour où l’on a vérifié ce qu’il prend, pas seulement ce qu’il rend.

Le cadrage, qui n’était pas un problème de largeur

Le canvas est en portrait. Il se cale sur la plus petite dimension de son cadre, donc il ne grandit qu’en gagnant de la hauteur, jamais de la largeur. Le titre posé au-dessus lui en mangeait 230 pixels.

Passé à côté, sur la même ligne, le jeu a gagné 1,6 fois en linéaire et deux fois et demie en surface. Aucune ligne de code du jeu n’a changé. Ce qu’on prend pour un problème de largeur est souvent un problème de hauteur.

Ce que je retiens

Une page 404 est une page qu’on subit. Autant qu’elle rende quelque chose.

Et un cadeau bien fait a une deuxième vie, à condition d’accepter de lui retirer ce qui le rendait complet. Le menu, les deux modes, l’écran de victoire : tout ce dont j’étais content, et dont la page d’à côté n’avait aucun besoin. Ce qui reste tient dans un fichier et neuf sons, et se joue au doigt.