Week 4 Reading Assignment

The same technology that simplifies life by providing more functions in each device also complicates life by making the device harder to learn, harder to use. This is the paradox of technology and the challenge for the designer.

I really enjoyed this reading and it’s especially nice to read such a thorough and well-articulated explanation for something I’ve experienced in everyday life without having the words to describe them. This chapter immediately reminded me of this hilarious reel I saw the other day. While this isn’t my original video, I’ve had experiences with fancy washrooms being so confusing with their water faucets and sometimes even the flush.

My favorite thing about this reading is that it’s so easy to connect to real-life situations. The concept of Norman’s door is such a strong symbolism for the distinction between bad and good designs. What’s interesting is that in many cases, the solution to the problem is very simple: a handle for pull doors, and a flat plate for push doors. As we can derive from this chapter, though, bad designs either 1) does not consider functionality at all or 2) overconcern aesthetic that it takes priority over functionality.

My problems with doors have become so well known that confusing doors are often called “Norman doors.”

Although many of the examples from the chapter are of physical things, I find it super helpful to apply this principle of design on interactive media projects. It makes me think about when we design applications, websites, or games, when can we rely on affordances and signifiers, and when do we truly need written instructions?

Whenever you see hand-lettered signs pasted on doors, switches, or products trying to explain how to work them, what to do and what not to do, you are also looking at poor design.

It makes sense to me that visual cues should ideally guide the users, but I also wonder where exactly we should draw the line between written instructions and symbols and affordances. At the end of the day, the designers of the “bad” designs mentioned in this reading probably believed that their design made sense when they first designed them. I personally think that we shouldn’t completely abandon written instructions, because sometimes they are the only/most effective way to communicate something clearly! In my future IM projects, I hope to be able to step out of my perspective and imagine being a user interacting with my creation for the first time. Although this is easier said than done, this would be a really helpful step to refine my designs.

Week 4 Assignment: Flights and Airports Visualization

My Sketch

Important Note: The dataset is HUGE and sometimes it takes a couple of seconds for the sketch to load. Please do not SPAM the checkboxes, and kindly wait 1 to 2 seconds before clicking/unclicking a checkbox.

My concept

I was inspired by the demo of world population we did in class and I really wanted to try something similar yet still distinct. The moment I thought of flight pathways, I knew I had to do this. I love traveling and flying and I knew that visualizing all the flight pathways could create the outline of the world map, and create a neural-network-looking artwork.

One of the difficulties I had was searching for a comprehensive dataset. I simply wanted the coordinates of the starting point and coordinates of the ending point for every flight, but most datasets I found organized the data a bit differently. I asked ChatGPT to help me find a dataset that has the structure I was looking for, and it suggested me this dataset on DataBazaar, which I end up did using.

what my sketch looks like with both checkboxes checked!

My highlight code

The highlight of my code is my checkboxes and how they function. There are 2 checkboxes, one to show the airports (as dots) and one to show the flight routes (lines). You can check none, one, or both. The checkboxes are in the setup() function, and I essentially want the map to change depending on which box(es) are checked.

check_routes = createCheckbox("SHOW ROUTES", true);
check_routes.changed(() => {
    loop();
  });

Normally, I could have if check_routes.checked() in the draw() function to indicate what I want to happen based on the status of the boxes. However, I have a noLoop() at the end of the draw(), otherwise it would be too computationally expensive to have dots and lines being mapped out and drawn 60 times every second. But because I had noLoop(), the draw() function only draws once and stops, which means that checking the checkboxes does not have any effects. This was also a major bug in my code, so I had to ask ChatGPT for help and it taught me about the changed() function! So, when the checkboxes are changed (aka clicked/unclicked), the sketch draw() will resume in looping. It has a noLoop at the end though, so technically it only loop once.

How this was made

My dataset has 4 columns: the longitude and latitude for the start, and the longitude and latitude for the end (destination). I extract each column into their respective arrays: start_lng, start_lat, dest_lng, dest_lat.

