Week 3 – Digital Art

Unlike week 2, which lacked user interaction, I wanted this project to focus on having user interaction project an artwork. However, at the same time I didnt want user to think to much in producing the result. So I arrived at the conclusion of making the user press random keys to produce a sort of a simple artwork.

The artwork I was inspired by is from our video.

I noticed that if I have grids of the canvas with each grid having a random diagonal value, it can produce something like this. Of course, the same direction meant the same color.

 

The random colors are stored in an array that looks like this:

this.palettes = [
      ["#fefae0", "#606c38", "#bc6c25"],
      ["#0f0f0f", "#e63946", "#f1faee"],
      ["#f1f1f1", "#118ab2", "#ef476f"],
      ["#22223b", "#f2e9e4", "#c9ada7"],
      ["#faf3dd", "#2a9d8f", "#e76f51"]
    ];

The first is for the background of the canvas, the second one is for the left diagonal and the third one is for the right diagonal. I did no mention how to change the colors randomly. Feel free to explore and find out how to change colors. Pressing randomly keys might help.

placeRandomDiagonal() {
    //wait if full
    if (this.gridFull()) {
      return;
    }
    //get randomindex to palce the diagonal line
    const randIndex = floor(random(this.emptyIndices.length));
    const i = this.emptyIndices.splice(randIndex, 1)[0];
    
    //get random direction 1 is left 2 is right diagonal
    const dir = random([1, 2]); 
    //get random strokeWeight
    const w = random(1, 5);
    this.cells[i] = { dir, w };
    //call reset to check if its full
    this.scheduleAutoResetIfFull();
  }

This is the code that I wanted to share. This is the place Random Diagonal function, It first gets the random Index to palce the diagonal lines and chooses the random direction and random stroke weight. At the end the of the function it calls another function which simply checks if the grid is full. If it is, it calls the reset function.

//grid cells are stored in 1D array while canvas is 2D so this is to conver linear index to 2D position
//const x is column number
const x = (i % this.cols) * this.cellSize;
//row number
const y = floor(i / this.cols) * this.cellSize;

//each are multiplied by cell size to get pixel coordinates 
//ex cellsize is 20, col is 5 anat i =7. that means 7%5 = 2 column 2 which is 40, row is at 7/5 = 1 at 20, so index 7 maps to position 40,20


The hardest part about this code was the transition of 1D arrays to 2D canvas. Since the information that stores the grid cells are in 1D array, I had to find a method to map each index to a coordinate in the grid. This is done here, the specifics are explained in the comments.

For future Improvements, I think that making mouse clicks have another action on the artwork. Maybe changing the diagonal lines can make the artwork more interactive. Rather than diagonal, it can try different angles with each mouse click.

Week 3 Reading Response

The reading defines interaction to be a cyclic process in which two actors alternately listen, think, and speak. Building on this, I see a strongly interactive system as one that sustains this cycle in a meaningful way, where the user feels their input is not only received but also interpreted in a way that shapes the system’s future responses. In other words, the system doesn’t just react once, but continues to evolve as the user engages with it. I think of strong interactivity as creating a sense of dialogue, where the user’s actions feel acknowledged and the system “remembers” or adapts over time. However, I don’t think strength alone guarantees a good interaction; too much responsiveness without clarity can overwhelm or confuse the user.

I am in the process of choosing my next interactive project for week 3. While I am still brainstorming, unlike week 2’s project that lacked user interaction, I want to create a project where user’s mouse interaction and possibly keyboard interactivity produces an artistic outcome. For example, I could design the sketch so that keyboard inputs that change the “rules of the system,” such as altering font sizes or styles, to produce something like looks like a digital caligraphy. This would make the sketch feel more like an ongoing exchange rather than a one-time reaction. Instead of static outputs, the artwork would emerge from the combination of system logic and user decisions. The key idea is to create a system where user’s input results in a artistic product. I’ll try to take this idea in a direct matter.

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.