Assignment 2: Box Hit

Here is my final sketch:

Concept:

For this project, I was inspired by the game Smash Hit, where a ball hits glass and causes it to shatter. Also, while looking through the computer art magazines, I came across Boxes I by William Kolomyjec. I decided to combine these two ideas by creating a grid of squares with a circle in the center that acts as a button that triggers the distortion of the squares. 

The circle turns green when the viewer hovers over it and turns red when clicked. Once you click on it, the circle causes the squares surrounded by it to distort, kinda like in the artwork I was inspired by. I wanted a controlled system that slowly breaks apart, while having a game-like effect. 

Here is the artwork I was inspired by that was in the magazine:

Code Highlight:

The code that I am particularly proud of is the section that distorts the boxes based on their distance from the center and the randomness to its position and sides. It gave the visual effect I wanted. 

//distortion control using the radius
if (ruined && radius < 155) {
  //so if the disortion is on and the radius hasn't reached its maximum, expand the radius. I chose the number 155 because I didn't want the disortion to go all the way out.
  radius += 4; //to increase the radius gradually (like a firework effect)
}

if (!ruined) {
  //if the distortion is off to reset the radius back to 0.
  radius = 0;
}

//the grid of boxes
for (
  let x = 0;
  x <= width - size;
  x += size //so it can loop horizontly
) {
  for (
    let y = 0;
    y <= height - size;
    y += size //so it can loop vertically
  ) {
    let d = dist(x, y, width / 2, height / 2); //to get the distance from the squares to the center of the canvas

    //to make the grid distorted
    if (ruined && d < radius) {
      // if the distortion is on and the square is in near the radius
      rect(
        x + random(-5, 1),
        y + random(-5, 1),
        size + random(-5, 1),
        size + random(-5, 1)
      ); //to make random x and y positions and change the width and height for a random messy effect. (for the disorted squares)
    } else {
      // if the squares are not within the radius.
      fill("white");
      rect(x, y, size, size);
    } // let the rest of the boxes to be straight and untouched
  }
}

Reflection/future work:

In class, we used loops to create a grid of circles, so I applied the same concept using nested for loops to build a grid of squares. At first, the size of each box was 50, but I felt that it was too large for the distortion effect to look effective. The larger size also caused some of the boxes to be cut off at the edges of the canvas. So I decided to reduce the size of the boxes to 20, which made the grid feel more refined and detailed. 

Also, the distortion effect was happening too fast it felt overwhelming. I tried to slow it down by reducing the values inside the random function, but this did not work the way I expected. So I just did the frameRate function and put it inside the setup function, like how you showed us in class. I think slowing down the frame rate helped the movement feel more controlled and intentional.

When the circle was clicked, all the boxes that were within the radius distorted at the same time. I was going to leave it this way, but I showed my friend, and she told me that I should try to make the distortion spread outward gradually, like a firework. I did this by using a radius that slowly increases over time, allowing it to expand from the center instead of appearing all at once. I think it made it more visually interesting, so I stuck with it.

While working on this project, I sometimes needed help with the order of the functions and how everything should be structured so the sketch would work properly. I already knew the functions from the slides and reference materials and wrote the code myself, but I was not always sure how to organize them in an order that would work. So I used ChatGPT and Google searches to help clarify those details, specifically how to control the distortion using a radius value and why that needs to be placed at the top of the draw function so it could update consistently, and where the mouse-pressed function should be placed. But most of my understanding came from the class examples, lecture slides, and trial and error with my code. 

In the future, I would like to add more details and incorporate more concepts that were previously discussed. I am interested in exploring different types of distortion and animation that really enhance the experience. 

Reading Reflection – Week 2

One thing I really loved in Casey Reas’ Eyeo talk on chance operations was how much inspiration came from bundling architecture. Looking at real neighborhoods and constructions, and then creating them into systems of rules and geometry, made the work really interesting and intentional. That approach really resonated with me, especially because I see a lot of overlap with Casey’s lecture and what we talked about in my Understanding IM class. We talked about the Dada movement and how chance was used as a response to control and structure after WWI, and many of the artworks, like the Jean Arp piece shown in the video, overlapped with examples we’ve already studied. In both of these cases, chance is used within a structure, which allowed forms to emerge rather than be fully planned. Moreover, to me, this video felt like a short history of digital art, showing how artists moved from pure experimentation to systems where form and behavior emerge over time, reminding me of the book he showed of a million random digits with 100000 normal deviations. I also really liked the idea of making things that are clearly artificial, but still giving them an organic quality.