The next step was to find the maximum and minimum of the latitudes and longitude. Because I had 2 arrays for latitudes and 2 arrays for longitude, I had to repeat this process 3 times for each value. For example, in the code below, I find the maximum latitude value in start_lat and the maximum latitude value in dest_lat, then comparing them to get the actual maximum latitude. This process is then repeat for min_lat, max_lng, min_lng.

let max_start_lat = max(start_lat);
let max_dest_lat = max(dest_lat);
let max_lat = max(max_dest_lat, max_start_lat);

Then, with these values, I can draw the flight lines and airport endpoints using the map() function. I loop from 0 to the numRoutes (the number of rows = the number of flight paths in the dataset) and in every iteration, I extract the coordinates into x1, x2, y1, y2. These x and y variables are then used to draw a line that resembles one flight route.

if (check_routes.checked()) {
    //loop every entry in numRoutes
    for (let i = 0; i < numRoutes; i++) {
      //save the coordinates in a variable and print the line connecting the points
      let x1 = map(start_lng[i], min_lng, max_lng, 0, width);
      let y1 = map(start_lat[i], min_lat, max_lat, height, 0);
      let x2 = map(dest_lng[i], min_lng, max_lng, 0, width);
      let y2 = map(dest_lat[i], min_lat, max_lat, height, 0);
      line(x1, y1, x2, y2);
    }
  }

The block of code to draw airports is also very similar; instead of drawing the line, I draw 2 circles with radius of 2, whose centers uses (x1, y1) and (x2, y2). There was one bug, though. For some reason, the p5 editor is unable to handle the amount of iterations in the drawing of the circles. If the loop had i increment by 1 (i++), the p5 would only draw some of the circles. When I changed it to i+=10, I reduced the number of circles it has to draw by 1/10, and ironically it displays more circles than when I had i++. I decided to keep it at i+=10, and I think this bug is most likely because my dataset has ~66K rows, which is a lot to handle.

when I had “i+=10”
when I had “i++”

Reflection

I think it looks incredibly cool, and I personally like how abstract the sketch is with just the routes mapped. I was a bit worried that my assignment would be quite similar to what we did in class initially, so I incorporated the checkboxes as a way to 1) challenge myself and take the visualization a step further, 2) encourage meaningful interactivity, and 3) create an opportunity to learn about new things. Compared to the demo population example you have us in class, drawing the flight pathways is also quite different. Most importantly, I learned new things in the process of this assignment, which is always a win :))

If I had more time, I would love to have a search bar where the user types in the code of the departure and arrival airport (e.g.: DXB, JFK, LAX), and the sketch would make that route a different color if a direct flight is available. I think that this is feasible, since the original dataset does come with airport codes. It would be quite complex though, since it involves searching, I think.

Resources I Used

Original Dataset: https://www.databazaar.io/datasets/71f62319-b8c7-42f1-be03-c7eb69308335
My edited dataset (via Google Sheets): https://docs.google.com/spreadsheets/d/187gIhSrk3LXHEgqG_BxJ3_Gz9WVtgrXmvUJ0kJV333s/edit?gid=2055795158#gid=2055795158
https://p5js.org/reference/p5/createCheckbox/

How Artificial Intelligence was used:
  • to find my original dataset
  • suggested to me the changed() function which I used for the checkboxes in setup()

Artificial Intelligence was not used in any other part of this assignment.

Week 3 Reading Assignment

I feel like I have used the term “interactive” so many times to describe artworks but I’ve never really sat down and defined what it means to me. The author identified the same issue, and I really like how they view it as a “conversation” — a “cyclic process in which two actors alternately listen, think, and speak.”

This reading also allows me to reflect critically about the difference between interactivity, reactions, participation, etc. I think I have used them interchangeably before, and looking at the author’s classification, my two biggest questions are:
1). Who decides what category something belongs to? is it perhaps the creator, the person who experiences it, or someone else?
2) Can something fall into 2 categories at the same time?

