« Dessiner une forêt en une ligne » ressemble d’abord à une promesse de démo : prendre une technique GPU installée depuis longtemps, la comprimer dans une syntaxe spectaculaire, puis laisser croire que le problème a disparu. Ici, la compression de la syntaxe est précisément le travail intéressant.

Akshat Patil a consacré un microgrant de la Processing Foundation à une nouvelle API d’instancing pour p5.strands ; dans son billet du 10 août, la forêt de démonstration compte 800 arbres pour deux draw calls, tandis que l’exemple minimal instances(500).sphere(20) soumet cinq cents sphères en une seule draw call instanciée.13

L’instancing, lui, n’a rien de nouveau : les moteurs 3D s’en servent depuis longtemps pour l’herbe, les foules, les particules ou les accessoires répétés. Patil travaille sur une question beaucoup moins photogénique et beaucoup plus utile : à quel moment cette mécanique doit-elle apparaître dans l’expérience d’une personne qui veut simplement remplir son canvas ?

Le projet relève donc surtout du design d’API, ce domaine où plusieurs semaines de discussion peuvent aboutir à une ligne dont l’ambition finale est précisément de ne pas impressionner.

Mille appels

Imaginez une forêt dont chaque arbre utilise la même géométrie de base. La façon naïve consiste à parcourir une boucle côté JavaScript : positionner l’arbre, le dessiner, recommencer mille fois.

Le résultat peut être visuellement correct tout en multipliant les soumissions entre CPU et GPU. Dans l’exemple pédagogique de Patil, dessiner 1 000 arbres séparément revient à envoyer 1 000 instructions de dessin alors que le GPU sait répéter la même géométrie à partir d’une seule soumission instanciée.1

L’instancing change surtout la formulation du contrat : « voici cet arbre, dessine-en mille instances » remplace une longue succession de « dessine cet arbre » côté application.

Le GPU reçoit la géométrie commune une fois pour cette draw call et différencie ensuite chaque copie grâce à un index d’instance. Cet index peut piloter position, couleur, taille ou toute autre variation calculée dans le shader.13

Cette plomberie peut faire une vraie différence sur les performances sans rendre les arbres gratuits : les sommets existent toujours, les pixels doivent toujours être produits, et un shader coûteux, beaucoup de transparence ou une géométrie lourde peuvent continuer à saturer le rendu après la réduction des draw calls.

L’API raccourcit donc le chemin entre l’intention et une stratégie de rendu raisonnable ; la facture GPU, elle, conserve obstinément le reste.

Avant la ligne

p5.js savait déjà faire de l’instancing avant ce projet. Le chemin existant passait par de la géométrie conservée : construire la forme avec buildGeometry(), la dessiner avec model() puis utiliser instanceID() dans le shader pour savoir quelle copie était en cours de rendu.1

Pour quelqu’un qui connaît déjà une pipeline 3D, le chemin précédent est logique. Pour la personne qui découvre les shaders parce qu’elle voulait cinq cents sphères, il demande en revanche d’apprendre buildGeometry(), model() et la notion d’instance avant même d’avoir obtenu le premier mur de boules.

Le tracking issue ne cache pas cet objectif d’accès : l’instancing doit devenir plus facile à découvrir et à utiliser sans exiger d’abord une formation complète à la pipeline de rendu.2 La Processing Foundation décrit d’ailleurs le microgrant de Patil par sa cible, une beginner-friendly instancing API.5

La proposition finale ressemble au reste de p5.js :

instances(100).sphere(20)

instances(100) retourne un objet sur lequel les primitives habituelles peuvent être dessinées. Pour une géométrie définie avec beginShape() et endShape(), le design propose aussi de passer le nombre d’instances à endShape() plutôt que d’introduire une seconde famille de concepts.3

Exemple visuel associé à la nouvelle API d’instancing p5.strands
Le projet essaie de garder l’instancing dans le vocabulaire habituel de p5.js au lieu d’exposer d’abord la plomberie de rendu.Akshat Patil / Processing Foundation

La notion d’instance reste visible ; ce qui disparaît, c’est une bonne partie du cérémonial nécessaire pour atteindre la technique.

Un index reste

Pour que 500 copies d’une sphère ne se superposent pas exactement au même endroit, le shader doit encore savoir laquelle il traite.

L’ancienne API utilisait instanceID(). Le projet introduit instanceIndex, conçu comme une valeur plutôt qu’un appel de fonction.23 La nuance paraît microscopique jusqu’à lire la discussion GitHub.

Dave Pagurek suggère que l’index se comporte davantage comme width ou mouseX : une donnée disponible dans le contexte, pas une opération que le débutant doit penser à appeler. Patil note au passage qu’une valeur évite l’erreur très banale consistant à oublier les parenthèses d’une fonction.2

Le mot change aussi. « ID » peut évoquer un identifiant unique et durable ; « index » décrit mieux ce qu’on possède réellement ici : un numéro dans une séquence. Le design proposal rapproche ce choix du builtin instance_index de WGSL et d’APIs voisines.3

