Assignment 1: Self Portrait

Concept:

For the first assignment self-portrait. I decided to create a code for an image of a frog, and the idea was to create it using the 2D primitives, and my goal was to take what we learned in class about how to code using the shapes and implement it into my own piece, changing the colors, the size, and layering of the shapes and experimenting with the code to see what works for the image I had in my mind. The goal was to practice layering and the proportions, and I added small details like the eyelash and the fly to give the frog more detail.

How it’s made:

The image was created entirely using p5.js, I first created the frame size then created the background color and then chose a subject to create which is a frog.

I used basic shapes like ellipse, circle, and lines, and arc to build the frog. I built it from the back to the front, layered it from legs to body to head, then eyes, and lastly the details. I used the color fills and stroke changes to separate different parts of the frog. In addition to the frog i added a small fly to add more detail to the portrait as I felt it was on theme.

A part of the code I’m proud of:

A particular part of the code that I’m proud of is the mouth and the eyelashes. Because they required more precision and experimentation than the rest of the shapes, I would say these parts took me the longest to perfect and be satisfied with the results. The mouth uses an arc function, which requires specific angles to create a smooth curved line, and the eyelashes had to be carefully placed using the line function to angle them naturally around the eye to give the appearance of eyelashes.

The arc required understanding about how the arc worked because it isn’t as straightforward as the ellipse or the circle. I had to experiment and reference to examples from the p5.js to understand how it works more clearly.

https://p5js.org/examples/shapes-and-color-shape-primitives/

https://p5js.org/reference/p5/arc/

Because the mouth needed to sit naturally between the eyes and the end of the face, I adjusted the X position and the Y position multiple times to get the expression right. And I experimented with the width and the height to get the most natural looking smile. I also increased the stroke weight for this part of the feature to give the mouth a bold cartoon like outline to make the mouth stand out without overpowering the eyes.

//mouth
noFill();
stroke(0);
strokeWeight(10);
arc(305,400,150,100,0,PI);

//eyelashes
stroke(0);
strokeWeight(6);

//left eye lashes
line(170,180,150,160);
line(200,170,200,150);
line(230,180,250,160);

//right eye lashes
line(370,180,350,160);
line(400,170,400,150);
line(430,180,450,160);

Reflection:

I’m satisfied with how expressive the frog looks, especially the eyes and the smile and the addition of the fly. It adds more detail, and something I think would be interesting to implement in the future once we learn how complete more complex code, is a blinking animations for the eyes or how to make the code responsive to interaction for example if I click on the fly it starts flying around. Overall, this project helped me understand how coordinate changes can affect the design and how layering can differ the shape placement and how line weight can be played around with and used to advantage and how to code shapes more confidently overall. I look forward to learning more complex codes for the future to help enhance my coding skills.

Week 1: Self Portrait by Mhara Al Nuaimi

My Concept:

For this assignment, we had to create a self-portrait using only code in p5.js. Instead of making a normal human face, I chose to turn myself into a panda character. I picked a panda because its one of my favorite animals honestly, and i felt like it represented me the most for my portrait. Since this is my first time working with p5, I wanted something simple that still lets me practice shapes and positions.

The whole portrait is drawn using code, not the mouse. I built the panda using basic shapes like circles, rectangles, and arcs. Each part of the panda, like the ears, eyes, nose, and body, is placed using x and y values. My goal was to learn how shapes can work together to form a character instead of just random objects on the screen.

My Portrait:

A part of the code I like is how I created the panda’s face using only circles and one arc. Even though it looks simple, getting the face to look right took a lot of adjusting.

Here is my code: 

function setup() {
  createCanvas(600, 600);
}

