Blog

  • Une IA peut-elle faire une démo VR toute seule ?

    Six jours, Claude Code, Unreal Engine et un casque Quest 3. J’ai demandé à une IA de construire une balade en réalité virtuelle, de la première ligne de code à l’application installée dans mon casque. Ce qu’elle a su faire seule, et ce que j’ai appris d’Unreal en la regardant travailler.

    Le lac, à 3 min 52 : le héron vient de se poser sur la barque. Image de la version finale, rendue sur PC.

    Ces derniers mois, les IA ont fait un bond, et les MCP se sont multipliés. Un MCP, c’est un connecteur qui laisse une IA piloter un logiciel : Unreal Engine a maintenant le sien, Blender aussi, et on en trouve pour ComfyUI, l’outil de génération d’images que j’utilise. Sur le papier, une IA peut donc faire tourner seule les outils d’un studio de jeu vidéo. J’ai voulu voir ce que ça donne en vrai.

    Le terrain d’essai est volontairement modeste : une démo VR de quatre minutes, sans interaction, qui tourne sur mon Quest 3. On est assis dans une barque qui dérive sur une rivière en forêt, du crépuscule à la nuit, dans le style des dessins animés japonais des années 80 et 90. La règle du jeu : pousser l’autonomie de l’IA au maximum. Elle écrit le code, génère les images et la 3D, choisit les sons, construit la scène dans Unreal, compile l’application, l’installe dans le casque et mesure sa fluidité. Moi, je fournis quelques images de référence et la musique, je mets le casque et je dis ce qui ne va pas.

    L’IA, ici, c’est Claude Code, l’agent de programmation d’Anthropic, avec le modèle Claude Opus 5.5. Je l’appellerai simplement l’agent. Il a travaillé du samedi 17 octobre, 14 heures, au vendredi 23, midi.

    Au bout de ces six jours, la balade tournait à 72 images par seconde dans le casque, et j’avais mes réponses aux trois questions de départ. Jusqu’où une IA peut-elle aller seule sur un projet comme celui-là ? Audio IA, assets IA : qu’est-ce qu’on sait faire en octobre 2026 ? Et qu’est-ce que j’allais apprendre d’Unreal Engine, que je ne connaissais que côté joueur ?

    Tout ce qui illustre cet article sort du dépôt du projet : captures du casque, vidéos de contrôle, mesures, journaux. L’agent a tout gardé.

    La bande-annonce : huit plans de la version finale, rendue sur PC en 4K. Dans le casque, l’image est moins fine.

    Le terrain d’essai

    La balade

    La rivière fait 239 mètres. La barque la descend à un mètre par seconde, sans jamais accélérer, et la caméra ne penche jamais sur le côté : ce sont les deux règles de base pour ne rendre personne malade en VR assise. Les yeux sont à 1,10 m au-dessus de l’eau, et le parcours ne tourne jamais de plus de 1,15 degré par seconde.

    Sept moments ponctuent les quatre minutes : le noir et le clapotis du départ, le ponton dans la brume, une bête lumineuse qui passe sous l’eau tout près de la barque, un troupeau qui traverse un gué, le passage sous un pont de statues, une créature géante devant la lune, puis le lac du final, où un héron vient se poser sur la barque.

    La balade vue de dessus, une image par moment (rendu PC de la version finale), et la musique sur laquelle tout est calé.

    La musique commande tout. « Luminous Wonder » est un morceau que j’ai généré sur Suno. L’agent en a analysé la structure et repéré les frontières : la plus forte, une modulation, tombe à 2 min 38 dans la démo et ouvre la scène de la lune. Après le noir du début, chaque moment démarre sur une de ces frontières. Le séquencier suit la musique, jamais l’inverse.

    L’atelier

    Côté matériel, rien d’exotique : un PC sous Windows avec WSL2, une RTX 3090 de 24 Go et le Quest 3 branché en USB. La carte graphique est partagée entre Unreal, ComfyUI et Blender, et l’agent ne lance jamais deux calculs lourds à la fois : un fichier verrou fait office de feu rouge.

    L’agent tourne dans WSL2 et pilote tout le reste : Unreal Engine 5.8 avec son serveur MCP, ComfyUI en local avec ses modèles d’image et de 3D (Z-Image Turbo, Qwen-Image-Edit, TRELLIS.2, Pixal3D, BiRefNet), Blender sans interface, Freesound pour des sons du domaine public, GitHub pour le code. Le casque se pilote par adb, l’outil de débogage d’Android : installer l’application, la lancer, capturer la vue du casque, lire la fréquence d’images. De mon côté, deux abonnements, Midjourney pour les images de référence et Suno pour la musique. L’agent n’a utilisé aucun service payant en dehors de Claude lui-même.

    L’atelier. Mes demandes et mes retours passent par la conversation, et la mémoire du projet tient dans quelques fichiers. Chaque outil a son canal ; l’agent et moi rejoignons le casque par deux chemins différents.

    Les règles

    Le projet tient dans un fichier de consignes, CLAUDE.md, que l’agent relit au début de chaque session. On y trouve la vision, le séquencier, les contraintes du Quest, et surtout une méthode. Cinq règles font l’essentiel du travail :

    1. La scène est du code. Tout ce qui la construit est un script Python versionné et rejouable, et une seule commande, python pipeline/build.py, reconstruit le projet depuis zéro.
    2. Le MCP sert à explorer. Toute modification retenue passe dans un script avant le commit.
    3. Tester petit avant de produire en série : deux ou trois essais courts, vérifiés, avant tout lot.
    4. Une décision attendue reçoit un défaut réversible. L’agent l’applique, la note avec ce qui changerait si je tranchais autrement, et continue.
    5. Pas de service payant, d’installation de logiciel ni d’envoi de données vers un service imprévu sans mon accord écrit.

    Deux fichiers tenus par l’agent complètent le dispositif. SUIVI.md donne l’état du projet : blocages, décisions en attente, points à vérifier au casque. HISTORY.md est le journal, une entrée par itération, et chaque itération donne un commit. Quand je reviens après une nuit, c’est le suivi que je lis.

    Mon rôle tient en une ligne du fichier : directeur artistique et relecteur. Je ne touche ni au code ni à la scène.

    Six jours en accéléré

    134 commits en six jours, heure par heure. La nuit est grisée.

    L’agent a fait 134 commits : 16 le premier après-midi et le premier soir, puis 31, 38 et 30 les trois jours suivants, et encore 19 sur la fin, de part et d’autre d’une pause de 37 heures. Le graphique montre aussi ses nuits. Je lui ai laissé les clés trois fois pour longtemps : huit heures le 18 au soir « pour repenser la scène complète », six heures sans casque le 19 après-midi, puis vingt heures d’un coup : « je te rends la 3D, le casque et tout, tu as 20 h devant toi ».

    Le premier jour a donné le ton. À 14 h 30, trente musiques générées en local, aucune écoutée, et ma réponse : « C’est long et c’est moche. » Unreal était installé à 19 h, la première application pour le Quest compilée à 20 h 49. Peu après minuit, je pouvais m’asseoir dans le casque, au milieu d’un premier décor de formes grises.

    Le deuxième jour, j’ai appris qu’un casque autonome ne sait pas appliquer de filtre « dessin animé », et l’agent a passé l’après-midi à gagner des millisecondes. Le soir, une phrase a tout changé : « le truc le plus moche ce sont les arbres ». Dans la nuit, la forêt entière était refaite en images peintes.

    La suite tient en 69 itérations de revue au total, et en quelques chantiers qui reviennent : les statues, l’eau, le tangage, une bête sous l’eau, un héron. Le 23 octobre à 11 h 45, l’agent a installé une version finale dans le casque, à côté de la version de travail.

    Toutes les vidéos de contrôle rendues pendant ces six jours suivent exactement la même trajectoire de caméra. Un même instant de la balade se retrouve donc, au même cadrage, d’une version à l’autre. Dans le montage qui suit, la barque avance sans coupure et le décor change de version toutes les six secondes.

    Le passage du pont, de 1 min 38 à 2 min 20, en sept versions du 19 au 23/10 (rendus PC). La date et le changement sont incrustés.

    Les captures du casque racontent la même histoire avec l’image du Quest lui-même : celle qu’affiche le casque, capturée par l’agent.

    Trois instants de la balade (0 min 30, 1 min 52, 3 min 52), vus dans le casque du 18 au 23/10.

    Jusqu’où l’agent est allé seul

    Ce qui le fait avancer

    La première leçon date du premier après-midi, et elle vaut autant pour moi que pour lui. Ma toute première demande : une chaîne de production musicale sur ACE-Step, un modèle qui tourne en local, en explorant trente graines en brouillon avant de rendre la meilleure en qualité. L’agent l’a fait à la lettre, sans pouvoir écouter une seule note. Une demi-heure plus tard, je lui écrivais : « Y’a pas vraiment de mélodie et après 15 s on dirait des loops qui se répètent […] je vois pas pourquoi t’en génères 30. C’est long et c’est moche. » La règle « tester petit » est née là. Le fichier de consignes le dit maintenant en une phrase : « Un fichier techniquement valide ne dit rien de sa qualité. »

    La deuxième règle, c’est le défaut réversible. Le premier soir, avant même qu’Unreal soit installé, l’agent avait neuf décisions en attente : quelle image sert de référence de style, où couper entre deux moments, à quelle échelle faire les créatures, quelle forme donner à la coque. Plutôt que de m’attendre, il applique un défaut, le note dans le suivi, et continue. Il y en a eu vingt sur le projet. J’en ai tranché certaines au casque. D’autres jamais : le défaut tenait.

    Une itération. L’agent boucle seul ; mes retours entrent dans la boucle quand j’ai le temps.

    Troisième règle, la scène comme du code : 58 scripts Python, un peu plus de 10 000 lignes, et une commande qui reconstruit tout. J’en ai vu l’intérêt dès le premier jour : « comme on automatise tout en Python je peux reprendre un élément sans tout refaire ». Une meilleure image, une autre musique : on dépose le fichier, on relance le script concerné. Il y a un revers. Chaque reconstruction réécrivait des fichiers binaires et faisait gonfler le dépôt, jusqu’à ce que l’agent ne réécrive plus que ce qui a changé.

    Le soir du deuxième jour, je lui ai envoyé une liste de défauts vus au casque, avec une consigne : enquêter sans toucher au casque. Il a consigné cinq symptômes et cinq causes avant de corriger quoi que ce soit. Le plancher dont la texture défilait : une texture projetée en coordonnées du monde, sur un objet qui avance. La voix entendue à l’entrée du lac : de la parole anglaise dans un enregistrement de Freesound, détectée par Whisper. Le brouillard « peu visible » : à 154 mètres, il ne voilait encore que la moitié de l’image.

    Ce qu’il a fait pendant que je n’étais pas là

    La nuit du 18 au 19 en donne la mesure. Vers 23 h 30, je valide l’idée des arbres peints et je lui laisse huit heures « pour repenser la scène complète et ajouter plus de vie et de volumes ». À 0 h 38, il commite une forêt entièrement repeuplée : un catalogue de familles d’images, leur génération en local, leur placement, un seul matériau pour toutes. Suivent des plantes qui luisent la nuit, une lanterne qui tangue, un arbre colossal repeint, et une reconstruction complète du projet vérifiée vers 1 h 45. Seul accroc : à 1 h 30, l’interface du Quest s’est mise à planter en boucle, et le casque a disparu du câble USB.

    Pendant les vingt heures du 19 au 20, il a appris à piloter Blender sans interface (deux secondes par lancement), retiré deux bancs qui flottaient dans la barque, régénéré et réimporté tous les modèles 3D, compris pourquoi l’eau faisait « pas fini », puis animé le héron du final et lui a donné un cri.

    Le casque aussi, il le pilote à distance. Il installe l’application, la lance, capture la vue et mesure la fluidité, tout par adb. Le 19 au matin, le Quest posé sur le bureau perdait son suivi de position et affichait une boîte de dialogue système qui bloquait l’application. Je l’ai autorisé à cliquer « Continuer sans le suivi ». L’arbre d’interface d’Android ne permettait pas d’atteindre le bouton ; il est passé par le clavier, flèche bas puis validation, en vérifiant chaque pas par une capture. Verdict : sans suivi de position, le Quest refuse d’ouvrir une application VR. Le casque a besoin de lumière pour se repérer. Le lendemain, dans la pénombre, l’agent relançait sa mesure toutes les trente minutes et ne gardait que celles où la carte graphique calculait bien la scène.

    Les MCP, en vrai

    J’étais parti avec l’idée que les MCP feraient le gros du travail. Celui d’Unreal n’exposait d’abord que la gestion de ses « skills » ; il a fallu activer ses jeux d’outils (éditeur, PCG, Niagara, configuration) pour qu’il serve à quelque chose. Ensuite, il a servi à regarder : inspecter la scène, interroger des objets, capturer la vue de l’éditeur.

    Pour construire, l’agent est passé par des scripts Python, exécutés dans l’éditeur ouvert ou par Unreal en ligne de commande. ComfyUI, il le pilote par son API, avec des workflows enregistrés et des graines fixées. Blender, par un script, sans fenêtre. La raison tient dans la deuxième règle : un geste fait par MCP ne se rejoue pas, un script si. Quand tout le projet se reconstruit d’une commande, on peut tout casser sans crainte.

    Mon point de départ était donc à moitié juste. Les MCP donnent à l’IA des yeux dans les outils, mais sur six jours, ce sont les scripts qui ont tenu le projet debout.

    Le casque, l’angle mort de l’agent

    L’agent voit des images : les captures de revue, les vidéos rendues sur PC, la vue du casque capturée par adb. Il lit des mesures. Ce qui lui échappe, c’est tout ce qui passe par le corps : le relief que donnent deux yeux, l’échelle, le mouvement de la tête, le confort, le son.

    Ce que l’agent pouvait juger seul, et ce qui me revenait.

    L’épisode le plus parlant date du deuxième soir. L’agent m’annonce 72 images par seconde. Le chiffre était juste, mais le casque, posé sur le bureau, regardait le ciel. Droit devant, au niveau de la rivière, on était à 62. Depuis, il capture la vue du casque avant toute mesure, pour savoir ce qu’il mesure.

    Ce que voit le casque posé sur le bureau : l’intérieur de la barque, de travers. Une mesure de fluidité prise dans ces conditions ne vaut rien.

    Ce que l’écran cache, le casque le montre tout de suite : des arbres peints qui pivotent avec la tête, des bandes verticales qui scintillent parce que des plans se croisent différemment pour chaque œil, ou encore le masque qui empêche l’eau d’entrer dans la barque, resté à la forme de l’ancienne coque, une ellipse, alors que la nouvelle est pointue. Aucun de ces défauts ne se voyait sur les captures.

    Le troupeau, lui, m’a gêné pendant deux jours avant que je trouve les mots : « J’ai enfin compris ce qui me choquait : leurs animations sont synchronisées. » Chaque bête avait bien son propre décalage de pas, mais réglé par une fonction qui n’était pas sauvegardée avec le niveau. Au lancement, toutes repartaient de zéro, ensemble.

    Le dernier matin, je n’arrivais toujours pas à montrer un défaut : « j’ai essayé de faire des screenshots mais pas très pratique ». J’ai demandé un raccourci sur le bouton A de la manette. L’agent a dû apprendre deux choses en route : la commande de capture du moteur n’avait jamais été essayée sur un Quest (elle marche, et donne l’image de l’œil gauche en pleine définition), et OpenXR ne remonte une touche que si elle est liée à une action déclarée dans la configuration. Chaque capture est datée par l’instant de la balade, lu dans le journal de l’application.

    Le premier usage a été le bon. Mes trois captures pointaient un « bug de texture » sur la droite, avant le gué. C’était une bande de forêt peinte, un plan de 160 mètres de large vu par la tranche, dont l’agent n’avait vérifié que le centre. Le défaut avait déjà été signalé et « corrigé » la veille, mais sur un seul des deux anneaux de bandes qui ferment l’horizon. Tant qu’il raisonnait sur l’aspect, l’agent a cherché du côté du troupeau, du bois mort, d’une statue, des feuillages. Il l’a trouvé en rendant l’éditeur au même instant que ma capture et en masquant les objets par moitiés : sept rendus. La leçon est dans son journal : un objet plat se vérifie sur toute son étendue, et une capture datée vaut mieux qu’une description.

    Une de mes captures au bouton A, à 1 min 58 : les aplats verts sont une bande de forêt peinte vue par la tranche.

    Ce qui reste à l’humain

    Le goût, d’abord. Les deux phrases qui ont le plus changé la démo sont des jugements : « le truc le plus moche ce sont les arbres », et le lendemain soir, « l’eau, c’est clairement ce qui fait pas fini ». Dans les deux cas, l’agent a trouvé la solution. Il n’avait pas désigné le problème.

    Les idées de mise en scène, ensuite. Le pont de statues vient d’un souvenir de cinéma, la descente en barque du Seigneur des Anneaux entre deux statues géantes. Le héron du final vient d’une fin que je trouvais « un peu morne », le tangage d’une barque « vraiment trop fixe ». L’agent les a mises en œuvre ; aucune de ces trois idées ne venait de lui.

    Restent le périmètre (« ne fais rien de plus », « tout noter sans corriger »), les accords pour installer Blender ou télécharger un modèle 3D, et le corps : redémarrer le casque, redémarrer l’éditeur pour activer un plugin, poser le Quest à la lumière, le mettre sur la tête.

    Qui a fait quoi, tâche par tâche, d’après le journal et le manifeste des assets.

    Images, 3D, son : ce qui marche en octobre 2026

    Les images peintes ont sauvé la forêt

    Le soir du deuxième jour, après une journée passée à gagner des millisecondes, la forêt restait le point faible : des arbres en 3D aux feuillages en boules texturées, qui faisaient jeu des années 90. L’agent a proposé trois pistes : des plans peints, des touffes festonnées, un autre décor. J’ai voulu les voir toutes : « Tu peux proposer les différentes idées sur les arbres dans le même APK ? » Vingt-quatre minutes plus tard, j’avais une application où la forêt changeait toutes les douze secondes, avec une lettre à la proue : A pour les arbres 3D, B pour des arbres peints, C pour des touffes ombrées. Mêmes positions, même fluidité.

    L’application de comparaison vue au casque : la même forêt en arbres 3D (A), en arbres peints (B) et en touffes ombrées (C).

    Le verdict est tombé à 23 h 30 : « les arbres peints ça rend vachement bien ! […] ça prend tellement moins de polygones qu’il faut creuser en ce sens ». Un arbre peint, c’est une image posée sur un plan de deux triangles ; l’arbre 3D qu’il remplaçait en comptait 2 700, pour un résultat bien pire. Les décors de dessin animé sont faits comme ça, et le jeu vidéo a un nom pour ce procédé : le billboard, ou imposteur.

    Cette nuit-là, l’agent a construit un système complet : un catalogue de familles d’images, leur génération et leur détourage en local, leur placement par script, un matériau unique. La démo compte aujourd’hui 114 images peintes. Elles sont posées 7 837 fois dans le décor, et 749 fois en mouvement : oiseaux, papillons, libellules, feuilles et pétales qui tombent. La forêt 3D a disparu, et c’est ce qui paie le reste : sur une scène bien plus dense, la démo tenait toujours 72 images par seconde.

    Seize des 114 images peintes, générées en local et détourées.

    La chaîne tient en quatre étapes. Z-Image Turbo, un modèle d’image qui tourne sur la carte graphique du PC, peint un sujet seul, sur fond gris uni, éclairé par la gauche. BiRefNet le détoure. Un script nettoie le résultat, puis l’agent pose l’image dans la scène. Les ratés ont été nombreux : des restes de fond autour des feuillages, des nuages qui s’invitaient dans les images, une plaque d’herbe peinte sous chaque tronc, des textures importées en 32 pixels par Unreal, une forêt où le même arbre revenait sans cesse (« la texture des arbres sur la gauche se répétait beaucoup trop souvent »), et la moitié des arbres éclairés du mauvais côté le jour où l’agent en a retourné une sur deux pour casser la répétition.

    D’une image générée à la forêt du casque : l’image brute, le masque, l’image nettoyée, puis la scène.

    Ce que j’apporte encore

    Les images de référence viennent de moi, sur Midjourney : des ambiances pour plusieurs moments, la barque, le ponton, la lanterne, les créatures, des textures, deux ciels. L’agent écrit les prompts dans un fichier dédié, avec une consigne : jamais de nom de studio ni d’artiste. Ma texture de mousse a servi de modèle pour repeindre les autres textures en local, avec Qwen-Image-Edit. Elles restent moins belles que l’originale.

    Mes images de référence, à gauche, et les mêmes lieux dans la démo, à droite (rendu PC).

    Un mot sur le style. Le projet le décrit comme « inspiré de l’animation japonaise des années 80-90 », sans plus. Aucun prompt ne nomme de studio ni d’artiste, et les créatures sont des designs originaux. Le premier soir, l’agent a quand même consigné une alerte : un concept provisoire montrait des créatures trop proches de celles d’un film d’animation connu. Il n’a servi qu’à extraire une palette de couleurs.

    De l’image à la 3D

    Pour les objets principaux, l’agent reconstruit un modèle 3D à partir d’une seule image, en local. Le premier jour, il a comparé deux modèles, TRELLIS.2 et Pixal3D, sur sept de mes images. Chacun gagne sur des objets différents : TRELLIS.2 sur la lanterne, le ponton, le troupeau et la créature volante, Pixal3D sur la bête des abysses. On a gardé les deux. Les silhouettes sont justes ; le détail est lissé, et l’arrière de l’objet, que l’image ne montre pas, est deviné.

    La barque : mon image de départ, puis deux reconstructions 3D faites en local, Pixal3D et TRELLIS.2, chacune sous quatre angles.

    Le pont est l’histoire la plus complète du projet. Le deuxième soir, je lance une idée en passant : « la fameuse descente en barque du Seigneur des Anneaux quand ils passent entre les deux statues géantes, mais ça ferait changer beaucoup d’ambiance, à débattre ensemble ». Le lendemain, l’agent revient avec trois concepts aux designs originaux, générés en local : deux gardiens moussus, une tête couchée dans la rivière, une arche faite de deux figures agenouillées. J’ai adoré le pont. La tête était « trop belle » pour ne pas finir sur une berge.

    Les trois concepts proposés par l’agent à partir de mon idée.

    J’ai fait l’image du pont sur Midjourney, en pierre nue : passée en 3D, la mousse se transforme en blocs. L’idée était d’obtenir un objet sans végétation, à habiller ensuite. L’agent l’a reconstruit avec TRELLIS.2, a calculé la mousse dans le matériau, sur les faces tournées vers le haut, et l’a posé sur la rivière. Au casque, le soir même : « waouh ».

    Le pont, de mon image Midjourney au casque.

    Deux questions m’ont occupé entre-temps : « Quel élément de la chaîne fixe le nombre de polygones ? », puis « est-ce le Quest qui impose la limite, ou nous qui nous mettons des limites absurdes ? ». Réponse : un peu des deux. Meta recommande de rester sous 1,5 million de triangles par image sur le Quest 3, et nos 500 000 étaient prudents. Mais le détail se perdait avant le casque : le workflow d’exemple de ComfyUI remaillait la forme sortie de TRELLIS à une résolution de 768, avec vingt passes de lissage. À 1 536 et cinq passes, le pont gagne nettement.

    Puis une de mes remarques a débusqué deux bugs : « Ta vue 3D sur fond gris est très belle, c’est pas du tout ce que je vois dans le jeu. » L’agent a monté une planche de comparaison, la vue tournante du modèle à côté de trois vues depuis la barque, et trouvé deux défauts d’import présents depuis le début. Nanite, la géométrie virtualisée d’Unreal, activée par défaut, faisait afficher au Quest un maillage de secours dix fois plus pauvre. Et les réimports gardaient l’ancienne texture : mon pont portait celle de son substitut gris, la nouvelle barque celle de l’ancienne. L’agent venait de projeter mon image sur la face avant du pont pour rattraper le détail. Son journal le résume : « La projection, astucieuse, compensait un bug qu’on n’avait pas vu. »

    Le pont et son passage en sept versions, au même instant de la balade (1 min 52).

    Dernier essai, Hunyuan3D 2.1, téléchargé avec mon accord : une géométrie plus sculptée, mais sans texture. On est resté sur TRELLIS.2. Les reconstructions prenaient 27 minutes, jusqu’à ce qu’on comprenne que la mémoire de la carte graphique était partagée avec l’éditeur d’Unreal ; avec un nœud de nettoyage de la mémoire branché dans ComfyUI, on est tombé à trois minutes par modèle.

    La musique vient de Suno

    L’échec d’ACE-Step ne s’est pas arrêté aux trente brouillons. Après un prompt réécrit, la mélodie était meilleure, mais « ça fait son midi, ça sature dans les aigus ». L’agent l’a confirmé par la mesure : un son deux fois plus brillant que celui d’un morceau de Suno (le centroïde spectral, le centre de gravité du spectre), et une stéréo déphasée. À 14 h 45 le premier jour, on a tranché : la musique viendrait de Suno, générée par moi sur un prompt écrit par l’agent. À 15 h, « Luminous Wonder » était dans le dépôt.

    Suno fournit aussi les pistes séparées d’un morceau, les stems. J’ai déposé les dix, et l’agent les a placées autour de la barque, en douze sources sonores : le clavier devant à droite, à hauteur de la proue, les basses sous la barque, là où passera la bête, les cordes en nappe derrière, les cuivres à douze mètres devant. Dans le casque, la musique vient de quelque part.

    Les pistes de la musique, placées autour du spectateur assis.

    Les sons, choisis sans oreilles

    L’agent ne peut rien écouter. Il a pourtant choisi les dix-sept sons de la démo dans la banque Freesound, uniquement parmi ceux qui sont dans le domaine public. Pour compenser, il mesure. Whisper, un modèle de transcription, lui dit s’il y a de la parole : c’est comme ça qu’il a trouvé la voix anglaise que j’avais entendue à l’entrée du lac, au début d’un enregistrement d’ambiance. Le niveau sonore et la part de basses fréquences lui signalent la circulation ou le vent.

    Le 19 au soir, je lui signale qu’à la fin, « on croit entendre une voiture qui démarre ». Il découpe l’ambiance du lac en demi-secondes, mesure la part de graves de chacune, et trouve un grondement 33 secondes après le début du passage joué. L’ambiance s’arrête maintenant deux secondes avant.

    L’ambiance du lac lue sans l’écouter : la voix du début, le passage gardé, puis le grondement.

    Même méthode pour le héron, dont le cri précède l’arrivée de deux secondes et demie et vient de sa direction : l’agent a vérifié l’enveloppe et le spectre des enregistrements, et m’a laissé, je cite, « la validation à l’oreille ».

    Le héron du final, précédé de son cri. Dans le casque, le son vient de sa direction ; dans cette vidéo, il est centré.

    Le bilan, outil par outil

    Ce qu’on a demandéOutilOùVerdict
    Décors peintsZ-Image Turbo, BiRefNeten localexcellent, c’est ce qui a sauvé la forêt
    Images de référenceMidjourneymon abonnementexcellent
    Textures repeintes, vues manquantesQwen-Image-Editen localcorrect, moins beau que Midjourney
    3D depuis une imageTRELLIS.2, Pixal3Den localbonnes silhouettes, détail lissé
    3D, autre essaiHunyuan3D 2.1en localplus sculpté, sans texture : écarté
    MusiqueACE-Stepen localdécevant
    MusiqueSunomon abonnementexcellent, pistes séparées comprises
    SonsFreesound, Whisperen ligne (gratuit), en localbon, par la mesure ; rien de généré
    Relief tiré des imagesDepth Anythingen localne tournait pas, et pas fait pour des images « cartoonisées »
    Simulation d’eauNiagara Fluidsdans Unreal« c’est très moche »
    Code, scène, compilation, mesuresClaude Code, Claude Opus 5.5en lignea tout fait tourner

    Unreal Engine vu par un joueur

    Unreal Engine, je le connaissais par les jeux qui l’utilisent. Ce chapitre suit les questions que j’ai posées à l’agent pendant ces six jours, et ce que chacune m’a appris.

    « Ça devait pas être un filtre de rendu UE ? »

    Le deuxième matin, j’ai regardé les captures et écrit : « Sur tes screenshots je ne vois pas le rendu dessin animé. Ça devait pas être un filtre de rendu UE ? » Dans ma tête de joueur, un style graphique, c’est un filtre posé sur l’image. Sur un Quest, c’est impossible : le rendu mobile du casque n’a pas le HDR dont le post-traitement a besoin. Le look peint est donc passé dans les matériaux. Un matériau maison, qui ne dépend pas de l’éclairage du moteur, ombre chaque objet en deux tons, avec un bord peint, un liseré de contre-jour et une lumière propre à chaque moment de la balade.

    Sur PC, pour les vidéos de contrôle, l’agent avait gardé un filtre de peinture. Je lui ai demandé de l’enlever, « vu qu’on l’a pas dans le Quest » : une vidéo plus jolie que le casque ne m’aide pas à juger.

    Le même instant en rendu PC, avec le filtre de peinture et sans. Le Quest ne sait pas appliquer ce filtre.

    C’est devenu un fil rouge : l’éditeur d’Unreal affiche une image correcte, et le casque en montre une autre. Le brouillard n’a pas été rendu au casque avant le deuxième après-midi, parce que sa passe exige elle aussi le HDR mobile. Les shaders calculés en 16 bits pixellisaient le ciel. Un matériau qui ne déclare pas qu’il sert à des objets instanciés s’affiche en gris. Et le profil Quest 3 d’Unreal hérite d’un profil Android moyen, qui affiche toutes les textures un niveau en dessous et coupe le filtrage anisotrope ; l’agent l’a découvert dans le journal du casque, le 21.

    « C’est vraiment une limite du casque ? »

    Le même jour, au casque, j’étais « surpris par le manque de polygones […] c’est vraiment une limite du casque ? ». J’ai demandé une version bien plus détaillée, quitte à tomber à 25 images par seconde, pour comparer. L’agent l’a installée à côté de l’autre : elle tournait entre 44 et 50.

    Pour savoir où partait le temps de calcul, il a mesuré en direct. Pendant que la démo tourne dans le casque, il envoie une commande à la console du jeu par adb, et compare le temps de calcul d’une image avant et après. Pour tenir 72 images par seconde, chaque image doit être calculée en moins de 13,9 millisecondes. Cacher les maillages du décor faisait gagner 8 millisecondes ; baisser la résolution à 60 %, un tiers de milliseconde. La scène était limitée par la géométrie. Baisser la résolution, le premier réflexe d’un joueur sur PC, ne servait à rien.

    Ce que chaque réglage faisait gagner par image, mesuré au casque le 18/10.

    Les mesures ont tranché à ma place. Couper l’anticrénelage faisait gagner 3 millisecondes, mais les bords scintillaient : on l’a gardé à 2×. Les ombres du soleil coûtaient 1,4 milliseconde pour un décor qui ne dépend pas de l’éclairage du moteur : coupées. Un prépassage de profondeur faisait tomber la démo à 27 images par seconde : écarté.

    Les niveaux de détail, ensuite. Quand j’ai demandé si Unreal savait faire « une version adaptative », la réponse était oui, sans Nanite, que le Quest ne gère pas : l’arche a reçu trois versions de 300 000, 100 000 et 30 000 triangles, choisies selon la distance. Une régression de fluidité, plus tard, m’a appris quelque chose sur la VR elle-même : le pont restait dans sa version la plus fine bien trop loin, parce qu’en VR un objet de 42 mètres paraît « grand à l’écran » beaucoup plus longtemps qu’on ne croit.

    « C’est trop bizarre »

    Le matin du troisième jour, après la première nuit en images peintes, j’ai écrit : « c’est très fourni et je confirme que les sprites c’est la bonne idée à creuser », et « les oiseaux et les feuilles qui tombent trop bien ! ». Puis : « les sprites qui bougent/tournent quand on bouge ou tourne la tête, c’est trop bizarre. Il faut que les sprites soient fixes. » Selon l’orientation de ma tête, des arbres se rentraient dedans ou non.

    Trois façons d’orienter les arbres peints, vues de dessus, et pourquoi seule la troisième tient en VR.

    Le schéma résume trois versions successives. Au départ, chaque plan se tournait vers l’œil. Chaque œil ayant sa position, un même plan avait deux orientations, et deux arbres voisins se croisaient pour un œil et pas pour l’autre : d’où les bandes verticales. L’agent a ensuite rendu tous les plans parallèles à la vue. Plus de croisement, mais les arbres pivotaient quand je tournais la tête. La version finale oriente chaque plan une fois pour toutes, au placement, vers le point de la rivière d’où on le voit à 45° devant. Deux voisins visent presque le même point : leurs plans restent presque parallèles, et rien ne bouge plus avec la tête.

    Chaque correction en a amené une autre. Une touffe vue par la tranche devenait un grand aplat : un fondu selon l’angle de vue l’efface. Ce fondu effaçait aussi les nénuphars, toujours vus en rasant depuis la barque : ils en sont exclus. Immobiles, les arbres étaient devenus figés : un balancement lent, calculé dans le shader, leur rend du vent, le pied fixe et la cime qui bouge. Ce vent est resté invisible un moment, à cause d’une erreur d’unité : cinq millimètres de balancement.

    Il fallait aussi du relief, parce qu’une image plate reste plate pour deux yeux. Depth Anything, un modèle qui estime la profondeur d’une image, refusait de tourner dans ComfyUI, et je lui ai confirmé que ça ne marcherait pas : « tes images sont cartoonisées et le moteur ne sait pas traiter ce genre de choses ». L’agent a tiré le relief de la silhouette peinte : chaque image se bombe en dôme, plus épaisse au centre qu’aux bords.

    « L’eau reste une surface plane »

    L’eau a été le chantier le plus long. Premier obstacle : sur le Quest, le matériau de l’eau ne peut pas lire la profondeur de ce qu’il y a dessous. Les pieux du ponton se voyaient nettement à travers l’eau ; j’avais trouvé le ponton « superbe », mais ce défaut-là, « ça mérite une vraie recherche sur Internet ». L’agent a retourné le problème : ce sont les objets immergés qui s’éteignent, selon la longueur de leur trajet sous l’eau.

    Deuxième obstacle, plus sournois. Les rides de l’eau étaient des traits allongés, orientés en travers de la rivière. Vers l’aval, ça donnait une eau peinte. Mais dès qu’on tournait la tête, la perspective les changeait en rayons qui convergent : l’effet « pluie » des captures du casque, et une bonne part de ce qui faisait « pas fini ». L’agent les a remplacées par un réseau de crêtes sans direction, que la perspective écrase en traits horizontaux, où que l’on regarde. Le sillage, qui partait de la poupe et ne se voyait pas, naît maintenant à l’étrave, comme celui d’un vrai bateau, et longe les flancs, sous les yeux du passager assis à l’arrière.

    Le même instant, avant et après la nuit du 19 au 20/10 : les rides orientées à gauche, les crêtes sans direction à droite.

    Puis j’ai écrit : « Ton souci c’est que l’eau reste une surface plane ». Un article d’Epic sur l’eau de Still Wakes the Deep, un jeu où elle est simulée, a servi de piste. La version Quest est plus modeste : une hauteur d’eau calculée pour la houle et le sillage, une grille fine qui se déforme autour de la barque, des reflets sur la pente des vagues, des nénuphars qui flottent. La simulation complète, Niagara Fluids, a été essayée au casque le 20 au soir. Mon verdict : « c’est très moche ». Elle est restée de côté.

    L’écume, enfin, restait invisible, pour trois raisons empilées : depuis la place assise, le plat-bord cache toute l’eau à moins de 1,10 m de l’axe de la barque ; une fonction de transition écrite à l’envers valait zéro à la compilation, et effaçait au passage une partie des ailes du héron ; le motif de découpe était fait de lignes trop fines. Refaite, elle sautait par taches. Elle a fini par disparaître : l’eau ondulée suffisait.

    « Je n’ai pas vu de fort tangage »

    Une règle de confort interdit de faire pencher la caméra. Pour que la barque bouge quand même, l’agent l’a montée sur un pivot qui porte la coque, la lanterne et le héron, mais pas la caméra : c’est la barque qui bouge sous le spectateur. Au premier essai, la coque a disparu, restée à l’origine du monde : dans Unreal, un parent « mobile » refuse des enfants « statiques ».

    Il a commencé prudemment, avec quelques degrés. Je ne voyais rien. Puis 9° au passage de la bête, et ma remarque : « je n’ai pas vu de fort tangage quand la bête remonte ». Avec la caméra droite, seul le bord de la barque bouge, et l’œil le remarque à peine. Je lui ai dit de doubler « pour voir » : 18° de roulis, une houle de 50 centimètres. C’est resté, avec mon verdict du dernier jour : « les vagues nickel, le tangage nickel ».

    Au casque, au passage de la bête : la barque penche de 18°, la caméra reste droite.

    Ce réglage a laissé un dernier défaut, trouvé dans la version finale : à 18° de roulis, l’avant du plancher, plus large que la coque à la flottaison, plongeait sous la surface, et une flaque apparaissait dans la barque. Le premier rendu de contrôle, une seconde trop tard, ne montrait rien. Une série toutes les demi-secondes l’a trouvée. Sous le plancher, l’eau reste maintenant 30 centimètres plus bas.

    Un moteur qui se pilote en Python

    Ce qui m’a le plus surpris, c’est qu’Unreal se pilote entièrement par script. Le projet lui-même a été créé par un script Python, avec les réglages du Quest 3 : OpenXR, Vulkan, rendu mobile en forward, multiview. Les réglages que l’éditeur ajoute de lui-même sont reportés dans le script, pour que tout reste rejouable. Les vidéos sortent de Movie Render Queue, l’outil de rendu d’Unreal, lancé lui aussi par script.

    Les pièges étaient souvent en dehors du moteur. Le JDK installé sur mon PC était trop récent pour Gradle, l’outil qui assemble l’application Android ; le niveau d’API « latest » visait Android 15, que le Quest 3 ne charge pas. Installé hors du launcher d’Epic, le moteur s’identifie par un identifiant unique : écrire « 5.8 » dans le projet faisait proposer une conversion à chaque ouverture. Pendant les rendus vidéo, le compilateur de shaders DirectX 11 plantait en boucle ; en passant au Shader Model 6, on est passé de 0,2 image par seconde à une vidéo de quatre minutes rendue en 50 secondes.

    D’autres pièges ne se voient qu’au casque. Un jour, je me suis retrouvé assis derrière la barque (« Je suis derrière le bateau ») : l’origine de la VR dépendait de la zone de jeu du Quest, et l’agent a écrit, par script, un Blueprint qui recentre le joueur au démarrage. Plus tard, j’ai vu la scène se construire « petit à petit » avant le noir du début. L’explication de l’agent : au premier lancement, chaque objet attend que ses shaders soient compilés. Tout ce qui se dessine est maintenant caché pendant quatre secondes.

    Il y a enfin deux applications dans mon casque : la version de travail, avec la console et la capture au bouton A, et la version finale, compilée en mode Shipping, qui charge moins le processeur (33 % au plus, contre 52 %). L’application de travail est passée de 719 à 414 Mo le dernier jour, quand l’agent a retiré les vidéos d’un essai abandonné : le moteur embarquait très probablement l’une d’elles dans chaque APK. La version finale pèse 369 Mo.

    Les mots que j’ai appris

    • Draw call : une commande envoyée à la carte graphique pour dessiner un objet. Notre budget de départ : 150 par image.
    • Triangle : l’unité de la géométrie 3D. Budget de départ : 500 000 visibles à la fois.
    • Niveau de détail (LOD) : plusieurs versions d’un même objet, de plus en plus simples avec la distance.
    • Nanite : la géométrie virtualisée d’Unreal 5, activée par défaut, inutilisable sur le Quest.
    • Rendu forward : la méthode de rendu des casques autonomes, plus légère que le rendu différé des PC.
    • Post-traitement : les filtres appliqués à l’image terminée. Absent ici.
    • MSAA : un anticrénelage qui lisse les bords des objets.
    • Billboard, ou imposteur : une image peinte posée sur un plan, à la place d’un objet 3D.
    • Shader : le petit programme qui calcule la couleur de chaque pixel, ou la position de chaque sommet.
    • World Position Offset : déplacer les sommets d’un objet dans le shader, sans squelette. C’est ce qui fait nager la bête, voler la créature et balancer les arbres.
    • Level Sequence : la ligne de temps d’Unreal. Ici, l’horloge de toute la balade, calée sur la musique.
    • Development et Shipping : la version de travail d’une application, avec ses outils, et la version finale, allégée.

    Ce que je retiens

    Le premier soir et le dernier jour, à la même place, au même instant de la balade.

    Ma première question a une réponse nette. Pour tout ce qui se voit sur un écran ou se mesure, l’agent est allé très loin seul. Il a écrit 10 000 lignes de code, généré plus d’une centaine d’images et vingt-huit modèles 3D, choisi les sons, construit la scène, compilé, installé et mesuré, la nuit comme le jour, sans que je touche une ligne. Il s’est arrêté au casque : le relief, l’échelle, le confort et le goût sont restés de mon côté. Et quand il ne pouvait pas voir, il m’a fabriqué des outils pour mieux lui montrer ; le bouton A en est le meilleur exemple.

    Côté outils, octobre 2026 a un grand gagnant : l’image générée en local. Les 114 images peintes sont cohérentes, dans le style voulu, et c’est grâce à elles que la démo tient au casque. La 3D depuis une seule image donne de bonnes silhouettes et un détail lissé. Pour la musique, Suno ; pour les sons, une banque libre et des mesures. Les solutions toutes faites, Depth Anything ou la simulation d’eau d’Unreal, n’ont rien donné ici.

    Côté Unreal, j’ai appris qu’un casque autonome n’est pas un petit PC : pas de filtre, un budget de géométrie serré, et un éditeur qui montre autre chose que le casque. J’ai aussi découvert qu’Unreal se pilote entièrement en Python, et qu’une image peinte vaut souvent mieux qu’un maillage pauvre.

    L’agent n’a pas tout réussi, et son journal le dit. Il a conclu trop vite sur une mesure, avec le casque qui regardait le ciel. Il a corrigé des symptômes sans en vérifier l’étendue : la bande de forêt « corrigée » sur un seul anneau, des fleurs en 3D oubliées depuis le tout premier décor. Et il a longtemps raisonné sur l’aspect d’un défaut plutôt que sur une capture datée.

    Le plan de départ n’a pas survécu non plus. CLAUDE.md prévoyait une architecture hybride : le décor proche en temps réel, le lointain en vidéo 360 stéréo pré-rendue, la brume pour faire le raccord. Dès que la forêt est passée en images peintes, toute la balade a tenu 72 images par seconde en temps réel. Le lointain en vidéo n’a jamais servi ; l’agent l’a retiré le dernier jour, avec 3,4 Go de vidéos de test.

    Il y a eu des incidents, aucun grave : l’interface du Quest qui plante en boucle une nuit, 2,4 Go de vidéos 4K envoyées par erreur sur GitHub, un historique git réécrit et un dépôt recréé pour repasser de 7,3 à 3,9 Go.

    Le projet en chiffres.

    Le lac, le 19 et le 23/10. La lanterne qui masquait le centre de l’image est descendue au fond de la barque, et le héron s’est posé.

    Le 23 octobre, j’ai écrit à l’agent : « bah là on a fini en vrai… Tu vois des choses à améliorer ? » Dans son suivi, il en note trois : un audit automatique des plans vus par la tranche, la famille de défauts revenue deux fois ; une vidéo partageable sans casque ; et le premier jet de cet article. Il reste aussi 34 éléments générés en local en attendant mieux, tous en place dans la démo. Pour remplacer l’un d’eux, il suffit de déposer une image et de relancer le script concerné. C’était écrit dans le fichier de consignes dès le premier jour.

    Le casque, c’était moi

    En lançant ce projet, je me demandais ce qu’une IA saurait faire seule. Au bout de six jours, la question qui m’intéresse est l’inverse : ce qu’il m’est resté à faire. Rien de technique. J’ai regardé au casque ce que l’agent ne pouvait pas voir, dit en une phrase ce qui n’allait pas, lancé quelques idées de mise en scène, et répondu oui ou non quand il demandait une installation. C’est un travail de directeur artistique.

    Pour quelqu’un qui crée seul, ce partage des rôles change l’échelle de ce qu’on peut tenter. Je ne maîtrise pas Unreal, et la démo existe quand même, avec un journal qui explique chacune de ses décisions. La limite est physique : tant que l’agent ne voit pas en relief et n’entend pas une voiture au fond d’une ambiance de lac, il faut quelqu’un dans le casque.

    La leçon de méthode vaut bien au-delà du jeu vidéo. Sans le fichier de suivi, les défauts réversibles et une scène qui se reconstruit d’une commande, je n’aurais pas laissé l’agent seul une nuit entière, encore moins vingt heures. Le modèle fait le travail ; ces quelques règles m’ont permis de lui faire confiance.

    La barque dérive toujours dans mon casque. Le héron s’y pose à 3 min 47, annoncé par un cri d’aigrette que l’agent n’a jamais entendu.

    Addendum : une 3D à 360° qu’Unreal ne sait pas rendre

    L’article à peine fini, j’ai posé une dernière question à l’agent, presque en passant : « au fait, Youtube gère de la 3D à 360° je crois. Tu peux voir les specs nécessaires et voir pour générer la vidéo en question ? ». J’imaginais un réglage d’export. Il m’a rendu sa copie une heure et douze minutes plus tard, et c’est l’épisode du projet qui m’a le plus surpris.

    Les specs, d’abord. YouTube veut l’image entière dépliée comme une carte du monde (la projection équirectangulaire), les deux yeux l’un au-dessus de l’autre, l’œil gauche en haut ; Meta conseille au plus 5760 × 5760 pixels pour les Quest. Le son peut être spatial : quatre canaux d’ambisonie, dont les sons restent accrochés au décor quand on tourne la tête, plus deux canaux stéréo qui suivent la tête, pour les ambiances. Enfin, le fichier doit porter des métadonnées que ffmpeg sait lire mais pas écrire. L’outil de Google qui les écrit demande une installation, et mes consignes interdisent d’installer quoi que ce soit sans mon accord écrit. L’agent a donc écrit son propre injecteur, 234 lignes de Python, et l’a vérifié deux fois : la vidéo décodée image par image est identique avant et après, et ffprobe relit « top and bottom », « equirectangular », « ambisonic 1+stereo ».

    Ensuite, l’obstacle. Unreal 5.8 sait rendre un panorama, mais depuis un seul point : sans relief. Une 3D à 360° demande, pour chaque direction du regard, deux yeux placés de part et d’autre de cette direction ; quand on tourne la tête, les yeux tournent sur un cercle de 6,4 cm. Le moteur a bien un plugin expérimental qui fait ce travail tranche par tranche, avec plus de 2 500 captures par image, mais il est incompatible avec le rendu VR du projet.

    Le principe, les vingt-six caméras et la chaîne jusqu’au casque.

    La parade de l’agent tient en vingt-six caméras attachées à la barque. Pour chaque œil, douze caméras étroites couvrent chacune une tranche de 30°, placées là où serait l’œil quand on regarde le milieu de la tranche ; deux autres visent le haut et le bas. Chaque caméra écrit aussi la distance de chacun de ses pixels. Entre le milieu et le bord d’une tranche, l’œil devrait glisser de quelques millimètres sur le cercle : le script d’assemblage s’en charge, en recalculant avec ces distances où chaque point serait vu. Cela fait 62,5 millions de pixels par image, sept fois et demie une image 4K.

    Avant de demander une minute de carte graphique, il a voulu prouver que la méthode tenait. Il a écrit un petit moteur de rendu en Python, qui calcule une scène de synthèse vue par chacune des vingt-six caméras : de l’eau en damier, une barque, des roseaux tout contre le bordé, une branche juste au-dessus de la tête. Il calcule aussi, rayon par rayon, l’image exacte que chaque œil devrait recevoir. Assemblée avec les distances, son image s’écarte de la vérité sur 0,07 % des pixels ; recollée sans elles, sur 0,81 %, et les bordés se dédoublent aux raccords.

    La scène de synthèse, œil gauche, vers l’avant gauche de la barque.

    Le son a suivi le chemin du 5.1 de la vidéo à plat : chaque source reprise à sa place et sur sa trajectoire, mais encodée dans sa direction autour du spectateur, hauteur comprise. Pour le vérifier sans l’écouter, l’agent a décodé les directions : la bête passe sous la barque, à 30° sous l’horizon ; la créature volante est à 20° au-dessus ; le héron descend vers la proue.

    Restait Unreal. L’agent y a construit « à blanc » les séquences, les vingt-six caméras et les travaux de rendu, les a vérifiés, puis a tout supprimé sans rien enregistrer. Une première tentative s’est arrêtée à mi-chemin, et la copie de séquence restée en mémoire refusait de disparaître : « en cours d’utilisation ». Le coupable, trouvé dans le journal de l’éditeur, était la trace Python de l’erreur, qui gardait la séquence en vie. Dernière étape, une répétition complète : des vues de synthèse en vraie définition jusqu’au fichier de 5760 × 5760 et à ses métadonnées.

    Puis il s’est arrêté, comme demandé, avec un devis : au moins quatre heures de carte graphique, 310 Go de disque au plus fort, et une proposition, un essai de trois secondes sur le cas le plus dur, les roseaux qui frôlent la barque.

    J’ai donné le feu vert pour l’essai, et par avance pour la suite : « une fois que ton test sur 3s fonctionne et que tu as controlé toi même le rendu, je te donne mon accord pour lancer le rendu total ». Le premier essai a raté. Unreal n’a rendu qu’une caméra sur vingt-six, rangée sous le nom « main_camera ». L’agent est allé lire le code source du module de rendu, livré avec le moteur : Movie Render Queue ne reconnaît une caméra qu’à la liaison de son composant caméra, celle que Sequencer crée tout seul quand on ajoute une caméra à la main. Les caméras créées par script n’en avaient pas. Il a ouvert chaque séquence dans Sequencer le temps de les créer, en vérifiant au passage que le niveau restait intact : sinon, le rendu se serait arrêté sur une boîte de dialogue que personne n’aurait vue.

    Le second essai a rendu les vingt-six vues, et l’agent l’a contrôlé de lui-même avant de me rendre la main. Les couleurs converties sont celles d’Unreal à une unité près sur 255 ; deux vues voisines ne s’écartent plus que de 0,14 sur 255 là où elles se recouvrent, contre 0,47 sans la correction par la distance. Il a mesuré le décalage entre les deux yeux : 49 pixels sur le plancher de la barque, 15 sur l’eau à 4 mètres, 3 sur les arbres à 20 mètres, ce qu’on attend d’un écart de 6,4 cm. Un décalage de 4 pixels sur ce qu’il croyait être des nuages l’a arrêté un moment, le ciel devant être à l’infini. Il a comparé les deux vues d’origine et la distance écrite par Unreal : il mesurait en fait la cime d’un arbre peint, à 14 mètres.

    Une image de l’essai, œil gauche, dépliée à plat : 360° de large, 180° de haut. La proue au centre, le banc où l’on est assis en bas, la poupe aux deux bords.

    Avant la balade entière, l’agent a voulu gagner du temps : une seconde de test avec deux fois moins d’échantillons par image. Le rendu n’allait que 19 % plus vite, le coût tenant surtout au nombre de vues ; il a gardé la qualité de la vidéo 4K.

    Ce qui m’a frappé, c’est l’ordre des opérations : comprendre ce que le moteur ne sait pas faire, inventer un contournement, construire de quoi le prouver, et seulement ensuite demander la carte graphique. Le relief, lui, l’agent ne le voit toujours pas. La vérification finale se fera dans mon casque.

  • On essaie de remplacer PHP depuis quinze ans. Pourquoi son moteur est-il toujours là ?

    En octobre 2026, deux failles du moteur PHP entraînent des mises hors ligne dans plusieurs pays. L’occasion de revenir sur les tentatives de remplacement de PHP, de HHVM à Rust, et sur ce qui les rend si difficiles : la compatibilité avec les applications existantes.

    27 octobre 2026. Chiffres et état des projets arrêtés à cette date.

    Octobre 2026 : deux CVE et des correctifs précipités

    Le 20 octobre 2026, les identifiants de deux vulnérabilités PHP circulent sur des forums avant leur divulgation officielle. Un score de 10/10 se propage, à tort : les deux failles sont notées 9,2 en CVSS v4.0. Toutes deux concernent la gestion mémoire et peuvent permettre une exécution de code à distance sans authentification, sous certaines conditions. PHP avance sa pré-annonce à 19 h 30 UTC et décide de publier les correctifs le 21 octobre au lieu du 22. Vers 20 h, Calif, société impliquée dans la découverte, met accidentellement en ligne une vidéo de démonstration. Elle est retirée rapidement, mais déjà diffusée ailleurs. 1

    Le 21 octobre, à 7 h 41 UTC, Chainguard distribue des images contenant le correctif, plus de six heures avant l’heure convenue. La PHP Foundation dénonce une rupture d’embargo. Les versions PHP 8.2.35, 8.3.36, 8.4.27 et 8.5.12 sont publiées à 14 h UTC. Le CERT-FR recommande d’isoler les systèmes exposés en attendant leur mise à jour. Plusieurs sites administratifs français affichent des pages de maintenance préventive. 1, 2, 3

    Les interruptions dépassent les frontières françaises. Reuters cite l’ITZBund allemand, le ministère belge des Affaires étrangères, le Port de Rotterdam et des hôpitaux néerlandais. Wikimedia, Internet Archive, la BBC, plusieurs services publics ou entreprises au Japon, ainsi qu’Orange en France, prennent également des mesures. Ces interruptions, parfois préventives, ne démontrent pas que les organisations concernées ont été compromises. 4

    Les alertes continuent après les correctifs. Entre le 21 et le 26 octobre, watchTowr observe des tentatives d’exploitation ; le CERT-FR en confirme sur des déploiements précis. Une troisième vulnérabilité, CVE-2026-115560, liée à session.upload_progress, est annoncée le 23 octobre. Trois jours plus tard, la CISA ajoute CVE-2026-109618 à son catalogue KEV des vulnérabilités exploitées.

    Chronologie : PHP Foundation, CERT-FR, watchTowr. Les tentatives observées ne signifient pas que tous les déploiements PHP sont exploitables. 1, 2, 5

    Sous le nom « PHP2Shell », on trouve deux défauts distincts dans Zend, le moteur de PHP :

    • CVE-2026-109618 : lorsqu’une allocation atteint memory_limit, un longjmp() interrompt brutalement une initialisation. Le nettoyage peut ensuite retrouver des structures partiellement initialisées et corrompre la mémoire. Sur Apache avec mod_php, une exploitation complète a été démontrée avant même l’exécution du code applicatif. 6
    • CVE-2026-109619 : une erreur fatale, un dépassement de temps ou un diagnostic tardif peut empêcher la remise à zéro d’un état global conservé par un worker. Une requête ultérieure, sur ce même worker, réutilise alors un pointeur vers de la mémoire libérée : c’est un use-after-free. Un chemin d’exploitation impliquant le cycle de vie de session.upload_progress a notamment été validé sur FPM. 7

    CVE-2026-115560 suppose une configuration plus particulière : le sérialiseur de sessions doit être réglé sur php_binary ou php_serialize, puis remplacé par php dans le code avant session_start(), avec le suivi des téléversements actif. Ce suivi est activé par défaut, mais cette seule condition ne suffit pas pour exploiter la faille. PHP recommande temporairement session.upload_progress.enabled=0 pour écarter ce vecteur, en attendant le correctif prévu dans le cycle normal. 8, 9

    Face à ces bugs mémoire, la réécriture de PHP en Rust mérite qu’on s’y intéresse. Rust empêche par conception une grande partie des erreurs d’accès mémoire permises en C. La difficulté est de conserver tous les comportements que les applications attendent de PHP.

    Rust : supprimer des bugs mémoire sans inventer un autre PHP

    En juin 2026, Francesco Tinti crée php-rust. Il reconstruit le parseur, la compilation en bytecode, la machine virtuelle, le ramasse-miettes et les fonctions internes. L’objectif est de reproduire le comportement observable de PHP, sans reprendre l’architecture de Zend. 10

    Le Rust sûr évite les accès hors limites, les doubles libérations et les use-after-free. La garantie s’arrête toutefois aux frontières unsafe et FFI, notamment lorsque le programme appelle des bibliothèques C. php-rust s’appuie déjà sur des bibliothèques natives pour certaines fonctions multimédias et de compression. Il lui faut donc assurer à la fois la sûreté de sa propre mémoire et la compatibilité des applications.

    Au 27 octobre, le dépôt compte 283 étoiles et 11 forks. php-rust reste un petit projet face à Zend, même si les résultats annoncés pour quelques mois de développement sont substantiels. Ils proviennent des tests de l’auteur : ils ne constituent ni une validation indépendante ni une garantie d’aptitude à la production. 10, 11

    La couverture varie fortement selon ce qu’on mesure. 1 017 des 2 143 fonctions internes sont disponibles (47 %), car plusieurs extensions entières n’ont pas encore été portées. Pour le cœur du langage, les fonctions standard et les dates, la couverture grimpe à 539 sur 654 (82 %).

    Côté validation, 2 655 tests Zend passent sur 4 067 exécutables (65,3 %). Le corpus contient au total 5 305 tests, dont 1 238 sont ignorés, notamment faute d’extensions. Quant à WordPress, l’auteur revendique une quasi-parité avec la suite de tests du cœur, sur les versions et scénarios effectivement évalués.

    Mesures du 16 septembre 2026, établies et publiées par le projet contre PHP 8.5.7. 11

    Sur les performances, php-rust accuse encore du retard. Le langage d’implémentation ne suffit pas à battre Zend, dont les valeurs, le copy-on-write, OPcache et le JIT ont bénéficié d’années d’optimisation. Un interpréteur neuf doit retrouver des optimisations comparables.

    Dans les micro-benchmarks publiés, php-rust consomme 2,1 fois plus de temps CPU en arithmétique, 2,3 fois plus pour les propriétés d’objet et 2,9 fois plus pour les tableaux. Le ratio atteint ×3,5 sur les appels de fonctions et ×4,3 sur les chaînes de caractères.

    Sur les suites applicatives, l’auteur mesure environ ×1,75 pour WordPress et ×7 pour Doctrine ORM.

    Mesures internes d’octobre 2026, pas benchmarks indépendants. « ×2 » signifie deux fois plus de temps CPU, et non deux fois moins de requêtes HTTP par seconde. 12

    Il reste la question des extensions natives. Une extension redis.so, imagick.so, Xdebug ou un agent de supervision compilé pour l’ABI Zend ne peut pas être chargé tel quel par une autre machine virtuelle. Le nouveau moteur doit recréer son comportement ou fournir une interface native compatible, au risque de réintroduire une partie des problèmes de mémoire.

    La compatibilité du langage réserve aussi des pièges. Le 25 octobre, un contributeur signale que filter_var('not-an-email', FILTER_VALIDATE_EMAIL) renvoie sous php-rust la chaîne d’origine au lieu de false. FILTER_VALIDATE_DOMAIN est également concerné. Une PR de correction est alors ouverte. Cette divergence ne provoque pas de corruption mémoire ; elle peut en revanche laisser passer une donnée que l’application croit avoir validée. 13, 14

    D’autres interpréteurs Rust, comme le php-rs expérimental de wudi, explorent la même voie sans prétendre constituer aujourd’hui une alternative générale à Zend. 15

    Le dépôt documente aussi une méthode de développement fortement assistée par IA : plans de sessions numérotées, consignes destinées aux agents, revues contradictoires et commits mentionnant Claude. Les protocoles de test et de mesure sont détaillés. Ce mode de développement ne suffit ni à invalider les résultats ni à garantir leur fiabilité. Reste une question de maintenance : qui adaptera ce moteur aux évolutions futures de PHP ? 10, 12

    HHVM : quand Facebook a choisi Hack

    Au début des années 2010, Facebook cherche à réduire le coût d’exécution de PHP. Après HipHop, un traducteur de PHP vers C++, l’entreprise développe HHVM, une machine virtuelle dotée d’un compilateur JIT. Face à PHP 5, elle obtient des gains de performances considérables.

    Fin 2015, PHP 7 change le rapport de force en accélérant fortement Zend. En mai 2017, Fabien Potencier annonce la fin du support HHVM dans Symfony 4 : les différences de comportement demandent trop d’efforts, l’avantage de vitesse s’est réduit et moins de 4 % des répondants à sa consultation souhaitent conserver le support. 16, 17

    En 2014, Facebook présente Hack, un langage dérivé de PHP avec un système de types plus ambitieux. En septembre 2018, l’équipe HHVM annonce qu’elle abandonne la compatibilité PHP. La version 4.0, sortie en février 2019, confirme ce choix : l’équipe peut faire évoluer Hack sans devoir reproduire chaque changement de PHP. 18, 19

    HHVM continue de servir chez Meta. En mai 2025, l’entreprise publiait encore des optimisations destinées aux traitements longs liés à l’IA générative. En revanche, les releases open source officielles avaient été arrêtées en 2023. Meta invoquait le coût du maintien d’anciennes versions face à l’automatisation et à la cadence de ses releases internes, ainsi que l’abandon de GCC au profit de Clang ; elle n’expliquait pas cette décision par une absence d’utilisateurs externes. 20, 21

    L’histoire de HHVM n’est donc pas celle d’un simple échec : c’est celle d’une technologie qui a réussi en cessant de vouloir être PHP.

    D’autres façons de réinventer PHP

    Les autres projets montrent qu’on peut modifier PHP de plusieurs façons : compiler le code pour une autre plateforme, réécrire le runtime ou réutiliser Zend dans un nouvel environnement.

    Stratégies PHP

    Figure 1 — Réécrire le moteur, compiler vers une autre plateforme, ou conserver Zend. WordPress Playground appartient clairement à cette dernière catégorie.

    Quelques exemples :

    • KPHP compile un sous-ensemble de PHP en C++, puis en binaire natif, au prix de restrictions sur certaines fonctionnalités dynamiques.
    • PeachPie compile PHP pour .NET et privilégie l’interopérabilité avec C#, avec une bibliothèque d’exécution à adapter.
    • HippyVM et JPHP explorent d’autres machines virtuelles et des usages plus spécifiques.
    • WordPress Playground compile le PHP officiel en WebAssembly : le moteur reste Zend, avec les contraintes du navigateur.
    • ext-php-rs permet d’écrire des extensions Zend en Rust, sans faire disparaître la frontière FFI/unsafe.

    Le compromis apparaît surtout avec KPHP, qui accepte de restreindre le langage pour faciliter la compilation native. WordPress Playground fait l’inverse : il conserve Zend et l’exécute en WebAssembly. ext-php-rs réduit la part de C dans les extensions, sans supprimer leur interface native. PeachPie, lui, cherche surtout l’interopérabilité avec C#. 22, 23, 24, 25

    FrankenPHP : moderniser PHP sans toucher à Zend

    FrankenPHP est probablement l’approche la plus directement utilisable aujourd’hui parmi ces alternatives. Le serveur s’appuie sur Caddy, écrit en Go, mais exécute le PHP officiel via son API native. Les requêtes PHP tournent dans des threads avec Zend Thread Safety (ZTS). Le moteur conserve donc son comportement et ses extensions, sous réserve de leur compatibilité ZTS. 26

    Figure 2 — L’unité de défaillance n’est pas la même. Un enfant FPM traite plusieurs requêtes successives, pas un nouveau processus par requête ; FrankenPHP traite plusieurs requêtes dans les threads d’un même processus.

    Avec mod_php, Zend vit dans les processus Apache : le serveur web et son interpréteur sont étroitement couplés. Avec Nginx + PHP-FPM, un processus maître supervise des enfants réutilisés au fil des requêtes ; si l’un meurt, les autres continuent. Avec FrankenPHP, plusieurs threads PHP partagent un processus unique. Cela simplifie l’intégration HTTP, mais change le rayon d’impact d’un plantage natif.

    Les analyses de CVE-2026-109618 éclairent la différence entre ces architectures :

    • Apache + mod_php : une RCE a été démontrée avant le code applicatif, avec la configuration par défaut. Le traitement et le nettoyage de la requête restent dans le worker Apache.
    • PHP-FPM : le processus enfant quitte si l’initialisation échoue. L’advisory ne documente pas de chaîne complète pour cette CVE sur FPM, ce qui n’exclut ni les chemins applicatifs ni CVE-2026-109619.
    • FrankenPHP : l’advisory juge ce modèle probablement exposé à la voie indépendante de l’application, par analogie avec mod_php. Une défaillance fatale du processus peut toucher toutes ses requêtes simultanées.

    Nuance documentaire : l’advisory ne décrit aucune démonstration complète pour CVE-2026-109618 sur FPM, alors que la PHP Foundation indique qu’une RCE via un chemin applicatif y a été confirmée. Ces formulations publiques ne sont donc pas parfaitement alignées. Ni FPM ni FrankenPHP ne doivent être considérés comme immunisés. 1, 6, 7

    FrankenPHP propose deux usages. Son mode classique réinitialise le contexte applicatif entre requêtes. Son mode worker conserve l’application initialisée : Composer, routeur et conteneur de dépendances n’ont plus à être reconstruits. Éviter cette initialisation peut accélérer le traitement des requêtes, à condition de maîtriser les états conservés entre deux appels. Pour le mode classique, une mesure indépendante de Tideways en septembre 2025 trouvait des performances proches de PHP-FPM, avec des écarts selon la charge et parfois un p99 moins bon. 27, 28

    Ce risque de fuite d’état s’est déjà matérialisé. En février 2026, CVE-2026-24894 montrait qu’en mode worker, le contenu de $_SESSION pouvait être accessible à la requête suivante traitée par le même worker, avant son propre appel à session_start() ; le défaut a été corrigé en 1.11.2. Le 21 octobre, l’advisory GHSA-4gfw-9v29-gp9h documentait un autre état non réinitialisé : des informations de diagnostic libxml (et, selon les notes de version, error_get_last()) pouvaient exposer des fragments de documents ou des chemins du serveur à la requête suivante sur le même thread. La correction figure dans FrankenPHP 1.13.2, qui corrige aussi trois autres failles propres au serveur (répertoire temporaire prévisible, déni de service dans l’attente d’un thread, crash sur tableau cyclique). Ces fuites d’état ne sont pas les mêmes vulnérabilités que celles de Zend : elles proviennent du cycle de vie particulier des workers persistants. 29, 30, 31

    Le correctif d’octobre change lui-même le comportement de PHP

    Le correctif des deux CVE modifie aussi la gestion des erreurs mémoire. Le commit 31c0ea7d 32 abandonne le longjmp() immédiat sur manque de mémoire : PHP reporte désormais l’erreur fatale « souple » au prochain point d’exécution sûr. En cas de dépassement d’un seuil mémoire dur, configurable via memory_hard_limit_percent, il quitte immédiatement le processus, sans nettoyage classique. La limite dure vaut normalement 200 % de memory_limit, ou memory_limit + 1 Gio au-delà de 1 Gio. 6, 33

    Dans FPM, cet arrêt touche un enfant du pool. Dans FrankenPHP, la documentation précise qu’il arrête le serveur et toutes les requêtes en cours jusqu’au redémarrage. L’issue php-src #24492, toujours ouverte lors de la consultation, signale déjà des cas où sprintf(), pack() ou bin2hex() déclenchent cet arrêt là où les versions précédentes produisaient une erreur mémoire récupérable. Il s’agit d’un signalement à investiguer, pas d’une régression généralisée démontrée. 33, 34

    Relancer les workers via l’API d’administration, --watch ou MAX_REQUESTS ne remplace pas le moteur PHP chargé par le serveur. Pour corriger les CVE de Zend, il faut déployer une image ou un binaire FrankenPHP intégrant les versions corrigées de PHP, puis redémarrer le processus serveur ; dans une compilation avec PHP partagé, il faut aussi mettre à jour la bibliothèque PHP effectivement chargée. Les images et binaires officiels FrankenPHP 1.13.2 embarquaient déjà ces correctifs. 27, 31

    La compatibilité reste aussi conditionnée aux extensions : la documentation FrankenPHP cite notamment New Relic, PCOV et certaines compilations d’Imagick avec OpenMP. Elle recommande les builds glibc plutôt que musl pour la production. Les solutions voisines — RoadRunner avec ses processus CLI persistants, OpenSwoole et ses coroutines, ou le jeune OxPHP écrit en Rust autour de Zend — déplacent autrement ces compromis. 33, 35, 36, 37, 38

    Pourquoi PHP reste-t-il partout ? Et qui paie pour cela ?

    W3Techs détecte PHP sur 69,8 % des sites dont le langage serveur est connu au 1er octobre 2026, contre 73,4 % au 1er octobre 2025. JavaScript côté serveur progresse de 5,0 à 7,6 % sur les mêmes relevés mensuels. Ce sont des proportions de sites détectables, pas du trafic ni la répartition de tous les serveurs. 39

    WordPress représente, de son côté, environ 39,6 % des sites étudiés. Composer et Packagist comptent plus de 469 000 paquets et 200 milliards d’installations cumulées, chiffres qui englobent évidemment CI et déploiements répétés. Entre WordPress, Drupal, PrestaShop, Magento et les applications métiers, le coût d’un changement de plateforme serait immense. 40, 41

    PHP reste adapté à un besoin très répandu : recevoir une requête HTTP, interroger une base de données et renvoyer une page ou du JSON. L’installation est simple, l’hébergement largement disponible, le modèle sans état applicatif persistant entre requêtes facile à exploiter. Le langage, lui, a continué à évoluer. Dans l’enquête Stack Overflow 2026, 17,7 % de 14 099 répondants déclarent avoir beaucoup utilisé PHP : ce petit panel volontaire mesure des développeurs, pas le nombre de sites installés. 42

    Le parc installé vieillit aussi. Fin octobre, W3Techs identifie encore 26,6 % de PHP 7 et 7,7 % de PHP 5 parmi les sites PHP : plus d’un tiers sur des branches privées de correctifs officiels. Les backports fournis par certaines distributions ou mainteneurs sont possibles, mais ne changent pas le fait que le risque réel dépend aussi du parc installé et de sa maintenance, pas uniquement du langage qui implémente Zend. 43, 44

    La sécurité de PHP dépend aussi des moyens consacrés à sa maintenance. Les premiers signes de CVE-2026-109618 ont été identifiés pendant les suites d’un audit Quarkslab financé en 2024, mais la recherche d’un correctif a stagné des mois : compatibilité des extensions, risques de déni de service, absence de consensus et manque de ressources. La PHP Foundation parle elle-même d’un échec structurel. 1

    En 2025, la fondation a reçu 730 534 dollars de contributions, pour 784 376 dollars de dépenses et 11 développeurs sous contrat à temps partiel ou plein fin décembre. Ce n’est ni toute l’équipe PHP ni tout le financement indirect de son écosystème, mais c’est un ordre de grandeur étonnant pour un logiciel si répandu. 45

    L’IA accélère désormais les deux côtés : l’audit Calif, les signalements indépendants d’OpenAI et Wiz ont contribué à mettre en évidence l’exploitabilité du problème de 2024 ; la Foundation utilise aussi des outils d’assistance pour vérifier les rapports. L’afflux de découvertes rend encore plus nécessaire un travail humain de tri, de correction et de revue. 1

    Le plus dur n’est pas d’écrire un nouveau PHP. C’est d’être PHP.

    Les failles d’octobre 2026 donnent de bonnes raisons de chercher des moteurs plus sûrs pour la mémoire. L’histoire de HHVM rappelle toutefois le coût de la compatibilité. KPHP assume ses restrictions ; WordPress Playground transporte Zend en WebAssembly ; FrankenPHP modernise l’exécution sans réinventer les règles du langage ; php-rust essaie de faire les deux à la fois.

    Toutes les vulnérabilités de PHP ne sont pas des bugs mémoire. CVE-2024-4577, exploitée sur certaines installations Windows en mode CGI, relevait d’une injection d’arguments ; une réécriture en Rust n’aurait pas automatiquement empêché ce problème. 46

    Un nouveau moteur doit préserver des milliers de comportements historiques, les extensions et la simplicité du déploiement, tout en apportant des améliorations de sécurité et d’exploitation. Le moteur actuel, lui, a besoin de moyens suffisants pour être audité, corrigé et maintenu.

    Un moteur alternatif finira peut-être par s’imposer. Il pourrait aussi, comme HHVM, surtout pousser PHP à progresser. Le plus dur n’est pas d’écrire un nouveau PHP. C’est de réussir à être PHP.


    Sources et bibliographie

    Sources consultées ou revérifiées le 27 octobre 2026. Les mesures des projets, annonces de sécurité et parts de marché correspondent à cette date ou à la date explicitement indiquée.

    1. PHP Foundation, Today’s PHP Security Release, and How We’re Strengthening PHP Security, 21 octobre 2026.
    2. CERT-FR / ANSSI, Alerte CERTFR-2026-ALE-014, mise à jour du 26 octobre 2026.
    3. Le Parisien, Préfectures, impôts… sites en maintenance, 21 octobre 2026.
    4. Reuters, Factbox — Governments, companies take websites offline over PHP flaws, 22 octobre 2026.
    5. watchTowr, PHP2Shell FAQ, signalements d’exploitation et KEV, mis à jour le 26 octobre 2026.
    6. PHP Security Team, CVE-2026-109618 : RCE via OOM corruption, 21 octobre 2026.
    7. PHP Security Team, CVE-2026-109619 : RCE via stale global state, 21 octobre 2026.
    8. PHP.net, Annonce CVE-2026-115560 : session upload progress, 23 octobre 2026.
    9. PHP.net, Session runtime configuration — upload progress activé par défaut, documentation consultée en octobre 2026.
    10. Francesco Tinti, Dépôt php-rust, architecture, contributions et commits, consulté le 27 octobre 2026.
    11. php-rust, COVERAGE.md — fonctions et tests exécutés, mesures du 16 septembre 2026.
    12. php-rust, NEXT_SESSION_WORDPRESS.md — journal de sessions et performances, octobre 2026.
    13. php-rust, Issue #6 — défaut de validation filter_var(), 25 octobre 2026.
    14. php-rust, PR #7 — correctif proposé pour EMAIL et DOMAIN, 25 octobre 2026.
    15. wudi, php-rs — autre interpréteur Rust expérimental, consulté en octobre 2026.
    16. PHP.net, PHP 7.0.0 — annonce de sortie, décembre 2015.
    17. Symfony, Symfony 4: End of HHVM Support, mai 2017.
    18. HHVM, Ending PHP Support, and the Future of Hack, septembre 2018.
    19. HHVM, HHVM 4.0.0, février 2019.
    20. Meta Engineering, Full-stack HHVM optimizations for GenAI, mai 2025.
    21. HHVM, Project Update and OSS Support Changes, octobre 2023.
    22. ext-php-rs, Guide officiel : extensions Zend en Rust, consulté en octobre 2026.
    23. VK, KPHP — compilateur PHP vers C++.
    24. PeachPie, Compilateur PHP vers .NET.
    25. WordPress, Playground: compiling PHP to WebAssembly.
    26. FrankenPHP, Documentation générale.
    27. FrankenPHP, Worker mode.
    28. Tideways, Testing FrankenPHP Classic Mode against PHP-FPM, septembre 2025.
    29. FrankenPHP Security, CVE-2026-24894 / GHSA-r3xh-3r3w-47gp — fuite de $_SESSION en mode worker, février 2026.
    30. FrankenPHP Security, GHSA-4gfw-9v29-gp9h — état libxml entre requêtes, 21 octobre 2026.
    31. FrankenPHP, Release 1.13.2 — PHP2Shell et correctifs propres à FrankenPHP, 21 octobre 2026.
    32. php-src, Commit 31c0ea7d — Avoid state corruption via bailout, 18 octobre 2026.
    33. FrankenPHP, Known issues : hard memory limit, extensions.
    34. php-src, Issue #24492 — OOM regression in sprintf/pack/bin2hex, octobre 2026.
    35. FrankenPHP, Performance optimization : glibc versus musl.
    36. RoadRunner, Site officiel et architecture.
    37. OpenSwoole, Site officiel.
    38. OxPHP, Dépôt expérimental en Rust autour de Zend.
    39. W3Techs, Historique mensuel des langages serveur, 1er octobre 2025 et 1er octobre 2026.
    40. W3Techs, Part de marché des CMS, octobre 2026.
    41. Packagist, 15 years of Packagist: Over 200 billion installs, 29 septembre 2026.
    42. Stack Overflow, Developer Survey 2026 — Programming languages, 14 099 répondants.
    43. W3Techs, PHP : versions déployées, 27 octobre 2026.
    44. PHP.net, Dates de fin de support des branches PHP.
    45. PHP Foundation, Impact and Transparency Report 2025, mai 2026.
    46. CERT-EU, CVE-2024-4577 : injection d’arguments PHP-CGI Windows, juin 2024.

  • Test des instances GPU OVH Cloud

    Ces dernières semaines j’ai passé pas mal de temps (perso) sur tous ces nouveaux algorithmes de deep learning au sens large, aussi bien sur les solutions SaaS (Chat GPT est la plus connue) que les solutions dites « ouvertes », c’est à dire avec à la fois le moteur ET les fichiers d’apprentissages téléchargeables. J’ai donc compilé et installé StableDiffusion, le moteur de création d’images à base de texte, ou dans un champ très différent le fameux « faceswap », qui permet de mettre la tête de quelqu’un d’autre sur une vidéo. Même si j’ai du bon matos en local, je me suis dit que ce serait un bon test de voir ce que donne les fameuses cartes nvidia « Tesla V100 », qui sont des cartes pro et qui est le modèle emblématique à louer dans les instances de cloud dites « GPU ». Voilà donc ma review rapide sur la solution d’OVH !

    Ce qui m’a décidé, ce sont les 200€ offerts sur les nouveaux projets. Chouette, voilà un test qui ne me coûtera rien (vous verrez plus loin dans cet article qu’il n’en sera rien).

    Setup du projet chez OVH

    Internet d’après MidJourney

    Je lance la création de mon premier projet, ce qui met bien cinq minutes. Comme les instances GPU coûtent « cher », j’ai décidé d’amorcer mon projet sur la VM de base la plus simple, le modèle « b2-7 ». Ma première VM boote assez vite et me voilà déjà à faire les mises à jour Rocky Linux et à préparer mon environnement « tensorflow » avec tous les packages qui vont bien.

    Voilà qui est fait ! comment ajouter du GPU sur une VM OVH maintenant ? Chez AWS ou GCP, il suffit d’attacher un GPU à une VM, cherchons maintenant chez OVH … Ah zut 1e tuile : il me faut convertir mon instance en type GPU, mais il me dit que ce type n’est pas dispo sur le datacenter choisi …

    En lisant en détail, il me faut donc lancer une VM sur le DC GRA-9. Bim, message d’erreur encore : « ce datacenter n’est pas configuré ». Je fouille donc dans les menus et je trouve laborieusement comment activer de nouveaux DC, ce qui prend plusieurs minutes.

    Reprovisionons donc une nouvelle instance sur le DC GRA-9, et recommençons le provisioning …. 1h plus tard, je suis prêt (oui bon ok je faisais autre chose en même temps). Convertissons la vm …. Bim message d’erreur : quota insuffisant. En effet, l’instance GPU la plus petite fait 8 cores et 45Go de RAM, soit au delà de la limite de 40Go de RAM initiale. Pour la dépasser, il me faut donc acheter un forfait quota (+10 VM), facturé 200€. Les petites lignes disent bien « non remboursable, à utiliser sous 12 mois ». C’est mort pour le test gratuit 🙁 Et franchement, le fait que la dénomination soit « + 10 VM » me fait vraiment douter que ce soit ça qu’il faille acheter (je veux pas plus de VM, je veux plus de RAM).

    Carte bleu sortie, forfait payé, et il ne se passe rien. Ah ok, il faut là aussi attendre quelques minutes. Damned OVH ! niveau UI, affichez au moins un bandeau pour prévenir « paiement en cours » ou je sais pas moi !

    Cette fois c’est bon, je lance la conversion de l’instance en type GPU. ça mouline pendant une bonne trentaine de minutes sans rien afficher d’un avancement ou autre. Après quelques centaines de F5, je vois bien ma nouvelle instance, et ça ping !

    L’utilisation du GPU sur une instance OVH

    Carte GPU (MidJourney)

    Je me connecte, j’installe « nvidia-smi » qui est l’utilitaire classique pour lister les GPU … Ouf c’est bon, ma carte Tesla est là !

    +---------------------------------------------------------------------------------------+
    | NVIDIA-SMI 530.30.02              Driver Version: 530.30.02    CUDA Version: 12.1     |
    |-----------------------------------------+----------------------+----------------------+
    | GPU  Name                  Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
    | Fan  Temp  Perf            Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
    |                                         |                      |               MIG M. |
    |=========================================+======================+======================|
    |   0  Tesla V100-PCIE-16GB            Off| 00000000:00:06.0 Off |                    0 |
    | N/A   36C    P0               36W / 250W|      0MiB / 16384MiB |      0%      Default |
    |                                         |                      |                  N/A |
    +-----------------------------------------+----------------------+----------------------+
    
    +---------------------------------------------------------------------------------------+
    | Processes:                                                                            |
    |  GPU   GI   CI        PID   Type   Process name                            GPU Memory |
    |        ID   ID                                                             Usage      |
    |=======================================================================================|
    |  No running processes found                                                           |
    +---------------------------------------------------------------------------------------+

    Bon, comment tester et comparer maintenant ? Je me lance avec « deepfacelab » car c’est mon dernier projet perso. On lance juste la phase apprentissage (toute les étapes de découpe / etc ont été faites en local pour gagner du temps et pas payer dans le vide). Mais c’est la douche froide : c’est très très lent … Ah mais oui : le training se fait en mode CPU ! Le GPU n’est pas détecté correctement ! Il est tard, j’éteins la VM pour la journée de travail et la nuit qui vient afin de ne pas dépenser trop dans le vide.

    Deux jours plus tard, je rallume ma VM, ce qui prend environ 15 minutes. Un nouvel essai l’après-midi me donne sensiblement le même délai. En clair, il faut pas être pressé si vous avez besoin d’une instance GPU … Après pas mal d’essais infructueux sur les drivers ou du kernel, je tombe enfin sur ce message qui informe qu’il faut downgrader Tensorflow … et ça fonctionne ! Ce n’était donc pas un souci de driver mais bien un souci applicatif python. Bon la question que vous vous posez tous : est-ce qu’une carte Tesla est vraiment plus rapide ? Voilà une comparaison des rythmes d’apprentissage obtenus :

    Type d’apprentissageDurée d’un cycle
    CPU19500 ms
    GPU maison « GTX 1080 »1360ms
    GPU Tesla V100240ms

    Une bonne nouvelle : c’est cher mais c’est 5 fois plus rapide que mon GPU maison, qui commence certe à être un peu vieux mais qui n’est pas non plus si mauvais que ça.

    Il est maintenant temps d’éteindre tout ça. Je devrais en avoir pour une dizaine d’euros de test d’après mes calculs. Et là, l’interface OVH m’indique … 60€ de consommation ! que s’est-il passé ? Argh, je me suis fait avoir comme un bleu. VOilà ce que donne les CGV d’OVH :

     La facturation d’une instance s’arrête quand l’instance est supprimée définitivement. Les états « arrêtée », « en pause », ou autre continuent à faire tourner le compteur de facturation tant que l’instance n’est pas supprimée. Dans les deux modes de facturation, l’unité de temps entamée est facturée.

    Mais bien sûr ! J’ai été trop habitué à Nua.ge, où on ne paye que quand l’instance est allumée, à l’exception du stockage… Mais alors pourquoi diantre faut-il 15 minutes pour allumer une instance ?? Quoi qu’il en soit, ce test m’aura coûté 60€ pour environ 4h d’usage GPU effectif. Oui c’est cher 🙂 D’ailleurs, avec un minimum d’achat à 200€, on peut donc plutôt prendre ce chiffre si je n’utilise pas les ressources d’ici 12 mois…

    Chez la concurrence, les instances GPU sont nettement moins cher 🙁 Là j’ai vraiment voulu tester le Made In France, mais un tarif x2 me parait prohibitif.

    Plate-formeTarif horaire
    GCP$0,74
    AWS$0,918
    OVH$1,99*
    ScaleWay1,221€ (Tesla P100)
    0,98€ sur RTX 3070
    * conversion EUR/USD avril 2023

    Conclusion

    Avec ce test de GPU, c’était la 1e fois que j’utilisais le cloud public d’OVH depuis plus de 2 ans, lors des études de concurrence pour Nua.ge ; je suis très déçu de la progression du produit, par rapport aux moyens d’OVH. L’interface est aussi compliquée qu’AWS, on ne comprends pas vraiment ce qu’on paye et quand, bref l’expérience utilisateur a été plus frustrante qu’autre chose. Il y a clairement un boulevard pour qui proposera un produit simple d’usage.

  • CentOS 8 : No URLs in mirrorlist

    Vous utilisez CentOS8 et vous avez cette erreur ?

    Error: Failed to download metadata for repo ‘appstream’: Cannot prepare internal mirrorlist: No URLs in mirrorlistExited with code exit status 1

    Nous allons voir ensemble comment la corriger 🙂


    En 2021, Red Hat a pris le contrôle de CentOS et a déprécié le fonctionnement de base de cette distribution pour en faire une version « stream ». Au 31 décembre, ils ont coupé les repository officiels, d’où l’erreur que vous avez. Pour corriger, il faut donc utiliser un miroir :

    sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-Linux-*
    sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.epel.cloud|g' /etc/yum.repos.d/CentOS-Linux-*

    vous pouvez maintenant relancer « dnf update », ça fonctionnera !

    Passer à Rocky Linux 8

    Le remplaçant officiel est ROcky Linux 8, créé par le fondateur de CentOS, et a pour objectif de mimer les packages Red Hat (comme CentOS). Vous pouvez upgrader votre distribution Centos8 pour passer sur Rocky grâce à un script de mise à jour :

    dnf -y upgrade
    curl https://raw.githubusercontent.com/rocky-linux/rocky-tools/main/migrate2rocky/migrate2rocky.sh -o migrate2rocky.sh
    chmod u+x migrate2rocky.sh
    ./migrate2rocky.sh -r
    reboot

    Et vous voilà sur Rocky, à vous les mises à jour (jusqu’en 2024 au moins sur la version 8).

  • Edit Ubuntu password on Windows10

    You installed Ubuntu on your Windows 10 through App Store ? Great ! But if you have a good memory, you know you type your user password. This one is needed for every single « sudo » instruction. Do you want to change this password without knowing the old one ? It’s very easy. No need to follow all the (very long) tutorial on the net with cryptic Windows command. Just go to this folder :

    C:\Users\<yourusername>\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\rootfs\etc

    Edit file « shadow » and find the line with your username. Line is like this :

    mylogin:$1$D5z55fgf$SDOizClhr1t8t4.:17687:0:99999:7:::

    Password is encrypted and is the second field (separator is « : »).

    Generate a new password with the following command :

    openssl passwd -1 -salt ARandomString yourSuperPassword

    Replace old password hash with the output. No need to reload anything, just type « sudo » with your user, you get prompted with a password, type « yourSuperPassword » and voilà !

     

  • Voyager pas cher aux Galapagos

    Voyager pas cher aux Galapagos

    Poste de blog inhabituel, puisqu’on va parler … voyage ! Et en presque 2000 mots, donc je vais pouvoir vous donner pleins d’infos et d’astuces pour voyager aux Galapagos en en profitant un maximum … et sans se ruiner ! 

    (suite…)

  • Troubles installing CentOS on XenServer

    You have a XenServer 6 or 7, and you have troubles installing CentOS (6 or 7) : the installer loads, but all you get is a black screen …

    This is a known issue, and I’m wondering why it wasn’t fixed because it’s there for so long.
    When having the boot menu, hit « tab » to show the option, and add the following :

    modprobe.blacklist=bochs_drm rhgb

    And then it worked !

    https://bugs.xenserver.org/browse/XSO-353

    https://discussions.citrix.com/topic/364226-centos-7-1503-installer-gives-blank-screen/

     

  • Une mauvaise expérience avec un e-commerçant français

    Je tenais à partager avec vous ma mésaventure avec un e-commerçant, en l’occurrence Designere.fr

    En mai 2015, j’ai cherché à acheter une plaque à induction. Mon choix se porte sur un modèle de chez Sauter, et je trouve ma référence à un très bon prix sur le site designere.fr. De plus, le site annonce la référence comme étant en stock, ce qui était un critère important pour moi. Mon compte est débité le jour de la commande (certains commerçants Internet choisissent de débiter leur client le jour de l’expédition, mais pas celui-ci) …

    Quelque jours après la commande, le site m’annonce que le produit est en rupture et qu’il faut compter une à deux semaines de délai. Ainsi soit-il pensait-je à l’époque, et va pour attendre un peu.

    Un mois après, le produit n’est toujours pas disponible et on me propose un article similaire mais bien plus cher. Je choisis donc d’annuler ma commande, et on me confirme que mon remboursement aura lieu dans les plus brefs délais.

    Fin juillet, soit 2 mois et demi après ma commande (et 40 jours après l’annulation), aucune nouvelle … Je relance donc deux fois par email. A chaque fois, on me dit « transmettre ma demande au service facturation », sans résultat. Leur numéro de support est surtaxé dès la première seconde (ce qui au passage est tout simplement interdit), et leur email se met à renvoyer des erreurs comme quoi le destinataire n’existe plus …

    En général, c’est là qu’on panique. Quelques recherches sur Internet montrent que je suis loin d’être le seul dans ce cas : de très nombreuses personnes ont commandées comme moi un produit annoncé en stock, qui finalement ne l’était pas, puis après avoir patientées des semaines, annulent leurs commandes … sans recevoir le remboursement. Pour résumer les mésaventures d’autres clients, il faut s’armer de patience au téléphone, râler très fort, et envoyer plusieurs recommandés pour obtenir gain de cause.

    Cette technique n’est malheureusement pas limitée à un seul commerçant mais se développe de plus en plus sur le net : cela permet de faire de la trésorerie sur le dos des clients … Pour faire encore plus fort, il suffit de ne pas livrer certaines références. Si un client sur dix tire un trait sur la dette et ne veut pas perdre de temps dans des recommandés, c’est autant de gagné.

    De mon côté, j’ai porté plainte auprès du tribunal de proximité début septembre, pour avoir une audience à la mi-janvier 2016.
    Et miracle au 20 décembre: mon remboursement est bien arrivé (avec six mois de retard donc). C’est pas incroyable tout ça ?

    Il y a bien une leçon à retenir de tout ça : si vous voyez un commerçant en ligne avec des tarifs très compétitifs, renseignez-vous avant sur son sérieux.
    Au final, j’ai commandé mon produit chez un concurrent certes 10% plus cher, mais reconnu et qui pratique le débit à l’expédition.

  • using a secured relay in Postfix

    I have a server on a LAN that need to send emails. Of course, this server has access to the internet through NAT, but for emails it is a bit more difficult : nowadays, email servers are checking many things when receiving emails : reverse, ‘from’ field, etc. and you may not have access to this kind of tuning for the internet IP address used with your NAT. So I needed to configure a relayhost, with direct access to the internet, that would accept my incoming email and relay it through normal channels. But I don’t want this server to be an open relay, so I have to setup authentication between them.

    (suite…)

  • Problèmes de réseau avec VirtualBox sur Windows 10

    Vous êtes passés sur Windows 10 ? Tant mieux pour vous. Mais si vous êtes un peu développeur / sysadmin, vous avez probablement VirtualBox qui tourne sur votre poste, et horrifié, vous vous rendez compte que vos VM n’ont plus de réseau depuis la mise à jour de l’OS. Si vous regardez les propriétés réseau, vous remarquez que le mode « Pont » (bridge) n’est plus utilisable (et c’est probablement ce mode que vous utilisiez).

    La solution est simple : installer une version beta de VirtualBox. Oui c’est moche … Elle se trouve ici : https://www.virtualbox.org/wiki/Testbuilds

    Redémarrez ensuite votre PC … Et le mode bridge est de nouveau dispo !

    Dernière subtilité : apparemment le réseau ne marche plus si votre ordinateur est passé en mode veille … Le réseau est toujours là, mais les paquets ne passent plus. Un reboot corrige l’histoire.

    En espérant que VirtualBox fasse rapidement les corrections nécéssaires !