Week2 – Reading Response

The video inspired me with my week 2 project. Similar to Casey’s examples, I wanted to create a piece that is randomly generated, a work where I don’t fully decide the outcome but instead design the conditions for it to emerge. What stood out to me is that randomness on its own often creates noise, but randomness framed by rules creates variation that still feels intentional. That’s why, in my own project, I didn’t let the lines fall anywhere on the canvas. If the verticals were completely random, sometimes they would overlap or sit too close together, which broke the balance I was trying to capture from Mondrian’s style. By enforcing rules, like keeping vertical lines at least 60 pixels apart and limiting where they could end, I was shaping the randomness so it produced results that still looked coherent.

This process made me think more about the balance between total control and total randomness. If I had forced every line into fixed positions, the piece would look the same every time and lose the surprise that makes generative art exciting. But if I gave up all control, the results would drift too far from what inspired me in the first place. The balance is somewhere in the middle: I act like the system builder, defining boundaries and constraints, while letting randomness fill in the details. For me, that tension is where the art lives. Creating a space where the computer surprises me, but always within a framework that reflects my intention.

Week 2 – Simple Art

I always liked Piet Mondrian’s art. Something about the lines and the geometrical shapes was satisfying. Although it is simple, I find his art, particularly his most famous work “Composition with Red, blue and yellow” to be one of my favourite art. I thought his art could be somewhat re-created with code so I decided to give it a shot.

 

Unlike its simplistic looks, creating this was much harder than I thought. At first, I just selected completely random vertical and horizontal lines. However, I quickly realized that in his work, the lines do not reach from one end to another. It sometimes fell short before reaching the total width or height of the canvas. Choosing random positions of the lines along with random lengths was not an easy process, so I decided to simple things out by controlling the lengths to a certain extent.

I made three rules. The first rule was to choose 2 verticals lines with spaces of at least 60 in between. If it wasnt for this, sometimes the vertical lines overlapped, or the distance between the lines fell so small that the outcome looked a bit ugly. The second rule was to controll the height of the vertical lines. The lines should start from 0, but the end point of the lines had to fall randomly between 60% of the height to the end. The last rule of the lines was to keep the distance between the horizontal lines to be atleast 80 pixels apart.

After setting these rules, I tried generating the lines. However, I was soon met with another problem. The horizontal lines did not intersect perfectly with the vertical lines. So to prevent this, I just placed horizontal lines where the vertical lines ended. I also decided not to play around with the lengths of the horizontal lines to keep things simple.

//the key function that chooses the lines drawn on the canvas
  function pickRandomWithSpacing(minVal, maxVal, existing, spacing) {
    for (let t = 0; t < 300; t++) {
      //first it chooses a random candidate in selecting lines.
      //usually selects lines from 0 to either height or width, this is saved as candidate
      const candidate = random(minVal, maxVal);
      let ok = true;
      //now we assume that this is a valid space, and try to check if the distance between the previously selected line and e has enough space.
      for (const e of existing) {
        //gets the previously selected value,
        if (abs(candidate - e) < spacing) { //checks if the distance is valid
          //if not, break the loop
          ok = false; 
          break; 
        }
      }
      if (ok){ //if the distance is valid, we found the valid line.
        return candidate;
      } 
    }
    return null; //if we couldnt find it, give up finding to avoid beig stuck in loop
  }

 

The key part for this artwork was randomly choosing the lines in a sort of a controlled manner. If it was chosen completely randomly, it would not look like the orignal art so I had to make some working boundaries. This is the function called pickRandomWithSpacing. It takes 4 variables: minVal, maxVal, existing, spacing. Line needs two points. The starting point of vertical line is chosen from a ranges 0 to width and is saved in array called verticalX with space of 60. The end point of vertical line is chosen from ranges 60%of height to height and is saved in array called verticalEndYs with spacing of 80. The idea of trial and error is applied when finding the correct spacing. When we first choose the space, we assume that it follows the rules that I made. However, it checks if the spaces are correctly picked. If it does not follow the rules, it breaks the loop and chooses another random space which hopes to follow the rule. It ensures that it finds the correct working spacing by repeating the loop 300 times.

This is an example of how its called

//pick two valid vertical lines
  for (let i = 0; i < 2; i++) {
    //we use the function that we created. from 0 to width, saved in arraynamed verticalX with spacing 60.
    let x = pickRandomWithSpacing(0, width, verticalXs, 60);
    verticalXs.push(x);

    //yEnd is the end value for the vertical line, I wanted to give it a end to the length of it. The vertical lines start at 0 but is randomly chosen when to end. The minimum is 60% of the height, all the way untill height. These values are saved in array calkled verticalEndYs
    let yEnd = pickRandomWithSpacing(MIN_VERTICAL_LENGTH, height, verticalEndYs, MIN_Y_SPACING);
    verticalEndYs.push(yEnd);
    
    //this pushing is key in finding the cells to color. It saves the coordinates of the lines created in order to select the different sized cells
    verticalLines.push({ x1: x, y1: 0, x2: x, y2: yEnd });
  }

 

After the lines were correctly chosen, the next part was correctly identifying the cells created. Since I saved all the nessasary points of the array. I went through several trial and error sessions to identify the cells. I found that there were 9 cells when I applied the rules and focused on finding the cells generated with the random lines. This was much more complicated then I thought because the cell sizes of x and y are completely different and unique. I had a notebook with me that kept track of the coordinates. The key point about identidying cells was finding out with points to skip. When the vertical lines fall short, when identifying the cell that does not include that particular vertical line, it needs to skip it. I was stuck on this for a long time and received advice from openAI on the logistics of it.