function draw() {
  background("rgb(255,151,235)");

  // head
  fill(255);
  circle(300, 260, 220);

  // ears
  fill(0);
  circle(200, 150, 90);
  circle(400, 150, 90);

  // eye patches 
  circle(250, 260, 70);
  circle(350, 260, 70);

  //eyes
  fill(255);
  circle(250, 260, 30);
  circle(350, 260, 30);

  fill(0);
  circle(250, 260, 12);
  circle(350, 260, 12);

  // nose
  circle(300, 300, 20);

  // mouth
  noFill();
  stroke(0);
  arc(300, 320, 50, 30, 0, PI);

  // body
  noStroke();
  fill(255);
  rect(220, 360, 160, 150, 40);

  // arms
  fill(0);
  rect(180, 380, 40, 100, 20);
  rect(380, 380, 40, 100, 20);

  // feet
  rect(240, 500, 50, 40, 20);
  rect(310, 500, 50, 40, 20);
  
  // bamboo
  fill(0,200,0);
  rect(470, 350, 30, 200);
  line(470, 390, 500, 390);
  line(470, 430, 500, 430);

}

Changing just a few numbers made the panda look very different. Sometimes the eyes looked crossed, and sometimes the mouth was too low. Fixing those small things helped me understand how exact the coordinates need to be.

How was this made? : 

First, I created the canvas and chose a background color. Then I made the panda’s head using a large circle and attached the ears to the top. After that, I worked on the face using circles for the eye patches, eyes, and pupils. I kept running the sketch and changing the x and y values until the face felt centered. This part took the longest because small changes made a big difference.

After finishing the face, I added the nose and mouth using a circle and an arc. Then I created the body, arms, and feet using rectangles with rounded corners so the panda looked soft instead of sharp. I also added a piece of bamboo next to the panda using rectangles and lines.

I mainly used what we learned in class, like rect(), circle(), arc(), line(), and fill(). When I forgot how something worked, I checked the p5.js reference. The hardest part for me was thinking in numbers instead of just drawing what I see.

Reflection and future ideas:

This project helped me see how coding can be used for art. At first, it felt strange to draw by typing numbers instead of using a pencil, but after testing and fixing things again and again, it started to make sense.

One thing I learned is that order matters in code, because shapes drawn later appear on top of others. I also learned how careful you have to be with placement, since even moving a shape a little can change the whole look of the character.

If I keep working on this, I would like to add more detail, like clothes or props, and maybe small animations like blinking eyes. I also want to try making the panda react to the mouse or keyboard. Overall, this was a good start for learning p5.js, and I feel more comfortable using shapes and coordinates now than before.

Week 1: Self Portrait using p5.js

CONCEPT

Before I started working on my self-portrait, I looked for inspiration on Pinterest and found this photo.

I wanted to create something fun and bubbly, so I began with a simple circle for the head, following the form of my inspiration, and added more shapes to create the features. Initially, I only planned to create a basic head and body figure. However, as I continued working, I struggled with how to design the body and make it visually cohesive with the head. Because of this, I decided to hide half of the character’s body by adding a table and placing the arms on top of it. This solution helped simplify my concern, but it also led to another challenge: I found it difficult to create the hands in between the forearms or the ellipses that I placed on top of the table. To resolve this, I added a phone to fill the space, which allowed the pose to feel more natural.

This decision led me to the creation of a figure that looks like a girl watching something on her phone, an activity that reflects something I do every day. After adding the phone, I felt that the figure was still lacking something. This led me to add animation to the piece. I realized that when people watch content on their phones, light from the screen reflects onto their faces. To create this effect, I used an ellipse to create a light source and hid parts of it under the bangs so that it would appear as though the glow was coming directly from the phone. To make it more realistic, I researched whether it was possible to create a flickering light effect on a loop. I found a video on YouTube that showed me how frame count works, and I asked for guidance from an AI on how to apply this concept to the ellipses I used to represent the phone’s light.

HIGHLIGHT

One of the biggest challenges I faced with this project was creating the hair. Initially, I intended to recreate my current hairstyle, but I found it too complex. As a result, I chose to recreate a previous version of my hairstyle when I had bangs and wavy hair. To achieve this, I experimented with layering ellipses on top of a rectangle to suggest waves and texture in the hair, and I adjusted the rotation of the ellipses to shape the bangs more naturally. Another challenge was animating the phone light so that it would blink continuously on a loop. Even after adding the ellipse that represents the phone’s light, it took me a lot of time to figure out how to animate it in a way that it would really look like the character was watching something on her screen.

 

