All Posts

Featured

Retro ASCII Art

Concept: Retro 3D Art

Retro Computer Graphics from Behance

I was inspired by old school computer graphics that you would see in movies like The Matrix. Because of this, I knew that I wanted to make some ASCII art in the signature green color that most retro graphics used. After some experimenting, I decided to make an ASCII representation of a Menger Sponge, which is a fractal geometry that I thought would be very interesting to look at.

Process

I began by creating a sample video that I could use to turn into ASCII art. To do this, I created a 3D cube in Processing, which is a predecessor of P5.js. I attempted to do this in P5.js, but found the saveFrame() function too limiting. I created a simple box using the 3D renderer in Processing, and added some lighting to give the sketch some dynamic range. This is important as I needed to use the difference in brightness later on when converting the video to ASCII, and the greater the dynamic range is the easier it is to perceive the ASCII video.

void setup() {
  size(600, 600, P3D);
}

float a = 0;
void draw() {
  background(0);
  noStroke();
  spotLight(10, 80, 240, width/2, height/2, 400, 0, 0, -1, PI/4, 2);
  pointLight(255, 0, 0, width/2, height/2, height/2);
  ambientLight(100, 0, 100);
  fill(255);

  translate(width/2, height/2);
  rotateX(a/2);
  rotateY(a/2);
  rotateZ(a/3);
  box(280);

  a+=PI/100;

  saveFrame("src/box-######.png");
}

I incremented the rotation angle by a fraction of pi because I wanted to be able to count when the cube resets to its original position. This made it easier to create a video that could be looped seamlessly.

Once I had the output frames, I combined them together using Microsoft Movie Maker. The final result was this video:

Next, I wanted to work on converting this footage to ASCII art. I followed Daniel Schiffman’s coding challenge on creating ASCII text images. After experimenting with the video size and character density arrays, the following sketch was the result I got:

However, I wanted to create something a bit more complex. This is when I remembered an old project that I worked on by following another one of The Coding Train‘s challenges, which was the Menger Sponge coding challenge.  After generating the frames and compiling them into a video, this was the result:

All I had to do then is to insert this video into the original code and play around with different parameters until I got the desired result.

Code Highlights

I’m really proud of the code that makes the animation look like its being built up slowly using ASCII characters. The way I achieved this is basically by filtering out the highlights on the Menger sponge. When I compiled the video, I saw that the lower right corner of the sponge had a bright highlight on it that was blocky.

//finding the right character based on brightness of pixel
let len = charArray.length;
let charIndex;

//playing with map values for the building up effect
charIndex = floor(map(apparentBrightness, 0, 100, len, 0));

When I filtered the brightest points of the sponge out, I essentially removed the lower left corner until it got a bit darker later in the animation, which created the building-up effect.

Reflection

Compared to the first assignment, I had a more solid idea of what I wanted to achieve. Because of this, I had planned out my workflow beforehand and that streamlined the entire creative process. I knew I had to create source animation and then convert it to ASCII characters. This made my code more readable, and I had better control of the sketch overall.

However, the building-up animation that I am most proud of is dependent on the source video. It looks the way it is because in the source animation the highlights are blocky as well. If I wanted to recreate this project, I want to work on some logic that allows the effect to be more generalizable. Maybe I could filter out sections of the object based on a distance function instead of the brightness levels. That way I can substitute different source videos and still get the cool effect.

Sources

 

 

Featured

Analog Musical Instrument

const int blueButton = 6;
const int servoPin = 9;
const int songLength = 8;
const int tempo = 115;

const int noteC3 = 130;
const int noteD3 = 146;
const int noteE3 = 164;
const int noteF3 = 174;
const int noteG3 = 196;
const int noteA3 = 220;
const int noteB3 = 246;


const int noteC4 = 261;
const int noteD4 = 293;
const int noteE4 = 329;
const int noteF4 = 349;
const int noteG4 = 392;
const int noteA4 = 440;
const int noteB4 = 493;
const int noteC5 = 523;

const int noteD5 = 587;
const int noteE5 = 659;
const int noteF5 = 698;
const int noteG5 = 784;
const int noteA5 = 880;
const int noteB5 = 987;


//int musicNotes[] = {noteC4, noteD4, noteE4, noteF4, noteG4, noteA4, noteB4, noteC5};
int musicNotes[] = {noteC5, 0, noteE5, 0, noteG5, 0, noteB5, 0};
int musicNotes2[] = {noteC4, 0, noteE4, 0, noteG4, 0, noteB4, 0};
int musicNotes3[] = {noteC3, 0, noteE3, 0, noteG3, 0, noteB3, 0};
int noteDuration[] = {2, 4, 2, 4, 4, 2, 4, 2};

void setup() {
  pinMode(servoPin, OUTPUT);
  pinMode(blueButton, INPUT_PULLUP);
  Serial.begin(9600);
}
void loop() {
 lightSensor();
  button();



}

