Colorful Hues: My Self-Portrait Adventure

Concept

For this first assignment, I had a clear idea to create a cartoonish/fashion-styled sketch using coding. My goal was to create a visual card, which can be comprehensive and rememberable for the audience. With simple shapes and colors, I wanted to show something specific to me, for instance, ginger hair, interest in fashion sketching ( e.g. many curved lines used to draw body and hair), and two of my favorite colors used in the background. You can see the second color by clicking on the mouse 🙂

Approach and Code Highlights

As I was initially drawing out some sketches, I thought that I’d use pretty basic, easy-to-do shapes. However, as I began coding, I immediately understood that I couldn’t do things I drew, as I was only aware of how to use ellipses, circles, and other functions of basic shapes we learned during our first class. At first, I decided not to worry about it and tried my best to do a portrait with those basic functions, however, it led me to a huge disappointment in both the work and myself. Below are my initial sketch vs. my first attempts to do a self-portrait:

I decided to challenge myself and take a risk in doing something I’d never done before. After a couple of videos, readings, and attempts to understand how different functions work, I decided to try out the beginShape(), and bezierVertex() functions to create more complex shapes and curved lines. I spent a lot of time trying to understand which points to choose and how to make smoothly curved shapes. The most time-consuming was to create a shape of wavy hair, which I am very proud of! Below you can see one of my failed attempts and the code I used to create the final result:

//hair

  fill(148, 60, 33);
  beginShape();
  vertex(166, 123);
  bezierVertex(125, 120, 110, 152, 126, 168);
  bezierVertex(133, 173, 136, 186, 130, 203);
  bezierVertex(124, 215, 110, 225, 89, 240);
  bezierVertex(48, 266, 29, 286, 19, 306);
  bezierVertex(12, 338, 26, 370, 85, 385);
  bezierVertex(328, 398, 365, 374, 360, 338);
  bezierVertex(348, 310, 280, 290, 287, 258);
  bezierVertex(294, 230, 323, 206, 316, 176);
  bezierVertex(304, 148, 274, 146, 254, 138);
  bezierVertex(242, 131, 240, 118, 238, 110);
  bezierVertex(236, 100, 229, 86, 198, 87);
  bezierVertex(186, 88, 173, 98, 166, 123);
  
  endShape();

The biggest problem when using these functions was identifying what anchor and control points are and how they work to create a certain shape I want. For better understanding, in the beginning, I separately drew out every shape and recorded every point with its coordinates in my sketchbook. It is a method I used mostly in creating my portrait:

As I was looking through other students’ portraits, I was curious about how to incorporate interactive elements into my work. Initially, I was planning to have falling leaves behind my portrait in the background, as a way to show that my favorite season is Autumn + I was born during this season. After watching a couple of videos on how to create an array of falling points (you can find this resource at the end of the post) and multiple attempts at doing this, I couldn’t figure out a way to manipulate a bezierVertex function with animation, so I wasn’t able to achieve that. However, I’m planning to learn how to do it next time. Nevertheless, I wanted to include some interactive components, therefore I changed my concept a little bit to include another aspect of myself, which is two of my favorite colors: light pink and light blue. To execute this, I used the following part of the code, which I am particularly proud of:

function draw() {
  
  //change background color when mouse is pressed 
  
  background(246, 215, 230);
  if (mouseIsPressed) {
    background(215, 233, 246);
  }

Reflection and Future Thoughts

I am proud of my first attempt to code an artistic sketch using JavaScript language. I understand now how learning how to code can be helpful for the design of posters and animations, which I would like to make one day. As I think about what could I have done better the first idea is to use variables. After learning how to create variables during our second class, I understood how easy it would be to utilize the bezierVertex() functions that I used a lot in my code. Instead of hardcoding and constantly changing every single coordinate point in bezierVertex(), I would have saved time and energy by using the Weight and Height variables, adding or subtracting as much as I wanted. So, it’s a note for myself for future projects. Also, as I mentioned previously in this post, I would love to learn how to code animations like falling stars/snowflakes/leaves, etc. Unfortunately, this was out of my league for this first project, but as I learn new functions in P5JS, I definitely can learn how to do these cool things too!

Resources used for this project

  • The most important one: https://p5js.org/reference/#/p5/
  • Somewhere to choose the RGB colors: https://imagecolorpicker.com/color-code/d7e9f6
  • Snowfall: https://www.youtube.com/watch?v=cl-mHFCGzYk&t=258s
  • TheCodingTrain channel: https://www.youtube.com/@TheCodingTrain
  • Book by Rune Madsen called  Programming Design Systems: https://programmingdesignsystems.com/shape/custom-shapes/index.html#custom-shapes-pANLh0l

 

 

Assignment 1 – Self Portrait

Concept
I’ve been learning German on Duolingo for the past few months, and I was inspired by simple yet expressive way in which the characters are drawn. I thought it would be possible to emulate that style with basic shapes in p5.js. I also really love sunsets, so I wanted to make the background a sunset gradient.

Highlight
A section of code I’m particularly proud of is the two arcs that form the shading for the neck. I placed this section of code before the section for the ellipse that makes the head, so I was able to fill them both in with a lower opacity, creating the shading effect defining the chin. When trying to get this shading to work, I realized that identifying the specific tasks you want the code to do (being less opaque, being able to define arcs in degrees) is integral to the process, because then you can look up commands online that complete those tasks and implement them into your code.

//shading
 fill(191, 172, 134, 130);
 noStroke();
 angleMode(DEGREES);
 arc(200, 285, 46, 40, 0, 180);
 arc(200, 285, 46, 20, 0, 180);

Embedded sketch

Reflection and ideas for future work or improvements
In the future I would organize my code better by making each part of the drawing, such as the face, a separate function, and then call that function to draw that part. This way, it will be easier to see everything at a glance and also troubleshoot. As you can see in the sketch above, there is a glitch creating an errant line on the right ear and a notch on the left ear – if I organized my code by section it would be easier to isolate bugs like this. I would also like to try animating parts of the drawing, like making the eyes move from side to side.

Resources used
https://vimeo.com/619224012
https://www.youtube.com/watch?v=EAY7S1tWbzc&t=9s
https://p5js.org/reference/

Self Portrait — With a Surprise!

Concept

I wanted to capture as much detail as possible in my self-portrait. Since we needed to use simple shapes to draw our portraits, I decided to work with a reference photo of myself and simplify my face into the most basic shapes possible. For an animated component, I added a blinking animation as well. I also wanted an interactive element, which is why I added some cats that pop out when you click on the mouse!

 

Code Highlights:

My favorite part was creating a cat class. It’s the highlight of the project since I can create a cat anywhere on the canvas and rotate it as well. You can even customize the base, iris, mouth, and nose colors for each cat. This flexibility made my code much cleaner and allowed me to have fun, creating different cats.

class Cat {
  constructor(x, y, angle, baseColor, eyeColor, mouthColor = color(255), noseColor = color(202, 137, 155)) {
    this.x = x;
    this.y = y;
    this.angle = angle;
    this.baseColor = baseColor;
    this.eyeColor = eyeColor;
    this.noseColor = noseColor;
    this.mouthColor = mouthColor;
  }

  show() {
    push();
    translate(scaledSize/this.x, scaledSize/this.y);
    rotate(this.angle);

    strokeWeight(2);

    //face
    push();
    fill(this.baseColor);
    triangle(
      -scaledSize / 25,
      -scaledSize / 120,
      -scaledSize / 21.81,
      -scaledSize / 17.14,
      -scaledSize / 100,
      -scaledSize / 26.66
    );
    triangle(
      scaledSize / 25,
      -scaledSize / 120,
      scaledSize / 21.81,
      -scaledSize / 17.14,
      scaledSize / 100,
      -scaledSize / 26.66
    );
    circle(0, 0, scaledSize / 12);

    pop();

    //eyes
    push();
    strokeWeight(2);
    fill(255);
    circle(-scaledSize / 60, -scaledSize / 120, scaledSize / 40);
    circle(scaledSize / 60, -scaledSize / 120, scaledSize / 40);
    pop();

    push();
    noStroke();
    fill(this.eyeColor);
    circle(-scaledSize / 60, -scaledSize / 120, scaledSize / 48);
    circle(scaledSize / 60, -scaledSize / 120, scaledSize / 48);
    pop();

    push();
    noStroke();
    fill(this.noseColor);
    triangle(
      0, scaledSize / 120,
      -scaledSize / 150, scaledSize / 300,
      scaledSize / 150, scaledSize / 300
    );
    pop();

    //mouth
    push();
    stroke(this.mouthColor);
    strokeWeight(1);
    line(0, scaledSize / 120, 0, scaledSize / 80);
    pop();

    push();
    stroke(this.mouthColor);
    strokeWeight(1);
    translate(-scaledSize / 1200, scaledSize / 80);

    rotate(PI / 3);
    line(0, 0, 0, scaledSize / 120);
    pop();

    push();
    stroke(this.mouthColor);
    strokeWeight(1);
    translate(scaledSize / 1200, scaledSize / 80);

    rotate(-PI / 3);
    line(0, 0, 0, scaledSize / 120);
    pop();

    pop();
  }
}

To add the blinking animation, I created open and closed states for the eyes.  I implemented the state changing using the following code:

 
//blink animation
if(eyeOpen)
  {
    if(frameCount%150== 0)
      eyeOpen = !eyeOpen;
  }
else{
  blinkFrames++;
  if(blinkFrames%11 == 0)
    {
      eyeOpen = !eyeOpen;
    }
  
}

The time between each blink is calculated using the frameCount variable. I want the eye to blink every 2.5 seconds, and since there are 60 frames per second, every time 150 frames are drawn 2.5 seconds elapse.

I also use a blinkFrames variable because I want to count the number of frames that pass while the eyes are closed. This way I can keep the blink time constant. Since I don’t want the time between blinks to be exactly the same, I use frameCount instead. The reason this inconsistency occurs is because frameCount still counts ahead while the eye state is set to be closed.

 

Approach and Reflection

After an houof experimenting, I came up with the following face shape using the rect, arc, triangle, createShape, and curveVertex functions:

One particular tool that I used extensively throughout this project was Microsoft Powertoys , which comes with a Color Picker tool. It helped me figure out the color scheme that I wanted to go with, e.g., when picking the color for the shadow under my face.

 

I struggled with creating the hair, as I wanted to create curls. After no luck with that, I changed the plan to feature me with short hair. Using triangles and arcs, I created my hairline:

The line in the picture above is a reference line I drew, as in the beginning I translated the canvas to the middle. However, it was hard to keep track of coordinated as printing the mouse coordinated did not work after translating the canvas using the translate function. Removing the translation was tedious after making so much progress, so I continued working like this. This is something I would choose not to do next time.

After finishing the hair, I quickly gave my portrait a body and then created the cat class. I worked in a separate file and added the class to the main project once I was satisfied.

I also wanted to implement a way to control the scale of the picture, so I added a factor to every coordinate and length that is controlled by the scaleFactor variable. The idea was to keep size of the portrait fixed relative to the canvas size, and to allow me to zoom in and out of the portrait. However, this implementation was not perfect, and I suspect the translation in the beginning to be causing some minor problems.

In retrospect, it would have been beneficial to plan out the project more thoroughly. I did not have a clear idea in the beginning of what I wanted, and added features as I went along. Still, I enjoyed the process and am happy with the end result!

 

Resources

 

 

Week 1 – Self – Portrait

Concept

For my personal portrait, I attempted to neglect facial details and focus on skeleton shapes. Therefore, I choose to create a simple Wooden Manikin Model to present myself with some primitive shapes. The whole image is about the model sitting on a beach watching the ocean and the sky while enjoying a cup of hot coffee. It is (or I am) having a wonder vacation!

I decided to use a manikin model because I want my self-portrait to focus less on my face and more on a lifestyle of mine (or at least a desired lifestyle of mine). Therefore, I decided to zoom out and give more space to the environment. In this portrait, the clouds are moving and the coffee is giving out heat. But try to drink the coffee? The coffee cup will move away! This adds a bit more fun to my vacation on the beach!

Highlight of my code

I am particularly proud of the coffee cup and its tray and its interaction. I used mouseX and mouseY to see if the mouse is on the coffee cup. If it is, the cup and the tray will move a bit to the left; if not, it will move back to the start position. Here’s the code:

let tray = 10;
// draw the coffee tray
stroke(255);

if (246 < mouseX && mouseX < 266 && 400 < mouseY && mouseY < 414) {
  beginShape();
  curveVertex(220, 414);
  curveVertex(220 - tray, 414);
  curveVertex(228 - tray, 418);
  curveVertex(247 - tray, 418);
  curveVertex(257 - tray, 418);
  curveVertex(265 - tray, 414);
  curveVertex(265 - tray, 414);
  endShape();
  stroke(0);

  stroke(255);
  rect(224, 400, 20, 14);

  heat(230);
  heat(240);
  heat(250);
} else {
  beginShape();
  curveVertex(233, 414);
  curveVertex(233, 414);
  curveVertex(241, 418);
  curveVertex(260, 418);
  curveVertex(270, 418);
  curveVertex(278, 414);
  curveVertex(278, 414);
  endShape();
  stroke(0);

  stroke(255);
  rect(246, 400, 20, 14);

  heat(250);
  heat(260);
  heat(270);
}

In the meantime, I created a function called heat() to draw the heat. It simplifies my code to some extent.

function heat(xcoord) {
  noFill();
  beginShape();
  curveVertex(xcoord, 370);
  curveVertex(xcoord, 370);
  curveVertex(xcoord - 5, 380);
  curveVertex(xcoord, 390);
  curveVertex(xcoord - 5, 395);
  curveVertex(xcoord - 5, 395);
  endShape();
}

Reflections and future considerations

I could better improve my work by adding more details to the background, such as waves, mountains, shells, etc, to better create an atmosphere of a vacation by the ocean. This could potentially strengthen the visual appeal of my work.

In terms of interactivity, I tried to make the arm of the model to move and to always look at the mouse. I tried different ways and functions such as atan2() and rotate(), but none of them worked. I look forward to learning more about how to do it.

Here’s the link to the full version of my code: https://github.com/Yupu-Chen/Intro-to-IM-Fall-2023/blob/main/selfportrait.js

Resources: p5.js reference page