For this assignment, we were asked to create a self-portrait using p5.js. I started by looking into the listed self-portraits and previous students work. I was heavily inspired by a few of them and later incorporated a few styles from their portraits. [1. https://editor.p5js.org/Sarthak-Malla/full/3NRqaQVKQ, 2. Koala portrait, 3. Assignment 1: Self-portrait]
I wanted to have a full-body portrait. However, I could not settle on one. Later, I came across this portrait online and decided to follow a similar structure. I initiated the process by sketching a rudimentary outline, focusing initially on the facial features. I used the bezier for the eye brows and the curveVertex for the hair. For the bezier shape, I used this tutorial. In the rest of the portrait, I just used the primitive shapes.
Each cell is a dynamic entity, its behavior governed by its position and the cursor’s proximity. The canvas isn’t just a static backdrop but a living part of the portrait, changing its hues and vibrancy in real-time. Each cell on the canvas pulsates with an oscillating brightness, creating a mesmerizing wave effect. This is not just a pre-programmed animation; the cells also react to the position of your mouse, creating an interactive dance of light and shadow.
function drawBackground() { for (let r = 0; r < rows; r++) { for (let c = 0; c < columns; c++) { let distance = dist(mouseX, mouseY, c * cellWidth, r * cellHeight); let offset = map(distance, 0, sqrt(sq(width) + sq(height)), 1, 0); let wave = (sin(time - c * r * 0.1) + 1) / 2; let brightness = map(wave, 0, 1, 100, 255) * offset; cells[r][c] = brightness; fill(brightness * 0.9, brightness * 0.7, brightness); noStroke(); rect(c * cellWidth, r * cellHeight, cellWidth, cellHeight); } } }
The eyes follow your cursor, adding a layer of depth and engagement. The mouth opens and closes, reacting to the cursor’s vertical position, making the portrait not just seen but also felt.
function drawMouth() { noStroke(); // calculate the openness of the mouth based on mouseY position let deltay = ((400 - mouseY) / 400) * 13 + 6; if(deltay <= 9.5) deltay=9.5; // outer mouth (lips) fill(255, 150, 122); // color for lips ellipse(200, 173, 27, deltay); // inner mouth (white part to represent teeth or inside of mouth) let innerMouthHeight = deltay - 2; fill(255); // white color for the inner mouth if (innerMouthHeight > 8) { // show inner mouth only if it's significantly open ellipse(200, 173, 20, innerMouthHeight); // black line to split the white part (teeth or mouth separation) if (innerMouthHeight > 10) { fill(0); // black color for the separation let separationHeight = 4; // height of the separation line rect(200 - 10, 173 - separationHeight / 2, 20, separationHeight, 10); // centered black line } else { fill(0) let separationHeight = 1; // height of the separation line rect(200 - 10, 173 - separationHeight / 2, 20, separationHeight, 10); } } }
While working, I couldn’t figure out the nose style. Most portraits that I have seen use triangles or similar shapes. I found this portrait to have a very distinctive style, which I followed. Creating the hair shape took a lot of time for me. I am still not particularly happy with the hair. I wanted more realism for the hair shape.
In the future, I want to create a Lego character resembling my character and try to create a self-portrait based on the Lego character, which I believe would be easily achievable using the primitive shapes.