//new tab: button
void button() {

  
  bool bluebuttonState = digitalRead(blueButton);
  if (bluebuttonState == HIGH) {
    for (int i = 0; i < songLength; i++) {

      int duration =   noteDuration[i] * tempo;

      tone(servoPin, musicNotes2[i], duration);
      delay(duration); //make the length of the time = length of the musical note(frequency)
      delay(15);

    }
  }    else {
    for (int i = 0; i < songLength; i++) {

      int duration =   noteDuration[i] * tempo;

      tone(servoPin, musicNotes[i], duration);
      delay(duration); //make the length of the time = length of the musical note(frequency)
      delay(15);


    };


  };

};
//new tab light sensor
void lightSensor() {
  int analogValue = analogRead(A0);
 Serial.print(analogValue);
  if (analogValue < 10) {
   Serial.println(" - Dark");
//   //add music note here
  } else if (analogValue < 200) {
   Serial.println(" - Dim");
    for (int i = 0; i < songLength; i++) {

     int duration =   noteDuration[i] * tempo;

     tone(servoPin, musicNotes3[i], duration);
     delay(duration); //make the length of the time = length of the musical note(frequency)
     delay(15);

    }

  }  };


Documentation:

Idea: Create sound directly from Arduino, like a musical drone sound by changing the musical notes from C4 to C5 when you click on the button or change from either to a 3rd octave when you dim the light sensor (all by changing the frequency from within the motor).

Positives:

I like how I could manipulate the motor sound based on its frequency to create a tone and tempo.In addition to that I was able to play around with that frequency within the motor to create an array of musical notes with different octaves. Once I could adjust the tempo and time spacing between notes through looping the array, I was able to integrate that into different parts of the code.

I like that I was able to introduce different noises from the motor by adding in different components that trigger different sounds like the button and sensor.

This was also surprisingly fun compared to other assignments for me because I learned that Arduino could be used for more than just LED circuits etc but you can incorporate and manipulate other media like sound.

Negatives:

I don’t think its particularly musical, however I think it follows the rules of music in terms of octaves and musical notes.

 

 

Featured

Candy colored spiral visual effect

This has gone through many iterations of trial and error to get the coolest effect.

Im using spiraled ellipses as a main element for my mid term, so I have been experimenting with it.

Code for creating the spiral:

float angle;

float x;
float y;
void setup() {
  size(800, 800);
  noFill();
  shapeMode(CENTER);
}

void draw() {

  //fill(255);
   fill(255,200,200); //pale rose, change the color of the candy.
  ellipse(height/2, width/2, 600, 600);
  
  int hundred=100;
  //rotate(angle);

  for (int i=0; i<500; i+=100)

  { 
    strokeWeight(20);
    stroke(0); //change the color of the spiral
    noFill();
    ;
    arc(height/2, width/2, hundred+i, hundred+i, radians(180), radians(360) );
    arc(height/2-25, width/2, hundred*1.5+i, hundred*1.5+i, radians(0), radians(180));
  }
  //angle=angle+0.1;
  //save("mySpiral.jpg");
};

I exported the code above to a .jpg format to use as an image in the sketch below.

Code for the animation:

float angle;
PImage img;
void setup() {
  size(900, 900);
  img = loadImage("mySpiral.png");
};
void draw() {
    background(255);
  for (int i=0; i<width; i++) {
 
     translate(width/2+i, height/2+i);
  imageMode(CENTER);
  rotate(angle);
  image(img, 0, 0,300,300);
  
 
  }
 angle=angle+1*1;
}

 

Next step :

I would like each candy spiral to go through several colored versions of itself:

 

  for (int a= 0; a<1; a++) {
     save(random(20)+"spiral");
  }

This code allows different colored versions from random to be saved as a new version of itself , I plan on using the saved photos as part of a sprite sheet to add to the animation above.

Featured

Psychedelic Geometry :)

float angle;
float grid= 400;
float i=0; 
void setup() {
  size(1080, 900,P3D);
};

void draw() {
 
  for(int x=0;x<=width;x+=grid){
    for(int y=0;y<=height;y+=grid){
  translate(mouseX,mouseY);
rotateX(radians(angle));
rotateY(radians(angle));
i=0; 
  while(i<width){
    i=i+20;
  beginShape();
vertex(i+100,100);
vertex(i+175,50);
vertex(i+250,100);
vertex(i+250,200);
vertex(i+175,250);
vertex(i+100,200);
vertex(i+100,100);
vertex(i+100,200);

endShape();
  };
  
  };
  };
//rotateX(radians(angle));
angle+=1; //does a full rotation
};

For this assignment,

I went through A LOT of experimentation,

at first, I wanted to recreate this pattern:

I almost got there:

Then, I didn’t really know what to do with it in terms of animation….

I  discovered the built in P3D  and started different experiments, but  I didn’t know how to integrate more effects while using the While loop, so I experimented further and created the animation at the top of the post.

The inspiration of this post for me was to experiment as much as possible, since it’s been helping me understand the mechanics of the code. And to save different blocks of code on GitHub if I find something interesting I could integrate in a different art piece later.

Reading Response: What is interactivity?

In Chapter 1 of The Art of Interactive Design, Chris Crawford describes interactivity as a continuous loop with three steps: listening, thinking, and speaking. A system that truly engages users is one that doesn’t just react—it listens, processes input and responds differently based on type of the input.

