Week 3 Assignment – Computer Vision

Sketch and video demo below!

(I think you might have to open this on a new table as it doesn’t seem like computer vision sketches are supported by wordpress…)

Concept:

Well, I have always been interested in learning and testing out computer vision, because I have seen really cool effects and my main inspiration is a woman who goes by the.poet.engineer and she has absolutely incredible computer vision work.
To make the tracking as smooth as I could, I used code from this git hub post which I will talk about later in this post. This took me a ridiculously long time to complete since I started learning computer vision the same day I made this, but they end result was worth it.

Implementation:

There is a lot that goes into this, 500+ lines of code, however everything is well commented if you would like to go line by line, I will explain the main concepts for creating this.

A bit of background, this was done using ml5js’s HandPose, and their hand tracker has specific keypoints for parts of your hand which was used to calculate distance, positions, rotation and so on for all the shapes used in this.

I did not use every connection as in the picture above, I manually selected the keypoints that I wanted to use in these 2 variables:

let fingerconnections = [
  [17, 18],
  [18, 19],
  [19, 20],
  [13, 14],
  [14, 15],
  [15, 16],
  [9, 10],
  [10, 11],
  [11, 12],
  [5, 6],
  [6, 7],
  [7, 8],
  [0, 1],
  [1, 2],
  [2, 3],
  [3, 4],
];
// Using ml5js classification I make a list of the connections I want for a curved line
let singlefinger = [
  [4, 5],
  [17, 16],
  [13, 12],
  [9, 8],
];

The way this works is that every hand that is seen on screen will be added to artInstances, of course I manually just made 2 but this can be scalable and automated for more than 2 hands.. (alien ?)

Each hand is assigned a class object of our class skeletal, which is where quite literally everything happens.

// Function for smoothing, uses linear interpolation to shorten how much we want the detector to move, since I used 0.8 for smoothing that means it only moves 80% of the way from original location to next location.
function gotHands(results) {
  if (activateSmoothing && hands.length > 0 && results.length > 0) {
    for (let i = 0; i < results.length; i++) {
      if (hands[i] && results[i]) {
        let oldHand = hands[i];
        let newHand = results[i];
        for (let j = 0; j < newHand.keypoints.length; j++) {
          newHand.keypoints[j].x = lerp(
            oldHand.keypoints[j].x,
            newHand.keypoints[j].x,
            smoothing
          );
          newHand.keypoints[j].y = lerp(
            oldHand.keypoints[j].y,
            newHand.keypoints[j].y,
            smoothing
          );
        }
      }
    }
  }
  hands = results;
}

This is the smoothing function that I took which I tried simplifying in the comments, it’s simply just linear interpolation so that the distance moved isn’t equal to the distance actually covered.

for (let i = 0; i < hands.length; i++) {
    if (artInstances[i]) {
      // Calling our inbuilt functions for every hand we have on screen
      artInstances[i].drawline(hands[i], singlefinger);
      artInstances[i].drawdoubleline(hands[i], fingerconnections);
      artInstances[i].drawcircle(hands[i], fingerconnections);
      artInstances[i].drawHUD(hands[i]);
      artInstances[i].drawSpeedHUD(hands[i]);
    }
  }
  
  drawMusicButton();
}

There are 6 methods in our skeletal class:
– drawline
– drawdoubleline
-drawcircle
-drawHUD
-drawSpeedHUD
-drawPopupHUD (this is used in the drawhud function so you won’t see it being called outside the class)

