Week 8: Unusual Switch

Concept

I have a little jewelry plate that I try my best to put all my jewelry on at the end of the day so that it is easier to find them next time. However, sometimes I forget to put my jewelry on it, especially my ring, and then I would have to look around to figure out where I put it last time.

This became my inspiration for creating my unusual switch – to remind myself to put my ring where it is supposed to be. When the ring is put on the plate, it closes the switch circuit and turns on the LED bulb!

My hands are involved in operating this switch, but not in the traditional sense, as it is the ring that is acting as the switch and is the main object, so I decided to continue with the idea.

Video demonstration

Implementation

I mainly referred to the switch circuit we saw in class to build my circuit. There are 2 exposed wires on the plate that are not touching. One of the wires is connected to the digital pin 2 and pin 2 is connected to GND with a 10k resistor in between. The other wire is connected to 5V. When the copper ring is put on the 2 wires, it acts as a conductor and closes the circuit.

digitalRead is used on pin 2 to detect when the switch circuit is open or closed. When the circuit is closed (ring is on the plate covering both wires), the LED lights up, and vice versa.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const int switchPin = 2;
const int ledPin = 13;
void setup() {
pinMode(switchPin, INPUT);
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
// read switch state
int switchState = digitalRead(switchPin);
if (switchState == HIGH) {
// turn the LED on when switch is closed
digitalWrite(LED_BUILTIN, HIGH);
Serial.println("Ring detected! Circuit closed.");
} else {
// turn the LED off when switch is open
digitalWrite(LED_BUILTIN, LOW);
Serial.println("No ring detected. Circuit open.");
}
delay(100);
}
const int switchPin = 2; const int ledPin = 13; void setup() { pinMode(switchPin, INPUT); pinMode(ledPin, OUTPUT); Serial.begin(9600); } void loop() { // read switch state int switchState = digitalRead(switchPin); if (switchState == HIGH) { // turn the LED on when switch is closed digitalWrite(LED_BUILTIN, HIGH); Serial.println("Ring detected! Circuit closed."); } else { // turn the LED off when switch is open digitalWrite(LED_BUILTIN, LOW); Serial.println("No ring detected. Circuit open."); } delay(100); }
const int switchPin = 2;
const int ledPin = 13; 

void setup() {
  pinMode(switchPin, INPUT);
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // read switch state
  int switchState = digitalRead(switchPin);
  if (switchState == HIGH) {
    // turn the LED on when switch is closed
    digitalWrite(LED_BUILTIN, HIGH);
    Serial.println("Ring detected! Circuit closed.");
  } else {
    // turn the LED off when switch is open
    digitalWrite(LED_BUILTIN, LOW);
    Serial.println("No ring detected. Circuit open.");
  }
  delay(100);
}

Challenges and Future Improvements

This week’s project helped me to get a lot more comfortable with building circuits and working with Arduino. I still had to refer back to the schemes we saw in class but I am sure I will get confident to build circuits on my own as we progress.

One of the mistakes I did this week was getting straight into building my circuit before reading again the project description. I glossed over the fact that we had to use digitalRead to get the state of the switch and do something with it. I initially built a circuit with a 330 resistor and an LED that simply closes when the ring is placed and causes the LED to light up, so initially there was no switch at all . After reading through the requirement, I modified the circuit such that there are 2 segments to the circuit: 1) LED circuit that is closed, and 2) switch circuit connected to 5V and a digital pin with a 10k resistor in between that is open and gets closed with the ring. Whether the LED lights up or not depends on the open/closed state of the switch circuit.

As I now write this documentation, I wonder if it would have been more intuitive if the LED turns off when the switch is on (when the ring is placed on the plate), so as to have the lit-up LED as a reminder to put my ring on the plate. Though this change does not alter the concept of the project, I think it makes it more meaningful in terms of usability and intentions. I hope to be more intentional with my future projects; to make things that are well thought out and build things that do actions for a reason, not just for the sake of it.

Reading Reflection – Week 8

Her Code Got Humans on the Moon

The story of Margaret Hamilton and her contributions to the Apollo mission’s success highlights perhaps the most important aspect in coding: error-proofing. When I was first introduced to coding through the Intro to CS course, I remember thinking it was silly to write extra lines of code to simple programs for error handling, i.e. if conditions to prevent from wrong input type being passed, when it seemed so obvious that such cases wouldn’t happen.

