Pixels_Petals
by Elisa Bernard, posted on December 1, 2025





Who I am
Specialized in print, I’m a graphic designer who loves mixing my passion for print and digital media — crossing boundaries between physical and digital artifacts.
I’m genuinely interested in branding, editorial and visual work, with an eye for typography and strong images. I’ve worked on a variety of projects: magazines, music labels, music festivals, independent publishing houses and speaker companies.— and I’ve always loved collaborating with people worldwide.
After working as a freelancer while travelling, I am now a student at HEAD – Geneva, studying Media Design.
Based between Paris and Geneva, I’m always looking to meet new people.
Feel free to contact me — I love to talk 🙂
instagram : @elisabernaard















When Pixels Bloom
I_Dream_In_Pixels_Petals explores how digital technologies can reconnect us with the living world. Inspired by The Algorithmic Beauty of Plants by biologist Aristid Lindenmayer, the project draws from his mathematical string-rewriting models that describe how plants grow in real life.
Extending these generative principles into the digital realm, I imagine a world where our dreams could be recorded and replayed — and asked myself: can pixels bloom? Pixels are a universal digital language, one whose codes we intuitively recognize. By blending this digital vocabulary with natural forms, the project examines how machines interpret life, and how nature might, in return, help us understand the logic of machines.

Process
I’ve been fascinated by blob detection, a technique often used in TouchDesigner. While TouchDesigner relies on a node-based interface to hide the code behind visuals, I wanted to explore how similar effects could be achieved in Processing. Using the BlobDetection library, I experimented with different approaches—thresholding, edge detection, and Laplacian of Gaussian—each revealing new possibilities for how forms emerge from pixels.
I also explored tiling to pixelate images and applied dithering techniques to reduce file size, discovering beauty in the limitations these methods impose. I Dream of Pixel Petals is the result of these experiments: a series of explorations where pixels unfold like petals, revealing delicate algorithmic patterns.
Also using Mistral AI to assist with debugging.




A favorite piece of code
In my recent work I have been exploring the command line (the Terminal) to edit media. The gifsicle libary was espeicially interesting for me. My favorite script is one for dithering gifs, which also reduces the filesize.
gifsicle -O3 --colors 6 --dither turmoil.gif -o output.gif
I also used BlobDetection, which can be found in the Processing library. Here’s an example that you can copy and paste to achieve the same results. Just replace the frames used to display the video with your own.
import blobDetection.*;
BlobDetection theBlobDetection;
PImage[] frames;
int currentFrame = 0;
int totalFrames;
int pixelSize = 12; // Force de pixelisation
boolean exportFrames = true; // Variable pour contrôler l'export
void setup() {
size(640, 480, P2D);
// Création du dossier exportframes dans le dossier data
String exportFolderPath = dataPath("exportframes");
File exportFolder = new File(exportFolderPath);
if (!exportFolder.exists()) {
exportFolder.mkdir();
println("Dossier 'exportframes' créé dans le dossier 'data'.");
} else {
// Supprimer les fichiers existants dans le dossier
File[] existingFiles = exportFolder.listFiles();
for (File file : existingFiles) {
file.delete();
}
println("Les fichiers existants dans 'exportframes' ont été supprimés.");
}
// Chargement des frames depuis le dossier 'frames'
String frameFolderPath = dataPath("frames");
File frameFolder = new File(frameFolderPath);
if (!frameFolder.exists()) {
println("Le dossier 'frames' n'existe pas dans le dossier 'data'.");
exit();
}
File[] frameFiles = frameFolder.listFiles((dir, name) -> name.toLowerCase().endsWith(".png"));
if (frameFiles == null || frameFiles.length == 0) {
println("Aucune image PNG trouvée dans le dossier 'frames'.");
exit();
}
totalFrames = frameFiles.length;
frames = new PImage[totalFrames];
for (int i = 0; i < totalFrames; i++) {
String framePath = "frames/frame_" + nf(i + 1, 4) + ".png";
frames[i] = loadImage(framePath);
if (frames[i] == null) {
println("Erreur lors du chargement de l'image: " + framePath);
exit();
}
frames[i].resize(width, height);
}
theBlobDetection = new BlobDetection(width, height);
theBlobDetection.setPosDiscrimination(true);
theBlobDetection.setThreshold(0.05f);
}
void draw() {
if (currentFrame < totalFrames) {
// Pixelisation avant l'affichage et la détection de blobs
PImage pix = pixelate(frames[currentFrame], pixelSize);
image(pix, 0, 0);
// Export des frames si nécessaire
if (exportFrames) {
String exportPath = "exportframes/frame_" + nf(currentFrame + 1, 4) + ".png";
saveFrame(exportPath);
}
pix.loadPixels();
theBlobDetection.computeBlobs(pix.pixels);
drawBlobsAndEdges(true, true);
if (frameCount % 2 == 0) {
currentFrame++;
}
} else {
// Désactiver l'export après une boucle complète
exportFrames = false;
currentFrame = 0;
println("Export des frames terminé. Le rendu continue.");
}
}
// -------------------------------
// FONCTION PIXELISATION
// -------------------------------
PImage pixelate(PImage img, int blockSize) {
PImage result = createImage(img.width, img.height, RGB);
img.loadPixels();
result.loadPixels();
for (int y = 0; y < img.height; y += blockSize) {
for (int x = 0; x < img.width; x += blockSize) {
int loc = x + y * img.width;
int c = img.pixels[loc];
for (int yy = y; yy < y + blockSize && yy < img.height; yy++) {
for (int xx = x; xx < x + blockSize && xx < img.width; xx++) {
result.pixels[xx + yy * img.width] = c;
}
}
}
}
result.updatePixels();
return result;
}
void drawBlobsAndEdges(boolean drawBlobs, boolean drawEdges) {
noFill();
Blob b;
EdgeVertex eA, eB;
for (int n = 0; n < theBlobDetection.getBlobNb(); n++) {
b = theBlobDetection.getBlob(n);
if (b != null) {
if (drawEdges) {
strokeWeight(3);
stroke(150, 0, 200);
for (int m = 0; m < b.getEdgeNb(); m++) {
eA = b.getEdgeVertexA(m);
eB = b.getEdgeVertexB(m);
if (eA != null && eB != null) {
line(eA.x * width, eA.y * height, eB.x * width, eB.y * height);
}
}
}
if (drawBlobs) {
strokeWeight(1);
noStroke();
rect(b.xMin * width, b.yMin * height, b.w * width, b.h * height);
}
}
}
}