class skeletal {
  constructor(linecolor, circlecolor, hudcolor) {
    // We take the color we want for the line, circle and the hud, as well as initialize the angle and last angle to 0
    this.linecolor = linecolor;
    this.circlecolor = circlecolor;
    this.hudcolor = hudcolor;
    this.hud1angle = 0;
    this.rotationSpeed = 0;
    this.lasthandangle = 0;
    this.popupActive = false;
    this.popupTimer = 0;
  }

There is 8 attributes assigned to each hand, where only the first 3 are you can choose and the rest is assigned to 0 / false by default as they will be changed and used in calculations for our functions.

Before I get into the function I am most proud of, I will briefly talk about what each function does.

drawdoubleline : There was no inbuilt shape that satisfied what I want, so I ended up writing my own shape using BeginShape and EndShape to give me that glitchy double line between most the connections.

drawline: I used the bezier function here, (calculated anchor points by getting the points 1/3rd and 2/3rd of the way from keypoint A to keypoint B) and this was used to connect bases of fingers to the top of the next finger with a curved line.

drawHUD: This has 2 elements, the palm HUD, and the exterior HUD, the palm hud is centered by calculating the midpoint the base of the finger (which is keypoint 9) and the wrist (which is keypoint 0), and so the shapes of the HUD is drawed around that point, we use the mapping functions so that it becomes bigger the further away your hand is from the camera, or smaller if it is closer to the camera. This has constraints so the HUD can never be too small or too big.

drawPopupHUD: This HUD is the exterior one, which contains a circle with sound waves along a quarter of it, and this only pops up if the hand rotates at a fast enough speed, and then disappears after a short while. I used Perlin’s noise to give that random sound effect to the waves.

drawcircle: This simply draws a small circle at each keypoint that I saved in the beginning of the program.

Finally the function I am proud of:

drawSpeedHUD(singleHandData) {
    if (!singleHandData) return;

    // We map the keypoints once again to their respective parts.
    let thumbTip = singleHandData.keypoints[4];
    let indexTip = singleHandData.keypoints[8];
    let wrist = singleHandData.keypoints[0];
    let palmBase = singleHandData.keypoints[9];

    if (!thumbTip || !indexTip || !wrist || !palmBase) return;

    // We calculate the palm size and the gap between our thumb and index finger
    let palmSize = dist(wrist.x, wrist.y, palmBase.x, palmBase.y);
    let fingerGap = dist(thumbTip.x, thumbTip.y, indexTip.x, indexTip.y);

    // Check if it is left or right hand to adjust rotation accordingly
    let isLeftHand = singleHandData.handedness === "Left";

    // Calculate the angle of the finger for rotation of the speedometer
    let fingerAngle = atan2(indexTip.y - thumbTip.y, indexTip.x - thumbTip.x);
    // Calculate the rotation and adjust based on left or right hand
    let sideRotation = isLeftHand ? fingerAngle + PI : fingerAngle;

    // Conditions for when the speedometer pops up
    let closeToCamera = palmSize > 80;
    let fingersSpread = fingerGap > 0 && fingerGap < 140;

    if (closeToCamera && fingersSpread) {
      // We create a mapping between the distance of finger and speed, where if the thumb and index are closed the speed is 0 to 50.
      let speed = map(fingerGap, 0, 130, 16, 50, true);
      let needleAngle = map(speed, 0, 140, PI, -PI);

      let midX = (thumbTip.x + indexTip.x) / 2;
      let midY = (thumbTip.y + indexTip.y) / 2;

      push();
      translate(-width / 2 + midX, -height / 2 + midY);

      // Rotate with our hand
      rotate(sideRotation);

      noFill();
      stroke(this.hudcolor);
      strokeWeight(2);
      // Draw the speedometer arc and ticks.
      arc(0, 0, 110, 110, PI + QUARTER_PI, TWO_PI - QUARTER_PI);

      for (let i = 0; i < 10; i++) {
        let a = map(i, 0, 9, PI + QUARTER_PI, TWO_PI - QUARTER_PI);
        line(cos(a) * 45, sin(a) * 45, cos(a) * 55, sin(a) * 55);
      }

      // Draw the needle
      push();
      rotate(needleAngle - HALF_PI);
      stroke(255, 50, 50);
      strokeWeight(3);
      line(0, 0, 0, -50);
      pop();

      // Draw the speed bar which changes with speed and needle.
      strokeWeight(1);
      rectMode(CENTER);
      noFill();
      rect(0, 25, 60, 5);
      fill(this.hudcolor);
      rectMode(CORNER);
      // Using your speed mapping to fill the 60px wide bar
      rect(-30, 22.5, map(speed, 16, 50, 0, 60, true), 5);

      pop();
    }
  }

This is the code for that interactive aspect of this computer vision, the speedometer that moves with your pinching of your thumb and index. The speedometer only pops up if your hand is close enough to the camera and the pinch isn’t too big, and we use the mapping function to spin the needle and change the progress bar. This speedometer also rotates around the hand which leads to it rarely clipping through your hand. This took so much trial and error, checking which angle works, so much time spent due to the needle going under the speedometer and completely opposite of where I would want it, or the speedometer rotating towards the hand clipping through it, overall it was a painful experience of trial and error.

A small addition I have is some background music that you can enable or disable, the song is “Color Your Night” and this is part of the soundtrack for Persona, and is an amazing song.

Reflection:

It was a painful, but incredibly fun experience, I am really happy I challenged myself and dived into computer vision, because if I did not know then I would have never, and I am really happy with the end result. There is endless possibilities of interactive art with computer vision, such as animating characters, or showcasing fluid dynamics and so much more which I would like to compare beyond a simple futuristic interactive HUD.

Week 2 — Reading Response

In reflecting on Casey Reas’ discussion of chance operations, I found my fundamental assumptions about artistic intent deeply challenged. I’ve always associated “intent” with manual, top-down control, but Reas’ argument for “jitter” or “noise” as a tool to keep a system from becoming “homogeneous” offers a much more interesting path. I am particularly struck by the idea of “homeostasis” within a system — where a work stays dynamic and shifting while maintaining a consistent texture. This shifts my perspective of the computer from an “obedient tool” to a creative collaborator. By utilizing randomness as a “jumping-off point,” I want to see if I can bypass my own aesthetic biases and “logical nonsense,” similar to how Reas and Tal Rosner used dice-rolls to spark compositions for their Chronograph project.

However, Reas’ mention of Rosalind Krauss’s critique of the “grid” leaves me with a lingering, uncomfortable question: if a work is generated through chance, can it still hold onto the “pure feeling” or “humane quality” that artists like Mondrian or Malevich aimed for? If I follow John Cage’s lead and use chance to “remove myself from the activity,” I worry that the work might lose its connection to social discourse and humanity. Yet, I am increasingly drawn to Gerhard Richter’s provocation that chance is “better” than the artist because it can “destroy what I’ve worked out wrong.” This suggests that the “human” element might actually reside in my willingness to let a system disrupt my own ego.

All these connections reinforced my goal to find an “optimum balance” between total randomness and complete control. Moving forward, I want to experiment with defining “minimal structures” or constraints that allow for “surprising visual qualities,” testing whether a system-based approach can actually feel more organic than one born of purely determined geometry.

Assignment 2: looped in love

For my concept, the repeated shapes in the attached PDFs reminded me of wallpapers and patterned backgrounds. Given that it’s February, I decided to create a static heart wallpaper using a grid of mini hearts. When the hearts are pressed, the background turns red and the hearts begin to move.

I initially started by coding the static wallpaper, but quickly realized that if I wanted to incorporate animation, I needed to plan for it early on. I also added a mouse press function so that I could toggle between the still wallpaper and the moving hearts.

For the hearts, I made them using two ellipses and a triangle, and I used nested loops to place the hearts in a grid, which created the wallpaper effect.

To add animation, I created a variable that updates every frame to move the hearts together. When the hearts are moving, this value added to their positions, which shifts the grid. I also used a boolean variable with the if statements to control when the animation occurs.

if (moving) {
    heartshift += speed;
    if (heartshift > 15 || heartshift < -15) {
      speed *= -1;
    }
  }

I’m proud of this part of my code because it controls the movement of the hearts. When the variable (moving) is true, (heartshift) updates every frame, which make the hearts move. And by adding the conditional statement, the speed reverses when the movement reaches the limit(15), so the hearts go back and forth instead of only moving one direction.


Overall, I’m happy with the sketch, and I enjoyed experimenting with different variables to see how they affect the hearts. The most challenging part was combining the animation with the nested loops and learning how to incorporate them with the boolean variable and if statements all together. For future ideas, I would like to add more interactions, or make it more intricate.

Reflection Response

Casey’s talk made a great point about randomness vs. order and the idea of losing total control. Reflecting on my past work, especially my heart wallpaper sketch, everything is very structured and controlled. Casey’s talk made me realize that adding small random elements in my sketch, like changes in color or even the movement of the hearts, could make the sketch feel less rigid. I could have created a static wallpaper of mini hearts and then, when pressed, made the hearts move randomly, but I think it is challenging to balance the randomness of the movement, given how rigid the static grid is when the sketch is pressed again.

Week 2 – Reading Reflection

Before watching the video, I assumed that randomness = messy or chaotic. I never before thought of controlled randomness, which initially sounds contradictory, but begins to make sense as the video goes on. I was fascinated by the artwork that was produced by programming randomness and it opened my eyes to different ways randomness can be used in my own work. In a way, some of the artworks Reas showed in the video, specifically the music pieces, reminded me of modern AI. The music sounded exactly what it was, random, yet also structured oddly enough. It reminded me of AI generated images and songs because AI tends to create some very messy looking images when you give it a specific prompt, and the randomly generated music somewhat mimics the idea of AI nowadays. More importantly, I was mostly impressed with the artwork that can be produced through computer graphics and code. Coming from a computer science backround, most of my coding involved creating algorithms. So, seeing a whole new world of coding to create abstract art pieces was captivating.

In my own artwork, I definitely plan to incorporate randomness by generating random colors and sizes for objects when needed, and especially for generating random positions and velocities for objects. I believe the optimum balance between total randomness and complete control is having almost complete control yet incorporating some elements of randomness when necessary. The control comes from writing the code yourself and deliberately inserting when you want randomness to be used. This helps create more complex art pieces because sometimes it is difficult to draw each element individually in the sketch and create more abstract pieces. So, the element of randomness allows for the creation of art pieces one might not have completely imagined in their mind.

Week 2: Reading Response

Thinking of randomness brings the idea of disorganization and something more instinctive that is done with no purpose to me. I came to realize that this is not always the case, and that randomness can rather be a more purposeful approach when working on a piece of art. The way Casey Reas presents and discusses randomness makes it seem like a much more important element to consider incorporating, and I was surprised to find myself relating what he was saying with what I had done so far and what I could do next. I completely agreed with his point that randomness in unplanned things can in fact improve your work, especially as I reflected on my last assignment while listening. I created a looped artwork of a drawing made of shapes, and at first I was too focused on organizing how they moved. However, I ended up using randomizing code, which allowed the shapes to move around more freely and resulted in a stronger outcome.

Along with randomness, I feel that the control and manipulation of it also really matters, as I keep adjusting how random I want each code to behave, and think “how random is too random?” Casey mentioned a quote that made me think more about this concept, “the computer is a unique device for the arts since it can function as an obedient tool with vast capabilities for controlling complicated processes.” This reflects how the computer actually works in my practices, it is just “obedient,” meaning that I am still controlling what it does no matter how random it gets. I plan to incorporate random elements into my work, especially when I’m working with a complex piece, for movement or timings, but I believe there should always be a balance between randomness and control. Complete randomness can possibly lead to a messy display and an overwhelming number of actions, while complete control does do exactly what is planned, it can sometimes result in a less engaging or satisfying outcome. Therefore, when using random elements, I would control the randomness to some extent.

Reading Reflection

How long something can stay random, when we are always looking for patterns in things. Does it helps simplifying world around. Probably yes. Just now, in the week2 assignment what started as random colliding circles, if I grey them out, I just see two giant Xes. Also when we manipulate randomness is it still random, what the speaker called “Controlled randomness”. But what would be something truly random. A roll of a die, but it’s bound by 6 choices. That also is a form of controlled randomness. Given this all physical systems are bound by laws of physics. That make all random events not so random. If by random, we mean lack of knowledge about the outcome by that I mean the choice of the outcome not the outcome itself, then to increase randomness is to increase the available for the system to choose from.

For an artwork, My balancing point of chaos and control will the total number choice available at a moment. I see one element with 100 choices and 10 elements with 10 choice the same way. Surely, I can play around with by introducing biases and triggers to manipulate the balancing point, this will come at a cost of increasing complexity. And I need to look out for the chaos coming from this complexity. There is beauty in order and chaos both. But ordered beauty and chaotic beauty carry representation of complete opposite worlds which in its own is a spectrum and in that who lies where become their balancing point.

Week 2: Loop Art

My concept:
As I started planning my work for assignment two, I decided I want to create something abstract using the new things we’ve learned in class, along with concepts I caught up on from previous classes. I started off by looking at the documents attached with the instructions, and two specific artworks grabbed my attention. I noticed how both have rectangles and squares in different sizes and complex ways, intersecting and overlapping one another. This inspired me and gave me the idea to create something similar using squares and rectangles, but make it colorful and moving to add life to it. Here are the references:


Embedded sketch:

A Code I’m proud of:
In this assignment I felt particularly proud of adjusting the movements of the shapes, as it was like trying to get the ranges of the sizes correct and ensure they moved within the frame while still appearing random, enhancing the image I was trying to display. I tried many different ways and ranges of numbers until I reached a result that satisfied me.

//Use if statements to ensure the shapes bounce back and forth instead of going out of frame
  if (rectX < -40 || rectX > 40) rectSpeedX *= -1;
  if (rectY < -40 || rectY > 40) rectSpeedY *= -1;