//hair
  noStroke();
  fill(0);
  arc(300,242,236,210,360,QUARTER_PI);
  
  fill(0);
  rect(181.5,240,237);
  
  fill(0);
  ellipse (195,305,50,145);
  
  fill(0);
  ellipse (195,405,50,145);
   
  fill(0);
  ellipse (402,305,50,145);
  
  fill(0);
  ellipse (402,405,50,145);

  push();

// bangs
  translate(250, 230);
  rotate(-PI / 6);
  fill(0);
  ellipse(0, 0, 150, 100);

  translate(80, 40);
  rotate(-PI / 6);
  fill(0);
  ellipse(0, 0, 100, 170);

  fill(0);
  triangle(-130,-110,-80,-100,-160,-57);

  fill(0);
  triangle(-70, 100, -2, 50, 10, 87);

  pop();
//light from the phone
  noStroke();
  fill(255, 255, 255, 50 + sin(frameCount * 0.09) * 20);
  ellipse(300, 340, 227, 280);
REFLECTION

After completing the final touches, I felt relieved and satisfied with how the project turned out. Although the final outcome differed from what I originally expected, I believe it was successful, and I genuinely enjoyed the process of creating it.

Here’s the final code:

function setup() {
  createCanvas(600, 600);
}

function draw() {
  background(130);
  
  //window
  fill(192);
  stroke(255);
  strokeWeight(30);
  rect(100,50,400,300,0)
  
  fill(255);
  noStroke();
  rect(275,50,50,150,0)
  
  fill(255);
  noStroke();
  rect(100,180,390,50,0)
  
  //hair
  noStroke();
  fill(0);
  arc(300,242,236,210,360,QUARTER_PI);
  
  fill(0);
  rect(181.5,240,237);
  
  fill(0);
  ellipse (195,305,50,145);
  
  fill(0);
  ellipse (195,405,50,145);
   
  fill(0);
  ellipse (402,305,50,145);
  
  fill(0);
  ellipse (402,405,50,145);
 
  //neck
  noStroke();
  fill('#D5C29B');
  rect(270,400,60);
  
  //head
  fill('rgb(248,229,190)');
  ellipse (300,300,200,240);
  noStroke();
  
  //left ear
  fill('#EBDBB8');
  ellipse (200,310,35,60)
  noStroke();
  
  //right ear
  fill('rgb(248,229,190)');
  ellipse (400,310,35,60)
  noStroke();
  
  //shirt
  fill('#784E80');
  ellipse(300,480,470,100);
  
  fill('#E5D1A6');
  ellipse(300,434,60,27);
  
  //table
  fill(23);
  rect(0,490,600);
  
  //light from the phone
  noStroke();
  fill(255, 255, 255, 50 + sin(frameCount * 0.09) * 20);
  ellipse(300, 340, 227, 280);
  
  //left thumb
  fill('#E5D1A6');
  rect(263,430,20,37,22);
  
  // right thumb
  fill('#E5D1A6');
  rect(323,430,20,37,22);
  
  //phone
  fill(40);
  rect(275,420,55,90,10);
  
  //forearms
  fill('#EBDBB8');
  ellipse(173,480,230,70);
  
  fill('#EBDBB8');
  ellipse(430,480,230,70);
  
  //hands
  fill('#F8E5BE');
  rect(233,450,55,70,22);
  
  fill('#F8E5BE');
  rect(313,450,55,70,22);
  
  push();
  
  // bangs
  translate(250, 230);
  rotate(-PI / 6);
  fill(0);
  ellipse(0, 0, 150, 100);
  
  translate(80, 40);
  rotate(-PI / 6);
  fill(0);
  ellipse(0, 0, 100, 170);
  
  fill(0);
  triangle(-130,-110,-80,-100,-160,-57);
  
  fill(0);
  triangle(-70, 100, -2, 50, 10, 87);
  
  pop();
  
  //right eye
  fill(0);
  circle(252,325,45);
  
  fill(255);
  circle(252,329,45);
  
  //left eye
  fill(0);
  circle(345,325,45);
  
  fill(255);
  circle(345,329,45);
  
  //right pupil
  fill(190);
  circle (343,334,35);
  
  fill (70);
  circle(342,339,25);
  
  fill (255)
  circle(350,337,10);
  
  //left pupil
  fill(190);
  circle (254,334,35);
  
  fill (70);
  circle(255,338,25);
  
  fill (255)
  circle(262,337,10);
  
  //nose
  fill ("#F1DABD")
  triangle (310,370,298,329,290,370)
  
  //left eyelash
  stroke(0);
  strokeWeight(3);
  line(236, 309, 228, 305);
  
  stroke(0);
  strokeWeight(3);
  line(230, 315, 220, 312);
  
  stroke(0);
  strokeWeight(3);
  line(228, 324, 220, 324);
  
  //right eyelash
  stroke(0);
  strokeWeight(3);
  line(364, 314, 375, 308);
  
  stroke(0);
  strokeWeight(3);
  line(368, 320, 382, 316);
  
  stroke(0);
  strokeWeight(3);
  line(369, 327, 379, 326);
  
  //left earring
  fill('yellow');
  noStroke();
  circle(200,345,20);

  fill(0)
  noStroke();
  circle(203,347,11);
  
  //right earring
  fill('yellow');
  noStroke();
  circle(400,345,20);

  fill(0);
  noStroke();
  circle(396,347,11);
  
  //upper lip
  fill('rgb(242,174,186)');
  ellipse(295,388,17,10);
  
  fill('rgb(242,174,186)');
  ellipse(307,388,17,10);
  
  //lower lip
  fill('rgb(242,174,186)');
  ellipse(301,393,35,15);
  
  //lip line
  stroke('rgb(216,146,159)');
  strokeWeight(2);
  line(285,392,316,392);
  
  //left eyebrows
  stroke(20);
  strokeWeight(4);
  line(230,295,276,293);
  
  //right eyebrows
  stroke(20);
  strokeWeight(4);
  line(320,293,370,295);
  
}

 

 

 

 

 