La discussion montre assez bien ce que « simplifier » veut dire ici : enlever une friction qui ne porte aucune idée graphique importante, tout en conservant l’index dont l’artiste a réellement besoin.

La personne n’a pas besoin d’apprendre immédiatement comment l’instance count traverse le renderer. Elle doit en revanche comprendre qu’il existe un numéro différent pour chaque copie, parce que c’est ce numéro qui lui permettra de dessiner une forêt plutôt qu’une tour de 800 arbres au même emplacement.

Cacher la plomberie

p5.strands poursuit déjà ce compromis avec les shaders : on écrit des snippets proches du JavaScript, puis la bibliothèque les transforme pour alimenter la pipeline graphique.4

La documentation contributeur décrit une pipeline en plusieurs étapes : le pseudo-JavaScript de l’utilisateur est transformé, exécuté pour construire un graphe des opérations, puis ce graphe est converti en GLSL.4 Le système ajoute aussi des commodités qui ressemblent davantage au monde shader qu’au JavaScript ordinaire, par exemple des vecteurs ou des swizzles.

p5.strands ne prétend pas pour autant qu’un shader est du JavaScript ; il fournit une façade familière qui permet de commencer à manipuler le GPU avant d’apprendre tout le dialecte graphique sous-jacent.

Un étage plus haut, l’instancing suit la même logique : la ligne courte ne fait pas disparaître les draw calls, elle choisit une soumission instanciée cohérente avec l’intention exprimée par le sketch.

Une bonne abstraction décide ainsi quel concept technique mérite encore d’arriver jusqu’à l’utilisateur, plutôt que de tout masquer indistinctement.

Le plus long : discuter

Le passage le plus intéressant du billet n’est peut-être même pas celui sur le GPU. Patil explique que l’implémentation finale a pris relativement peu de temps et que la majorité du projet a été consacrée au design : lire les entrailles du renderer, regarder les choix d’autres bibliothèques, dessiner des alternatives dans tldraw, écrire des propositions et discuter sur Discord et GitHub.1

La trace publique le confirme. Le tracking issue liste comme travail terminé l’alias puis la nouvelle valeur instanceIndex, l’objet instances(count), l’intégration aux primitives, les tests WebGL/WebGPU, la documentation et les exemples.2

Même le nom et la forme de instanceIndex ont bougé au fil des reviews.2 Le résultat est volontairement banal à lire, ce qui est précisément ce qu’on demande à une API destinée aux débutants.

Cette banalité visible déplace le travail vers la conception. Derrière instances(n) il faut encore décider le comportement avec zéro instance, les primitives compatibles, la géométrie personnalisée, la présence de l’index selon les backends et le sort de l’ancien instanceID() afin que les sketches existants ne deviennent pas les victimes collatérales d’une API plus jolie.

La simplicité visible est le produit fini d’une complexité négociée en amont.

Pas encore 2.4

Le billet du 10 août annonce que cette API doit arriver dans p5.js 2.4 et indique que le travail est mergé.13 Au 21 août, il faut conserver le futur : la dernière release stable publiée dans le dépôt est v2.3.2, datée du 30 juillet.6

Pour une API, cette différence de calendrier n’est pas cosmétique : copier aujourd’hui l’exemple dans la dernière installation stable de p5.js ne permet pas encore de présenter instances(500).sphere(20) comme une fonction publiée.

Le tracking issue est fermé depuis le 30 juillet et ses tâches sont cochées, mais « mergé » et « disponible dans la dernière version stable » sont deux états différents.26

Le design est suffisamment avancé pour être analysé, mais la preview doit rester une preview jusqu’à la release qui l’embarque réellement.

Le bon raccourci

Cette forêt se raconte mal dans deux directions opposées : prétendre qu’une ligne remplace le travail de mille arbres, comme si le GPU avait cessé de les dessiner, ou balayer le projet parce que l’instancing existe depuis longtemps. Dans les deux cas, on passe à côté du produit.

Une bibliothèque de creative coding ne se contente pas de rendre une capacité possible ; elle décide aussi du moment où cette capacité devient accessible sans formation préalable en graphics programming. p5.strands travaille sur cette frontière, avec assez de familiarité pour produire rapidement quelque chose et assez de concepts visibles pour pouvoir ensuite le transformer.

La future ligne instances(500).sphere(20) masque la répétition des soumissions mais garde le nombre d’instances sous les yeux ; instanceIndex, de son côté, retire un détail d’API maladroit tout en laissant visible le fait que chaque copie possède une place dans la séquence.

C’est un peu le bon test pour une abstraction de creative coding : retirer l’administration sans retirer la décision graphique intéressante.

Pour dessiner une forêt, l’artiste n’a plus besoin d’écrire mille appels ; tout ce qui reste à faire est justement la partie créative, décider pourquoi le huit-centième arbre ne ressemble pas tout à fait au premier.