I find this distinction useful, but I also want to complicate a claim that Crawford made. The author strongly suggests that printed books are not interactive, for no matter what the audience do or say or think, the content of the page does not change. Though this is true, I also think it exposes a gap in Crawford’s definition of interactivity. Crawford insists that the emotional engagement one experiences when reading a book is a reaction, not an interaction. Is this really true, I thought. In murder mystery books, for example, many authors leave clues and hints and expect their audience to try to solve the mystery along with the reading process. Isn’t this an interaction? Actively trying to solve the book like it’s a puzzle. Even at this exact moment, I’m having internal conversations about what Crawford wrote in this first chapter of “The Art of Interactive Design.” I’m contemplating, finding examples, finding contradicting details, trying to justify my thoughts—I think that encapsulating all of that in the term “reaction” will not do it justice. Maybe, it’s some degree of interaction, I suppose.

We tend to think of interactivity as a Boolean property…but why not think of interactivity as a continuous variable with relative measures…we might speak of interactivity as high, moderate, low, or even zero, thus solving our problem with the subjective nature of interactivity.

– Chris Crawford, page 6

On to the guiding question of how I will create a strong interactive system in my p5 sketches. I think this is a big question overall, but at the moment I’m thinking of meaningful interactions. For example, instead of mouse-pressed or key-clicked interactions, perhaps I can have more complex ones that make sense to my project. Also, decision-making can be a really fancy way of interaction. For example, in the in-class example we had with the floating balls appearing as we click on the mouse, the new ball appears at mouseX, mouseY instead of at a pre-defined coordinate. Then, the user can choose where to add a new ball. I also think that a strong interactive system makes it clear to the audience that this artwork is interactive. This can be done through explicit instructions or strong visual cues.

Week 3 Assignment: vy’s garden using OOP

My Sketch

Please view the full sketch here.

My concept

I really love how much freedom we have for this assignment! I came up with an idea to create a garden of flowers, called “vy’s garden.” I wasn’t inspired by any particular sources but I’ve always wanted to create a game of planting flowers =)

My idea was to have the user click on their mouse to plant a new flower, which would have random characteristics. This way, every click comes with curiosity and surprise. When all the flowers in the garden are unique and random, each final garden feels personalized and special.

My highlight code

My canvas size is 700x400, and my garden can hold up to 7 flowers (the number of flowers it can hold is 1/100 of the width of canvas). After the 7th flower, if the user continues to click their mouse, all of the current flowers will be removed and they can plant up to 7 new flowers.

// if garden.length > 7
  if (garden.length > width/100) {
    //loops until garden is empty
    while (garden.length > 0) {
      garden.pop();
    }
    x = 100;
  }

In my mousePressed() function, after creating a new flower and adding it to the array, I have an if-statement checking the size of the garden. If it is larger than (width/100) (in this case, 7), then remove all the flowers in the array garden by using pop() until the garden is empty. After that, I reset x to 100, this is the starting x-coordinate of the first flower.

How this was made

I created a class Flower, which serves as the template for my flowers! The flower objects are stored in the garden array.

one of the first variations in the process
each flower is a combination of 5 circles, a line, and sometimes, a triangle

Each flower can be one of two species (tall/short), have a random petal color, a random center circle color, and a random number of leaves. The details are as follow:

  • 2 possible species: short & petals creating an “X” shape OR tall & petals creating a “+” shape.
  • 4 possible petal colors
  • 4 possible flower center colors
    let petals = ["#FF6B6C", "#f1b709", "#FC814A", "#9ecb15"];
    let flower_center = ["#5f247b", "#095314", "#AD1F2D", "#202C59"];
  • 3 possible variation of leaves: no leaves, left leaf, right leaf

There are 96 possible variations of a flower (2×4×4×3). For a garden of 7 plots, where flowers can be repeated, and the order of the 7 plots do matter, there are 967 possible unique gardens. That is equal to 75,144,747,810,816 aka ~75 trillion variations!!
To put 75 trillion into perspective, it is:
~2.5 times the amount of cells in the human body
~38 times the number of galaxies in the universe
~642 times the number of humans ever been born in the human history

To create the randomization, when I instantiate a new flower, its arguments are randomized. For example, int(random(2)) gives 0 or 1, which will indicate the species. Similarly, petals[int(random(0, 4))] returns the string hexcode at a random integer index of the array petals.

//constructor(species, x, petal, center, leaves)
  /*
  2 possible species: 0 for the tall one and 1 for the short one
  x: x-coordinate of the flower
  petal: a random color from the array petal, accessed by indexing at a random integer index
  center: a random color from the flower_center array
  leaves: 0 = no leaves, 1 = right leaf, 2 = left leaf
  */