I have now come to realize, from taking more CS courses and practical experiences, that there will always be bad actors and mistakes are bound to happen. A key responsibility of software developers is to minimize potential failure points and handle them gracefully to make the system robust and prone-to-failure. Murphy’s law reflects the importance of anticipating all possible error scenarios and designing software that can withstand them.

The monumental Apollo mission could have ended in failure due to the absence of error-checking code, had it not been for Hamilton who identified the issue, and her team who found a fix just in time. It is always better to be safe than sorry, and not adding that few extra lines of code could potentially cost way more than we realize.

murphy's law' Mug | Spreadshirt

Attractive Things Work Better

This reading was very very interesting. Even before I read the piece, I silently agreed with the title, that attractive things work better. I have been learning to appreciate and be consciously aware of design of everyday objects around the house, mostly through seeing well designed pieces on my social media and watching videos that break down designs and their intentions behind them. I realize that a good design can truly elevate the user experience, especially now having read the points made by Norman about how positive affect created by good design can actually streamline the efficiency and usability of an object.

Norman’s argument that creative design is not always preferable, and that it depends on the seriousness and urgency of the situation was very insightful and made me reflect on how I personally interact with things. When I am in an extreme hurry (like extreme extreme) or madly stressed, I can not function at all — I find it hard to do some of the things that I can normally do without even thinking, whether that is writing text messages or opening a bottle of drink or tying my shoes. It is not dependent on the design of things; rather it is due my own state of mind. As Norman explains, in these high-stress situations, design should not interfere and instead be highly-tailored and easy to follow. This was an interesting statement that reminded me that intricate design is not always needed and wanted, and sometimes the simple things are indeed the better.

Midterm Project – Dodge the Droppings!

Link to fullscreen sketch

Concept

The idea behind my game came from a personal incident, and is intended to be funny and inspired by our very own NYUAD campus. The game setting is such that the player is working under the palm trees outside C2 competing with time to complete their assignment due in 5 minutes. Birds above have started attacking the player, who now needs to dodge the droppings falling from above while at the same time finish their assignment as soon as possible. The game is an adaptation of the classic dodge-style game with an added game element. This added element is a progress bar that represents the player’s assignment progress and it gets filled upon the repeated pressing of the space bar. The main goal is to fill up the progress bar (the win condition), while dodging the droppings falling from above without getting hit by any (getting hit is the lose condition).

Game components

The sketch starts by displaying a home page with instructions on how to play the game. Upon pressing the “Enter” button, the game starts. The images of the keyboard buttons on the instructions are taken from flaticon.com.

The main game screen consists of four elements:

  1. bird
  2. dropping
  3. computer
  4. progress bar (yellow long rectangle on the right).

Bird poop (dropping) falls from where the birds are located at the top of the screen, at regular intervals across random columns in different speeds. The computer is situated at the bottom of the screen and can be moved horizontally to different columns using the left and right keyboard buttons. When the space bar is pressed, the progress bar fills up (red indicates the level of assignment progress). At the top right, there is a timer that shows how much time has elapsed in seconds, and the best time  – which is the lowest time taken to complete the assignment, since the goal is to finish the assignment as quickly as possible without getting hit by a dropping – will be tracked across game plays and displayed on the home page.

I drew the images used for the bird, dropping and computer on Procreate and used a background remover application to make the background transparent. The image in the game background with the palm trees is taken from NYUAD’s website.

game screen
screen shown when the assignment is complete
screen shown when hit by / collided with a dropping
home page with the best time displayed

Implementation

Each of the four main elements (bird, dropping, computer, progress bar) are implemented as a class and have their respective functions, including display, fall, move, fill etc. There is also a Game class that manages game state variables, starts/resets the game and checks for collision between computer and the droppings.