Before watching this video, I mostly thought of randomness as something that makes things messy and out of control. I think that came from my own experience, when I tried to make a random background for my self-portrait, and the words kept filling the space until it turned completely black. This video helped me realize that randomness is not completely random, but is intentional and planned. There is always a set of instructions and the use of precise geometry behind it. I remember Reas said something along the lines of “pure randomness with symmetry, you start to see faces, and triggering our imagination,” and that stuck with me. In my own work, I will now set clear rules first and then allow small random changes, like with the position or scale. I think that feels like the best balance between control and chaos for me because I still design the system, but do not fully control the outcome. The results can be something better than I planned from the start. I do believe the author is biased in a way by portraying chance positively, like yes, chance can open up to new possibilities, but it only works because of the structure placed around it. The video left me thinking about how much control an artist should give up. Also, I am interested in exploring ideas like a maze or a shape that grows outward from the center, similar to what was shown in the video.

Week 2 Assignment – 3D Conway game of life

Conway game of life 3D sketch below!

Concept:

The inspiration for this is Conway’s game of life, computer generated art is like an umbrella term, I started with searching up old computer generated art in the 50’s and 60’s, and there were some cool concepts like using computer parts to make some sort of art out of them, but it wasn’t exactly what I was looking for. Then it hit me! Conway’s game of life, but not just 2D, that would not work for me, I had to make it 3D, and that’s how this sketch started.

I had to tweak the rules a bit to give this a more generative art feel as well as per-determining the spawn position for the starting cells.

Implementation:

Before we get into the technical implementation of this, let me cover  the theory and rules that the sketch runs by.

Of course for 3D we had to use webgl, to get our 3rd axis (z), and also use orbital control to allow us to move around the area.

createCanvas(400, 400, WEBGL);
// Start with a view of the entire resolution
camera(0, 400, 4900, 0, 0, 0, 0, 1, 0);

orbitControl();

 

A dead cell becomes alive only if it has exactly 3 or 6 neighbours. (Neighbours are alive cells that are next to the cell we are checking)

A living cell stays alive only if it has exactly 5 or 6 neighbours, otherwise it will die.

For the starting spawn, I first started with giving each cell a random chance of 2% to become alive on initialization, it would work however sometimes the  cells would all die or I would not get a good looking design, so I decided to spawn the cells at each corner.

// Create the array for all th cells state
  for (let x = 0; x < res; x++) {
    grid[x] = [];
    next[x] = [];
    for (let y = 0; y < res; y++) {
      grid[x][y] = [];
      next[x][y] = [];
      for (let z = 0; z < res; z++) {
        // Seed with 3x3x3 clusters at corners
        let inCorner1 = x < 3 && y < 3 && z < 3;
        let inCorner2 = x >= res - 3 && y < 3 && z < 3;
        let inCorner3 = x < 3 && y >= res - 3 && z < 3;
        let inCorner4 = x >= res - 3 && y >= res - 3 && z < 3;
        let inCorner5 = x < 3 && y < 3 && z >= res - 3;
        let inCorner6 = x >= res - 3 && y < 3 && z >= res - 3;
        let inCorner7 = x < 3 && y >= res - 3 && z >= res - 3;
        let inCorner8 = x >= res - 3 && y >= res - 3 && z >= res - 3;

        grid[x][y][z] =
          inCorner1 ||
          inCorner2 ||
          inCorner3 ||
          inCorner4 ||
          inCorner5 ||
          inCorner6 ||
          inCorner7 ||
          inCorner8
            ? 1
            : 0;
        next[x][y][z] = 0;
      }
    }
  }

To check for neighbours of each cell, we use a triple nested loop and offset from -1 to 1, to check behind, center forward for each axis.

A couple things to note is, I run the rule checking code once every 30 frames, so that the cells don’t populate too fast and so that we can actually see what is happening and enjoy the chaos that is happening.

A couple things I am proud of is my optimization and coordinate calculation.

Context: There are 2 grids that we are using, our “present” grid and the “next” grid, to not confuse the computer, we apply our rule application and calculation to our present grid, but store the results in our next grid, now originally to switch between the 2 I would turn grid into a long string JSON, then parse it tot equate it to next, but that would lead to thousands of operations. So what I figured out was actually something from c++ and dealing with pointers, and it’s simply just changing the name. To provide context, in javascript you can’t equate the 2 grids to each other to change them, because then they would be connected and if you affect one grid the other is also affected which defeats the purpose of having 2 grids.

let temp = grid;
grid = next;
next = temp;

These 3 lines may not seem much, but it saves our computers from doing thousands of operations every 30 frames, and reduces those thousand of operations to simply 2 operations.

Now, I had to limit the cells to spawn in a specific area of our canvas, otherwise the calculations would get too much, and wordpress would not be able to handle that many calculations.

let cellSize = 50;
let res = 20;

Resolution is basically how many cubes we want, or in this case we want a dimension of a 20 by 20 by 20 cubes, which is 8000 cubes, and 8000 operations every 30 frames, anymore and the browser would slow down tremendously so 20 was the sweet spot.