Week 1 Self Portrait- Sky

Concept:

For the very first assignment in Intro to Interactive media, I was able to make a 2D portrait of my own face which also included parts of my identity such as lifted eyebrows, bigger eyes and writing some parts about my identity such as being a polyglot from Thailand for the very first time using coding as I previously don’t have any previous experience in coding.

Below is my finished portrait:

Screenshot

http://<iframe src=”https://editor.p5js.org/po2127/full/E8eNn1n2a”></iframe>

How it’s made:

This portrait was made using p5.js functions using 2D shapes and different functions such as Fill, Elipse, Stroke, Strokeweight, noStroke, Circle, Arc and line, these basic functions help me have a good foundation of what I need to make a realistic face and to explore and add different more specific features such as eyebrows etc.

When I started it was pretty difficult as it took me quite a while to watch the 3 videos available on YouTube and the link provided to get a good basic grasp of the functions of this platform because I haven’t coded much before and which code I need to do to make the shape of each facial features compatible with the body.

Screenshot

I designed my face to be more of starting with face frame using fill and elipse with no stroke, then eye using ellipse, and the eyehole using fill and circle, then eyebrow using arc, and nose lines and more specifically far apart because my nose is more big, lip, hair and ear using arc and neck using lines and shirt using text and stroke.

Later on adjusting more specific details such as arc of lips and eyebrows to make it fit well with how I look and my body proportions or look like me was also another hard step.

As highlighted code I’m proud of:

The part I am proud of is I was trying to add different parts of my identity to my shirt such as giving purple theme and writing some things that really resonate with me and are part of my identity on the shirt and to be able to write design of NYU shirt on the portrait.

Screenshot

Reflection:

I enjoyed creating my  self portrait, alsthought hroguhout the journey I had some difficulties especially at positioning the shapes and getting used to how the gird works, I was able to get through and I think the main thing I struggled was when to apply the noStroke function and finding the right strokeWeight for specific places such as lips etc.

I think in the future what could have been better is after I get better and more used to program I can add some side effects such as animations, and different gimics into the portrait.  But overall, I think as the first work it was already pretty fun and I feel proud to create my first very own portrait.

