My concept is that I’m buggy, so I used bugs.
This code was particularly awesome:
// I'm a big bug
textSize(150);
text("🐞", 10, 250);
Reflection
I like the bugs, but next buggier
My concept is that I’m buggy, so I used bugs.
This code was particularly awesome:
// I'm a big bug
textSize(150);
text("🐞", 10, 250);
Reflection
I like the bugs, but next buggier
I dove into the assignment blind, no initial sketches or anything as such, instead of conveying an idea through the portrait beyond simply portraying myself, I thought it would be fun to try to implement other features of p5js into it, which lead to us having a color changing shirt, moving clouds and automatic blinking animation!
Honestly I did not sure on how to make my curly hair, at first I thought I could use the curve() function of p5.js to maybe simulate “curl” layers over a circle layer on the head. However it did not turn out realistic whatsoever, so my next step was to first add a rectangular layer behind the head, (besides the forehead part where I added a small rectangle on top of the forehead so that it looks like the hair is covering). I then added a bunch of circles centered inside the hair layer so that it looks like curls on top of my head, there was an emoji I used as reference actually and it was this: 👨🦱, not exactly the same however I took the top part of the hair as reference.
// hair fill(0); noStroke(); rect(210, 50, 180, 80); circle(220, 60, 30); circle(220, 80, 30); circle(220, 100, 30); circle(220, 120, 30); circle(240, 50, 30); circle(260, 50, 30); circle(280, 50, 30); circle(300, 50, 30); circle(320, 50, 30); circle(340, 50, 30); circle(360, 50, 30); circle(380, 60, 30); circle(380, 80, 30); circle(380, 100, 30); circle(380, 120, 30); // face noStroke(); fill(223, 170, 139); ellipse(300, 150, 175, 200); // hair over face fill(0); rect(236, 50, 125, 20);
Next is the automatic blinking, there is 3 variables that are used for this process, lastblink, blinkInterval and the boolean blinking. The way the logic works is that I use the in built function millis(), and what this does is keep track of how much time has passed since the sketch started running, using that we can subtract our last blink and check if it is greater than our blink interval, so here I use that so the blink interval is 3 seconds, so when the sketch start running lastblink is going to be 0, so when millis reach 3001 milliseconds we get 3001-0 which is greater than 3000, meaning it is time for the character to blink, this sets blinking to true which “disables” the eye and pupil code giving the illusion of blinking. However we want the character eyes to open up after a bit so we use setTimeout() which waits a certain time we set before executing a command, so here we wait 300 ms / 0.3 seconds before setting blinking to false and opening up the character’s eyes.
let lastBlink = 0;
let blinkInterval = 3000;
let blinking = false;
// Check if time to blink
if (millis() - lastBlink > blinkInterval) {
blinking = true;
setTimeout(() => blinking = false, 300);
lastBlink = millis();
}
// eyes and pupils
stroke(223, 170, 139);
strokeWeight(2);
if (!blinking) {
fill(255);
ellipse(260, 125, 50, 40);
ellipse(335, 125, 50, 40);
noStroke();
fill(0);
circle(260, 125, 15);
circle(335, 125, 15);
}
The color changing shirt logic is pretty simple, for smooth transition I use sin on our shirtcolor variable, sin goes between -1 and 1, however rgb takes from 0 to 255, so we multiply by 127 to get -127 and 127 and then add 128 to this to get a range from 0 to 255, for r we just use sin of colorshirt, for g and b we delay the sin by two_pi / 3 and 2*two_pi / 3, and finally we add 0.03 every time draw runs to shirtcolor.
let shirtcolor = 0; // shirt color changing let r = 128 + 127 * sin(shirtcolor); let g = 128 + 127 * sin(shirtcolor + TWO_PI / 3); let b = 128 + 127 * sin(shirtcolor + 2 * TWO_PI / 3); shirtcolor += 0.03; // shirt noStroke(); fill(r, g, b); rect(200, 290, 200, 250);
The final thing that is worth mentioning is the cloud movement in the background, I use a function that takes in the x, y and s (where s is set to 1 unless specified otherwise), and draw our cloud using an ellipse with the parameters we put. I initialize 14 different “clouds” with different x,y and s values in an array, now to draw and move the clouds, I use a loop to move through the cloud array and call the drawCloud function that draws each cloud, then to move it along the background, I add 1 to every cloud’s x value in the loop and to make the clouds move up and down add the value of sin(shirtcolor) to the y value of each cloud, since shirtcolor constantly changes it’s a good variable to use in this case. Finally to make sure the clouds wrap back, we check if the x value of each cloud has went past the width of canvas by 25 pixels or not, if it has we set the x value to -50! This gives the illusion of clouds wrapping around without them “disappearing” from one side and “appearing” at the other.
let clouds = [];
clouds = [
{x: 80, y: 80, s: 1},
{x: 200, y: 60, s: 1.3},
{x: 350, y: 90, s: 0.9},
{x: 500, y: 70, s: 1.2},
{x: 120, y: 140, s: 0.8},
{x: 300, y: 160, s: 1.1},
{x: 450, y: 140, s: 0.7},
{x: 80, y: 200, s: 1},
{x: 200, y: 260, s: 1.3},
{x: 350, y: 290, s: 0.9},
{x: 500, y: 270, s: 1.2},
{x: 120, y: 340, s: 0.8},
{x: 300, y: 460, s: 1.1},
{x: 450, y: 340, s: 0.7},
];
// draw and move clouds
for (let c of clouds) {
drawCloud(c.x, c.y, c.s);
c.x += 1;
c.y += sin(shirtcolor)
if (c.x > width+25) c.x = -50;
}
function drawCloud(x, y, s = 1) {
stroke(255);
strokeWeight(1);
fill(255);
ellipse(x, y, 50 * s, 24 * s);
}
Overall code:
let shirtcolor = 0;
let clouds = [];
let lastBlink = 0;
let blinkInterval = 3000;
let blinking = false;
function setup() {
createCanvas(600, 550);
// initialize clouds
clouds = [
{x: 80, y: 80, s: 1},
{x: 200, y: 60, s: 1.3},
{x: 350, y: 90, s: 0.9},
{x: 500, y: 70, s: 1.2},
{x: 120, y: 140, s: 0.8},
{x: 300, y: 160, s: 1.1},
{x: 450, y: 140, s: 0.7},
{x: 80, y: 200, s: 1},
{x: 200, y: 260, s: 1.3},
{x: 350, y: 290, s: 0.9},
{x: 500, y: 270, s: 1.2},
{x: 120, y: 340, s: 0.8},
{x: 300, y: 460, s: 1.1},
{x: 450, y: 340, s: 0.7},
];
}
function draw() {
// Check if time to blink
if (millis() - lastBlink > blinkInterval) {
blinking = true;
setTimeout(() => blinking = false, 300);
lastBlink = millis();
}
// shirt color changing
let r = 128 + 127 * sin(shirtcolor);
let g = 128 + 127 * sin(shirtcolor + TWO_PI / 3);
let b = 128 + 127 * sin(shirtcolor + 2 * TWO_PI / 3);
shirtcolor += 0.03;
// background
background(178, 237, 232);
fill(0);
strokeWeight(1);
text(`${mouseX}, ${mouseY}`, 20, 20);
// draw and move clouds
for (let c of clouds) {
drawCloud(c.x, c.y, c.s);
c.x += 1;
c.y += sin(shirtcolor)
if (c.x > width+25) c.x = -50;
}
// hair
fill(0);
noStroke();
rect(210, 50, 180, 80);
circle(220, 60, 30);
circle(220, 80, 30);
circle(220, 100, 30);
circle(220, 120, 30);
circle(240, 50, 30);
circle(260, 50, 30);
circle(280, 50, 30);
circle(300, 50, 30);
circle(320, 50, 30);
circle(340, 50, 30);
circle(360, 50, 30);
circle(380, 60, 30);
circle(380, 80, 30);
circle(380, 100, 30);
circle(380, 120, 30);
// face
noStroke();
fill(223, 170, 139);
ellipse(300, 150, 175, 200);
// hair over face
fill(0);
rect(236, 50, 125, 20);
// jaw and neck
noStroke();
fill(223, 170, 139);
quad(224, 200, 377, 200, 340, 250, 260, 250);
rect(260, 250, 80, 40);
// shirt
noStroke();
fill(r, g, b);
rect(200, 290, 200, 250);
// eyebrows
fill(0);
stroke(0);
noFill();
strokeWeight(10);
arc(265, 100, 25, 7, PI, -0.2);
arc(332, 100, 25, 7, PI, -0.2);
// eyes and pupils
stroke(223, 170, 139);
strokeWeight(2);
if (!blinking) {
fill(255);
ellipse(260, 125, 50, 40);
ellipse(335, 125, 50, 40);
noStroke();
fill(0);
circle(260, 125, 15);
circle(335, 125, 15);
}
// nose
stroke(194, 132, 103);
noFill();
line(297.5, 150, 295, 170);
line(302.5, 150, 305, 170);
arc(305, 170, 10, 10, -0.5, PI / 2);
arc(295, 170, 10, 10, PI / 2, PI + 0.5);
// mouth
fill(180, 13, 61);
noStroke();
arc(300, 200, 50, 50, 0, PI);
// glasses
stroke(0);
noFill();
ellipse(260, 125, 40, 30);
ellipse(335, 125, 40, 30);
line(275, 115, 320, 115);
line(355, 125, 385, 130);
line(240, 125, 210, 130);
// ears
stroke(194, 132, 103);
fill(194, 132, 103);
arc(385, 145, 30, 30, -PI / 2, PI / 2);
arc(214, 145, 30, 30, PI / 2, -PI / 2);
}
function drawCloud(x, y, s = 1) {
stroke(255);
strokeWeight(1);
fill(255);
ellipse(x, y, 50 * s, 24 * s);
}
I am not a very artistic person so honestly I did not know how this would go, I did not know how I’d draw myself using just 2D shapes, and to be honest I did struggle, especially with the hair, however I am pretty happy with how it turned out, in the future I am hoping to be able to make more realistic animations, for example for the blinking animation maybe the animation of the eye slowly closing up and opening up would be a cool thing to experiment with and add. Maybe also a scene where my character is doing some sort of activity! I love wall climbing so something where my character is climbing would be cool to do in the future.