The strongest interactive systems create a kind of “conversation” between the user and the program. It’s not just about clicking a button and getting a fixed response, but more about the system understanding the user’s input and adapting to it. For example, a simple calculator provides weak interaction because it only gives pre-programmed results. On the other hand, a video game provides a much richer experience by reacting to the player’s choices and strategies in real time.

To make my p5.js sketches more interactive, I could start by making them more adaptive to user behavior. My most important takeaway from the chapter is separating ‘participation’ as compared to ‘interactivity’. In my old sketches I have used ‘participation’ where no matter how the user approaches the system, the system outputs from a list of fixed effects. Moving to making my systems interactive would mean that any system I design would not just respond the same to each output but contain an algorithm that accurately digests the variability of inputs whether that is length, type of interaction, active or passive interaction and then react accordingly.

Instead of just reacting with basic changes like color shifts when the mouse is clicked, I hope to now build in more complex systems of interactivity in my sketches. For instance, if the user holds the mouse down longer, maybe the system reacts in a different manner. Or, I could design it so that the faster someone moves their mouse, the more dramatic the visual change. The more, the user thinks that not just their input matters, but their type of input matters as well, the more I will know I did a good job creating an interactive piece of media. By adding this kind of real-time feedback, the sketch can feel more dynamic and have deeper user involvement, a system of ‘inputs’, ‘processing’ and ‘outputs’, that Crawford would consider ‘interactive’.

Assignment 3 Generative AI

Concept:

For this project, I viewed a lot of generative artwork created using P5.JS but the idea for my particular design did not click until I was watching a 3Blue1Brown, a math youtuber’s video on Dirichlet’s theorem. The theorem tries to explain how prime numbers create certain spirals, and the randomness of primes means you cannot accurately predict the pattern. Thus the idea of working with random spiral shapes was born. I decided to create a 6×6 grid of spirals, each generating a gradient of color for unique spiral shapes. I wanted to add a level of interactivity which I did by making the spirals spin when the mouse hovers over them (mimicking an electron’s spin and change of behavior when ‘observed’). Furthermore I made it so that any mouse click on a particular spiral, changed the direction of the spin.

Code I am particularly proud of:

A part of the code I am particularly proud of is the draw_ background() function that created a smooth gradient for the background. In plain white the sketch looked dull and uninteresting but I believe the new gradient background has allowed each spiral color to ‘pop’.

//this function draws the background gradient going from dark blue to pink
function draw_background() {
  let c1 = color(60, 120, 200);  // navy blue shade
  let c2 = color(255, 182, 193);  // light pink shade
  //loop through each pixel row to create the gradient
  for (let y = 0; y <= height; y++) {
    let gradient_color = lerpColor(c1, c2, y / height);  //finds an in-between color for each pixel depending on height in the sketch
    stroke(gradient_color);  // color for the current row
    line(0, y, width, y);   // draws a line of that color
  }
}

 

Final Sketch:

Reflections:

I am happy with the results of this assignment. It is visually interesting, pleasing to look at and interactive but I do believe there is room for improvement. Another level of interactivity I hope to add is the spirals changing colors when hovered over or maybe when clicked upon. Another idea I had was the spirals morphing into different shapes based on user movement across the canvas. Overall I believe this assignment sets up a good basis for further development of this seemingly simple idea.

Week 3 Assignment: Generative Artwork using OOP: Screensavers and Physics

Concept and Inspiration

For this assignment, I drew inspiration from the the era of late 1990s and early 2000s. At that time, Windows 98 and Windows XP had some unique screensaveers which I still remember and which evokes a sense of nostalgia in me. These screensavers (link) often featured geometric shapes and repetitive, hypnotic patterns, such as oscillating lines, spirals, and pendulum-like movements.

Fig: Some of the Windows Screensavers

For this, I used robotic arms and “Inverse Kinematics” as my inspiration. From my point of view, I saw this as the perfect oppertunity to blend computational techniques into this visual style. The robot arms represent the pendulums of the past, but with a unique twist. Instead of being merely simple lines, these arms demonstrate the principles of Object-Oriented Programming (OOP), where each pendulum is treated as an independent object, following specific behaviors such as oscillation and length growth. Moreover, inverse kinematics allows each arm to dynamically respond to changing positions, mimicking the flexibility and precision of robotic motion. The result is a digital artwork that blends the nostalgia of retro visuals with the sophistication of modern computational design.

Code Explanation

“Arm” class

This class features a constructor which initializes each pendulum’s amplitude, angle, angle velocity, and length growth. It also has the “update()” function to update the pendulum’s length (amplitude) and oscillation (angle). Using the “display()” function, it calculates the current position of the pendulum and draws a line from the previous position.

// Defining the Arm class
class Arm {
  constructor(amplitude, angle, angleVel, lengthGrowth) {
    this.amplitude = amplitude;      // Initial length of the arm
    this.angle = angle;              // Starting angle
    this.angleVel = angleVel;        // Angular velocity
    this.lengthGrowth = lengthGrowth; // How fast the arm grows in length
  }

  // Method to update the arm's properties (growth and oscillation)
  update() {
    this.amplitude += this.lengthGrowth; // Increase length over time
    this.angle += this.angleVel;         // Update angle for oscillation
  }

