Assignment 1: Self Portrait

When I received this assignment, I did not give it much thought initially. However, I had a concept of adding my favorite, which I believe would reflect myself and create a self-portrait of a girl. Initially, I was uncertain about where to begin this illustration.

From the concepts learned from the class, I knew how to develop shapes but I could not think about a complete self portrait. I started by coding the background and slowly each parts of the body. One of the most engaging aspects of the process was determining the coordinates for each body part. It felt like solving a puzzle.

One of the highlights of my code was the decision to use ellipses for the body and hair instead of rectangles which turned out to be a successful choice.

Looking ahead, I am eager to explore more interactive projects with meaning backgrounds. This assignment has provided me with a strong foundation and inspiration for my future works.

Self-Portrait: On the Run

Project Concept

My concept for this project is to portray a guy (supposedly) running on the road. Then I started to think about the elements I could add because just a static picture is boring. I figured the two elements I could improve are the background and the figure. I decided to add some animation to the figure and some interactivity with the user’s mouse so that it would chase the user’s mouse along the X-axis. For the background at first, I only wanted to implement a sky featuring a sunrise, that is to use a gradient color for the background. But it still seemed boring so I decided to add a moon and alternate them over time. Naturally, that comes with stars and different sky colors so I implemented that also. I already have the cycle for a day, why not extend it to a month? Therefore I also added phases of the moon. Then the project finally seemed complete, and it is shown below.

Code I am Proud of

I am not very proud of anything in particular actually, because it is all simple code, and not many calculations were needed. If I have to mention something then it would be the implementation of the Sky. It involved calculations based on deltaTime,  relative positions of the sun and the moon, integrated loops with the looping nature of the draw() function to ensure the coherency of sky color changes, and used a wide variety of shapes and curves. The code goes on like this, and the comments should be enough to explain the code.

class Star{
  constructor(){
    // Random position of stars
    this.x = random(400);
    this.y = random(400);
    this.speed = 1;
  }
  
  display(t){
    this.y -= this.speed * deltaTime / 20; // Star movement
    if(this.y <= 0){
      this.y = 400 - this.y % 400;
    }
    // Transparency control
    fill("rgba(255,255,255,"+t+")");
    stroke("rgba(255,255,255,"+t+")");
    let starBody = rect(this.x,this.y,2,2);
  }
}

class CrescentMoon{
  constructor(y){
    this.phase = 0;
    this.moonY = y;
  }
  
  display(){
    this.phase = this.phase % 16;
    fill("white");
    
    // Moon shape
    beginShape();
    vertex(200,this.moonY-75);
    if(this.phase <= 9){
      bezierVertex(200-(100-25*(this.phase)),this.moonY-75,200-(100-25*(this.phase)),this.moonY+75,200,this.moonY+75);
      bezierVertex(200+100,this.moonY+75,200+100,this.moonY-75,200,this.moonY-75);
    }
    else{
      bezierVertex(200-100,this.moonY-75,200-100,this.moonY+75,200,this.moonY+75);
      bezierVertex(200+(100-25*(16-this.phase)),this.moonY+75,200+(100-25*(16-this.phase)),this.moonY-75,200,this.moonY-75);
    }
    endShape();
  }
}

class Sky{
  constructor(){
    this.sunY = 400;
    this.speed = 1;
    this.moonPhase = 0;
    this.timer = 0;
  }
  