for (let x = 0; x < res; x++) {
    for (let y = 0; y < res; y++) {
      for (let z = 0; z < res; z++) {
        // Check if the cell is alive
        if (grid[x][y][z] === 1) {
          // Calculate the position of the cell in the canvas using the res and size as reference.
          let xPos = x * cellSize - (res * cellSize) / 2;
          let yPos = y * cellSize - (res * cellSize) / 2;
          let zPos = z * cellSize - (res * cellSize) / 2;
          push();
          translate(xPos, yPos, zPos);

          // Map scales our rgb colors based on the location so the cube looks like a spectrum.
          let r = map(x, 0, res, 0, 255);
          let g = map(y, 0, res, 0, 255);
          let b = map(z, 0, res, 0, 255);
          fill(r, g, b);
          stroke(0, 50);
          box(cellSize);
          // Take the pointer back to 0,0,0
          pop();
        }
      }
    }
  }

Our coordinate system of the resolution is going to be different to the canvas coordinate system, so with a little bit of math, we could take our raw x y z coordinate and convert them into our resolution coordinates to allow us a proper bounded area.

Finally giving us that beautiful color spectrum, we use the map functions which allows us to scale our resolution with the rgb values, for example at 10 (half way into resolution) the  r value would be 127 (half of 255).

Reflection and Improvements:

Honestly I am particularly happy about how this turned out, I thought it would be quite difficult to implement but it turned out a lot better and easier than I expected it to and I am happy that I went through with it.

A couple ways I would think about improving it is adding a gradient color background, and maybe implement more shapes for cells to be rather than simply a cube.

Week 1 Assignment – Self-Portrait

Concept:

For my self-portrait, I was trying to think of a creative way to display my head. I was mainly thinking of a way that would help me avoid drawing my hijab as I was not too sure how to implement that. I also wanted to force myself to use some creativity since I don’t always get the opportunity to do that in my Computer Science classes. I ended up drawing my head in the shape of a cloud using ellipses. I drew this inspiration from my name, which means a rainy cloud in Arabic. I also added the rain effect in the background to complete my name’s translation. As for the tree, I attempted to draw an olive tree, to represent my Palestinian identity.

Implementation:

I mainly used ellipses and rectangles to create everything in the sketch. I used a rectangle for the tree’s trunk, and circles for the bushes and olives. For the cloud, I drew one ellipse in the centre and then drew multiple ellipses around it, placing them in a way that would create a cloud shape. For the smile, I later realized I could have used the arc() function, but for this specific implementation I drew an ellipse and then put a rectangle over it in the same color as the cloud to create a semi-circle. The nose used the line() function, the eyes are ellipses, and the eyebrows are drawn using the arc() function.

For the rain in the background, I watched this YouTube tutorial on how to add it. Considering my computer science background and my familiarity with using classes and functions, I was able to understand the code and implement it.

Code I am Proud of:

//eyebrows
  stroke(0);
  arc(254, 220, 20, 3, radians(180), radians(355));
  arc(315, 220, 20, 2, radians(180), radians(355));

Despite its simplicity, the eyebrows were definitely the most difficult part to implement. I watched a YouTube video to understand further how it works, as I was a little confused. Through this video, I discovered you can typecast from degrees to radians, which made the function much easier to use since radians are a bit difficult to work with.

//array to store all created raindrops
drops = []

class RainDrop{
  constructor(x, y){
    this.pos = createVector(x, y)
    this.vel = createVector(0, random(8, 11))
    this.length = random(20, 40)
    this.strength = random(255)
  }
  
  //displays each rain drop
  show(){
    stroke(255, this.strength)
    line(this.pos.x, this.pos.y, this.pos.x, this.pos.y-           this.length)
  }
  
  //makes the rain fall
  update(){
    this.pos.add(this.vel)
    if (this.pos.y > height + 100){
      drops.shift()
    }
  }
}

function setup() {
  createCanvas(400, 400);
  p = createVector(random(width), 200)
}

function draw(){
  background(173,212,247);
  
  //loop to create many raindrops and store them in an array
  for (let i = 0; i < 5; i++){
    drops.push(new RainDrop(random(width), 0, 0))
  }
  
  //show each rain drop
  for (let d of drops){
    d.show()
    d.update()
  }
}

The other code snippet I am proud of is the rainfall. Despite using lots of help from YouTube, I was proud of coming up with the idea and having it implemented exactly how I wanted it.

Reflection:

Overall, I am quite happy with the final result and truly enjoyed the process of making this. Despite coding frequently in my CS classes, this type of coding felt different and more enjoyable, less algorithmic. It also feels more rewarding as you create your final sketch because you can see results as soon as you type the code. For the future, I would definitely like to explore more with adjusting strokes so that they only outline certain parts of a shape, especially for parts of the sketch developed by layering multiple shapes.

Self Portrait Assignment – Dina

My Concept:

In this assignment, I attempted to make a self-portrait using only 2D shapes such as circles, rectangles, lines, and arcs. To start, before beginning the assignment or the course itself, I had little to no knowledge of coding, so this was definitely something outside of my comfort zone and a bit challenging. I mainly wanted the portrait to be an almost realistic vision of what I usually look like, so that was what I went for for the portrait. With that being said, I “styled” certain features with a lot of attention to small details that I think I could’ve gone without (which was time-consuming for me, but I thought the portrait wouldn’t be realistic enough without them). I kept the portrait basic with only the face and shoulders showing, and nothing in the background.

Here is the finished result:

The Process:

The first thing I started with was the face. This was probably the easiest task I did for the portrait; I just inserted an ellipse and changed its color using the RGB hex.

I then moved on to the eyes. I was using a series of arcs to create the round shape I wanted. It was difficult at first since I was dealing with radians, and I noticed the unit circle in p5 is actually in reverse.  I kind of gave up midway through with it and switched over to degrees, which was easier for me. But I realized I can’t always rely on degrees, so I switched back to radians, got the hang of it, and the second (right) eye became much easier to make. I added my usual glasses and gave myself some eyebrows as well.

The nose was particularly interesting to make. I initially made the little bulb (circle) of the nose in the center and built everything else around it. I put in the lines of the nose bridge, the surrounding area near the nostrils using arcs, and finally the nostrils themselves. Since the nostrils I could draw were small, I opted for points rather than an arc, since their sketch would be small. I chose this specific structure of the nose to make the sketch as realistic as possible instead of drawing a few lines or a triangle on the side.

The real challenge for me was drawing the lips and hair.

The lips: Since I was going for a realistic look,  I faced a challenge building the upper lip. For reference, I always begin on the left side of the face for all my features. The first thing I did was draw a straight line that led to almost the center of the face, then I created a small arc connected to it in order to resemble a cupid’s bow. The real challenge was recreating this on the right side of the face with the correct dimensions. It was a huge trial-and-error process, where I constantly adjusted the x and y coordinates to recreate the arc’s dimensions as it connected to the line. Once I figured it out, I added a large arc to seal off the upper lip, and I added another larger arc at the bottom for the bottom lip.

The hair: I realize now that there could have definitely been an easier way to draw the hair, but I started it off on a challenging note. I decided to draw singular curls using arcs and just alternating the arcs and their patterns on the whole head. That was unfortunately extremely time-consuming, and I could’ve gone on an easier path. That’s when I realized I could use large circles, put them around the head, and fill them. This made the process much quicker.

Code Highlight:

I am particularly proud of the way the lips turned out as I managed to recreate the same angles and dimensions on both the right and left side of the mouth.

//lips
  //upper lip
  
  line(175, 240, 192, 235);
  arc(193, 236, 10, 2, -PI, 0);
  arc(203, 236, 10, 2, -PI, 0);
  line(205, 235, 222, 240);
  
  arc(198.6, 240, 47, 12, 0, PI);
  
  //lower lip
  arc(198.7, 241, 47, 30, 0, PI);

Reflection:

It was pretty fun to experiment with code, as I had minimal experience with coding before this. There are definitely things I did that made the task much harder when there was a much simpler alternative (such as using circles for the hair, or I could have just built the mouth using 2 arcs).

Overall, I am pretty satisfied with the outcome, especially considering the trial-and-error and effort it took. If there’s one thing I would take away from this assignment, it is to always look for a simpler, less time-consuming way to do things. In the future, I would like to be able to add some interactive element to my work, or even make it more abstract, to get out of my comfort zone and create something different than what I would usually create.

Here is the final code:

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