One of the design decisions I made was to divide the canvas into (invisible) columns and have the computer move one column at a time instead of moving continuously, and droppings fall randomly from one of the columns instead of from any random pixel. This way, the user can’t “cheat” by placing the computer at a location where droppings rarely fall and it has to constantly dodge droppings, which I personally felt made the game a bit more challenging and fun.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Computer {
constructor() {
this.x = 0;
this.y = 480;
this.alive = true;
// initially static
this.direction = 0;
// place the computer at a random column
this.col = int(random(0, numCols));
}
move(dir) {
// if the right arrow is pressed
if (dir === 1) {
// numCols - 1 so that the last column is not accessible
// last column space is reserved for placing the progress bar
if (computer.col < numCols - 1) {
computer.col++;
}
// left arrow is pressed
} else {
if (computer.col > 0) {
computer.col--;
}
}
}
display() {
this.x = colWidth * this.col + leftMargin;
image(computerImg, this.x, this.y, computerWidth, computerHeight);
}
}
class Computer { constructor() { this.x = 0; this.y = 480; this.alive = true; // initially static this.direction = 0; // place the computer at a random column this.col = int(random(0, numCols)); } move(dir) { // if the right arrow is pressed if (dir === 1) { // numCols - 1 so that the last column is not accessible // last column space is reserved for placing the progress bar if (computer.col < numCols - 1) { computer.col++; } // left arrow is pressed } else { if (computer.col > 0) { computer.col--; } } } display() { this.x = colWidth * this.col + leftMargin; image(computerImg, this.x, this.y, computerWidth, computerHeight); } }
class Computer {
  constructor() {
    this.x = 0;
    this.y = 480;
    this.alive = true;
    // initially static
    this.direction = 0;
    // place the computer at a random column
    this.col = int(random(0, numCols));
  }
  
  move(dir) {
    // if the right arrow is pressed
    if (dir === 1) {
      // numCols - 1 so that the last column is not accessible
      // last column space is reserved for placing the progress bar
      if (computer.col < numCols - 1) {
        computer.col++;
      }
    // left arrow is pressed
    } else {
      if (computer.col > 0) {
        computer.col--;
      }
    }
  }
  
  display() {
    this.x = colWidth * this.col + leftMargin;
    image(computerImg, this.x, this.y, computerWidth, computerHeight);
  }
}

A new dropping is created for every 1/3 second and its column and speed is randomly chosen. I settled on 1/3 second (frameCount % 20 === 0) among other values to make the game just challenging enough without frustrating the user by bombarding them with too many obstacles.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// create a new dropping
if (frameCount % 20 === 0) {
droppings.push(new Dropping());
}
for (let i = droppings.length - 1; i >= 0; i--) {
let dropping = droppings[i];
// check if any dropping has collided with the computer
if (this.checkCollision(dropping, computer)) {
// play sound effect
blopSound.play();
// remove the dropping from the array
droppings.splice(i, 1);
gameOverFail = true;
break;
} else {
dropping.fall();
dropping.display();
}
// remove droppings that have went beyond canvas
if (dropping.y > height) {
droppings.splice(i, 1);
}
}
// create a new dropping if (frameCount % 20 === 0) { droppings.push(new Dropping()); } for (let i = droppings.length - 1; i >= 0; i--) { let dropping = droppings[i]; // check if any dropping has collided with the computer if (this.checkCollision(dropping, computer)) { // play sound effect blopSound.play(); // remove the dropping from the array droppings.splice(i, 1); gameOverFail = true; break; } else { dropping.fall(); dropping.display(); } // remove droppings that have went beyond canvas if (dropping.y > height) { droppings.splice(i, 1); } }
// create a new dropping 
if (frameCount % 20 === 0) {
  droppings.push(new Dropping());
}

for (let i = droppings.length - 1; i >= 0; i--) {
  let dropping = droppings[i];
  
  // check if any dropping has collided with the computer
  if (this.checkCollision(dropping, computer)) {
    // play sound effect
    blopSound.play();
    // remove the dropping from the array
    droppings.splice(i, 1);
    gameOverFail = true;
    break;
  } else {
    dropping.fall();
    dropping.display();
  }
  
  // remove droppings that have went beyond canvas
  if (dropping.y > height) {
    droppings.splice(i, 1);
  }
}

Problems I ran into

One of the biggest challenges was managing the state of the game such that the game can be restarted and the correct screens based on whether the game is in process, lost or won is shown without fail. I was able to do this by keeping track of three boolean variables:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
let gameStarted = false;
let gameOverSuccess = false;
let gameOverFail = false;
...
// switch screens
if (!gameStarted) {
instructions.display();
} else {
game.run();
}
if (gameOverSuccess) {
...
} else if (gameOverFail) {
...
}
let gameStarted = false; let gameOverSuccess = false; let gameOverFail = false; ... // switch screens if (!gameStarted) { instructions.display(); } else { game.run(); } if (gameOverSuccess) { ... } else if (gameOverFail) { ... }
let gameStarted = false;
let gameOverSuccess = false;
let gameOverFail = false;

...

// switch screens
if (!gameStarted) {
  instructions.display();
} else {
  game.run();
}

if (gameOverSuccess) {
  ...
} else if (gameOverFail) {
  ...
}

I track these variables in the draw() function and change what is shown on screen should any of these variables are modified.

Checking for collision between the computer and droppings was not very challenging as I implemented it as a simple function that checked whether any dropping overlaps in any direction with the computer, but it required configuring of values to ensure that collision was detected when the two appear to touch each other to the visible eye (instead of being triggered as soon as they barely touch) . As I had already tested creating a progress bar that fills upon a key press in the previous week as part of my midterm progress, incorporating it into the game took little time.

Future improvements

I quite like how the game turned out, both in terms of visuals and playing experience. However, one thought that I kept having was how nice it would be to be able to choose the difficulty of the game. I didn’t want to make the game too challenging so I settled on values for the speed and frequency of obstacles that would cater to the general audience, but this means that for certain groups of people who enjoy playing games, my game may appear very trivial. Therefore, one of the main improvements I would be interested in making is creating multiple degrees of difficulty (easy, medium, hard) that the user can choose from. I think this would make the game more engaging and potentially have users come back to the game to succeed in each round, instead of just attempting once.

Week 5: Midterm Project Progress

Concept

For my midterm project, I wanted to create something that is 1) funny and 2) inspired by our campus. I thought hard and long, and eventually came up with an idea to create a game based on an “incident” that happened to me at the Palms. I love sitting under the palm trees outside C2 when the weather is nice, and one day I was joyfully sitting on one of the tables doing work on my laptop when suddenly I heard a quick popping sound and felt something splatter in front of me – a bird had pooped on my laptop.