  // Method to display the arm
  display(prevX, prevY) {
    let x = sin(this.angle) * this.amplitude;  // Calculate x position
    let y = cos(this.angle) * this.amplitude;  // Calculate y position
    line(prevX, prevY, x, y); // Draw line from previous position to current
    return { x, y };          // Return current x, y for the next arm
  }
}
“setup()” function

The “setup()” function initializes the canvas size and prepares the environment. It disables fills for the shapes and sets default stroke properties. It randomizes the number of pendulum arms (num_arms) and the other arm’s properties, with each arm receiving random values for amplitude, angular velocity, and growth rate. The arms are stored in an array, each represented as an object with properties for oscillation and growth.

function setup() {
  createCanvas(800, 800);
  noFill();
  stroke(255); // Initial stroke color
  strokeWeight(1); // Initial stroke weight

  // Randomize the number of arms between 2 and 10
  num_arms = int(random(2, 10));

  // Initialize the Arm objects with random values
  for (let i = 0; i < num_arms; i++) {
    let amplitude = random(70, 150);
    let angleVel = random(0.01, 0.05);
    let lengthGrowth = random(0.1, 0.5);

    // Create new Arm and push to the arms array
    arms.push(new Arm(amplitude, 0, angleVel, lengthGrowth));
  }

  // Initially set the center to an off-canvas position
  centerX = -1000;
  centerY = -1000;
}
“draw()” function

This function creates a semi-transparent background overlay to maintain the fading trails without fully erasing the canvas. The “rect()” draws a slightly transparent rectangle over the entire canvas, producing the trailing effect. The “translate()” function shifts the origin of the canvas to the clicked point (centerX, centerY), which acts as the center of the pendulum system. A loop iterates over each arm, calculating its new position based on its current angle and amplitude using “Inverse Kinematics.” The arms are drawn as lines connecting from one pendulum to the next, simulating the robot arm movement whos length increases with time.

// Draw Function
function draw() {
  if (hasStarted) {
    fill(0, 10); // Semi-transparent background to maintain trails
    rect(0, 0, width, height);

    // Set the center of the arms to the clicked position
    translate(centerX, centerY);

    let prevX = 0;
    let prevY = 0;

    // Loop through each arm and update and display them
    for (let i = 0; i < arms.length; i++) {
      let arm = arms[i];

      // Update arm properties
      arm.update();

      // Display the arm and update the previous position for the next one
      let newPos = arm.display(prevX, prevY);
      prevX = newPos.x;
      prevY = newPos.y;
    }
  }
}
“mousePressedI()” function

The “mousePressed()” function updates the center of the pendulum system to wherever the user clicks on the canvas (mouseX, mouseY). This triggers the pendulum animation by setting “hasStarted” to true. Upon clicking, it randomizes the stroke color, weight, and number of arms, creating variety and making each user interaction unique. It also reinitializes the pendulum arms with new random values, ensuring a different pattern is generated with every click.

// This function will run when the mouse is pressed
function mousePressed() {
  // Set the new center of the arm system to the clicked location
  centerX = mouseX;
  centerY = mouseY;
  hasStarted = true;

  // Randomize background and stroke properties
  stroke(random(0, 255), random(0, 255), random(0, 255));
  strokeWeight(random(1, 10));

  // Randomize the number of arms between 2 and 6
  num_Arms = int(random(2, 6));

  // Reinitialize the arms array
  arms = [];
  for (let i = 0; i < num_Arms; i++) {
    let amplitude = random(80, 150);
    let angleVel = random(0.01, 0.05);
    let lengthGrowth = random(0.1, 0.5);

    // Create new Arm objects with random values
    arms.push(new Arm(amplitude, 0, angleVel, lengthGrowth));
  }
}

Sketch

Further Improvements which can be made

Smoother Transitions: Currently, the background might change too quickly when clicking. Adding a smooth transition effect between pendulum sets can make the animation more fluid and visually appealing.

Scaling upto 3D space: I had originally though of introducing a responsive 3D canvas using “WEBGL” mode in p5.js, but that was making the idea of user interaction a little complex, so I had to drop that for now.

Damping: Currently, the simulation runs the pendulums until another person clicks it. Introducing damping can be another way to introduce realism to it.

Collission: Various arms when coming in contact with each other change their path/length can be another aspect which can be looked to.

Reflection

This project modernizes the retro screensaver aesthetic using modern programming techniques such as OOP and inverse kinematics, combined with user interactivity. The code is modular, making it easy to add new features or improvements, and the possibilities for further customization and expansion are vast.

Reading Reflection 2

The art of interactive design:

Chapter One of “The Art of Interactive Design,” Chris Crawford uses the example of conversation to explain the importance of feedback. He points out that just like in a conversation between people, where you expect immediate and relevant responses to keep the dialogue going, interactive systems also need to provide clear and timely feedback to keep users engaged.

Applying this idea to my artwork or interactive sketch, I should think of the system as if it were another person in a conversation with the user. If the system doesn’t respond quickly or appropriately, it’s like talking to someone who doesn’t reply or doesn’t give useful responses. This would make the interaction feel disconnected and less interesting.