  if (squareX < -40 || squareX > 40) squareSpeedX *= -1;
  if (squareY < -40 || squareY > 40) squareSpeedY *= -1;

//Rectangles:

  //Set the loop to ensure multiple appear in each frame
  for (let i = 0; i < 25; i++) {

    //Randomize the position for pattern
    let w = random(20, 70);
    let h = random(20, 50);

    //Make width and height random to get a variety of sizes
    let x = random(0, width - w) + rectX;
    let y = random(0, height - h) + rectY;

//Squares:

  //Set the loop to ensure multiple appear in a frame
  for (let i = 0; i < 25; i++) {
    
    //Randomize the sizes of the squares for diversity
    let s = random(20, 60);

    //Adjust positions of sqaures
    let x = random(0, width - s) + squareX;
    let y = random(0, height - s) + squareY;

Another part that I’m surprisingly proud of is choosing the background color. I was just experimenting with different colors until I realized I could make it transparent, allowing the previous frames to show through and giving a more realistic look that matches the inspiration pictures.

//Set the background color to fit the goal
function draw() {
  background("#BDE0FF84");

Reflection and ideas for future work or improvements:
After completing this assignment, I felt satisfied with the progress I made and the final result. I feel that I was able to create a plan and successfully execute it through code, using the different techniques learned in class and resources provided. I enjoyed setting many colors at once and watching them appear on the display using random() and my colors array. I gained a stronger understanding of using loops, specifically for loops, and what really stood out to me is how much they can simplify and improve the flow of code. I also became more comfortable using if statements with || to adjust the movements. For future work, I would like to improve this artwork by learning how to make the shapes float around more smoothly in a specific pattern, or by adding more complex shapes to make it even more interesting.

References:
I referred back to the weekly slides provided by the professor to recap using the “if” statements as we did in class, which I used to keep the shapes within the frame.
I used the “for loop” reference page from the p5 website to learn more and generate multiple shapes within each frame and control their repeated movement.
https://p5js.org/reference/p5/for/ 
I used ChatGBT to set the colors of the shape outlines. I wanted to work with a range of selected colors rather than a single randomly changing color. It showed me how to use an array of strings to store the colors I wanted, and then, within the shape movement, use stroke(random(colors)) to randomly select and display these colors in each frame.

Reading Reflection – Week 2

“Controlled randomness” is, and I think most people would agree, an oxymoron. I agree that art is at its best when there’s a healthy mix of chaos and order, but is true randomness even possible? I think randomness is something that is inherently controlled to some extent, since there will always be certain parameters for everything we can conceive of. We are a species that operates on frameworks and pattern recognition, after all. I think we’ve coined the term “random” to describe outcomes for which we can’t see a clear logical process, and naturally, as the finer workings of the universe remain far out of our reach, most things in the world make little sense to us.

Talk to a quantum physics enthusiast and they might tell you that, on a quantum level, there exists some randomness in the way particles interact with one another. Likely, this is just a placeholder theory to explain away our confusion, but regardless, the universe seems to operate both on processes we can follow logically and processes that we can’t. In other words: chaos and order. Maybe that’s why people find so much beauty in “controlled randomness” – it mirrors the way we understand the world: nature, humanity, time, etc. Art mirrors life, after all.

there are no perfect circles in nature

This assignment was non less than an endeavor in its own. All I had in mind was circles when I started. I thought of them as colliding an getting bigger or something like that. The hardest part was to come up with something which uses loops. Not from the coding end but from the creative one. The it suddenly hit me; draw in itself if a big while TRUE loop. Honestly I still didn’t know what to do about it, but at least I had starting point.
I wanted to add an interactive side to it as well, so the first thing I did was created pointer for the pointer, so that it has visible interactions. I borrowed the monitor frame from my self portrait to add somewhat character to the framing.

The moment I had two circles bouncing off each other. I noticed the the repeating motion. To observe it in detail I wanted to add the trail into it. I had some what trouble doing that because of the monitor underneath, the trail was hidden under it. I asked chatgpt about it. It made me realize that I don;t want my monitor to be draw again and again. So I just put it up in the setup. no I could see their movement.

The most interesting part about it was they never collided if left undisturbed. Because of the movement variables I set. But if it is disturbed by the mouse the kinda stuck in the loop. This discovery is what I am most proud of, I am not sure which part of the code represents it. It randomly reminded me of how it is necessary to go off road to meet people or places that quite possible create the biggest impact in our lives.
I used the https://p5js.org/reference/p5/text/ to write the text. It represents a vague idea I conclude from above about living

lastly the part of code I think is highlight is as follows

x += 1
  if (x % 220 == 0){
  c1 = random(0,255)
  c2 = random(0,255)
  c3 = random(0,255)
  fill(c1,c2,c3)
  
}

I like this because this where I manipulated the draw loop code to change the circle colors

The Sketch

What I can add to this is. I feel like this is very sketchy. To represent the, I would want to make more calm and smooth

Week 2 Animation, Conditionals, Loops

For this assignment I wanted to do something spiraley and trippy. Honestly, I didn’t have a particular vision for this one and figured it out as I went along.

First thing I did was the background. At first I had it be a bunch of overlapping shapes moving randomly accross the screen, but it was too jarring. I ended up simplifying it to what you can see above: an animated perpetual wave of thin white concentric circles over a black background.

if (frameCount % 8 > 6) {
  cc = 29;
} else if (frameCount % 8 > 4) {
  cc = 24;
} else if (frameCount % 8 > 2) {
  cc = 19;
} else {
  cc = 17;
}
while (cc > 1 && cc < 1000) {
  strokeWeight(0.5);
  circle(200, 200, cc);
  cc += 14;
}

As you can see, I used a boolean and frameCount to animate the circles, and a while() loop to draw them. I used the p5 reference library to remind me of how they work and get the finer details right, and I learned about frameCount by watching several youtube videos on animation.

Next I added the eye in the middle using a text box and orienting it so that it aligns with the centermost circle. Pretty simple and straightforward.

  strokeWeight(0);
  textSize(90);
  fill(250);
  textAlign(CENTER, CENTER);
  text("", 210.5, 174.5);

Finally (somehow this was the hardest part), I added the text in the middle and animated it to fade in and out.

 stroke(t);
 if (frameCount % 30 < 15) {
 t = t + 15;
} else if (frameCount % 30 < 30) {
 t = t - 15;
}

for (let x = 200; x < 400; x += 600) {
  for (let y = 20; y < 460; y += 30) {
    textSize(10);
    textFont("Courier New");
    strokeWeight(0.5);
    noFill();
    text(
      "it is only by dying to ourselves that we find life",
      x,
      y,
      [500],
      [10]
    );
  }
}

I used a for() loop to duplicate the text accross the y axis, and a boolean and frameCount to animate the fading. This one was a pain in the ass because I kept messing up the text’s orientation and it took a few tries to get the frame remainders right. I also think the first for() function where I duplicate it accross the x axis is extraneous, but the code messed up when I tried to get rid of it so I just left it in there.

One thing I could potentially add to this is a sound effect or some music to add to the aesthetic. I also feel like I should’ve added an interactive aspect to it; that completely skipped my mind. Otherwise, I’m happy with the final product. It’s nothing too impressive but I think it fits together nicely.