At a loss for words as to why, out of all the open space around me, it chose to land right on me, I questioned my luck and intensely cleaned around. Ever since then, whenever I go to the palms, I am reminded of that day, laugh at how “lucky” I was, and glance up from time to time to check for birds above me.

Design

The idea of the game is to dodge bird 💩 falling from the sky while at the same time trying to finish your homework. The user needs to protect the computer using the left and right arrow keys without getting hit from what is falling from above. To complete the homework, the user needs to repeatedly press the space bar until the progress bar fills up. If the user stops pressing the space, the space bar will decrease. The goal is to finish the homework as fast as possible without getting hit. The shortest time will be recorded for the user to see.

Challenging Parts

  • Creating the progress bar and incorporating decay so that the progress decreases upon inactivity
  • Checking for collision between the computer and bird poop
  • Making bird poop fall randomly from the sky (from random columns at random times with perhaps random speeds)
  • Showing time elapsed and keeping track of minimum time
  • Creating a visually appealing game interface

I tried creating a simple progress bar that fills up on the pressing of the space bar. It works by drawing a rectangle where the width is based on the progress variable, and this variable is incremented when the space bar is pressed and decremented slightly every time to add to the challenge.

Reading Reflection – Week 5

Computers do not have any general visual understanding unless they are told exactly what to look for, and this is quite shocking considering how powerful they are. A task as simple as differentiating between a dog and a cookie can pose an incredible challenge to machines.

Tim Stearns on X: "I use @teenybiscuit's wonderful animal vs. food images when I teach 1st-year undergrads about the challenges that AI faces in image recognition tasks that we're good at, but

Humans process visual information holistically, incorporating prior knowledge, experience and intuition to interpret the visible world. Meanwhile, computers speak the language of 1s and 0s, and they have to be taught by humans to see and discern certain objects and situations, using specific techniques like frame differencing for detecting motion, background subtraction for detecting presence and brightness algorithms for tracking objects. As it is up to humans to teach computers how to make sense of pixels, I think there is room for creativity in how we translate and represent the visual world to machines.

In a world where computers are developing a greater sense of “sight” day by day, data privacy and surveillance becomes an important topic of discussion. Those developing and deploying products based on computer vision, including computer scientists, artists and anyone in between, have to ensure fair and ethical use of the data, if any is collected. At the same time, I think it is also important for everyone else to learn to recognize and question the real world applications of computer vision as we go on with our daily lives.

Reading Reflection – Week 4