Self Portrait

For my self portrait I decided to draw a zombie because I like zombies. I wanted it to look like something I might’ve drawn in MS Paint, so I played around with shapes and colors until it felt and looked right.

The part I’m most proud of is the hair and bangs, since I had to align the points and angles perfectly to the pixel to make it one big coherent shape and honestly it was incredibly painful to do.

 //bangs
strokeWeight(0)
fill('black')
arc(300,225,320,200,PI,0)
triangle(300,220,100,350,150,200)
triangle(280,220,270,400,350,200)
triangle(340,220,500,400,460,220)
triangle(200,220,100,500,150,240)
triangle(80,100,500,300,300,105)
triangle(380,126,550,245,460,240)

I didn’t have any help with this aside from a few tips from the p5 reference page. I just kinda figured things out as I went along. Because of this, there are some aspects of the code that probably could’ve been done more efficiently, so I’ll carry what I learned through trial and error here and apply it to my next project. I’m also thinking of having the colors in her eyes shift so that it looks like it’s spiraling. That’d be pretty cool I think.

Week 1 – Self Portrait

My Concept:

Going into this assignment as a beginner in p5, I knew I wanted to choose a muse that would give me the flexibility to play around with the basic foundations of the coding language, especially with coordinates and the order of execution of lines of code. I ended up choosing to create a simple sketch of a mouse. The simplicity of the shapes were beginner friendly and helped me become more comfortable with coding in p5. The final result left me quite satisfied, as I ended up with a cute animated animal created using 2D shapes.

Highlight of Sketch:

One part of the code I was particularly proud of was the use of arcs. It may seem quite simple; however, I was quite satisfied with how I was able to utilise what I learned in class to create a desired outcome for the mouse’s mouth.

I was also quite satisfied with the placements of lines to represnt whiskers, as it took some trial and error.

//MOUTH
//arcs are tilised to create a cute representation of a mouse's mouth
noFill();
stroke(0)
strokeWeight(2)
arc(190,239,18,14,0,PI)
arc(208,239,18,14,0,PI)

//WHISKERS
//lines are drawn in pairs of two and parralel of eachother to create a set of whiskers for the mouse
line(160,235,185,230)
line(160,245,185,230)
line(240,235,215,230)
line(240,245,215,230)

 

Embedded Sketch:

How this was Made:

I started the process by setting my mind on an idea, and I ended up choosing a mouse. I decided to emphasize what we learned in class in my code, and I reviewed the links to the different shapes we created to refresh my memory of coordinates and different lines of code.

I started by creating the body and ears with circles first to put them in the back. I then drew the mouse’s head right after to account for the overlapping of the shapes. I then drew the mouse’s features using a variety of shapes such as circles, ellipses, and lines. Finally, I went over my code and added more detail to the code visual to try to add dimension to the mouse as much as possible, given that it is two-dimensional.

Reflection and Future Ideas:

Overall, considering this is my very first time playing around with p5, I am satisfied with the outcome and quite fond of my cute mouse illustration. In most coding experiences, there was obviously some trial and error, which, in my opinion, is the art of coding. I was able to create an outcome I am proud of, and hopefully in future assignments I can see how far I’ve come from my very first assignment.

In future assignments, I hope to incorporate interactivity and some dynamic features into the code. I also hope that as the lessons progress, I am able to obtain more knowledge in p5 and create even more impressive coding experiences.

Assignment 1: Self Portrait

Here is my final self-portrait:

Concept:

For this project, I created a self-portrait using only 2D primitives. My goal was to make the portrait have many traits of me while still keeping the forms simple, using what we learned in class about 2D primitives and the p5 reference website. I used the same hijab color I wear most often, gold jewelry that is similar to what I wear every day, and burgundy for the shirt since it is my favorite color. 

How this was made:

The portrait was created using basic 2D primitives such as the ellipse, rect, arc, line, and quad. It was really hard positioning the shapes, especially when creating the lashes and the bent right arm. It was a constant trial-and-error process, which helped me better understand how each value in the shape controls the position and form.