function draw() {
  background(180, 130, 190);
  
  print(mouseX, mouseY); 
  
  //neck
  stroke(0, 0, 0);
  strokeWeight(1);
  fill(241, 223, 210);
  rect(155, 288, 90, 66);
  
  //shirt
  //actual shirt
  fill('rgb(210,5,5)');
  stroke(218, 13, 13);
  rect(20, 350, 360, 70, 30, 30, 30, 30);
   //collar
  fill(218, 13, 13);
  stroke('rgb(185,6,6)');
  strokeWeight(20);
  arc(200, 344, 90, 10, 0, PI);
  //collar lines
  stroke('rgb(164,7,7)');
  strokeWeight(1);
  line(200, 340, 200, 359);
  line(208, 340, 208, 357);
  line(216, 339, 216, 359);
  line(223, 339, 224, 357);
  line(229, 339, 230, 355);
  line(234, 339, 238, 355);
  line(240, 337, 243, 353);
  line(244, 336, 250, 353);
  line(247, 335, 253, 349);
  line(249, 335, 255, 342);
  line(191, 339, 191, 358);
  line(184, 339, 183, 358);
  line(175, 339, 175, 357);
  line(168, 338, 166, 356);
  line(161, 337, 159, 351);
  //face and skin
  stroke(1);
  strokeWeight(1);
  fill('rgb(241,223,210)');
  ellipse(200, 180, 200, 240);
    
  
// left eyes successful radians attempt 1
  fill('white');
  arc(150, 150, 50, 30, -HALF_PI, 0);
  arc(150, 150, 50, 30, -PI, -HALF_PI);
  arc(150, 150, 50, 30, 0, PI);
  
  //left eye color
  fill(140, 155, 100);
  circle(150, 150, 25);
   fill('black');
  circle(150, 150, 15);
  
  
// right eyes successful radians attempt
  fill('white');
  arc(250, 150, 50, 30, -PI, 0);
  arc(250, 150, 50, 30, 0, PI);
  
  //right eye color
  fill(140, 155, 100);
  circle(250, 150, 25);
   fill('black');
  circle(250, 150, 15);
 

  
  // eyes attempt radians
    //left eyes shape
 
  //fill('white');
  //arc(150, 150, 50, 30, 0, 2);

  //left eyes shape degrees
  //angleMode (DEGREES);
  //fill('white');
  //arc(150, 150, 50, 30, -90, 0);
  //arc(150, 150, 50, 30, -180, -90);
  //arc(150, 150, 50, 30, 0, 90);
  //arc(150, 150, 50, 30, 90, 180);

  //left eye color
  //fill(140, 155, 100);
  //circle(150, 150, 25);
  
  //right eye shape
  //angleMode (DEGREES); 
  //fill('white');
  //arc(250, 150, 50, 30, -90, 0);
  //arc(250, 150, 50, 30, -180, -90);
  //arc(250, 150, 50, 30, 0, 90);
  //arc(250, 150, 50, 30, 90, 180);
  
  //right eye color
  //fill(140, 155, 100);
  //circle(250, 150, 25);
  
//nose
  stroke('rgb(72,54,24)');
  strokeWeight(1);
  noFill();
  circle(200, 210, 17);
  arc(195, 217, 12, 6, HALF_PI, PI);
  arc(205, 217, 12, 6, 0, HALF_PI);
  
  line(195, 198, 192, 166);
  line(205, 198, 208, 166);
 
 //nostril left 
  strokeWeight(1);
  arc(193, 217, 4, 2, HALF_PI, PI);
  arc(188,207, 6, 16, HALF_PI, PI + HALF_PI);
  
 //nostril right  
  arc(207, 217, 4, 2, 0, HALF_PI);
  arc(212, 207, 6, 16, -HALF_PI, HALF_PI);
  
//lips
  //upper lip
  
  line(175, 240, 192, 235);
  arc(193, 236, 10, 2, -PI, 0);
  arc(203, 236, 10, 2, -PI, 0);
  line(205, 235, 222, 240);
  
  arc(198.6, 240, 47, 12, 0, PI);
  
  //lower lip
  arc(198.7, 241, 47, 30, 0, PI);
  
//eyebrows
  //left eyebrow
  fill('rgb(96,37,37)');
  noStroke();
  rect(132, 120, 42, 6, 0, 3, 0, 0);
  triangle(132, 120, 132, 126, 126, 126);
  
  //right eyebrow
  rect(226, 120, 42, 6, 3, 0, 0, 0);
  triangle(268, 120, 268, 126, 274, 126);
  
//eyelashes
  //left eye
  stroke(1);
  line(127, 144, 120, 138);
  line(128, 141,123, 133);
  line(130, 140, 128, 132);
  line(137, 136, 135, 130);
  line(144, 136, 142, 129);
  line(148, 134, 150, 128);
  line(155, 134, 156, 127);
  line(158, 136, 162, 128);
  line(162, 137, 168, 126);
  line(167, 139, 171, 130);
  line(170, 140, 176, 134);
  
  //right eye
  line(227, 143, 223, 137);
  line(230, 140, 226, 133);
  line(232, 139, 230, 130);
  line(235, 137, 234, 128);
  line(239, 136, 238, 128);
  line(242, 136, 242, 127);
  line(246, 134, 247, 127);
  line(251, 134, 252, 127);
  line(256, 134, 257, 127);
  line(259, 136, 263, 128);
  line(263, 136, 267, 129);
  line(266, 137, 272, 130);
  line(268, 140, 274, 134);
  line(271, 142, 277, 137);
  
//glasses
  noFill();
  strokeWeight(2.7);
  //left lens
  rect(119, 124, 60, 47, 5, 10, 10, 10);
  //right lens
  rect(221, 124, 60, 47, 10, 5, 10, 10);
  //bridge
  rect(179, 139, 42, 1);
  
  
//hair
  fill(96, 37, 37);
  stroke('rgb(96,37,37)');
  strokeWeight(5);
  // arc(199, 77, 60, 10, PI + HALF_PI, HALF_PI);
  // arc(199, 90, 67, 17, PI + HALF_PI, HALF_PI);
  // arc(200, 65, 60, 10, PI + HALF_PI, HALF_PI);
  // arc(200, 78, 67, 17, PI + HALF_PI, HALF_PI);
  arc(240, 70, 69, 19, PI + HALF_PI, HALF_PI);
  arc(262, 88, 69, 19, HALF_PI, PI + HALF_PI);
  arc(240, 109, 69, 19, PI + HALF_PI, HALF_PI);
  arc(262, 109, 69, 19, HALF_PI, PI + HALF_PI);
  arc(227, 65, 60, 10, PI + HALF_PI, HALF_PI);
  arc(227, 95, 67, 17, PI + HALF_PI, HALF_PI);
  arc(243, 72, 60, 10, PI + HALF_PI, HALF_PI);
  arc(229, 70, 67, 17, PI + HALF_PI, HALF_PI);
  arc(290, 102, 60, 20, PI + HALF_PI, HALF_PI);
  arc(247, 81, 67, 17, PI + HALF_PI, HALF_PI);
  arc(290, 84, 60, 20, PI + HALF_PI, HALF_PI);
  arc(276, 91, 67, 17, PI + HALF_PI, HALF_PI);
  arc(278, 114, 60, 25, PI + HALF_PI, HALF_PI);
  arc(260, 95, 67, 17, PI + HALF_PI, HALF_PI);
  arc(267, 67, 60, 10, PI + HALF_PI, HALF_PI);
  arc(279, 69, 67, 17, PI + HALF_PI, HALF_PI);
  arc(272, 113, 60, 10, PI + HALF_PI, HALF_PI);
  arc(284, 126, 67, 17, PI + HALF_PI, HALF_PI);
  // arc(288, 130, 69, 19, HALF_PI, PI + HALF_PI);
  arc(286, 149, 69, 19, PI + HALF_PI, HALF_PI);
  // arc(291, 147, 69, 19, HALF_PI, PI + HALF_PI);   arc(287, 146, 69, 19, HALF_PI, PI + HALF_PI);
  arc(290, 169, 69, 19, PI + HALF_PI, HALF_PI);
  arc(311, 186, 69, 19, HALF_PI, PI + HALF_PI);
  arc(287, 202, 69, 19, PI + HALF_PI, HALF_PI);
  arc(312, 219, 69, 19, HALF_PI, PI + HALF_PI);
  arc(282, 236, 69, 19, PI + HALF_PI, HALF_PI);
  arc(307, 253, 69, 19, HALF_PI, PI + HALF_PI);
  arc(277, 270, 69, 19, PI + HALF_PI, HALF_PI);
  // hair second part copy paste
  arc(303, 183, 69, 19, PI + HALF_PI, HALF_PI);
  arc(320, 203, 69, 19, HALF_PI, PI + HALF_PI);
  arc(288, 168, 69, 19, PI + HALF_PI, HALF_PI);
  arc(307, 253, 69, 19, HALF_PI, PI + HALF_PI);
  
  //circle hair
  circle(260, 109, 27);
  circle(290, 142, 27);
  circle(307, 136, 27);
  circle(274, 88, 27);
  circle(306, 93, 27);
  circle(308, 112, 27);
  circle(263, 67, 27);
  circle(244, 67, 27);
  circle(283, 65, 27);
  circle(302, 182, 27);
  circle(304, 164, 27);
  circle(318, 159, 27);
  circle(313, 133, 27);
  circle(319,109, 29);
  circle(311, 76, 32);
  circle(314,88, 32);
  circle(322, 124, 27);
  circle(325, 97, 27);
  circle(322, 141, 27);
  circle(328, 156, 27);
  circle(332, 141, 27);
  circle(332, 114, 27);
  circle(333, 168, 27);
  circle(335, 187, 27);
  circle(328, 203, 27);
  circle(320, 222, 60);
  circle(322, 242, 60);
  circle(271, 127, 27);
  circle(286, 161, 27);
  circle(211, 65, 20);
  circle(222, 61, 20);
  circle(287, 195, 20);
  circle(285, 205, 20);
  circle(281, 232, 20);
  circle(328, 266, 27);
  circle(339, 272, 27);
  circle(336, 285, 30);
  circle(338, 308, 30);
  circle(340, 326, 30);
  circle(310, 290, 60);
  circle(285, 249, 30);
  circle(283, 267, 27);
  circle(286, 295, 27);
  circle(329, 150, 60);
  circle(343, 183, 60);
  circle(343, 217, 50);
  circle(347, 248, 40);
  circle(354, 270, 50);
  circle(361,232, 40);
  circle(358, 207, 40);
  circle(354, 295, 50);
  circle(351, 316, 40);
  circle(350, 330, 50);
  circle(314, 344, 50);
  circle(286, 325, 50);
  circle(272, 292, 35);
  circle(271, 261, 25);
  circle(223, 77, 27);
  circle(283, 79, 20);
  circle(270, 104, 20);
  circle(224, 91, 20);
  circle(194, 58, 20);
  circle(168, 78, 40);
  circle(177, 56, 20);
  circle(189, 70, 15);
  circle(143, 87, 30);
  circle(124, 101, 30);
  circle(108, 118, 30);
  circle(98, 136, 30);
  circle(107, 136, 30);
  circle(97, 154, 30);
  circle(93, 164, 30);
  circle(98, 178, 30);
  circle(95, 192, 30);
  circle(96, 208, 30);
  circle(105, 185, 30);
  circle(103, 160, 30);
  circle(106, 209, 30);
  circle(104, 225, 30);
  circle(111, 240, 30);
  circle(107, 194, 30);
  circle(114, 251, 30);
  circle(108, 267, 30);
  circle(115, 270, 30);
  circle(139, 97, 30);
  circle(119, 112, 30);
  circle(125, 270, 30);
  circle(121, 288, 50);
  circle(135, 309, 40);
  circle(140, 330, 40);
  circle(139, 350, 40);
  circle(70, 184, 65);
  circle(77, 123, 65);
  circle(72, 233, 65);
  circle(72, 277, 65);
  circle(76, 321, 65);
  circle(109, 314, 65);
  circle(106, 340, 70);
  circle(100, 83, 40);
  circle(129, 69, 30);
  circle(151, 59, 30);
  
  
  
}
  
  
  
  

 

 

 