let newFlower = new Flower(
    int(random(2)), //random(2) is exclusive of 2
    x,
    petals[int(random(0, 4))],
    flower_center[int(random(0, 4))],
    int(random(0, 3))
  );

Each new flower is then pushed into the garden array and the entire garden is being drawn in the draw() function.

Reflection

I’m super proud of this artwork since I’ve wanted to create a digital garden of my own for sooooo long! I love how interactive it is, and I’m really amused at how many possible variations of flowers and gardens there are!

I’m also incredibly happy that this simple assignment involves many of the concepts we learned in class. I used for loops, while loops, conditionals, the random function, multiple arrays (garden, petals, flower_center), and most importantly, objects and classes. If I had more time, perhaps I would also want to give some movement to the flowers when they appear rather than just abruptly appearing like it is right now. For example, I could have it “rise” from the ground by controlling with speedy. But overall, I’m super happy with what I came up with already!

Resources I Used

https://p5js.org/reference/p5/text/
https://p5js.org/reference/p5/textStyle/
https://p5js.org/reference/p5/textFont/

No Artificial Intelligence was used in the making of this sketch or its documentation.

Week 2 Reading Assignment

Original video: https://vimeo.com/45851523
Quotes and ideas referenced from the video comes with a timestamp in purple: (MM:SS)

When I first started making things in code…every line of code was precisely determined by myself and it produced something exactly as I wanted it to be.
– Casey Reas, (0:22)

I connect to this so incredibly well and in fact, it’s quite visible in my Week 2 assignment (hehe). I feel like randomness and chaos often comes with the feeling of “uncontrollable mess” which is intimidating. However, after watching this video, I have the impression that with art, perhaps randomness can produce what is beyond (and even better than) my imagination.

Piet Mondrian. Broadway Boogie Woogie. 1942–43

One of the most eye-opening and interesting facts I learned from the video is on Piet Mondrian’s grid paintings. I’ve come across these so many times, and they look like one of those “anyone can do that” paintings at first. I, personally, felt emotionless every time I saw them, so I was surprised when Reas said that these paintings contain brush strokes of “emotional, human-quality (21:54).”  I learnt that Mondrian was indirectly inspired by boogie-woogie music, a repetitive, persistent, and repeating bass on the piano.

texture of Mondrian’s painting; screenshot from video

Yet, if looked closely in person, one can notice the visible and thick brush strokes, and the wobble at the edges of the line (21:10). When I learned that Mondrian did not use masking tape for clean edges and wanted to, instead, retain the human texture, my view of his grid paintings completely changed. 

Listening to the lecture also reminds me of something I was told as a kid: that real artists don’t use erasers, rulers, or drawing compasses. I never really understood why, but as a child, I thought that it’s because professionals don’t make drawing “mistakes.” After this video lecture, I’m reminded that art is more about embracing humanness, the imperfections, the nuanced and delicate strokes.

Above all, it’s never a blind chance; it’s a chance that is always planned but also always surprising.
– Gerhard Richte (18:30)

On to the guiding question of “where is the optimum balance between total randomness and complete control,” I think it’s difficult and arguably impossible to come up with the “optimum balance,” since it varies by artists and what they’re comfortable with. However, with me personally, I think incorporating random elements would certainly add some “spice” to my work, as it provokes curiosity and excitement. Some of the ideas I came up with ironically involve controlling the randomness:

  • Random combinations of colors from a list/array of colors that are on-themed.
  • Shapes/elements appearing/moving at random coordinates/speeds within the canvas boundaries
  • Random number of shapes/elements but there is a lower limit and upper limit.

This way, I can have some “randomness” within my sketch without making them total chaos.

Resources I Used

[photo] https://www.moma.org/collection/works/78682

Week 2 Assignment: Artworks, patterns, and loops

My Sketch

My concept