  display(){//10sec/round
    // Timer sync
    this.timer = this.timer % 20;
    this.sunY = -this.timer * 50 + 400;
    
    // Moon phase add
    if(this.sunY + 500 <= -125){
      this.moonPhase++;
    }
    
    // Create colors for gradient
    let topColor;
    let botColor;
    
    // Transparency control
    let displayVar;
    
    // Frames 0-300 back to sunrise
    if(this.timer < 3){
      topColor = lerpColor(color("rgb(23,23,197)"),color("rgb(135,206,235)"),this.timer/3);
      botColor = lerpColor(color("rgb(23,23,197)"),color("rgb(255,77,00)"),this.timer/3);
      displayVar = 1-(this.timer/3);
    }    
    // Frames 300-1000
    else if(this.timer >= 3 && this.timer < 10){
      // Frames 300-650 sunrise
      if(this.timer<6.5){
        botColor=lerpColor(color("rgb(255,77,00)"),color("rgb(135,206,235)"),(this.timer-3)/3.5);
      }
      // Frames 650-1000 day
      else{
        botColor = color("rgb(135,206,235)");
      }
      topColor = color("rgb(135,206,235)");
      displayVar = 0; // Transparent
    }
    // Frames 1000-1300 dawn
    else if(this.timer >= 10 && this.timer < 13){
      topColor = lerpColor(color("rgb(135,206,235)"),color("rgb(23,23,197)"),(this.timer-10)/3);
      botColor = lerpColor(color("rgb(135,206,235)"),color("rgb(23,23,197)"),(this.timer-10)/3);
      displayVar = (this.timer-10)/3;
    }
    // Frames 1300-2000 night
    else if(this.timer >= 13 && this.timer < 20){
      topColor = color("rgb(23,23,197)");
      botColor = color("rgb(23,23,197)");
      displayVar = 1; // Solid
    }
    
    // For loop used to create gradient
    for(let y = 0; y < 250; y++){
      let lineColor = lerpColor(topColor,botColor,y/250);
      stroke(lineColor);
      line(0,y,400,y);
    }
    
    // Display stars
    for(let i = 0; i < 120; i++){
        starlist[i].display(displayVar);
    }
    
    // Display sun and moon
    noStroke();
    fill("#FFC107");
    ellipse(200,this.sunY,150,150);
    moon = new CrescentMoon(this.sunY + 500);    
    moon.phase = this.moonPhase;
    moon.display();  
  }
}

Possible Improvements

As I said, I am not particularly proud of the project. It has lots of room for improvement in many aspects. To begin with, the code itself is sloppy and unorganized. It is stitched with different coding styles and practices, and it is unoptimized in aspects such as (but not limited to) time and methodology. This can be improved with more advanced coding techniques and better planning in code structure. The figure’s animation is also unsatisfying. Because p5js doesn’t support rigging, therefore it was extremely difficult to animate the figure. It was impossible to accurately illustrate each of the keyframes of the figure running because the figure is fundamentally just a set of shapes combined together. Each approximate keyframe takes a huge amount of time to finish. It also doesn’t support interpolating the keyframes so the intended fluctuations in the Y-axis to make the character more lively turned out to seem mechanical and shaky. I could try to smoothly interpolate the keyframes using algorithms, but I don’t have enough mathematical knowledge to support me in achieving this. This can be improved by using careful calculations to accurately orient the keyframes and adding a lot more frames to the animation, then using linear interpolation to connect the keyframes together. However, this method will still feel unnatural because linear interpolation will result in mechanical movements with sharp turns. I tried a few third-party libraries but none could really achieve this. For instance, I tried using Toxiclibsjs’ modules to create a skeleton, but it focuses more on physics instead of animation, therefore it still cannot be used for creating a perfect animation, unless I can find mathematical equations that could represent the movements of every joint, which I tried to do but failed dramatically.

assignment 1: self portrait

I wanted to create something simple but with some interactivity, so I made a portrait of summer in Abu Dhabi. In the drawing I’m holding up a sign that says “click to make me go inside/outside”, and the user can click anywhere really, and the background toggles between an indoor and outdoor scene. The highlight here is my glasses: they fog up when I’m outside, which is a never ending nuisance, and they clear up again when I’m inside.

The full code is on the p5.js editor and can be seen here, but this is what the final result looks like:

I’m particularly proud of this line:

isInside ? "click to make me go outside" : "click to make me go inside",

I could’ve gone the if/else way, which I always do, but using the conditional operator today made me feel like I had achieved a feat in code optimization.

Certainly, a few small tweaks could make this drawing look much more polished. I wanted to draw some beads of sweat rolling down my face when I’m outdoors. But I would need to think a little more about randomization/motion, so for now I’ll leave this project as is.