Something that drives me crazy is doing laundry on campus. Many of the drying machines in my building don’t show the duration of the cycle/program, so it is hard to know exactly when it will finish and often times, I end up making multiple trips to the laundry room just to take out my clothes from the dryer. The washing machines are also not without their problems – even though they have timers that show how much time is left for the cycle to finish, the timers are often inconsistent and don’t match up with the progress of the washing programs. I feel that my laundry experience would be so much better if the laundry machines enabled me to: 1. know exactly how long it will take 2. by the time I get to the laundry room after my alarms go off, my laundry is actually finished and I don’t have to wait extra 10-15 minutes for  the machine to stop spinning. Something as routine as laundry should be intuitive and not become a guessing game.

I think the ideas mentioned in the book are all applicable and relevant to interactive media. Affordances and signifiers are important to make the interaction between the user and the system clear. For example, buttons should look clickable and links need to be presented in such a way to indicate that they can be clicked. The controls that allow the user to interact with the system need to logically map to their effects to provide an intuitive experience. When the user performs an action, they should get a feedback signal to notify that their actions were registered.

Week 4: Data Visualization

Concept

I was looking through the datasets linked in Professor Mathura’s repo when I came across the data for the World Happiness Report. It contained numerical data for various happiness indicators across countries around the world and I was immediately interested as I was looking to create some sort of a color visualization. Using this data, I decided to create a data visualization using color that shows where each country stands in terms of happiness, as well as in terms of the other indicators that impact happiness, including health, freedom and economy.

Process

I planned to create individual visualizations for each of the four focus indicators using different colors, and then make them transition into one another smoothly. While searching about ways to do color transitions and gradients, I came across the lerpColor() function that works similarly to the lerp() function, and allows to interpolate between two colors based on a parameter.

Initially, I drew equal size rectangles for each country. Even though I liked that the whole canvas was perfectly divided into uniform rectangles, it felt like it lacked coherent meaning. I thought it might make the visualization much more interesting if it offered a comparison of the indicators across not only countries, but also across continents. The original data file unfortunately did not have continent data, so I manually went through the CSV file to add a continent column entry for each country. Using this data, I divided the canvas vertically into 6 equal columns representing each continent (Asia, Europe, Africa, South America, North America, Oceania), and then further divided each column into smaller segments for each country. This way, the visualization offered a richer narrative – it illustrates how happiness indicators vary across the different regions in the world, and exposes potential trends linked to geography and economic factors.

After
Before

 

 

 

 

 

 

Brighter/lighter colors represent higher scores on the happiness level indicators and darker colors represent lower scores. This means that a country ranks high in happiness if it consistently appears bright throughout the visualization. Toggle feature is added to allow the sketch to also be viewed solely as an art piece, so if you want to see the countries represented by the boxes, you have to click the mouse.

Challenges

Implementing the transition between the visualizations was the biggest challenge of this project.

I created a helper function to get the color based on the score and the indicator, by mapping the score ranges for each indicator to RGB color ranges (0-255). Setting the RGB color values took surprisingly many trials and repeated references to RGB color theory to get the exact color gradient I wanted for each indicator.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// helper function to get the color based on score & indicator
function getColorForIndicator(score, indicator) {
// higher the score, brighter/lighter the color
// lower the score, darker the color (nearing 0)
if (indicator === "Happiness") {
// yellow <-> dark
return color(map(score, 2, 8, 0, 255), map(score, 2, 8, 0, 255), 100);
} else if (indicator === "Health") {
// green <-> dark
return color(100, map(score, 0, 1, 0, 255), 100);
} else if (indicator === "Freedom") {
// blue <-> dark
return color(50, map(score, 0, 1, 50, 200), map(score, 0, 1, 50, 200));
} else if (indicator === "Economy") {
// magenta <-> dark
return color(map(score, 0, 2, 40, 255), 100, map(score, 0, 2, 20, 255));
}
return color(200);
}
// helper function to get the color based on score & indicator function getColorForIndicator(score, indicator) { // higher the score, brighter/lighter the color // lower the score, darker the color (nearing 0) if (indicator === "Happiness") { // yellow <-> dark return color(map(score, 2, 8, 0, 255), map(score, 2, 8, 0, 255), 100); } else if (indicator === "Health") { // green <-> dark return color(100, map(score, 0, 1, 0, 255), 100); } else if (indicator === "Freedom") { // blue <-> dark return color(50, map(score, 0, 1, 50, 200), map(score, 0, 1, 50, 200)); } else if (indicator === "Economy") { // magenta <-> dark return color(map(score, 0, 2, 40, 255), 100, map(score, 0, 2, 20, 255)); } return color(200); }
// helper function to get the color based on score & indicator
function getColorForIndicator(score, indicator) {
  // higher the score, brighter/lighter the color
  // lower the score, darker the color (nearing 0)
  if (indicator === "Happiness") {
    // yellow <-> dark
    return color(map(score, 2, 8, 0, 255), map(score, 2, 8, 0, 255), 100);
  } else if (indicator === "Health") {
    // green <-> dark
    return color(100, map(score, 0, 1, 0, 255), 100);
  } else if (indicator === "Freedom") {
    // blue <-> dark
    return color(50, map(score, 0, 1, 50, 200), map(score, 0, 1, 50, 200));
  } else if (indicator === "Economy") {
    // magenta <-> dark
    return color(map(score, 0, 2, 40, 255), 100, map(score, 0, 2, 20, 255));
  }
  return color(200);
}