When I first heard that the artwork can use loops to create a pattern, my mind jumps straight to the iconic terrazzo tiles that are used on the sidewalks of the streets in Hanoi, my home city. These tiles come in different patterns, and every Vietnamese, including myself, strangely finds nostalgia in these tiles because it is what we grew up with. Vietnam’s terrazzo tiles have a much deeper history than I could ever imagined: In the early 1990s, a project was set out to get rid of the bumpy concrete pavements. Inspired by the European, particularly Venice’s floral tiles, tile designers and suppliers decided to simplify them into designs of geometrical shapes. There are so many designs, each iconic in their own way, and I decided that for this assignment I wanted to draw one of these patterns on p5.

there are many terrazo tiles designs!
4 tiles together

 

 

 

My highlight code

for (let col = 0; col < numOfTiles; col++) {
    for (let row = 0; row < numOfTiles; row++) {

      push();
      if ((col%2==1 && row%2==0)||(col%2==0 && row%2==1)) {
        translate((col+1)*tileSize, row*tileSize);
        scale(-1, 1);
      } else {
        translate(col * tileSize, row * tileSize);
      }
      drawTile(numOfTiles);
      pop();
    }
  }

I draw each individual tile in the function drawTile() then in the main draw() function, I call drawTile() for a total of 16 times to create the 4×4 grid as shown above. I was especially proud of this section of code because instead of having to manually call drawTile() 16 times, I used a nested for loop to optimize the process! Within each iteration, I use a conditional statement to check whether I should flip the tile (basically creating a mirrored version of it) based on its position in the grid. The next section, “How this was made,” goes into detail of the algorithm and why I used certain functions. It also links to the tutorials and reference pages I used.

How this was made

This assignment was way harder than I had initially expected! I first explored on a default 400×400 canvas, where I created one single tile. I have also embed the sketch for it here, as this was a crucial step in my process. I tried to be as accurate as possible, while also challenging myself to use the different p5 primitives. While I could have just drawn the indented lines, I decided to challenge my approach and draw the shapes and blocks instead. I primarily used squares and arcs to create the geometric shapes.

Sketch for 1 single tile (hardcoded on 400×400 canvas):

One of the things I was being super cautious about was with the numbers and math. Symmetry is an important feature, so I had to make actual calculations with the coordinates to ensure everything aligns. I used nested for loops to create the small squares in the top-left and bottom-right, and also gave them some border-radius (this is different from the actual tiles).

the blue part is the stroke; the green part is the fill. (I put green for demonstration but in the actual sketch I set it to noFill())

Then, for the curves in the top-right and bottom-left, I used arcs that are one quarter, make them transparent in the middle (noFill) and gave them a big stroke to create the curves.
For the curved triangle in the corners, they are actually squares, but an arc sits on top of each square, basically hiding half of it and giving it a curve hypotenuse. Overall, since the single tile is mirrored over y=-x, it was not difficult, I just had to be careful with the placement and order of the primitives.

the red arcs sit on top of the top-right and left-bottom squares. later I set it to match the color of the background, hence “covering” half of the square

However, the real challenge really came when I wanted to put multiple tiles together to make a pattern. When I was drawing the single tile, I was hard-coding the coordinates for a 400×400 canvas. Therefore, I won’t be able to resize or place them next to each other easily. Then, I had to create a duplicate file of my single tile, and manually change the values to be responsive to the width and height of the canvas (it was a tedious process but the code wasn’t extremely long because I had used for loops to avoid redundancy). You can find the sketch for the responsive tile here. The hard-coded and responsive tile looks exactly the same on a 400×400 canvas, but they are massively different in the numbers and variables I used to draw them.

Once I have made the single tile responsive to the canvas dimensions, the next thing on the checklist is to put several of them together to create a pattern. I put all the primitives in a function drawTile(), then in draw(), I call drawTile() when I need to draw a tile. Notice that the pattern is created with the tiles and its flipped version (symmetry at y=-x and y=x). In order to create the flipped variations, I used scale(-1,1) to flip the tile over the y-axis. I watched The Coding Train’s “How to use scale() in p5.js” tutorial and also referenced the corresponding p5 reference. I also notice a pattern with the tiles that are to be flipped. If we do a 4×4 grid of the tiles, and have columns and rows numbered from 0-3, the flipped tiles would either:
– have even-numbered row + odd-numbered column; or
– have odd-numbered row + even-numbered column
I used an if statement here, evaluating odd/evenness using modulus 2. If it is one of the 2 scenarios above, I call scale(-1,1) before I call drawTile().