In my p5.js sketch with bubbles, if the outer circles don’t react well to user interactions, it’s like having a conversation partner who ignores what you say. For example, if the outer bubbles don’t clearly expand or contract in response to user actions, or if clicking on a bubble doesn’t produce a visible effect, it would be frustrating for users. They wouldn’t get the feedback they need to understand what’s happening or adjust their actions.

To improve this, I should make sure that the artwork responds clearly and promptly to user actions, just like a good conversation partner would. This means making the outer bubbles change size in a noticeable way when interacted with, and adding visual or sound effects when bubbles are clicked. This approach makes the system feel more alive and engaging, similar to how a lively conversation keeps people interested and involved.

Assignment 3: Wallmart OSU

Concept:

In this project, I utilized objects and classes to recreate one of my favorite rhythm games called OSU. I took inspiration from OSU’s circles mode gameplay where the player needs to click on the ‘beat’ circle once an outer contracting circle coincides with the inner ‘beat’ circle, in time with the rhythm as referenced in the image below.

osu! Skins | Circle People

Highlight:

During the creation of this sketch, I began by simplifying the interface and establishing the core functionality. I decided to start with a canvas containing a set number of circles. When a viewer hovers their mouse over the canvas, outer circles corresponding to the initial circles would appear. These outer circles would contract until their diameter reached zero, then expand again up to a predefined maximum diameter. Additionally, if the viewer clicks on a circle while the outer circle is either within or touching the inner circle, both the inner circle and its respective outer circle would disappear.

To achieve this, I first developed the Bubble class to handle the drawing of the initial circles on the canvas. Next, I created the outerBubble class, which managed the appearance and resizing of the outer circles. I designed functions to make these outer circles appear and update their diameter accordingly.

// Create an outer expanding circle for the bubble
bubblepopper() {
  let outerCircle = new outerBubble(this.x, this.y, 150, this.colorValue);
  this.outerBubble = outerCircle;
  expandingBubbles.push(outerCircle);
}

A key challenge was ensuring that the outer circles correctly enveloped their respective inner circles. I solved this by calling the outerBubble class functions from within the Bubble class, which allowed the outer circles to align precisely with the inner circles.

