Paradise · Control Center

juin 2026 → juillet 202617 min de lecture

Code source

J'ai commencé ce projet après avoir regardé Paradise, la série de Dan Fogelman : 25 000 personnes vivent enfermées dans une ville souterraine du Colorado, trois ans après une catastrophe. L'intrigue est policière, mais ce qui m'a occupé l'esprit après l'épisode, c'est l'intendance. Comment on nourrit une ville fermée. D'où vient son oxygène. Ce que ça coûte en électricité d'éclairer des cultures qui ne voient jamais le soleil. La série ne répond pas à ces questions, ce n'est pas son sujet. J'ai voulu voir si je pouvais y répondre en chiffres.

Paradise · Control Center est donc le poste de commandement de cette ville. La simulation tourne en continu côté serveur, en temps réel 1:1, et le navigateur ne fait rien d'autre que la regarder vivre.

Des chiffres calculés, jamais posés à la main

C'est la contrainte que je me suis donnée au départ. Presque aucun chiffre affiché dans l'interface n'existe dans le code : ils sont tous calculés, à chaque heure simulée, à partir d'une petite dizaine de constantes physiques. Et ces constantes ne sont pas inventées non plus, elles viennent de sources réelles, écrites dans un document du dépôt avec leur formule avant d'être codées.

Constante Valeur D'où elle vient
Oxygène respiré 0,80 kg par personne et par jour Moyenne sur 24 heures, dormeurs inclus, recoupée entre le référentiel de support-vie de la NASA et la physiologie respiratoire
Gaz carbonique rejeté 0,95 kg par personne et par jour Calculé depuis l'oxygène par le rapport respiratoire mesuré, dans les mêmes sources
Eau utilisée 75 L par personne et par jour Somme des usages domestiques (30 de douche, 12 de chasse, 12 de lessive, 8 de lavabo, le reste en cuisine et ménage), à comparer aux relevés de la NASA sur l'usage de l'eau
Taux de recyclage de l'eau 88 % Ce qu'atteint un réseau urbain performant, loin des 93 à 98 % de la Station spatiale. Il reste donc 9 L par personne à trouver chaque jour
Alimentation 2 400 kcal ingérées, 2 688 à produire Besoins énergétiques humains, FAO et OMS, moyenne pondérée par âge et par sexe, plus 12 % de pertes de transformation
Surface de culture 45 m² par personne La surface qu'il faut pour cultiver ces 2 688 kcal chaque jour. Nourrir demande beaucoup plus de plantes que recycler l'air : 12 à 15 m² suffiraient pour l'oxygène seul, comme dans l'expérience BIOS-3
Éclairage des cultures 90 kWh par tranche de 1 000 kcal Milieu d'une fourchette de 78 à 116 recoupée sur trois travaux indépendants, dont une estimation sur le blé en ferme verticale. Le rendement des LED horticoles utilisé pour ce calcul, 2,7 µmol/J, vient de cette synthèse
Réacteur 64,7 MWe par module Un petit réacteur NuScale fournit 73,5 MW au réseau. Comme aucun réacteur ne tourne toute l'année (rechargement, maintenance), je ne compte que les 88 % observés en moyenne par l'AIEA, soit 64,7 MW sur lesquels je peux compter
Chaleur dégagée 120 W par personne Moyenne sur 24 heures du dégagement métabolique, chapitre « apports des occupants » du manuel de référence ASHRAE
Roche profonde 14 °C stables Température du sous-sol à quelques dizaines de mètres, qui sert de puits de chaleur passif

Le reste se déduit de ces valeurs. Avec la réserve et la capacité de l'usine de récupération, la ville peut soutenir environ 28 000 habitants en eau. La répartition Eurostat des types de ménages, appliquée à la pyramide des âges, donne 10 901 foyers de 2,29 personnes en moyenne. Le taux d'emploi appliqué aux 18-64 ans donne environ 10 500 actifs. Et comme la surface des serres est dictée par les calories, elle dépasse largement ce qu'il faudrait pour l'air : les cultures produisent 2,8 fois plus d'oxygène que les habitants n'en respirent.