For the face, I was not sure what exactly to do, especially because my knowledge of the platform and scope was limited. But I kept the features minimal by just using simple shapes, like an ellipse for the eyes, a small half-triangle nose made with lines, and an arc for the mouth. I was initially unsure how to create the hijab, but by experimenting with a large black ellipse and putting it behind and around my face, I was able to create a framing effect that resembles a hijab without making it too difficult. 

After making the portrait of myself, I decided to experiment with some motion and interactivity, even though we have not covered it yet in class. I started looking at some examples, and I saw many people make the pupils of the eye follow the mouse. So I did this by slightly adding mouseX and mouseY values to their original positions and then limiting the movement by multiplying by 0.008 so the pupils stayed inside the eye. I learned this by reviewing the slides to get an understanding of the variables and watching some tutorials from “The Coding Train” to get a better idea of it. 

Code highlight:

One part of the code that I am particularly proud of is the thought bubble interaction. When you click on the portrait, two thought bubbles appear with random words generated inside them to represent my thoughts. 

let showThoughts = false;//to make the thoughts hidden at first

let thoughts = [
  "THINKING",
  "IDK",
  "SCHOOL",
  "FUTURE",
  "TIME",
  "WHY",
  "WHAT IF",
  "PRESSURE",
  "OK",
  "TIRED"
]; //words I want to have in the background

function setup() {
  createCanvas(400,400);
  }

function mousePressed() {
  showThoughts = !showThoughts;
}//so if you click the thoughts the thoughts appear and if you click again the thoughts disappear.

function draw() {
background('#D2D1C7');//to set the background
print(mouseX, mouseY)
 
//Thought words (the background layer)
//fill(0,80); //making the words black and low opacity
//stroke(0,80);//so the stroke is the same color as the fill
//strokeWeight (3); //to make the text bold
//textSize(20);//to make the text bigger
//text(random(thoughts), random(width), random(height));//to draw a random word at a random position.

  
  
//Only draw thoughts if clicked
if (showThoughts) {
//Thought bubbles
noStroke();
fill(255, 230);//soft white and slightly transparent
ellipse(110, 90, 200, 140);//left bubble shape
ellipse(297, 85, 200, 140);//right bubble shape

//Thought words
fill(0);//black
stroke(0);
strokeWeight(2);
textSize(20);

//Left bubble text
text(random(thoughts),random(110 - 60, 110 + 50),random(90 - 40, 90 + 40))//picks one word randomly, then I put padding so it does not go outside the circle 
//Right bubble text
text(random(thoughts),random(277 - 60, 277 + 50),random(85 - 40, 85 + 40));//same thing here just with different placements (I put a smaller number, 277 instead of 297 because some of the text still went outside the bubble for some reason)
  }

This section was quite challenging because I had to control where the text appeared so it stayed inside the bubbles. 

Reflection/future work:

I did not want the background to be left empty. At first, I wanted random words to fill the entire background, but I figured it was too visually messy, and I did not like the result of the background just being black. So I decided to scratch that and just make a thought bubble using white ellipses and have my thoughts scatter in them, where they only appear when you click on the portrait. 

I liked this idea because it allows the viewer to first see my appearance, then reveal what is happening internally, making my self-portrait a bit more personal. At first, you only see me holding up a peace sign, but when you click on the portrait, you get a deeper look into my mind. So I’m particularly proud of this part of the code because it took some time learning how to do it by searching questions and the keyword functions like the function mousepressed and random, and what I needed to equal them with for the function to work, on Google, and watching multiple tutorials, especially from “The Coding Train” like these:

https://youtu.be/POn4cZ0jL-o?si=yuoXE6cM294Seczd

 https://www.youtube.com/watch?v=Bn_B3T_Vbxs 

At one point, I was confused about why words were still appearing in the background. I later realized this was because I left the background in the setup function instead of the draw function, which caused text from previous frames to accumulate on the canvas rather than being cleared each frame.

For future work, I would like to be more accurate with the positioning of things, add smoother animations, and create more interactive elements to further express more emotion and movement.

Week 1 – Self Portrait

Concept:

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!

Implementation:

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);
}

 

 

Reflection:

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.