function mousePressed() {
  // Remove bubble and expanding circle if clicked within the bubble and near the circle
  for (let i = 0; i < bubbleList.length; i++) {
    if (bubbleList[i].click() && expandingBubbles[i].diameter - bubbleList[i].radius < 5) {
      bubbleList.splice(i, 1);
      expandingBubbles.splice(i, 1);
    }
  }

The most difficult part of the project was making the circles disappear when clicked at the right moment. I tackled this by using the splice() method to remove elements from the arrays containing both the inner and outer bubbles. By looping through these arrays, I was able to erase the clicked bubbles efficiently.


Reflections:

In completing this sketch, I aimed to enhance the user experience by introducing complexity through a more interactive and skill-based challenge. The current version allows users to pop bubbles when the outer circle is touching or overlapping with the inner circle. However, I realized that this mechanic doesn’t fully capture the level of precision I initially envisioned for the project.

For future improvements, I would like to introduce a condition where the user needs to be very precise with their timing. Instead of allowing the bubble to be clicked as long as the outer circle touches the inner circle in any way, I want to restrict the interaction so that the bubble can only be popped when the outer circle perfectly aligns with the outline of the inner circle. This would require more skill and quick reflexes from the user, making the game more engaging and challenging. The added difficulty would create a more rewarding experience for players as they master the timing needed to pop multiple bubbles in one session.

Assignment 2: Loops – Passage of Time

Concept:

I wanted to create a piece about the movement of clouds in the sky. It is reminiscent of when I traveled with my family to Switzerland and I would always stare at the clouds from the balcony. They appear still, but when I would leave and come back, the clouds would’ve moved and changed positions and the sky would look different. The piece isn’t exactly about the Switzerland scenery I saw, of mountains covered in snow and trees, but rather a beach scene. The reason for choosing a beach scene is because there is more movement in it, of the waves and the shadows and light and birds and rain and wind. I wanted to be able to create movement using loops, so with every detail I added to the piece, I used a loop to make it more dynamic instead of being a still piece.

Code I’m proud of:

Overall, I was very proud of the clouds in the end. It was a struggle to figure out their movement and to vary their positions. I first created the clouds in the sky without thinking of their movement, and I was later going to use loops to generate them instead of creating them one by one. This was so confusing for me and I didn’t know how to do it. Moving the clouds was simple; I just had to increment their x-coordinates. What wasn’t simple was generating them at different positions with variable sizes. I used the random function to create the offset values for the clouds, slightly varying their shapes with every new iteration of the code. 

//loop through clouds to draw them at different positions 
for (let i = 0; i < 6; i++) {
  //move the cloud 
  cloudX+=2; 
  //set cloud position based on current iteration 
  if (i == 0){
    xDiff = x1;
    yDiff = y1;
  }
  else if (i == 1) { 
    xDiff = x2; 
    yDiff = y2; 
  } 
  else if (i == 2) { 
    xDiff = x3; 
    yDiff = y3; 
  } 
  else if (i == 3) { 
    xDiff = x4; 
    yDiff = y4; 
  } 
  else if (i == 4) { 
    xDiff = x5; 
    yDiff = y5; 
  } 
  else if (i == 5) {
    xDiff = x6; 
    yDiff = y6; 
  } 
  //drawing the cloud 
  ellipse(cloudX + xDiff, 75 + yDiff - 50, 100, 80);
  ellipse(cloudX - 40 + xDiff, 60 + yDiff - 50, 80, 60);
  ellipse(cloudX + 40 + xDiff, 70 + yDiff - 50, 60, 50);
  ellipse(cloudX - 20 + xDiff, 70 + yDiff - 50, 80, 60);
}

The loop runs 6 times to draw the 6 different clouds. I used if and if else statements to set the values of xDiff and yDiff. Once they were set, each cloud was drawn according to its position as stored in those variables.

What I had to also figure out was how to get the clouds to loop, meaning when they leave the frame, I wanted it to reappear again on the other side. This was confusing to do and I wasn’t sure what code to write to make it work, but in the end I figured out that I simply had to reset the cloud position. 

//reset clouds once they move off the screen
if (cloudX > width+50) {
  cloudX = -100; //reset cloud to start over from the left
}

The code checks if the cloud has moved off the canvas and resets the x-coordinate, causing the clouds to loop back to the left side of the canvas and start moving across again. 

In the night scene, I was proud of the code I wrote for the lighting. 

//lightning
if (random(1) < 0.03) { //3% chance of lightning 
  stroke(255);
  strokeWeight(3); 
  let xLightning = random(width);
  let yLightning = 0; 
  //7 lines to form the lightning
  for (let l = 0; l < 7; l++) {
    let xLightningEnd = xLightning + random(-20, 20); 
    let yLightningEnd = yLightning + random(30, 50);  
    line(xLightning, yLightning, xLightningEnd, yLightningEnd); 
    xLightning = xLightningEnd; 
    yLightning = yLightningEnd;
  }
}

The if statement at the beginning of this code segment was interesting to me because I could determine the chance that lighting would occur on the screen; I did a 3% chance of the lighting occurring. To generate the actual lighting piece, I chained 7 line segments together that varied in their x and y positions so that the lightning would occur on different parts of the screen. The lighting starts at a random x position at the top of the canvas and descends downwards (in a jagged way) based on random values for the end positions of the lines. 

Embedded sketch:  (press down to see night scene!!)

Reflection:

I was really happy with the end product. Every element of the piece required thinking, but bringing all the elements together formed a beautiful end piece. I kept adding random details which made for a coherent piece in the end. I started with the background (the sky and the sun and moon). I, then, worked on the clouds for a long time because I couldn’t figure out how to do it in the beginning. Then I added the beach scene with the moving waves and the changing grains of sand. The piece still felt empty, so I added a boat and added changing shadows and movement to make it look like the boat was swaying on the water. The scene still looked empty, so I added birds in the day and rain at night, and finally lighting because I wanted to contrast the day and night scenes more drastically. It took a long time to do this, but I eventually got a rhythm to my work, because I would focus on one element, make sure it worked before adding a new element. I didn’t add the beach until I finished the clouds and I didn’t add the boat until I finished the waves and sand, etc. 

Ideas for future work and improvements: 

For future work, I would love to play with more motion. I like the passage of time that this piece elicits. It’s as if it’s a time lapse of a boat on the water, from day to night. I want to play with more movement in different scenes, maybe like the look of building in the day and at night when all the lights are on, or when the street lamps turn on at night, illuminating the world, or when the world is so busy in the morning with cars rushing past and people trying to get to work and school and more, but the quiet of night when few cars pass on the road. I might also want to do something like this: as if you are sitting in a car and watching the view from the window, and the view is constantly changing as you drive on and on. 

For improvement, I would like to make my code more efficient. I kept changing the code, adding more and more variables to be able to iterate and change them. However, maybe if I used an array for the clouds, it would’ve been easier to do, or if I made the cloud an object in and of itself. Maybe I’d also want to use classes to encapsulate the behaviors of the different elements I have in the scene, of the clouds, the birds, the lighting, etc. 

ASSIGNMENT 3

Concept:

For this assignment my main inspiration came from my niece, actually, during family lunch, she was playing around with a kaleidoscope, and i remembered how as a kid, I was so intrigued by them. I searched up inspirations and patterns online and i definitely wanted to try and create a design similar to a kaleidoscope. So i created jittery, dot shape that turns into this, flowy dot when you click on the mouse, just as how the shapes change when you rotate a kaleidoscope.

Reflection:

Honestly the code wasn’t as complex and was made up of mostly arrays, OOPS what we did in class mostly, and maybe next time i should try and be more complex with it and try new codes not taken in class, but I’m still very proud of the outcome. However, I did try sin and cos to create the waviness and jitter movements with the shape , which i got inspired from Amna’s previous code last week, and I’d say that’s the code I’m most proud of as it was something new to me that took some time to get right, but after reading about it on the P5 website, I figured it out, turns out it was just coordinates and using the function random to create those flowy movements, and this is the code that allows for the dots to then turn into wavy dots so it can then look like those squares when u click/toggle on the mouse, hence if I remove it the dots would just flow up and down or side to side instead of creating the square trail.

here is code im most proud of:

// Function to move the dot
move() {
  // Create two different movement patterns based on `flowDirection`
  if (flowDirection === 0) {
    this.x += random(-2, 2); // Random jitter in the x direction
    this.y += random(-2, 2); // Random jitter in the y direction
  } else {
    this.x += sin(this.y * 0.05) * 2; // Wavy movement based on the y-coordinate
    this.y += cos(this.x * 0.05) * 2; // Wavy movement based on the x-coordinate
  }

here is my sketch:

Assignment #3: Non-Ugly Ducklings


Concept & Inspiration.

My primary inspiration comes from the game Flappy Bird, which I used to play on my phone as a child. While reflecting on it, I didn’t intend to fully replicate the game, but rather merge its concept with something else. I then remembered the children’s book The Ugly Duckling and thought about incorporating that theme. One duckling may be considered ugly, but what if there were an endless number of them? Would they still seem as ugly?

With this idea in mind, I decided to write code using classes, and through careful use of color, I aimed to show that a whole flock of supposedly ugly ducklings might not look so ugly after all.

Highlights.

For this project, I created a class and made it an array birds[] so that the user can add endless number of ducklings on canvas. Moreover, I used the .push and .pop functions for adding and removing the ducklings. Even though it took me a while to understand how to flip the bird horizontally after bouncing, I eventually implemented it in my code by creating the facingRight variable inside the class.

// Flip the bird after bouncing
if (this.x > width - 60 || this.x < 0) {
  this.xspeed *= -1;
  this.facingRight = !this.facingRight;
}

It was also the first project where I worked with text() (which was relatively easy) and some of the text-related functions.

 if (birds.length === 0) {
  fill(255);
  textSize(24);
  textAlign(CENTER);
  textStyle(BOLD);
  textFont("Helvetica");
  text("mouse click to add a duckling", width / 2, height / 2);
}

Reflection.

I am proud of this project, as I was able to implement almost all the new functions we learned in class this week. Additionally, I experimented with incorporating text for the first time. One challenge I faced was with the ducklings near the right corner, which glitch after a mouse click. I attempted to solve this issue using generative AI, but unfortunately, I wasn’t successful.

Overall, I believe the project achieved its intended purpose. In the future, I hope to create a more detailed background for the canvas, as I didn’t have enough time to include that in this version.

Embedded Sketch.

Mouse click to add a new duckling & click any key to remove the last one.

Assignment 3: Apa itu Buah Kawung? (What is Buah Kawung?)

Concept

Batik is an Indonesian pattern. Batik-making is a traditional art that uses wax to create Batik patterns on a fabric. I wanted to recreate a Batik pattern for this assignment. The main pattern that serves as my inspiration is the Batik Kawung pattern, which I incorporated into my work as the background.

Initially I didn’t know what Kawung meant, but after researching, Kawung is a fruit (Sugar Palm fruit) and this fruit is the inspiration behind the Batik Pattern. I also incorporated this fruit into my piece as the inspiration behind the floating yellow flowers that appear when the mouse is clicked anywhere on the canvas.

My next inspiration came from the use of floral designs in many Batik textiles, so I went onto Pinterest to look for a specific color scheme as well as flower designs. I love the look of a soft color palette, especially pinks and greens, so I went with this specific Pin as my main inspiration for the objects in my work.

 Highllight

My favorite part of the code has to be the functions I made for the main central flower. I learnt how to use the rotate function to create the petals of the flower, and I’m proud of myself for figuring out how to create the line details on the petals (with increasing and decreasing lengths) using for() loops because it took me quite a while to get it right since I kept messing up the angle of rotation between each repetition. I challenged myself further by making a spin() function in my Flower() class so that whenever the mouse touches the flower, the flower will start spinning.

show() {
  angleMode(DEGREES);
  translate(200, 200);
  rotate(this.angleSpeed);

  // outer flower
  stroke("rgb(245,235,216)");
  fill("#65180b");
  for (this.petal = 0; this.petal <= 7; this.petal += 1) {
    ellipse(0, 50, 60, 80);
    rotate(45);
  }

  // line detail
  for (this.petal = 0; this.petal <= 7; this.petal += 1) {
    for (this.i = 0; this.i <= 10; this.i += 5) {
      line(0, 0, 0, 60 - this.i);
      rotate(7.5);
    }

    for (this.i = 0; this.i <= 10; this.i += 5) {
      line(0, 0, 0, 45 + this.i);
      rotate(7.5);
    }
  }

  // inner flower
  stroke("#65180B");
  fill("#df8d7b");
  for (this.petal = 0; this.petal <= 7; this.petal += 1) {
    rotate(45);
    ellipse(0, 20, 30, 40);
  }
}

spin(){
  // spin flower
  this.angleSpeed += 2
}

 

Sketch

Hover over the central flower to see it spin and click on any part of the canvas to add more designs onto the piece!

Reflection

In this project I got to learn more about the history of Batik patterns as well as the origins behind them. In terms of technicalities, I see the appeal of using Objects; it made this project so much neater and more organized than my previous ones. Incorporating my culture into making this project is something I would definitely think about for future projects.

Assignment 3 – Celestial Drift

Inspiration

For this project, the task was to create a generative art piece. I decided I wanted to create something that captured the beauty of natural randomness, yet felt interactive and personal. I really love stargazing so this idea was immediately accompanied by a desire to incorporate stars (in a night sky setting) in some way. I thought it would be interesting to also use the stars to make randomly generated constellations. Initially, most of my ideas were all over the place and ended up progressing along with different stages of project.

Concept

After combining all of these concepts and also trying to add an element of interactivity, I arrived at a more cohesive idea. This piece essentially generates stars that slowly drift across the canvas and form constellations by connecting with nearby stars. These connections fade as the stars move farther apart, creating an ever-changing night sky. By clicking on the canvas, users can add new stars, and hovering the mouse near existing stars creates connections with the mouse pointer, making the viewer an active part of the art. I tried to replicate a portion of the night sky by creating a background gradient, which shifts from dark blue to a purplish hue.

Implementation

Here’s what it looks like!

The core of the project focuses on the generative placement and connection of stars, while other features such as mouse interactions and background gradients enhance the experience.

Most of the functionality of the code resides in the Star() class. I also initially created a global variable numStars which controls the initial number of stars that appear on the canvas (currently set to 70). However, more stars can be added when the user clicks on the screen.

Here are important aspects of the code that I’m proud of :))