In order to place the tiles at the correct spots on the grid, I used the translate() function which allows me to set a new origin of (x,y) instead of the default (0,0). I watched this tutorial and looked at the p5 reference page for this function. One thing I noticed in the Youtube tutorial is that you have to use push() and pop() before and after translate(). While I haven’t fully understood how these 2 functions work, it appears that they sort of “resets” the origin to (0,0) after every tile is drawn, otherwise the next time I call translate(), it would be relative to the (x,y) arguments I last passed. I used nested for loops to create the 4×4 grid, then calls drawTile() after translating the origin.

Reflection

This tile is one of the more complicated designs but what made it especially so difficult is the diagonal symmetry. I took a lot more time than I had initially expected, but ultimately in the end, I think the pattern turned out really nice.

I basically had to do 3 steps: a tile on 400×400 canvas, making the coordinates relative & dynamic to the canvas dimensions, and printing the pattern. I feel like my current approach is definitely over-complicated and takes more time and effort than it needs to be. However, my experience with p5 is quite limited, so I tried my best with the knowledge and resources I have.

Also, if I had more time, I would want to play around with the colors. Maybe I can make the pattern changes color every couple seconds or when the end-user clicks on the canvas. I think that would add a very cool layer of interactivity to this otherwise static pattern.

Resources I Used

https://www.youtube.com/watch?v=pkHZTWOoTLM&t=56s
https://p5js.org/reference/p5/scale/
https://www.youtube.com/watch?v=maTfm84mLbo
https://p5js.org/reference/p5/translate/
[photo] https://topmatstore.vn/gach-terrazzo-p4501.html
[photo] https://gachngoihanoi.com/gach-via-he-terrazzo/gach-via-he-terrazzo-mat-nai-do-post239.html
all other screenshots and figures are my own

No Artificial Intelligence was used in the making of this sketch or its documentation.

Week 1 Assignment: Self-Portrait on p5.js

My Sketch

My concept

For this project, I wanted to depict a regular “headshot” of myself using different p5.js functions. My preference for aesthetic leans toward minimalism and simplicity, so I tried to make the portrait look “clean” and “slick” instead of super realistic. I chose a pink-green color scheme since one of my favorite drinks is strawberry matcha, and I want to incorporate parts of my personality into the portrait.

Ultimately, my goal for this project was to explore a variety of features on p5.js, so I practiced with the different primitive shapes we learned in class, as well as researched some other functions on my own.

My highlight code

As ironic as it may sound…my favorite part about my portrait is my background! Initially, I had a 1-color pink background, but it seemed too boring and I wanted some personality! I decided to create a checkered background with 2 different shades of pink. In order to create this, I worked with nested for loops, running variables i and j until the canvas width/height and having them as the (x,y) coordinates for the colored squares. I had previous coding experience with python and java, so I only really had to refer to p5 reference for the correct syntax.

//print checkered background
  //loop 1 -- prints odd numbered rows
  for (let i =0; i<width; i += 100 ) {
    //ROWS--loop until width of canvas, increment by 100 each time to "skip" a block as it goes from left to right
    for (let j =0; j<height; j += 100 ) {
    //COLUMNS--loop until height of canvas, increment by 100 each time to "skip" a block as it goes from top to bottom
      fill("#ffb8bb")
      noStroke()
      //the top x,y coordinate is the i & j that increments
      square(i,j,50);
    }
  }
  //second loop-print even numbered rows
  /* similar logic to previous loop except this time i start i from 50 to create the "checkered" effect*/
  for (let i =50; i<width; i += 100 ) {
    for (let j =50; j<height; j += 100 ) {
      fill("#ffb8bb")
      noStroke()
      square(i,j,50);
    }
  }
the bean-shape mouth!

