Depuis quelques mois, ChatGPT est devenu un assistant plutôt indispensable pour mes "side-projects". Il relit et améliore mon code, guide mes réflexions, et parfois met même la main à la pâte pour coder avec moi. Je lui trouvais en revanche assez peu d'utilité dans mon travail quotidien de designer. Jusqu'à hier...

Une illustration en moins d'une heure

La première graine

Il est toujours délicat d'illustrer l'Écologie ou le Climat sans tomber dans les clichés faciles. Nos Gestes Climat mise largement sur l'utilisation des emojis, pour illustrer chacune des nombreuses facettes de notre empreinte carbone.

Pour une refonte de page d'accueil, j'avais cette idée vague d'utiliser une composition de ces nombreux émojis, qui pourraient accompagner la promesse du site. Expérimenter avec autant de pictogrammes devient vite très laborieux : sélectionner les bonnes icônes, ajuster leur disposition, taille, orientation, opacité... Chaque nouveau test nécessite de retravailler chaque image individuellement.

Je me décourageais d'avance, lorsque la solution m'a sauté aux yeux. Une composition d'icônes n'est qu'un assemblage d'images SVG ; il ne doit pas être si complexe de coder la composition de l'image. Et ChatGPT est généralement particulièrement performant sur ce type de tâches simples et circonscrites.

Un premier pas timide

Pour l'expérience, un simple script Node fera l'affaire. Quelques pictos en SVG dans un dossier et me voilà prêt pour un premier test.

Hey! Can you create a Node script that combine random svg file (from a /svg/ folder) into a new svg file? On the new svg, can you dispose all svg pieces into a circle that's 200px from the center?

En quelques secondes, ChatGPT me fait une proposition :

const fs = require("fs");const path = require("path");// Path to the folder containing the SVG filesconst svgFolderPath = path.join(__dirname, "svg");// Path to the new SVG fileconst newSvgFilePath = path.join(__dirname, "combined.svg");// Reading the SVG files from the folderfs.readdir(svgFolderPath, (err, files) => {  if (err) {    console.error("Unable to read the SVG folder:", err);    return;  }  // Filter SVG files  const svgFiles = files.filter((file) => path.extname(file) === ".svg");  // Randomly select SVG files  const selectedSvgFiles = svgFiles.sort(() => 0.5 - Math.random()).slice(0, 2);  // Combine selected SVG files into a single SVG file  let combinedSvgContent = '<svg xmlns="http://www.w3.org/2000/svg">\n';  selectedSvgFiles.forEach((file) => {    const svgContent = fs.readFileSync(path.join(svgFolderPath, file), "utf-8");    // Extract inner SVG content (between <svg> and </svg>)    const innerSvgContent = svgContent.match(/<svg[^>]*>([\s\S]*?)<\/svg>/i)[1];    combinedSvgContent += innerSvgContent + "\n";  });  combinedSvgContent += "</svg>";  // Write the combined SVG content to a new file  fs.writeFileSync(newSvgFilePath, combinedSvgContent, "utf-8");  console.log("SVG files combined successfully!");});

Un copier-coller dans mon éditeur, je lance ce premier script et...

Première tentative de ChatGPT

...ce n'est pas fou 😄 Cela-dit, c'est techniquement juste: 5 images disposées le long d'un cercle. Je poursuis la discussion en lui demandant des ajustements. Et là je suis tombé de ma chaise, en 15 minutes, j'ai déjà un premier résultat concluant.

Can you do the same but with 25 pieces around the circle ? If there aren't 25 svg files, it's ok to use the same one twice

Deuxième tentative de ChatGPT

Une collaboration fructueuse

Perfect! Now I'd like to add a second circle, this time 300px from the circle, width 30 pieces, doing the same thing.

Ici, ChatGPT devine qu'on va ajuster les résultats, et factorise de lui même son code, ce qui me laisse tout le loisir d'expérimenter de mon côté.

// [...]placeSvgsInCircle(25, 200);placeSvgsInCircle(30, 300);//[...]

Can you decrease the size of each piece by 20% (a make this percentage easily tweakable?)

Lorsque je suis content de mes résultats je lui propose de mettre à jour son code avec mes propres tests.

Please from now on, use this code instead