1. Generating Stars and Constellations

The star generation begins by randomly placing stars across the canvas. Each star drifts slowly, giving the impression of a calm, subtly changing night sky. The stars are connected by lines if they are within a specific distance from each other, creating constellations. These lines fade with increasing distance between the stars they connect and disappear completely once they cross a certain threshold. The distance threshold is adjustable (maxDistance), allowing for varying constellations as stars drift closer or further from one another.

/ Connecting stars that are within a certain distance
  connect(stars) {
    for (let i = 0; i < stars.length; i++) {
      let d = dist(this.x, this.y, stars[i].x, stars[i].y);
      if (d < maxDistance) {
        stroke(255, 255, 255, map(d, 0, maxDistance, 255, 0)); // making line fade as distance increases
        line(this.x, this.y, stars[i].x, stars[i].y);
      }
    }
  }

2. User Interaction

User interaction is a key feature of the project. Clicking anywhere on the canvas creates a new star at the mouse’s location.

// Adding a new star at the mouse position when the mouse is clicked
function mousePressed() {
  let size = random(2, 5); // the star created has a randomized size
  stars.push(new Star(mouseX, mouseY, size)); // creating a new star at the mouse position (with given size)
}

In addition to adding stars, the mouse also exerts an influence on the stars already in place. When the mouse hovers near a star, stronger and thicker connections are drawn between the star and the mouse, creating a sense of gravitational pull. This connection changes based on the distance between the mouse and the stars, offering a layered sense of interactivity.

