Résumé EN (abstract): This advanced creative coding course takes designers and developers to production-level skills for exhibition-grade generative works. Students write GLSL shaders (fragment and vertex), build real-time systems in TouchDesigner, integrate complex simulations (cellular automata, evolutionary algorithms, reaction-diffusion), and apply performance optimization. The capstone is an exhibition-ready generative installation — interactive or autonomous — tested and documented. Outline: GLSL shaders (fragment) → WebGL in p5.js → TouchDesigner node architecture → Advanced audio-reactive systems → Cellular automata & reaction-diffusion → Evolutionary & generative systems → Performance optimization → Exhibition work.
Objectifs d'apprentissage
À la fin de ce cours, l'étudiant·e sera capable de :
- Écrire (Bloom : creating) un fragment shader GLSL produisant un effet visuel intentionnel.
- Intégrer (applying) des shaders dans un sketch p5.js via WEBGL et dans Shadertoy.
- Construire (creating) un réseau node-based dans TouchDesigner pour une installation temps réel.
- Implémenter (applying) une simulation de réaction-diffusion ou un automate cellulaire exposable.
- Optimiser (evaluating/applying) un sketch pour maintenir 60 fps sur du matériel d'exposition standard.
- Produire (creating) une œuvre de niveau exposition documentée avec fiche technique + intention artistique.
Module 1 — Fragment shaders GLSL (2.5h)
Concept
Un shader est un programme exécuté sur le GPU pour chaque pixel (fragment shader) ou chaque sommet de mesh (vertex shader). Infiniment plus rapides que le CPU pour les opérations visuelles — ils calculent des millions de pixels en parallèle.
Structure d'un fragment shader minimal : `glsl // Fragment shader GLSL precision mediump float;
uniform vec2 u_resolution; // taille du canvas en pixels uniform float u_time; // temps en secondes
void main() { // gl_FragCoord = position du pixel courant vec2 uv = gl_FragCoord.xy / u_resolution; // uv.x = 0 (gauche) → 1 (droite), uv.y = 0 (bas) → 1 (haut)
// Couleur : gradient horizontal vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time)); gl_FragColor = vec4(color, 1.0); } `
Fonctions GLSL fondamentales :
| Fonction | Description |
|---|---|
sin(x), cos(x) | Oscillation — même sémantique qu'en JS |
abs(x) | Valeur absolue |
fract(x) | Partie fractionnaire — fract(3.7) = 0.7 |
floor(x) | Partie entière |
mod(x, y) | Modulo |
step(edge, x) | 0 si x < edge, 1 sinon |
smoothstep(e0, e1, x) | Interpolation douce entre 0 et 1 |
mix(a, b, t) | Interpolation linéaire entre a et b |
length(v) | Longueur d'un vecteur |
dot(a, b) | Produit scalaire |
normalize(v) | Vecteur unitaire |
Ressource indispensable :
Gonzalez Vivo, P., & Lowe, J. The Book of Shaders. https://thebookofshaders.com
Lab 1.1 — Shader dans Shadertoy
Étapes :
- Ouvrir https://shadertoy.com → "New Shader"
- Reproduire le gradient minimal ci-dessus (adapter les noms de variables Shadertoy :
iResolution,iTime) - Ajouter : un dégradé radial (
length(uv - 0.5)) - Ajouter : une animation
sin(iTime)sur la couleur - Publier le shader avec un titre descriptif + lien vers le sketch
Module 2 — Shaders dans p5.js (WebGL mode) (2.5h)
Concept
p5.js peut utiliser le GPU en mode WEBGL. Les shaders s'écrivent en GLSL et s'appliquent à un plan (createGraphics en WEBGL).
`javascript let shaderProg;
function preload() { shaderProg = loadShader('vert.glsl', 'frag.glsl'); }
function setup() { createCanvas(800, 800, WEBGL); // WEBGL = mode GPU }
function draw() { shader(shaderProg); // Passer les uniforms au shader shaderProg.setUniform('u_resolution', [width, height]); shaderProg.setUniform('u_time', millis() / 1000.0); shaderProg.setUniform('u_mouse', [mouseX, mouseY]); // Dessiner le plan sur lequel le shader s'applique rect(-width/2, -height/2, width, height); resetShader(); } `
Vertex shader minimal (requis en WEBGL) : `glsl // vert.glsl attribute vec3 aPosition; attribute vec2 aTexCoord; varying vec2 vTexCoord;
void main() { vTexCoord = aTexCoord; vec4 positionVec4 = vec4(aPosition, 1.0); positionVec4.xy = positionVec4.xy * 2.0 - 1.0; gl_Position = positionVec4; } `
Combiner shaders et p5.js :
- Générer une texture avec p5.js classique → passer au shader comme
sampler2D - Le shader applique un traitement (distorsion, pixel art, aberration chromatique)
Lab 2.1 — Shader + p5.js interaction
Étapes :
- Créer les fichiers
vert.glsletfrag.glsl - Fragment shader : dessiner des cercles Voronoi ou un motif répété
- Passer la position souris comme uniform → le pattern réagit à la souris
- Bonus : passer une texture p5.js (createGraphics → shader)
Module 3 — TouchDesigner : architecture temps réel (2.5h)
Concept
TouchDesigner est un environnement de programmation visuelle node-based développé par Derivative (Toronto, 2000). Il est la référence pour les installations, mapping vidéo, VJing et performances audiovisuelles.
Documentation : https://docs.derivative.ca · Téléchargement gratuit (usage non-commercial) : https://derivative.ca/download
Quatre familles d'opérateurs (nodes) :
| Famille | Suffixe | Usage |
|---|---|---|
| TOP (Texture Operators) | moviefilein, noise, level, blur, composite | Image/vidéo 2D |
| CHOP (Channel Operators) | audioin, math, lag, lfo, null | Signaux continus (audio, données) |
| SOP (Surface Operators) | box, sphere, noise, transform | Géométrie 3D |
| DAT (Data Operators) | text, table, script, null | Données tabulaires, scripts |
Flux de données dans un réseau TD : [audioin CHOP] → [analyze CHOP] → [math CHOP] → [null CHOP] ↓ [noise TOP] → [level TOP] → [transform TOP] ← amplitude channel ↓ [out TOP] → Sortie finale
Lab 3.1 — Installation audio-réactive dans TouchDesigner
Étapes :
- Ouvrir TouchDesigner → nouveau projet
- Créer un
moviefilein TOPounoise TOPcomme source visuelle - Connecter un
audioin CHOP→analyze CHOP(amplitude) →null CHOP - Connecter l'amplitude à un paramètre visuel (ex.
leveldulevel TOP) - Ajouter un
blur TOPmodulé par les basses fréquences - Exporter vers un écran secondaire ou une fenêtre de projection
Module 4 — Audio-réactif avancé : fréquences et rythme (2.5h)
Concept
Au-delà de l'amplitude globale, l'audio-réactif avancé discrimine les fréquences (basses, médiums, aigus) et détecte les temps rythmiques (beat detection).
FFT en p5.js — bandes de fréquences : `javascript let fft;
function setup() { fft = new p5.FFT(0.8, 1024); mic = new p5.AudioIn(); mic.start(); fft.setInput(mic); }
function draw() { let bass = fft.getEnergy('bass'); // 20–140 Hz let lowMid = fft.getEnergy('lowMid'); // 140–400 Hz let mid = fft.getEnergy('mid'); // 400–2600 Hz let highMid = fft.getEnergy('highMid'); // 2600–5200 Hz let treble = fft.getEnergy('treble'); // 5200–14000 Hz
// Mapping sur des paramètres visuels distincts let tailleFormes = map(bass, 0, 255, 10, 200); let vitesseFond = map(highMid, 0, 255, 0, 5); } `
Beat detection simplifié : `javascript let prevAmplitude = 0;
function draw() { let amp = amplitude.getLevel(); // Peak detection : hausse soudaine > seuil = beat if (amp > prevAmplitude + 0.15 && amp > 0.2) { onBeat(); } prevAmplitude = lerp(prevAmplitude, amp, 0.1); // suivi lisse }
function onBeat() { // flash, spawn de particules, changement de couleur... } `
Lab 4.1 — Visualiseur fréquences multiples
Étapes :
- Diviser le canvas en 5 zones (une par bande de fréquence)
- Chaque zone réagit à sa fréquence (basses → grandes formes, aigus → détails)
- Ajouter une détection de beat → flash ou explosion de particules
- Tester avec différents genres musicaux — est-ce que le visuel "colle" à la musique ?
Module 5 — Systèmes complexes : réaction-diffusion (2.5h)
Concept
La réaction-diffusion est un modèle mathématique décrivant comment deux substances chimiques interagissent et diffusent dans l'espace. Proposé par Alan Turing (1952) pour expliquer les motifs biologiques (léopard, zèbre, coraux), il génère des textures organiques d'une grande richesse.
Turing, A. M. (1952). The Chemical Basis of Morphogenesis. Philosophical Transactions of the Royal Society B, 237(641), 37–72. https://doi.org/10.1098/rstb.1952.0012
Deux substances, A et B :
- A se transforme en B en présence de B² (réaction autocatalytique)
- B se dégrade naturellement
- Les deux diffusent à des vitesses différentes (dA > dB)
Implémentation GPU (shader) — la plus performante : `glsl // Fragment shader reaction-diffusion (Gray-Scott) uniform sampler2D u_prevState; // état de la frame précédente uniform vec2 u_resolution; uniform float dA; // taux de diffusion de A (typ. 1.0) uniform float dB; // taux de diffusion de B (typ. 0.5) uniform float feed; // taux d'alimentation en A uniform float k; // taux de mort de B
void main() { vec2 uv = gl_FragCoord.xy / u_resolution; vec2 texel = 1.0 / u_resolution;
// État courant vec2 state = texture2D(u_prevState, uv).rg; float a = state.r; float b = state.g;
// Laplacien (diffusion par convolution 3×3) vec2 sum = vec2(0.0); sum += texture2D(u_prevState, uv + vec2(-texel.x, 0)).rg; sum += texture2D(u_prevState, uv + vec2( texel.x, 0)).rg; sum += texture2D(u_prevState, uv + vec2(0, -texel.y)).rg; sum += texture2D(u_prevState, uv + vec2(0, texel.y)).rg; sum -= 4.0 * state; // centre du Laplacien
// Gray-Scott equations float reaction = a b b; float newA = a + (dA sum.r - reaction + feed (1.0 - a)); float newB = b + (dB sum.g + reaction - (k + feed) b);
gl_FragColor = vec4(clamp(newA, 0.0, 1.0), clamp(newB, 0.0, 1.0), 0.0, 1.0); } `
Lab 5.1 — Simulation réaction-diffusion
Étapes :
- Implémenter le shader Gray-Scott ci-dessus dans Shadertoy ou p5.js WEBGL
- Commencer avec une tache de B au centre (seed), laisser évoluer 200 frames
- Faire varier
ketfeed— cartographier la "phase diagram" des motifs (spots, spirales, labyrinthes) - Ajouter l'interactivité : clic souris → injecter de B à la position
Module 6 — Algorithmes évolutifs et agents (2.5h)
Concept
Les algorithmes génétiques s'inspirent de la sélection naturelle pour optimiser une solution : une population d'individus (solutions candidates) évolue par sélection, croisement et mutation vers des configurations qui maximisent une fonction de fitness.
Architecture en creative coding :
- Population initiale : N "organismes" avec des gènes (paramètres visuels)
- Évaluation : calculer le "fitness" de chaque organisme (distance à une cible, complexité, esthétique)
- Sélection : les meilleurs survivent
- Reproduction : croisement + mutation des gènes
- Nouvelle génération → retour à 2
Systèmes multi-agents (Boids — Craig Reynolds, 1986) : Trois règles simples génèrent un comportement émergent de groupe (murmuration d'étourneaux) :
- Cohésion : s'approcher du centre de masse du groupe local
- Séparation : éviter les collisions avec les voisins proches
- Alignement : s'aligner sur la direction moyenne du groupe local
Reynolds, C. W. (1987). Flocks, herds and schools: A distributed behavioral model. SIGGRAPH '87. https://doi.org/10.1145/37401.37406
`javascript class Boid { constructor() { this.pos = createVector(random(width), random(height)); this.vel = p5.Vector.random2D().setMag(2); this.acc = createVector(0, 0); }
flock(voisins) { let sep = this.separation(voisins).mult(1.5); let ali = this.alignment(voisins); let coh = this.cohesion(voisins); this.acc.add(sep).add(ali).add(coh); }
update() { this.vel.add(this.acc).limit(3); this.pos.add(this.vel); this.acc.mult(0); this.pos.x = (this.pos.x + width) % width; // wrap edges this.pos.y = (this.pos.y + height) % height; } //... méthodes separation, alignment, cohesion } `
Lab 6.1 — Simulation boids
Étapes :
- Implémenter la classe Boid avec les 3 règles
- Population de 200 boids, visualisation simple (triangle orienté selon vitesse)
- Paramétrer les 3 forces (sliders) — trouver les valeurs qui créent des comportements intéressants
- Enregistrer 3 seeds de paramètres produisant 3 comportements distincts (nuée, explosion, tourbillon)
- Ajouter un prédateur (attraceur/répulseur selon la distance)
Module 7 — Performance et optimisation (2.5h)
Concept
Une œuvre exposée doit tourner à 60 fps pendant des heures sans dégradation. L'optimisation est une discipline créative à part entière.
Règles d'or :
- Mesurer avant d'optimiser :
frameRate()dansdraw(), Chrome DevTools Performance tab - GPU > CPU : déplacer les calculs dans les shaders quand possible
- Éviter les allocations répétées : ne pas créer d'objets dans
draw()— les réutiliser - Batch les opérations similaires : dessiner toutes les formes du même type ensemble
Techniques concrètes p5.js : `javascript // ✗ Mauvais : createVector dans draw() à chaque frame function draw() { let v = createVector(mouseX, mouseY); // allocation à chaque frame }
// ✓ Bon : déclarer une fois, modifier let mouseVec; function setup() { mouseVec = createVector(0, 0); } function draw() { mouseVec.set(mouseX, mouseY); } `
`javascript // ✗ Mauvais : redessiner une texture statique function draw() { for (let i = 0; i < 10000; i++) ellipse(random(width), random(height), 5); }
// ✓ Bon : pré-calculer dans un buffer hors-écran let buffer; function setup() { buffer = createGraphics(width, height); for (let i = 0; i < 10000; i++) buffer.ellipse(...); } function draw() { image(buffer, 0, 0); // 1 seule opération GPU } `
Profiling TouchDesigner : Performance Monitor (F7) → identifier les TOPs les plus coûteux → réduire la résolution des buffers intermédiaires.
Lab 7.1 — Audit et optimisation
Étapes :
- Reprendre votre projet de simulation le plus lourd (boids ou réaction-diffusion)
- Mesurer le framerate de base
- Identifier les 3 opérations les plus coûtées (Profiler Chrome ou
performance.now()) - Appliquer 2 optimisations concrètes
- Mesurer le gain : documenter avant/après
Module 8 — Œuvre exposable : production et scénographie (2.5h)
Concept
Passer du sketch au dispositif exposé requiert des compétences qui vont au-delà du code.
Checklist production exposition :
Technique :
- [ ] L'œuvre tourne sans crash pendant 8h (test de stabilité)
- [ ] Reset automatique après N minutes d'inactivité
- [ ] Pas de curseur visible (
noCursor()) - [ ] Résolution adaptée à l'écran/projecteur cible
- [ ] Démarrage automatique au boot (si ordinateur dédié)
Scénographie :
- [ ] Format d'écran et orientation définis
- [ ] Distance de vision optimale (texte lisible, formes perceptibles)
- [ ] Gestion de l'ambient light (l'œuvre est-elle lisible dans une salle éclairée ?)
- [ ] Son : actif, silence, ou réactif au son ambiant ?
Documentation :
- [ ] Fiche technique : hardware, logiciel, résolution, fps
- [ ] Note d'intention (1 page) : concept, processus, positionnement
- [ ] README opérateur : démarrage, reset, contacts
`javascript // Exemple : setup exposition function setup() { createCanvas(displayWidth, displayHeight, WEBGL); // fullscreen frameRate(60); noCursor(); randomSeed(SEED); noiseSeed(SEED); }
let idleTimer = 0; function draw() { idleTimer++; if (idleTimer > 60 60 10) { // reset après 10 min d'inactivité idleTimer = 0; reset(); } }
function mouseMoved() { idleTimer = 0; } `
Lab 8.1 — Préparation exposition
Étapes :
- Choisir votre meilleur sketch du cours
- Appliquer la checklist production
- Rédiger la fiche technique (1 page)
- Rédiger la note d'intention (1 page) — pas de description technique, seulement l'intention
- Simuler 15 minutes d'exposition : l'œuvre tourne-t-elle sans intervention ?
Projet fil rouge — "Œuvre de niveau exposition"
Brief
Créer une œuvre générative de niveau exposition — un travail qui pourrait être présenté dans un festival d'art numérique, une galerie ou un espace culturel. L'œuvre doit être à la fois techniquement solide et artistiquement cohérente.
Contraintes
- Technologie : p5.js WEBGL + shader(s) ET/OU TouchDesigner
- Au moins 1 shader GLSL intégré
- L'œuvre est stable 60 fps pendant 1h (testé)
- Interaction optionnelle mais documentée si présente
- Note d'intention (≠ description technique)
Livrables
- Code source (p5.js + shaders et/ou fichier.toe TouchDesigner)
- Export vidéo de 2–5 min de l'œuvre en fonctionnement
- Fiche technique : hardware, logiciel, résolution, fps, espace requis
- Note d'intention (1 page) : concept, références artistiques, positionnement
- README opérateur (≤ 1 page)
- Présentation (10 min) : contexte artistique + démo live + réflexion sur le processus
Rubrique d'évaluation
| Critère | Insuffisant (1) | Satisfaisant (2) | Bon (3) | Excellent (4) |
|---|---|---|---|---|
| Maîtrise shader | Pas de shader ou shader copié sans compréhension | Shader fonctionnel, 1 effet | Shader intentionnel, paramétré | Shader sophistiqué, combiné avec d'autres systèmes |
| Complexité du système | Sketch de base | 1 système avancé (flocking, RD, TD) | 2 systèmes combinés | Systèmes qui s'enrichissent mutuellement |
| Qualité temps réel | < 30 fps ou crashes | 60 fps stable 15 min | 60 fps stable 1h, pas de leak mémoire | Optimisé, reset auto, production-ready |
| Intention artistique | Absent ou décoratif | Intention présente | Cohérence conception/intention | Dialogue fort technique/sens, références citées |
| Documentation | Absente | Fiche technique seule | Fiche + note intention + README | Dossier complet, exportable tel quel vers un commissaire |
Lectures & ressources
- The Book of Shaders : https://thebookofshaders.com
- Shadertoy : https://shadertoy.com
- TouchDesigner documentation : https://docs.derivative.ca
- The Nature of Code (Shiffman) — agents, évolution : https://natureofcode.com
- The Coding Train — flocking, reaction-diffusion : https://youtube.com/c/TheCodingTrain
- Perlin, K. (1985). An Image Synthesizer. SIGGRAPH '85. https://doi.org/10.1145/325334.325247
- Turing, A. M. (1952). The Chemical Basis of Morphogenesis. Phil. Trans. Royal Society B. https://doi.org/10.1098/rstb.1952.0012
- Reynolds, C. W. (1987). Flocks, herds and schools. SIGGRAPH '87. https://doi.org/10.1145/37401.37406
→ Continuer : Hub — Creative Coding → Projet d'exposition ? Prendre rendez-vous — 30 min, gratuit
Workshop intensif (1–2 j) pour écoles, studios, musées — théorie condensée, labs guidés, accompagnement projet.
Prendre rendez-vous (30 min, gratuit) →