Voici comment chaque circuit fonctionne, dans l'ordre où ils s'influencent.

L'air

Chaque habitant consomme 0,80 kg d'oxygène par jour et rejette du gaz carbonique. Pas de façon régulière : la consommation suit un profil d'activité sur 24 heures, donc on respire moins en dormant. C'est le même profil qui creuse la nuit et fait remonter les courbes le matin.

Les serres reprennent ce gaz carbonique pour fabriquer des plantes, et relâchent de l'oxygène au passage. Rien n'entre ni ne sort de la ville : le carbone tourne entre l'air, la nourriture et les déchets végétaux qu'on composte, et le total ne bouge jamais. Il vaut 135 tonnes, et il ne dérive pas d'un gramme sur des milliers d'heures simulées, parce que les stocks sont comptés en entiers plutôt qu'en nombres à virgule.

Le vrai problème d'une atmosphère fermée n'est pas de manquer d'oxygène, c'est que les plantes et les humains n'échangent pas leurs gaz dans les mêmes proportions : l'oxygène s'accumule et le gaz carbonique s'épuise. C'est le compostage des déchets végétaux qui rattrape ce déséquilibre en rendant leur carbone à l'air.

Tableau de bord Life Support, sous-système Oxygène : réserve de 1 959 tonnes, production des serres à 56 t/j contre 20 t/j de respiration, projection sur 24 heures

La nourriture

Les serres produisent à la demande : ce que la ville vient de manger, plus de quoi remonter un stock d'avance de 10 jours. Elles ne produisent donc pas à plein régime en permanence, et c'est ce qui rend visible le moindre problème d'approvisionnement.

Trois choses peuvent limiter la récolte : la surface de culture, qui est fixe, l'électricité disponible pour l'éclairage, et le gaz carbonique présent dans l'air. Le dernier plafond compte : sans le compostage, les plantes finiraient par manquer de matière première.

L'opérateur n'a qu'un levier alimentaire, une consigne de ration. Elle réduit ce que chacun mange, donc elle étire les réserves quand la production baisse. Son plancher est fixé à 70 %, parce que le besoin énergétique minimal retenu par la FAO vaut 1 680 kcal par adulte et par jour, soit 70 % des 2 400 nominaux. En dessous, la consigne est refusée.

Vue Stocks et logistique : les trois réservoirs de carbone avec leurs flux, le bilan de masse conservé à 135 tonnes et les pics de ponction aux repas

L'électricité