// Connecting stars according to mouse position
  mouseConnect() {
    let mouseDist = dist(this.x, this.y, mouseX, mouseY);
    
    // If the star is within the influence of the mouse, create more prominent connections
    if (mouseDist < mouseInfluenceDistance) {
      stroke(233, 217, 255, map(mouseDist, 0, mouseInfluenceDistance, 255, 50)); // Stronger lines with mouse
      strokeWeight(map(mouseDist, 0, mouseInfluenceDistance, 2, 0.5)); // Thicker lines near the mouse
      line(this.x, this.y, mouseX, mouseY); // Draw line to the mouse position
    }
  }

3. Gradient Background

The background gradient is crucial to creating a more realistic cosmic atmosphere. A vertical gradient transitions from a deep, dark blue at the top to a lighter, purplish hue at the bottom, simulating the natural gradient of a night sky fading into early dawn. This gradient creates a contrast to the white stars and their connections, making the work feel more immersive. In creating this gradient, I learnt how to use lerpColor(), which allowed me to smoothly blend two colors based on a transition value. By mapping the vertical position of each line to a gradient value, I was able to simulate the soft shift from night to early morning. The setGradient() function essentially draws horizontal lines from the top to the bottom of the canvas (slightly changing the color each time to create a smooth gradient).

// Creating a vertical gradient
function setGradient(x, y, w, h, c1, c2) {
  for (let i = y; i <= y + h; i++) {
    let inter = map(i, y, y + h, 0, 1); 
    let c = lerpColor(c1, c2, inter); // when inter is 0,  the colour is c1 and when inter is 1, the colour is c2 (for any other value it is a mix of c1 and c2)
    stroke(c);
    line(x, i, x + w, i); // drawing a line across the width of the gradient area
  }
}

Reflections and Further Improvements

This assignment gave me the chance to play around with randomness and interactivity in a simple yet engaging way. I enjoyed seeing how small things like mouse movement and clicks could bring the stars to life and create an immersive atmosphere. The project feels peaceful yet active, and I’m happy with how the core mechanics came together.

Looking ahead, I’d like to add more depth to the stars—maybe a twinkling effect or layered constellations with different star types. It could also be interesting to introduce sound, where each star interaction triggers a soft note, giving the project an audio element. Another idea is to have the background gradient shift gradually over time, mimicking the change from night to dawn for added realism. I could maybe also experiment with adding other celestial bodies like planets and nebulae.

Overall, I am pretty proud of how this turned out and look forward to making more advanced projects :))