Theatre Script Builder
mars 2025 → juin 20255 min de lecture

En master, on avait une UE où il fallait monter une pièce de théâtre, pour travailler la communication. Au final, la pièce ne s’est jamais jouée, mais on a quand même travaillé dessus et fait des répétitions.
Dans une pièce, il n’y a pas que le texte. Entre deux répliques, la lumière change ou une musique démarre, et tout ça arrive dans un ordre bien précis. J’ai voulu un outil pour écrire le tout à la suite, dans l’ordre de la scène, puis le sortir en PDF.
Theatre Script Builder, c’est cet outil. On crée un script, on ajoute ses personnages, puis on empile les éléments un par un. Le code est public sur GitHub.
Sept sortes d’éléments
Dans un script, on mélange la réplique, la narration, la lumière, le son, l’image, la mise en scène et le mouvement. Chacun a ses propres champs. Une lumière a une place sur scène et une couleur. Un son peut être un fichier ou une vidéo YouTube, avec son timecode et sa durée. Un mouvement dit quel personnage va d’où à où.
Une lumière reste allumée jusqu’à ce qu’on l’éteigne, et une musique joue jusqu’à ce qu’on la coupe. Pour éteindre ou couper, on ne retape rien. On choisit parmi les lumières et les sons lancés plus haut dans le script.
Chaque personnage a deux noms, celui du rôle et celui de la personne qui le joue, et une couleur à lui. Les deux noms s’affichent côte à côte, jusque dans le PDF, pour savoir tout de suite qui joue qui.
Une seule liste
Côté base de données, ces sept types très différents doivent tenir dans une seule liste ordonnée. Chaque élément est un ScriptItem, qui ne garde que sa position et son type. Ses champs à lui vont dans une table à part, une par type (Dialogue, Lighting, Sound…), reliée à un seul ScriptItem. On insère n’importe quel type à n’importe quelle place, et pour lire le script, il suffit de trier par position.
Rapide, et à plusieurs
On voulait pouvoir travailler à plusieurs, sur une interface intuitive où tout va vite. On ajoute un élément juste avant ou juste après n’importe quel autre, on en sélectionne plusieurs pour les supprimer d’un coup, et on réordonne tout par glisser-déposer avec dnd-kit, qui marche aussi au clavier.
Les modifications restent dans le navigateur, dans un store Zustand, jusqu’à ce qu’on enregistre avec le bouton ou Ctrl+S. Tant qu’il reste des changements en attente, un voyant le rappelle, et le navigateur prévient si on ferme la page.
Pour le travail à plusieurs, j’ai fait simple, sans comptes. Chaque script a son adresse, que toute l’équipe pouvait ouvrir. Il n’y a pas d’édition en temps réel, chacun enregistre ses changements de son côté.
Les sauvegardes comptaient aussi. Un bouton exporte tout le script en JSON, personnages compris, pour en garder une copie chez soi. L’import fait l’inverse et remplace le contenu d’un script par celui du fichier.
Le PDF et l’aperçu
Pouvoir sortir le script en PDF, c’était important. Il se génère dans le navigateur avec react-pdf, où le document s’écrit en composants React. Chaque élément est numéroté et n’est jamais coupé entre deux pages. Le nom du personnage s’écrit dans sa couleur, et les indications techniques sortent dans des encadrés gris, pour ne pas se mélanger au texte dit.
Avant de générer, on peut cocher des personnages, et leurs répliques sortent surlignées en jaune, pour qu’un comédien retrouve vite les siennes. On peut aussi enlever les images.
Sur un petit script de démonstration, avec la gardienne cochée, ça donne ça :

L’aperçu, lui, montre le script en lecture seule. Une flèche marque l’élément où on en est, et on l’avance avec les flèches du clavier. En revenant sur la page, on la retrouve au même endroit. Les sons et les vidéos YouTube s’écoutent directement dedans.
Sur un vrai script
Tout ça marchait bien sur un petit script. Le nôtre a fini à près de sept cents éléments, et là, j’ai galéré, surtout sur la sauvegarde et sur le tri. Il faut dire que je découvrais tout en même temps : Next.js et son App Router, les Server Actions, Prisma et une base Postgres serverless chez Neon.
Pour enregistrer, le navigateur envoie la liste des changements en attente à une Server Action. Elle revérifie les données avec Zod, puis applique tous les changements dans une seule transaction. Le problème, c’est qu’un déplacement réécrit la position de tous les éléments du script. Sur sept cents éléments, ça fait sept cents requêtes dans la même transaction, chacune envoyée à une base distante. Les transactions duraient tellement que j’ai fini par monter leur délai maximum à cinq minutes.
Pour limiter la casse, seul le dernier ordre de la liste part à l’enregistrement, quel que soit le nombre de déplacements. Côté écran, j’ai mémoïsé le rendu des éléments, pour qu’un élément ne se redessine que quand ses données changent.
Les images et les sons importés en fichier n’aidaient pas. Ils étaient stockés directement en base, encodés en base64, et repartaient avec le script à chaque chargement et dans chaque export JSON. Avec le recul, je les rangerais à part, dans un stockage de fichiers.
Où en est le projet
C’est un de mes premiers projets complexes, et je l’ai arrêté là, à cause de ces problèmes de technos. En mai 2025, j’ai bloqué l’éditeur pour ne laisser que la lecture, puis j’ai supprimé la démo.