Une illustration avec ChatGPT
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...
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...
...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
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.
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.