I’m also really proud of how I made the mouth. I knew I wanted a blob for an open-mouth instead of a smiley line because it looks cuter (and also less creepy). However, a blob isn’t a primitive shape and therefore I couldn’t figure out how to create the mouth the way I had envisioned. Then, it suddenly clicked that I can totally make it a super small arc with a huge stroke (I felt like a genius), which is exactly what I did to achieve the darker red mouth.

//mouth -- blob is created using an small arc with huge stroke
  stroke("#a20b0b")
  strokeWeight(25)
  noFill()
  arc(200,240,50,50,(PI/3),(2*PI/3));

How this sketch was made

the bangs (transparent green)

I started by planning out what would be the bottom-most layer (excluding the background), which would be the hair. The hair is simply 2 overlapping ellipses, which creates the middle hair part. Then, my head looked like an egg so I had to put some bangs. I did this by having 2 lines with a huge stroke, covering the top diagonally. The photo aside illustrates this, with the bangs being a transparent green for demonstration.

The eyes are just many circles placed on top of one another, while the eyelid is a half-circle arc (no fill) that sits behind the white of the eye. Because most of the features on this portrait (not my face :)) are symmetrical, it was quite easy to duplicate something twice. The nose is, also, just a small arc positioned in the center of the face.

This was what I wanted to do with the quadrilateral

The neck has two parts to it: a rectangle and the collar part, which is the bottom half of a circle. In between the neck and the collar is a green shirt, which I created by using a rectangle. I was really contemplating on whether to use a rectangle or a quadrilateral, since with a quadrilateral, I can make the slant of the arms, which I prefer a lot more. However, I then realized that the corners of these shapes are too sharp, which makes the body boxy and therefore less coherent. This could be fixed with border-radius, which allows for rounded corners, but there were only available as a parameter for rectangles, which I decided to stick with at the end.

function wink() {
  //first I cover the original eye using an ellipse the blends with the face
  fill("#d9a582");
  noStroke();
  ellipse(230,195,60,60);
  stroke("#341504")
  strokeWeight(3)
  noFill();
  //then the 2 lines
  line(245,175,212,191);
  line(245,210,212,191);

  //after wink() runs for 500 milliseconds, openEye() runs for the rest of the setInterval which would be 2.5 secs in this case
  setTimeout(openEye, 500);
}

I created a function wink() to also play around with animation. This function draws a large ellipse on top of my right eye in the same color as my face, which covers it. Then, 2 lines created the “<” which indicates a wink. Then, I put setInterval(wink, 3000) in my setup function so that it winks after 3 seconds. But then, I stumbled upon a bug as the person wasn’t winking when I ran the code at all! I then realized that the draw function, which currently contains all of my code, immediately redraws the face on top of my winked eye. Since the portrait was completely static at this point, I moved all of my drawing to setup(). This solves the issue, but then my person stays winked. I tried solving this issue by having another function that draws the original eye, called openEye() and add setTimeout(openEye, 500) to my wink(). What this does, essentially, is have the person wink for 500 milliseconds and then the normal eye appears for 2.5 seconds, and then another interval happens where it winks again. I’m not sure if this is the most optimized way to create this animation, but I had fun playing around with the facial expressions for sure!

Reflection

Overall, I’m really happy with how my portrait turned out! I think I have, in general, achieved the “clean” look I was aiming for. I also love how colorful and vibrant the sketch is, I feel like it captures my energy really well. One thing I feel like I could improve if I had more time is to make it more like me! This would be a bit of a challenge since I don’t like anything that is too detailed, but I’d like to put myself through those challenges some time! I could try having little blushes/freckles, or a different accessory (since I never wear bows).

Also, a mistake I made in the beginning was not determining the appropriate canvas size. I kept it at the default size, 400×400. I only realized after sketching the head and the hair that this wouldn’t leave me enough space for the body, so I had to change the height to 500 pixels. In future projects, I would like to avoid this mistake as I could have to manually shift everything up/down/left/right by a certain value (which is kind of a hideous thing to do :)).

Resources I Used

https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout
https://www.w3schools.com/jsref/met_win_setinterval.asp
https://p5js.org/reference/p5/for
https://p5js.org/reference/p5/arc/
https://coolors.co/f58484-ffb8bb-d9a582-12631c-68d34a

No Artificial Intelligence was used in the making of this sketch or its documentation.