La demande de la ville se compose de trois postes : les serres pour 252 MW en continu, les habitants pour environ 1,2 kW chacun (logements, ascenseurs, ventilation, traitement de l'eau), et les groupes froids. Les serres pèsent donc à elles seules 83 % de la consommation, ce qui décide de la taille de la centrale : sept petits réacteurs, six en charge et un pour couvrir les rechargements.

Quand la capacité baisse, l'ordre de service compte plus que le total. Le support-vie est alimenté en premier, et les serres prennent ce qui reste. Une panne de réacteur ne coupe donc pas l'air, elle réduit les récoltes, et la famine arrive des semaines plus tard. Il faudrait un effondrement quasi total, avec une capacité tombée sous les besoins vitaux, pour que la ville manque d'air.

Le froid est rangé au même niveau que les serres et jamais parmi les besoins vitaux. Concrètement, arrêter ou démarrer des groupes froids change la quantité de nourriture produite, mais ne peut jamais couper la ventilation.

L'eau

Chaque habitant utilise 75 litres par jour, mais l'eau tourne en circuit fermé et 88 % sont récupérés. Il reste donc 9 litres par personne à remplacer chaque jour, soit 225 m³ pour la ville, et c'est le seul poste réellement consommable.

La première version en faisait un compte à rebours : une réserve de 180 jours qui se vidait lentement, et la ville mourait à date. Je ne voulais pas de ça, mais je ne voulais pas non plus supprimer la contrainte. J'ai donc ajouté un équipement, dimensionné à la demande comme tout le reste : une usine de récupération sur place qui produit jusqu'à 252 m³ par jour, de quoi couvrir l'appoint de 28 000 habitants. En dessous de ce seuil la réserve reste stable et les 180 jours redeviennent un tampon de sécurité ; au dessus, elle se vide et la pénurie apparaît d'elle-même.

Ce qui sort de la réserve n'est pas perdu pour le calcul, il part dans un second réservoir d'eau usée, et la récupération fait le trajet inverse. La somme des deux ne change jamais.

La chaleur

La chaleur vient de quatre endroits : les habitants eux-mêmes, à 120 W chacun, l'éclairage des serres qui finit presque entièrement en chaleur, les réacteurs et leurs pertes de distribution, et les pompes du support-vie. Elle est évacuée par les groupes froids et par la roche, qui absorbe d'autant plus que la zone est chaude par rapport à ses 14 °C.

La température n'est pas recalculée à l'affichage, c'est une valeur qui avance heure par heure et qui garde son passé. Elle met environ 8 heures à réagir, ce qui correspond à l'inertie mesurée pour un grand volume souterrain accolé à la roche. Conséquence : arrêter un groupe froid ne fait pas sauter un compteur, la température monte progressivement, et elle finit par se stabiliser sur un palier, parce que plus la zone est chaude, plus la roche en absorbe. Quand on relance le groupe, elle redescend au même rythme.

Le circuit se referme sur l'électricité : évacuer de la chaleur en consomme, et cette électricité manque ensuite aux serres. Une ville plus chaude produit donc un peu moins de nourriture.

Les naissances et les morts

À chaque heure simulée, chaque habitant a une chance de mourir qui dépend de son âge : une part constante, celle des accidents, et une part qui croît avec l'âge et double tous les huit ans environ. La pyramide des âges à la fondation n'est pas dessinée à la main, elle est déduite de cette même règle.

Les naissances dépendent de la structure d'âge. Une femme peut avoir un enfant entre 15 et 49 ans, avec le maximum de chances entre 25 et 29 ans, et le nombre total est calé sur un indice de 2,1 enfant par femme, soit le seuil de remplacement. La courbe de population n'est donc écrite nulle part : elle sort du calcul, tranche d'âge par tranche d'âge, confronté à la mortalité.

La pénurie agit sur les deux côtés. La ville regarde en permanence laquelle de ses quatre ressources vitales est la plus juste (nourriture, oxygène, eau, support-vie) et en tire un niveau de suffisance. Quand ce niveau baisse, il y a d'abord moins de naissances, puis des morts supplémentaires, en nombre proportionnel à la part de population que les ressources ne peuvent plus nourrir, étalées sur 60 jours, durée tirée de la physiologie du jeûne. Ce mécanisme s'éteint tout seul : quand la population baisse, les ressources redeviennent suffisantes.

Recensement de la population : pyramide des âges par sexe, 10 901 foyers de 2,29 personnes en moyenne, répartition de l'emploi par secteur et des types de foyer

C'est le résultat que je voulais voir. En coupant 55 % de la capacité des serres, la population passe de 25 000 à environ 12 600 puis se stabilise au niveau que la production peut nourrir, sans jamais s'éteindre. En arrêtant les 12 serres, la rupture des vivres est annoncée pour dans 9 jours et demi ; en descendant la ration à 70 %, elle recule à 13 jours et 4 heures, soit le rapport d'une division par 0,7, qui n'est écrite nulle part.

La journée de chacun

Chaque habitant a un métier, un foyer et une routine. Les 25 métiers sont dérivés des besoins réels de l'infrastructure : il faut tant de personnes pour tenir les serres, les réacteurs, les usines d'eau, les écoles, la santé, la sécurité, la maintenance. Les services qui tournent en continu sont couverts par trois équipes qui pivotent chaque semaine. Les repas et les heures de coucher sont corrélés au foyer, donc les colocataires mangent ensemble.

Fiche d'un habitant né pendant la simulation : bracelet, caméras qui le voient, foyer de six personnes avec leurs rôles, journée recalculée heure par heure et historique de vie avec sa mère et le soignant présent à la naissance

Rien de tout cela n'est stocké habitant par habitant. La position sur la carte, l'activité en cours, l'historique de vie sont recalculés à la demande à partir de la graine du monde et de l'heure, avec un ancrage sur un vrai bâtiment et une interpolation pendant les trajets. La carte n'envoie jamais 25 000 positions d'un coup, seulement un échantillon du cadre visible.

Carte tactique zoomée sur quelques îlots : chaque point est un habitant, coloré selon son activité en cours

Ce niveau de détail réserve des pièges. À midi, l'équipe de quart d'un réacteur passait à l'état « repas », et la salle de commande se retrouvait comptée à zéro occupant. Le personnel des installations critiques est désormais compté en poste sur toute sa fenêtre de service, pauses sur site comprises.

Rejouer la même histoire

L'état de la ville est une fonction de son passé : la graine du monde, le temps écoulé, et la liste des interventions de l'opérateur. Rien d'autre. Les sauvegardes sur disque ne sont donc qu'un cache : rejouer dix ans depuis la fondation prend 216 ms, on peut toujours s'en passer. Le prototype qui a validé cette architecture, avant la moindre ligne du serveur, engendre les 25 000 habitants en 11,7 ms et tient le registre de la population en 17 octets par personne, contre 128 pour un objet complet.

Le front ne calcule rien

Le dépôt est un monorepo Bun en trois morceaux.

Paquet Rôle
packages/protocol Le contrat partagé : types échangés et dérivation de l'horloge de la ville
apps/server Elysia sur Bun : héberge le moteur, expose REST et SSE, persiste dans bun:sqlite
apps/web Vite, React 19 et Tailwind v4 : présentation seule

Le navigateur n'a aucune règle métier et surtout pas d'horloge qui avancerait de son côté : l'horloge est dérivée dans le paquet partagé, importé à la fois par le moteur et par l'interface, donc il n'en existe qu'une seule implémentation. Quand la liaison se dégrade, le front fige l'heure sur la dernière vérité connue et l'annonce, plutôt que d'égrener des secondes que personne n'a calculées. Les tableaux tournent avec TanStack Query, Table et Virtual, le filtrage et la pagination étant faits par le moteur, et l'état de navigation vit dans l'URL via nuqs, donc chaque vue est partageable telle quelle.

Mon premier projet écrit entièrement par une IA

C'est le premier projet que j'ai mené de bout en bout sans écrire le code moi-même. Tout Paradise a été produit par Claude Code à partir de mes prompts. Ce que j'ai fait, moi : décider ce qu'on construit et dans quel ordre (le cahier des charges fait 138 Ko et 16 modules, découpés en lots livrables), choisir les technologies et trancher les évaluations, arbitrer la conception avant la première ligne de code, et fixer des critères de qualité auxquels je n'ai pas dérogé.

Critère Ce que ça impose
Porte verte Tests, type-check, lint et build du front. Rien n'est déclaré livré autrement
Aucune valeur inventée Un chiffre non sourcé fait reporter la fonctionnalité
Vérification dans le navigateur Chaque module essayé pour de vrai, sans erreur en console, les autres vues contrôlées sans régression
Accessibilité Une alerte est toujours doublée d'un texte ou d'un symbole, pour rester lisible sans la couleur et sans le son
Revue adversariale Après chaque module, des agents cherchent des défauts, d'autres essaient de les réfuter. Je ne garde que ce qui survit

Ce que j'en ai pensé

Au début, l'exercice est très bon pour mettre les idées au clair. Formuler une intention et la voir revenir sous forme de questions fermées m'a obligé à trancher des choses que j'aurais laissées floues. La recherche d'informations est l'autre gain évident : le modèle de ressources décrit plus haut n'existerait pas sans elle, parce que chercher, recouper et documenter des chiffres de physiologie, d'agronomie et de nucléaire aurait été un projet à lui seul. Il y a aussi la vitesse de production, et la boucle de retour très courte : je disais qu'un flux de mesures noyait les vrais événements dans le journal, et il disparaissait dans la demi-heure.

Au bout d'une vingtaine d'itérations, je me suis lassé, et la raison est simple : je ne maîtrisais plus ce qui se passait. À la fin, je ne savais plus dire comment fonctionnait une bonne partie du code. Si je reprenais Paradise aujourd'hui, j'aurais du mal, et il me faudrait un temps sérieux de relecture avant de toucher quoi que ce soit. C'est un coût réel, et il s'installe progressivement, sans qu'on le voie venir. La porte verte et les revues adversariales compensent en partie, parce qu'elles vérifient des choses que je ne relis pas, mais elles ne remplacent pas le fait de connaître son propre code.

Pour ce projet précis, l'échange reste très favorable. Le but était personnel : voir vivre une ville. J'ai eu le plaisir de la regarder évoluer semaine après semaine, avec un niveau de détail que je n'aurais jamais atteint autrement, et c'était franchement divertissant. Sur un projet que je dois maintenir dans la durée ou reprendre à plusieurs, je m'y prendrais autrement.

Où ça s'arrête

Version v0.28, en pause depuis le 26 juillet 2026
Tests 230 sur 230
Code environ 25 600 lignes de TypeScript
Modules livrés Poste de commandement, population, bracelets, surveillance, journal et audit, design sonore, support-vie, stocks, plus la carte tactique, les bâtiments, la température et l'historique de vie
Modules jamais commencés Dôme et éclairage, santé, gouvernance, protocoles d'urgence, sécurité, surface, assistant
Chaîne d'outils TypeScript 7.0.2, Biome 2.5.5, React 19.2, Tailwind v4, Elysia, bun:sqlite

L'état est vert et reproductible : les tests, le type-check, le lint et le build passent, le serveur démarre sur une genèse de 25 001 habitants, et à la relance l'horloge de la ville repart exactement où elle s'était arrêtée.

Pourquoi je m'arrête là

Le projet n'est pas abandonné, il est en sommeil, et pour une raison que je n'avais pas prévue en commençant : à force de construire des leviers, je me suis rendu compte que ce qui m'intéressait était de regarder, pas de piloter. Couper un bracelet, geler une caméra, rationner une ville, c'était l'idée de départ, celle qui vient de la série. Ce qui me retient devant l'écran, c'est la pyramide des âges qui dérive toute seule et la rupture de stock annoncée dans neuf jours et demi sans que j'aie touché à rien.

Le projet reste en place dans son état figé, avec ses tests au vert et sa base reproductible. Si je le reprends un jour, je repartirais de zéro sur une bonne partie du code, et je changerais deux choses dans ma façon de faire. D'abord découper la logique en morceaux beaucoup plus petits et isolés, chacun testable seul : c'est ce qui permet de garder la main sur un projet de cette taille. Ensuite guider et relire chaque choix technique un par un, et surtout regarder de près comment chaque chiffre est produit et calculé, parce que c'est là que se trouve la vraie compréhension du modèle, et c'est ce qui m'a manqué à la fin.

Le sujet, lui, continue de m'intéresser beaucoup. Ce moteur pourra servir de base à d'autres projets de simulation de la vie, sous une forme ou une autre.

Ce que j'en retiens

La règle « aucun chiffre en dur » demande plus de travail qu'elle n'en a l'air. Elle interdit d'ajuster un modèle vers un résultat plaisant, donc elle oblige à chercher les sources, à les écrire, et parfois à repousser une fonctionnalité entière faute de données. En échange, chaque chiffre affiché se remonte jusqu'à une cause, et les comportements que je trouve intéressants (la population qui se stabilise au lieu de s'éteindre, l'excès d'oxygène, la rupture de stock qui recule quand on rationne) sont arrivés sans que je les programme.

Technologies utilisées