Week 1 — Self Portrait

1.   Sketch and Code


Code

2.   Overview

For our first assignment, I developed a sketch that is a non-literal self-portrait. The project features a shark – my favourite animal – swimming through a deep-sea environment. The sketch incorporates procedural animation, gradient rendering, and state-based movement logic.

3.   Concept

My goal of this project was to communicate identity through something other than a human face. I chose a shark because it is my favourite animal. In fact, I own a vast collection of shark themed items in real life, so it felt like the most authentic representation of my personality.

4.   Process and Methods

My process began with translating shark anatomy into geometric shapes:

    • I broke the shark down into a main body (ellipse), a tail (triangles), and facial structure (lines and arcs).
function drawSharkSprite() {
  noStroke();
  fill(160); 
  
  // BODY
  ellipse(85, 60, 150, 50); 
  
  // TAIL
  let tailX = 145;   // Local variable
  triangle(tailX, 45, 185, 60, tailX, 75);  // Connection fin
  triangle(195, 95, 145, 45, 185, 60);      // Bottom tail lobe
  triangle(200, 30, 145, 75, 185, 60);      // Top tail lobe
  
  // FINS
  triangle(90, 15, 65, 40, 95, 40);
  
  // GILLS
  stroke(80); 
  strokeWeight(1);
  noFill();
  for(let i = 0; i < 3; i++) {
    let gx = 65 + (i * 5); // Spacing the gills
    arc(gx, 60, 10, 25, -HALF_PI, HALF_PI);
  }

  // --- HEAD DETAILS ---
  noStroke();
  // Teeth
  fill(255);
  for (let tx = 25; tx < 55; tx += 6) {
    let slantY = map(tx, 25, 55, 72, 65); 
    triangle(tx, slantY, tx + 6, slantY, tx + 3, slantY + 7);
  }
  
  // Slanted Eye
  stroke(0); 
  strokeWeight(3);
  line(25, 58, 35, 52); 
  
  // Slanted Mouth Line
  stroke(166, 58, 55); 
  strokeWeight(2);
  line(22, 72, 58, 65); 
}
    • I studied some p5.js functions to avoid static movement of the shark. Instead of the shark simply sliding across the screen, I used the sin() function to give it a bobbing effect.