To achieve a smooth color transition, I used the lerp() and lerpColor() functions to get the colors corresponding to the indicator scores interpolated by the current progress of the transition.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// get the country's scores for the current and next indicator
let currScore = getScore(countries[j], currIndicator);
let nextScore = getScore(countries[j], nextIndicator);
// get the current and next score belend by interpolating by the transition progress
let blendedScore = lerp(currScore, nextScore, transitionProgress);
// get the colors for the current and next indicator based on blended score
let col = getColorForIndicator(blendedScore, currIndicator);
let nextCol = getColorForIndicator(blendedScore, nextIndicator);
// retrieve the final color by similarly interpolating by progress
// this ensures smooth color transition between the visualizations
let finalColor = lerpColor(col, nextCol, transitionProgress);
// get the country's scores for the current and next indicator let currScore = getScore(countries[j], currIndicator); let nextScore = getScore(countries[j], nextIndicator); // get the current and next score belend by interpolating by the transition progress let blendedScore = lerp(currScore, nextScore, transitionProgress); // get the colors for the current and next indicator based on blended score let col = getColorForIndicator(blendedScore, currIndicator); let nextCol = getColorForIndicator(blendedScore, nextIndicator); // retrieve the final color by similarly interpolating by progress // this ensures smooth color transition between the visualizations let finalColor = lerpColor(col, nextCol, transitionProgress);
// get the country's scores for the current and next indicator
let currScore = getScore(countries[j], currIndicator);
let nextScore = getScore(countries[j], nextIndicator);
// get the current and next score belend by interpolating by the transition progress
let blendedScore = lerp(currScore, nextScore, transitionProgress);

// get the colors for the current and next indicator based on blended score
let col = getColorForIndicator(blendedScore, currIndicator);
let nextCol = getColorForIndicator(blendedScore, nextIndicator);
// retrieve the final color by similarly interpolating by progress
// this ensures smooth color transition between the visualizations
let finalColor = lerpColor(col, nextCol, transitionProgress);

Reflections & Improvements for Future Work

I learned a lot while working on this week’s project, including new functions like lerpColor(), millis() and loadFont(). The process was not only technically challenging, but also required thinking more deeply about how to transform raw numbers into a compelling visual story. Moving forward, I hope to continue being intentional about the design and meaning of my projects.

Week 3: Generative Art

Concept

One of my goals for this week’s project was to incorporate live movement as my previous sketches were all static. I also found Tuesday’s in-class exercise on bouncing balls and collisions interesting, so I wanted to incorporate collision into my sketch as well.

I got inspiration from Casey Reas’ CENTURY piece to work with lines– specifically exploring randomness in lines in terms of coordinates, speed and strokes.

CENTURY, Casey Reas

When I think of lines in classical art, Piet Mondrian’s artworks are the first that come to my mind. I decided to take ideas from both of these artworks, each representing the digital and physical world, to create my generative artwork piece.

Piet Mondrian

Process

This sketch consists of a class for drawing a moving line across the canvas. Each line is randomly sized (length & stroke weight), located and colored; it can either move towards the top or the bottom of the screen and at randomly selected speeds. Each line moves across the canvas and when it goes out of the canvas bounds, it wraps around and appears again from where it originally started. When two lines intersect, the color of both temporarily changes to white. The colors used in the sketch are inspired by Mondrian’s color palette and a black border is drawn around the canvas to give the sketch a more traditional art feeling.