placeSvgsInCircle(20, 200); // 1st circle with 20 pieces and radius 100px placeSvgsInCircle(25, 250); // 2nd circle with 30 pieces and radius 200px placeSvgsInCircle(30, 300); // 3rd circle with 40 pieces and radius 300px placeSvgsInCircle(35, 350); // 4th circle with 50 pieces and radius 400px placeSvgsInCircle(40, 400); // 5th circle with 60 pieces and radius 500px

Now could you rotate each piece to point to the center?

Chaque idée prend vie instantanément, c'est assez magique. Et ça me pousse à tenter plein d'idées avec des résultats pas toujours probants.

Now can you rotate each svg piece randomly from -10% to 10%

Le script final :

const fs = require("fs");const path = require("path");const svgFolderPath = path.join(__dirname, "svg-cute");const newSvgFilePath = path.join(__dirname, "cute.svg");if (fs.existsSync(newSvgFilePath)) {  fs.unlinkSync(newSvgFilePath);  console.log("Existing svg deleted.");}fs.readdir(svgFolderPath, (err, files) => {  if (err) {    console.error("Unable to read the SVG folder:", err);    return;  }  const svgFiles = files.filter((file) => path.extname(file) === ".svg");  if (svgFiles.length < 1) {    console.error("No SVG files in the folder.");    return;  }  let combinedSvgContent =    '<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200">\n';  const centerX = 600;  const centerY = 600;  // Define the scaling factor (e.g., 0.8 for 80%, meaning a 20% reduction in size)  const scalingFactor = 0.6;  const placeSvgsInCircle = (numPieces, radius, opacity = 1) => {    for (let i = 0; i < numPieces; i++) {      const randomIndex = Math.floor(Math.random() * svgFiles.length);      const svgContent = fs.readFileSync(        path.join(svgFolderPath, svgFiles[randomIndex]),        "utf-8"      );      const innerSvgContent = svgContent.match(        /<svg[^>]*>([\s\S]*?)<\/svg>/i      )[1];      const angle = (i / numPieces) * 2 * Math.PI;      const x = centerX + radius * Math.cos(angle);      const y = centerY + radius * Math.sin(angle);      let rotation = angle * (180 / Math.PI); // Convert angle to degrees      // Offset for the piece's geometric center      const offsetX = 32;      const offsetY = 32;      // If the piece is on the left side, add 180 degrees to the rotation      if (x < centerX) {        rotation += 180;      }      // Add a random rotation between -10% and 10% of the initial rotation      const randomRotation = rotation * (Math.random() * 0.2 - 0.1);      rotation += randomRotation;      combinedSvgContent += `<g transform="translate(${x},${y}) scale(${scalingFactor}) translate(${offsetX},${offsetY}) rotate(${rotation}) translate(${-offsetX},${-offsetY})"  style="opacity: ${opacity};">\n`;      combinedSvgContent += innerSvgContent + "\n";      combinedSvgContent += "</g>\n";    }  };  placeSvgsInCircle(30, 300); // 3rd circle with 40 pieces and radius 300px  placeSvgsInCircle(35, 350); // 4th circle with 50 pieces and radius 400px  placeSvgsInCircle(40, 400); // 5th circle with 60 pieces and radius 500px  placeSvgsInCircle(45, 450); // 1st circle with 20 pieces and radius 100px  placeSvgsInCircle(50, 500); // 2nd circle with 30 pieces and radius 200px  combinedSvgContent += "</svg>";  fs.writeFileSync(newSvgFilePath, combinedSvgContent, "utf-8");  console.log("SVG files combined successfully!");});

Aussi simple que ça... L'expérience est vraiment plaisante. Je peux tester chaque idée, apporter des corrections, revenir en arrière, essayer autre chose. On mêle créativité et technique en ping-pong jusqu'à arriver à un résultat satisfaisant.

Conclusion

Pour tester une nouvelle librairie d'icônes ? Il me suffit de les déposer dans un dossier. Comme chaque génération possède un part d'aléatoire, il m'arrive de regénérer la même illustration plusieurs fois et de récupérer les éléments qui me semblent les plus cohérents. Je conserve chaque tentative pour les comparer.

Les résultats en image

La cerise sur le gâteau, c'est que l'illustration finale est en SVG, parfaitement éditable dans Figma si je souhaite remplacer un émoji ou corriger une orientation.

ChatGPT deviendra-t-il un compagnon récurrent sur mes projets de design ? Probablement pas, le cas d'usage reste très spécifique. Cependant, c'est clairement un outil malin auquel je penserai plus spontanément à l'avenir.