// Apply bobbing motion using sin() to the Y translation
translate(shark.x, shark.y + sin(shark.yOff) * 10);
scale(2.0); 
drawSharkSprite();
5.   Technical Details
    • To create depth, I wrote a drawOcean() function that uses a for loop to iterate through the canvas height, and using the lerpColor() function transitions from a darker blue to a lighter blue.
    • For the bobbing motion of the shark, I had to make a sort of mathematical function to make it look like the shark is floating in the water. The y-axis position of the shark is updated in the updateShark() function, which makes it so that the values of the y-offset (animation timer) are altered accordingly to be used in the sine function.
function updateShark() {
  shark.x += shark.vx;
  shark.y += shark.vy;
  shark.yOff += 0.05;    // Increment animation timer

  // Boundary checks
  if (shark.x < 30 || shark.x > 220) shark.vx *= -1;
  if (shark.y < 40 || shark.y > 220) shark.vy *= -1;
}
    • To keep the shark within the visible “ocean” and prevent it from swimming off canvas, I implemented a kind of “bounce” motion with a collision system. Since the shark’s movement is controlled by a y and x velocity, its direction can be reversed by simply flipping the sign of the values if the shark passes a certain x or y value.
    • Rather than drawing each tooth and gill individually, I used loops to ensure that, if the shark’s position changes, the features remain aligned..
    • I used translate() and scale() within the draw() loop to allow the shark to move as a single unit without having to update the (x,y) coordinates for every single shape in the sprite.