The rest of it was easy. I had to pick random colors from the color pallete that I chose. (The color pallete from the original work).

As you see here, I repeated null three times to increases the chances of white rectangles. This is because the original artwork contains more background than the colored ones.

Lastly, I drew the lines on top of the colored rectangles to create the final look.

For future improvements, I can apply random horizontal lengths so that not all horizontal lines reach from one end to another. Also I can also apply controlled randomization in choosing the colors instead of my complete randomization that I have now with colors. In the original artwork, when the color is chosen, the same color should not be next to each other. However, for simplicity I allowed this.

 

Week 1 – Self Portrait

My look-alike animal I chose to portray myself is an owl. It didn’t have to be an animal, but I chose it simply because I like cute animals. It was tough trying to pick between an owl and a raccoon, but I stuck with an owl because when I during my military service, my teammates called me “angry bird” because of my sharp-looking eyebrows. The fact that I was bold probably made it worse. I combined both “angry bird” and owl to create “angry owl”.

Simplistic, but straight to the point. I added 2 animations during my work on this project. Please feel free to find both of them.

I love working at night because it’s quiet and it’s the time of day when I feel most focused. So if you try to get the owl’s attention by clicking, it will lift its eyebrow as a warning. It’s a do-not-disturb signal.

//eyebrow only moves at night
 if (!morning && eyebrow) {
   move += direction * 1.2; //speed of animation

   if (move <= -20) { //moved distance is max 20, if greater, change direction to go down
     direction = 1;     
   }
   if (move >= 0) { //when it reaches its original position
     move = 0;
     eyebrow = false;   //stop animation
     direction = -1;    //reset direction
   }
 }

That is the animation code for the eyebrows. One of the hardest parts of this code was changing the direction of the movement. If the position changes by 20 pixels, then it changes the sign of the direction so that it goes in the opposite direction. It was also difficult to trigger the animation. I have two animations that are played in different conditions: night and day. So coordinating the timing was probably the most difficult part of coding. I will explain the coordinating part after showing the code for morning animation.

(Explanation of Code, Skip this if this is a bit boring)

move+= direction is for the speed of the animation. But the sign of the direction indicates whether it goes up or down. If the animation moves for 20 pixels upwards, it satisfies the first condition, and the sign is changed. It was set to -1 before. So now, the eyebrow moves downward back to its original position. Since the sign has flopped, move slowly turns positive as it goes through the move+=direction line. At some point, it will reach 0, where the variable named eyebrow, which is set to true when the user clicks on the owl, turns false, and the direction changes to positive.

As you could guess, if you click on the moon, it changes the time of day. Also, if you click on the owl during the day, it will show the ZZZ animation.

function mousePressed() {
  //if position of sun/moon is pressed
  if (dist(mouseX, mouseY, X, Y) <= D) {
    //change day/night
    morning = !morning;
    //stop eyebrow
    eyebrow = false; move = 0; direction = -1;
    return;
  }

  // start Z animation in morning
  if (morning) {
    zActive = true;
    zStart = millis();
    return; // don't trigger eyebrows
  }

  //night means eyebrows haha
  if (!eyebrow){
    eyebrow = true;
  } 
}

I used two flags to trigger the animation: zActive and eyebrow. I set these values to true when the screen was clicked based on the time of day. I also had to carefully coordinate so that the flags were triggered where I wanted them to be.

(Explanation of Code)

When the mouse is pressed, it first checks if the user clicks on the area where the sun and moon are placed. If it is, then moring = !morning changes from day to night, vice versa. When the time of day changed, I made sure once again to reset variables just in case the animation plays. Now, if that was not the case, and the user clicks, it checks if it is morning or night. If it is morning, the zActive variable is set to true, and this sets off the ZZZ animation. If it is not morning, then it sets the eyebrow flag to true, which triggers the eyebrow animation. Figuring out this logic was probably the most difficult part of the code.

Finally, this is my ZZZ animation. 

/ZZZ animation
if (morning && zActive) { //only when morning and user clicked
  const elapsed = millis() - zStart; //measure time

  //number of Zs
  let visible = 0;
  if (elapsed >= 0){
    visible = 1;
  }
  if (elapsed >= 300){ //basically every 300ms, add one
    visible = 2;
  }
  if (elapsed >= 600){
    visible = 3;
  }

  ZZZ(width/2 + 70, height/2 - 120, visible);

  // after all Zs have appeared and held, hide all at once
  if (elapsed >= 3 * 300 + 600) { //600 is extra time just in case
    zActive = false; // disappears together
  }
}

I used millis(), which is used to measure the timing of animation. I wanted the Zs to play out one by one, so I had to time them by 300ms. The logic here is quite straightforward. 

function ZZZ(x, y, count) {
  noStroke();
  fill(0);                
  textAlign(LEFT, BASELINE); //aligns text to left and at baseline of font
  
  if (count >= 1) {
    textSize(20); 
    text('z', x, y); 
  }
  if (count >= 2) { 
    textSize(24); 
    text('z', x + 12, y - 12); 
  }
  if (count >= 3) { 
    textSize(28); 
    text('z', x + 28, y - 26); 
  }
}

This is the helper function to create the ZZZ effect. visible and count shows how many Zs are present, and it is incremented with time elapsed, measured with the millis function. Any further explanation of the code can be provided in class. 

For improvements, I was thinking about adding another condition. For now, when the owl is disturbed, it just repeats the animation. However, adding another animation when the owl is disturbed more than 10 times might be interesting. Owl spreading its wings and flying away sounds good. 

P.S. I was scrolling through the posts and found a post that is quite similar to mine (haha), although the contents were totally different. It was nice to see a fellow owl.