Most of the line attributes (speed, stroke weight, line count etc) are chosen randomly within two sets of boundaries and I plugged in various different values for each bound before settling on certain values to ensure that the movements are not too quick and pleasant to the eyes.

Challenges

Detecting the intersection between two lines was the trickiest part of this project and had to get mathematical. I referred to a StackOverflow comment to write the function to check for intersection between two lines, which I later found was based on Cramer’s rule.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
intersects(other) {
// function to detect line-line intersection
// reference: https://stackoverflow.com/a/55598451
let det = (this.x2 - this.x1) * (other.y2 - other.y1) - (this.y2 - this.y1) * (other.x2 - other.x1);
// lines are parallel so will never intersect
if (det === 0) return false;
let s = ((other.x1 - this.x1) * (other.y2 - other.y1) - (other.y1 - this.y1) * (other.x2 - other.x1)) / det;
let t = ((other.x1 - this.x1) * (this.y2 - this.y1) - (other.y1 - this.y1) * (this.x2 - this.x1)) / det;
// intersection occurs if both s and t are in the range (0, 1)
return s >= 0 && s <= 1 && t >= 0 && t <= 1;
}
intersects(other) { // function to detect line-line intersection // reference: https://stackoverflow.com/a/55598451 let det = (this.x2 - this.x1) * (other.y2 - other.y1) - (this.y2 - this.y1) * (other.x2 - other.x1); // lines are parallel so will never intersect if (det === 0) return false; let s = ((other.x1 - this.x1) * (other.y2 - other.y1) - (other.y1 - this.y1) * (other.x2 - other.x1)) / det; let t = ((other.x1 - this.x1) * (this.y2 - this.y1) - (other.y1 - this.y1) * (this.x2 - this.x1)) / det; // intersection occurs if both s and t are in the range (0, 1) return s >= 0 && s <= 1 && t >= 0 && t <= 1; }
intersects(other) {
  // function to detect line-line intersection
  // reference: https://stackoverflow.com/a/55598451
  let det = (this.x2 - this.x1) * (other.y2 - other.y1) - (this.y2 - this.y1) * (other.x2 - other.x1);
  
  // lines are parallel so will never intersect
  if (det === 0) return false;

  let s = ((other.x1 - this.x1) * (other.y2 - other.y1) - (other.y1 - this.y1) * (other.x2 - other.x1)) / det;
  let t = ((other.x1 - this.x1) * (this.y2 - this.y1) - (other.y1 - this.y1) * (this.x2 - this.x1)) / det;
  
  // intersection occurs if both s and t are in the range (0, 1)
  return s >= 0 && s <= 1 && t >= 0 && t <= 1;
}

The update function for moving the lines according to their gradients and wrapping them around the canvas was also a bit time-consuming as the code had to change depending on which direction the lines were going. When it got confusing, I did lots of testing with slowed up line speeds to debug and figure out why things were drawing incorrectly.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
update() {
this.x1 += this.speed;
this.x2 += this.speed;
this.y1 = this.computeY(this.x1, this.initial_x1, this.initial_y1);
this.y2 = this.computeY(this.x2, this.initial_x2, this.initial_y2);
if (this.go_up) {
// check if the line has went beyond the top or left boundary
if (this.x2 <= 0 || this.y2 <= 0) {
// reset the line so that it appears from the bottom
this.x1 = width;
this.y1 = this.computeY(this.x1, this.initial_x1, this.initial_y1);
this.x2 = this.x1 + this.delta_x;
this.y2 = this.y2 + this.delta_y;
}
} else {
// check if the line has went beyond the bottom or right boundary
if (this.x1 >= width || this.y1 >= height) {
// reset the line so that it appears from the top
this.x2 = 0;
this.y2 = this.computeY(this.x2, this.initial_x2, this.initial_y2);
this.x1 = this.x2 - this.delta_x;
this.y1 = this.y2 - this.delta_y;
}
}
}
update() { this.x1 += this.speed; this.x2 += this.speed; this.y1 = this.computeY(this.x1, this.initial_x1, this.initial_y1); this.y2 = this.computeY(this.x2, this.initial_x2, this.initial_y2); if (this.go_up) { // check if the line has went beyond the top or left boundary if (this.x2 <= 0 || this.y2 <= 0) { // reset the line so that it appears from the bottom this.x1 = width; this.y1 = this.computeY(this.x1, this.initial_x1, this.initial_y1); this.x2 = this.x1 + this.delta_x; this.y2 = this.y2 + this.delta_y; } } else { // check if the line has went beyond the bottom or right boundary if (this.x1 >= width || this.y1 >= height) { // reset the line so that it appears from the top this.x2 = 0; this.y2 = this.computeY(this.x2, this.initial_x2, this.initial_y2); this.x1 = this.x2 - this.delta_x; this.y1 = this.y2 - this.delta_y; } } }
update() {
  this.x1 += this.speed;
  this.x2 += this.speed;
  
  this.y1 = this.computeY(this.x1, this.initial_x1, this.initial_y1);
  this.y2 = this.computeY(this.x2, this.initial_x2, this.initial_y2);
  
  if (this.go_up) {
    // check if the line has went beyond the top or left boundary
    if (this.x2 <= 0 || this.y2 <= 0) {
      // reset the line so that it appears from the bottom
      this.x1 = width;
      this.y1 = this.computeY(this.x1, this.initial_x1, this.initial_y1);
      this.x2 = this.x1 + this.delta_x;
      this.y2 = this.y2 + this.delta_y;
    }
  
  } else {
    // check if the line has went beyond the bottom or right boundary
    if (this.x1 >= width || this.y1 >= height) {
      // reset the line so that it appears from the top
      this.x2 = 0;
      this.y2 = this.computeY(this.x2, this.initial_x2, this.initial_y2);
      this.x1 = this.x2 - this.delta_x;
      this.y1 = this.y2 - this.delta_y;
    }
  }
}

Reflections & Improvements for Future Work

I spent a considerable amount of time deciding on this week’s project and struggled to settle on one, but once I found an artwork I liked, I was able to seek and find more sources of inspiration. The actual implementation process, though with some challenges, went considerably smoothly once the idea was set, thanks to StackOverflow and repeated slow testing. In the next assignments, I hope to continue seeking inspiration from not only the digital world, but also from the physical world and from what I see with my eyes everyday.

Reading Reflection – Week 3

I appreciate that Crawford made the differentiation between reaction, participation and interaction. I may or may not have used reaction and interaction to mean the same things before, and now I know to do better 😉. I also liked the point that he made about interactivity existing on a continuum, rather than as a binary property, which in turn allows people to approach it in a subjective manner. In the web that we know today, interactivity exists in some form or another and in varying magnitudes, from the use of simple buttons to complex reciprocal elements. And those that incorporate interactions in meaningful and interesting ways capture attention and attract user traffic. So in this way, one could argue that there now exists a certain threshold for interactivity that applications on the web have to meet to ensure the work gets to the audience.

Building on top of Crawford’s definition of interaction – a cyclic process in which two actors alternately listen (input), think (process), and speak (output) – I think a strongly interactive system could be one that has exposed to the user multiple areas for sending input, listens carefully through those channels, and once it receives input, reciprocates by sending back a meaningful response that satisfies its purpose. It is important that the “speaking” part of the cycle conforms or relates to the “listening” and that it’s not just gibberish; if I ask a local person for directions to the Eiffel Tower in French, I expect exactly what I asked for, and not, for instance, directions to the Shibuya crossing in Japanese. In other words, it is not sufficient to just have all three segments of the cycle present for a system to be strongly interactive, and the content and quality of each is just as important in ensuring user satisfaction.

Reading Reflection – Week 2

Are order and randomness truly mutually exclusive? I found it very fascinating when the speaker spoke about the process behind the commission for a building incorporating chance and randomness, the actual procedure of preparing and creating the art was so orderly. Seen in this way, order can lead to, or support the creation of, chaos. On the other hand, there are plenty of examples from nature that shows randomness can lead to order, including in bird swarms and ant colonies. The relationship between order and randomness, therefore, seem to not be so simple.

As much as it is mesmerizing to observe the “creation”  of chance and randomness, I do think randomness should be utilized consciously. It may be easy to leave everything to chance, whether in art or elsewhere, and let it dictate the meaning of the final output; however, incorporating randomness to any work should not take time away from the ideation and meaningful reflection process that needs to happen throughout. Now that I have been introduced to great examples of how randomness can be utilized in art, I hope to explore (the presence or absence of) the boundary between order and chaos, while at the same time ensuring that, unless the main intention of the work is to observe randomness, randomness is not forced and incorporated just for the sake of having it in.