6.   Reflection

This project was a significant learning curve in managing coordinates. Initially, my shark would fall apart when it moved because the fins weren’t “attached” to the body’s x and y variables. Learning to use translate() solved this by creating a new local coordinate system for the shark. Also, I had a lot of trouble with the “bounce” mechanism of the shark before properly understanding how the coordinates worked. I am proud of my final result, but I hope that in the future I will be able to complete the coding process quicker now that I’ve better grasped some of the built-in functions and coordinate system.

7.   Resources

Assignment_1: Self portrait

I started the work with directly showing my visible form with basic shapes. I did have to look up a new function “curve” to draw my bangs because the shape the “arc” function formed was not good enough. I drew the edge with the “curve” function and then filled in with the arc.

When it comes to portraying myself I thought of including my identity of being a science student apart from my looks, because I love what I do and it is a big part of me and I love to play with the sterotypes. At first I tried to do that by showing myself in a lab coat, but it was sort of difficult because I honestly didn’t know how to draw a lab coat, and I gave up to make a T-shirt looking thing. Then I included the “blowing things up” interation (which might be a bit inappropriate) because of how we used to joke about such things in lab courses.

I got the idea of eye tracing from the “Embarrassed Koala” example and Self-portrait by Pauline Wee that we were shown in class. But because I had the idea of making the explotion animation already, I thout it would be funnier if I made the eyes go in different dirrections and looked stupid, so I flipped through the p5js tutorials and founs the “variables and change” page which I then based my code on (I tried to look at the codes for the embarrased koala example but something was wrong with by browser and the code couldn’t show up). However what I intended to do was somehow different from the original code so I tried a few things and switched things around until the animation worked.

I like the eye tracking and the mouse click interaction codes so I put them below.

//eye movement
  lefteyeX=170+mouseX%width/26
  lefteyeY=150+mouseY%width/24
  righteyeX=230+(-mouseX)%width/26
  righteyeY=166+(-mouseY)%width/23
  //these codes are for normal (non-stupid) eye tracing
  // righteyeX=213+mouseX%width/26
  // righteyeY=150+mouseY%width/23

These are the codes for the eye tracking. I used -mouseX and -mouseY for the right eye so the direction of movement was oppodite from the left (I just instinctively tried this and found that it works so this likely isn’t the most concise code). It looked fun. I also kept the normal eye-tracking codes just in case I wanted to change into a normal look.

if(mouseIsPressed===true) {
    fill('orange')
    stroke('rgb(254,254,0)')
    strokeWeight(3)
  } else {
   noFill()
    noStroke()
    }
    triangle(170,300,160,276,180,292);
    triangle(179,291,185,271,197,290)
    triangle(197,290,232,278,226,298)
    triangle(179,300,157,317,187,312)
    triangle(179,307,182,338,198,312)
    triangle(197,290,206,263,211,293)
    triangle(198,312,207,328,214,304)
    triangle(214,311,232,324,225,300)
    triangle(225,292,241,295,224,306)
    noStroke()
    ellipse(200,300,57,32)
if(mouseIsPressed===true) {
    textSize(25)
    fill('rgb(197,0,0)')
    stroke('rgb(0,0,0)')
    strokeWeight(4)
  } else {
   noFill()
    noStroke()
    }
    text('BOOM!',164,308);

This is the code for the explosion animation on click. The explosion cloud was sort of bad because I couldn’t really figure out how to draw the fancy anime explosions, so it was just composed of an oval and a bunch of triangles. I learned the “if” “else” code from the Conditionals and Interactivity page of the p5js tutorial.

The final results look like this. My friends and I had a good laugh at it.

The space for future improvement that remains is that a lot of my codes are lengthy and verbose. I think this will be improved as I learn mode codes that will be able to show the same effects in a more consice and controlable way. I should also have researched more on the p5js page to perfect the codes I alread know but I was sort of exhausted by figuring out the curves and animations. And I think I lack some creativity in the thinking process as it took me quite some time to think of doing something other than regular portraying. Maybe spending some time brainstorming before I start assignments would help.

Overall I am relatively satisfied with the outcomes and really did enjoy playing around with codes and animations. And I am looking forward to more interesting activities 🙂

Citations:

  • https://p5js.org/tutorials/conditionals-and-interactivity/
  • https://p5js.org/tutorials/variables-and-change/
  • Pauline Wee Assignment 1: Self Portrait https://intro.nyuadim.com/2022/09/01/assignment-1-self-portrait-6/
  • https://p5js.org/reference/

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