Week 3 – Shahram Chaudhry – Connections

We are surrounded by people every day , especially as students living on campus. We run into people everywhere, be it the dining hall, the library or even the laundry rooms. I wanted to capture that feeling by filling the entire canvas with people (the dots represent people).

But we don’t actually connect with everyone we see. In my sketch, I use distance as a threshold that determines whether a line forms between two people. The line symbolizes the emotional closeness. If two people get close enough emotionally, a connection forms between them. But life happens. People drift apart, connections break (i.e. line disappears). This is very dynamic, just like in real life.

Also, when the user clicks, a new person appear almost like meeting someone new. At first they are brightly colored because they don’t exactly fit in our network of people just yet, but over time they fade into black, representing how over time they become part of our network. While they are still “new” they don’t form any connections which mirrors how it usually takes time before we can emotionally connect with a new person. The more you click, the more new people are added, just like how engaging with others more often leads to a growing network.

Here’s the code:

let people = [];
let numPeople = 30;
let connectionDistance = 80;

const newPeopleColour = [
  [255, 0, 0],    
  [0, 255, 0],    
  [0, 0, 255],    
  [255, 255, 0],  
  [255, 0, 255],  
  [0, 255, 255],  
  [255, 128, 0],  
  [128, 0, 255]   
];

function setup() {
  createCanvas(600, 400);
  // Create initial people 
  for (let i = 0; i < numPeople; i++) {
    people.push(new Person(random(0,width), random(0,height), false));
  }
}

function draw() {
  background(240);
  for (let i = 0; i < people.length; i++) {
    // Update & display the person
    people[i].move();
    people[i].updateColor();
    people[i].display();

    // Check distance with all other people and make connections 
    for (let j = i + 1; j < people.length; j++) {
      let d = dist(people[i].x, people[i].y, people[j].x, people[j].y);
      //if close enough and not new make connection
      if (!people[i].isNew && !people[j].isNew && d < connectionDistance) {
        stroke(0,50);
        line(people[i].x, people[i].y, people[j].x, people[j].y);
      }
    }
  }
}

function mousePressed() {
  people.push(new Person(mouseX, mouseY, true));
}

class Person {
  constructor(x, y, isNew) {
    this.x = x;
    this.y = y;

    this.speedX = random(-3, 3);
    this.speedY = random(-3, 3);

    if (isNew) {
      // Pick a bright random color
      let c = random(newPeopleColour);
      this.r = c[0];
      this.g = c[1];
      this.b = c[2];
      this.isNew = true;
    } else {
      // For initial people make them black
      this.r = 0;
      this.g = 0;
      this.b = 0;
      this.isNew = false;
    }
  }

  move() {
    this.x += this.speedX;
    this.y += this.speedY;

    // Bounce off edges
    if (this.x < 0 || this.x > width) {
      this.speedX *= -1;
    }
    if (this.y < 0 || this.y > height) {
      this.speedY *= -1;
    }
  }

  updateColor() {
    if (this.isNew) {
      // Fade to black eventually 
      this.r = max(0, this.r - 1);
      this.g = max(0, this.g - 1);
      this.b = max(0, this.b - 1);

      if (this.r === 0 && this.g === 0 && this.b === 0) {
        this.isNew = false;
      }
    }
  }

  display() {
    noStroke();
    fill(this.r, this.g, this.b);
    ellipse(this.x, this.y, 8, 8);
  }
}

The main challenge I faced was that at first, two lines were being drawn between the same two people which broke the clean look of the network. I solved this by using a nested for loop and tarting j from i + 1 which avoided checking duplicate pairs and made sure each connection line was only drawn once. I also really like the colour-fading logic, it’s simple but menaingful as new people slowly assimilate into the network. The max() function is a small but important detail, it prevents colors from becoming negative and making sure the isNew flag flips to false so these people can eventually form connections.

In terms of future work and improvements, I could add a ‘main character’ , a single person who’s at the center and stays still while the rest of the network moves and connects around them. This would focus on the idea of how one person’s network changes.

Week 3 – Fish

For this project, I created a small aquarium scene, where colorful fish swim across the screen. Each fish is an object with its own properties like color, speed, and direction, and they move infinitely using simple math. I used arrays to manage multiple fish at once, and clicking on a fish makes it disappear, while pressing “C” brings all the fish back.

I decided to do this because I wanted to practice the class material but keep it interesting at the same time. Initially, I just had fish swimming in one direction with different speeds but I thought it was too boring so i added different directions and made them more interactive.

One problem I ran into was making fish face the correct direction when swimming left or right, but adding a simple check for xspeed solved it. Overall, this project helped me practice object-oriented programming and arrays while making a fun, interactive visual.

The part of the code that I’m proud of is this part:

move() {
    if (!this.visible) return; // skip if hidden

    this.x += this.xspeed;
    this.y += this.yspeed;

    let bodyHalf = 30;
    if (this.x > width + bodyHalf) this.x = -bodyHalf;
    if (this.x < -bodyHalf) this.x = width + bodyHalf;
    if (this.y > height + bodyHalf) this.y = -bodyHalf;
    if (this.y < -bodyHalf) this.y = height + bodyHalf;
  }

Here, it makes the fish disappear when clicked. It took me some time to figure out where I’d need to press and how not to let the “invisible” fish get in the way.

Something that I would like to improve in the future would be adding more aesthetics to it and making the fish more sophisticated-looking so to say.

But after all, here’s what I got, try to catch them all 😛

Reading Response – Week 3

Reading Chris Crawford’s The Art of Interactive Design reshaped the way I think about interactivity. Crawford defines interactivity not as a simple feature or function but as a conversation, a cyclical process of listening, thinking, and speaking. What really drew me into this reading the most was his insistence that many things we casually call “interactive” are not interactive at all, because they lack a genuine back-and-forth exchange. Clicking a button or watching an animation play is not enough if the system does not “listen” and respond with meaningful output that takes the user’s input into account. This shifted my perspective away from thinking of interactivity as a checklist item and toward understanding it as a measure of quality and how deeply the system engages with its user.

One passage that stood out to me was Crawford’s description of the conversation between Gomer and Freedgund, where both must listen, think, and respond to create a true dialogue. It made me question: How do we measure good listening in a digital system? Algorithms process data, but does that count as “thinking” or “understanding”? This question feels even more relevant today, with AI systems claiming to “converse” with users. Crawford’s argument suggests that the test of interactivity lies in whether the user feels heard and whether the system’s response prompts further exploration or reflection.

Reflecting on my own work with p5.js sketches, I realize I have often stopped at surface-level interactivity: a mouse click might trigger a color change, or the position of the mouse might move a shape. But these interactions rarely invite deeper engagement. A strongly interactive system, in my view, should encourage curiosity, creating an open-ended dialogue where the user feels like a co-creator. Moving forward, I want to implement more engaging interactivity for example, using the speed of the mouse movement to alter the size or complexity of generative patterns, or incorporating audio could be an interesting way of engagement. Adding unpredictability by using randomness is also a key factor that could make the experience feel more alive. Ultimately, I want my sketches to feel less like static art pieces and more like evolving systems that “listen” to the user and respond in a way that makes them want to keep interacting.

Week 3 – Reading Response

The author differentiates “tools” for utility, such as the hypothetical Nintendo fridge, from something “fun” and “interactive”. Thus, he raises a question, “Is interactivity utterly subjective?”, only to discuss the process of interactivity as a subjective flow. In particular, he argues that the thinking that spurs creativity–and a certain level of randomness as we discussed last week–is a crucial element of interactivity.

I agree that the thinking for creative responses is the most important part. In the past, even now, some low- and mid-level interactive creations, as the author would categorize, are solely dependent on a set of rules that only attempt to be generative. Their output doesn’t serve a meaning itself, only reflecting part of a bigger scene defined by the rule setter. Ideally, however, every output should prompt some further thinking in the receiver of the response, or the originator of the conversation. It is indeed fairly difficult to achieve so, particularly in the past.

The advent of Generative AI could bring some change, especially when it’s seemingly untouched in the interactive visual art sphere. What if, I say what if, some code is written in real-time, following a certain broader set of rules? What if, in addition to a set of rules, some new impromptu visual rules are created in real time?

Week 2 – Blackboard

For my project, I decided to add an element of interactivity. I was inspired by the visuals that were posted. I initially intended to make an “experience”, where I could use randomized texts to create unique shapes, however, I decided to create a simple blackboard instead. The idea had initialized in the library rooms, where my friends and I were studying for Calculus. My peer started writing on the whiteboard, and he had to keep on erasing previous work to progress further, when I wondered “what if we had a digital white board? One that erases on command?”

//if pixel is active, check how long it's been active
if (pixels[i][j] > 0) {
  //stay white for 2 seconds
  if (millis() - pixels[i][j] < 2000) {
    fill(255); 
  } else {
    //time expired
    pixels[i][j] = 0;
    fill(0); 
  }
} else {
  //default state is black
  fill(0);
}

I am especially proud of this part of my code. It took me 5 hours just skimming through pages and brain-numbingly consuming youtube videos to figure out how to make my pixels change colour and stay and go back to default.

  //on left mouse press, activate the pixel under the cursor
  if (mouseIsPressed && mouseButton === LEFT) {
    //convert mouse position to grid coords
    let i = floor(mouseX / pixelSize);
    let j = floor(mouseY / pixelSize);

    //only update if inside the canvas
    if (i >= 0 && i < cols && j >= 0 && j < rows) {
      //store the activation time
      pixels[i][j] = millis();
    }
  }
}

I also enjoyed programming this part of m code, where I had to find the location of the cursor and change the pixel colour.

Overall, I think I incorporated the loops smoothly into my project, however, I wanted to integrate a “menu bar”, with and eraser and a pen colour. I hope to involve more complex interactions in my upcoming assignments.

Week 2 Reflection – Organized Chaos

Reas’ talk on chance operations fascinated me and made me reflect on how “organized chaos” is present in our everyday life, such as jazz. Jazz musicians often make up melodies on the spot, riffing off each other. There’s a theme, but most of the melodies come from being in the moment and catching the rhythm. That’s what I think most of Reas’ projects are where the “non-human agent” creates them; the experience or art comes to be by following a basic line of code to give a unique piece of visuals.

The video opened my eyes to how one can use randomness to be creative. Before watching Reas’ talk, I used to attach a negative connotation to “chaos”. That it means destruction. So rather than welcoming it, I would try to eliminate variables that would disrupt the aesthetics or homogeneity of my projects. I realized that allowing a little bit of “chance” would help me discover new experiences, such as attending a surprise dinner party would allow me to form relationships with incredibly, grounded people.

What stood out to me the most is the perfect balance and tension of chaos and order. Reas’ examples of using very simple shapes, like dots and dashes, or even a single line of code from the 10 PRINT program indicated that there’s beauty in a set of instructions mixed with randomness.

Week 2 – art work

Turning Math into Motion: Playing with Sine and Cosine in p5.js

I’ve always liked the concept of sine and cosine waves. They’re simple functions on paper, just waves that go up and down forever. But I was wondering what would it make if it’s involved in creating virtual art. So I decided to use them to make something that looks alive on the screen.

The Concept

The artwork is based on the idea of ripples. Every click on the canvas sends out a circle that grows, but instead of being a perfect circle, it wobbles slightly — almost like the ripple is remembering where it’s been. Over time, these ripples overlap and fade, and the whole thing feels less like a diagram and more like a memory unfolding.

I think that’s what I love about it: it’s math doing what math does, but the output feels alive.

Highlights in the Code

I don’t want to break it down line by line, but a few details stood out to me while I was building this:

1. The Wavy Distortion
let offset = 15 * sin(this.t / 10);
ellipse(this.x, this.y, radius + offset);

Here, the sin() function is doing all the work. Instead of the circle growing in a boring, linear way, sin(this.t / 10) gives me a smooth back-and-forth motion between -1 and 1. Multiplying by 15 stretches that into a range of -15 to +15.

So when I add offset to the radius, the circle doesn’t stay steady. It breathes, gently expanding and shrinking as it grows. That tiny detail is what makes the ripple feel alive rather than mechanical.

2. The Colors That Shift on Their Own
stroke(
map(sin(this.t / 70), -1, 1, 100, 255),
map(cos(this.t / 25), -1, 1, 100, 255),
map(sin(this.t / 30), -1, 1, 150, 255),
180
);

At first, I hard-coded colors, but it looked flat. Then I realized I could let sine and cosine drive the colors, too.

Here’s how it works:

sin() and cos() output values between -1 and 1.

map() stretches those into ranges that make sense for color (e.g., 100–255).

Because the math is always oscillating, the stroke color is always changing — slowly shifting through different shades.

It’s not random; it’s predictably unpredictable. That’s why the transitions feel smooth instead of jarring.

3. Here is where I used loops:
function draw() {
background(10, 10, 20, 30);
for (let r of ripples) {
r.update();
r.show();
}
}

Each click I add one ripple to an array of ripples where I do the get bigger – wave distortion effect. And here I loop over them to update and show the effect.

Every ripple has its own this.t, which increases every frame. That’s what drives everything: the size, the offset wobble, the color shifts. Without t, nothing would move. With it, the math unfolds frame by frame, and the artwork comes alive.

Week 2 – Looped

This week we’re asked to create an artwork that incorporates the “loop” concept in code. I saw dynamic squares before, and would personally like to create a grid that gently “breathes” and drifts. Each square’s size, brightness are driven by layered sine waves using a shared time phase so the whole field feels organic and connected, like a low‑key pixel ocean.

Below is the code for core motion + styling logic (the vibe engine).

const w = sin(phase + (x * 0.35) + (y * 0.45));  // wave seed
const s = map(w, -1, 1, cell * 0.25, cell * 0.85);  // size pulse
const dx = sin(phase * 0.7 + x * 0.3) * 6;
const dy = cos(phase * 0.7 + y * 0.3) * 6;
const hueVal = (x * 8 + y * 6 + frameCount * 0.4) % 360;
const bri = map(w, -1, 1, 35, 90);
fill(hueVal, 60, bri, 0.9);
rect(px, py, s, s, 6);
  • What works: Simple math, no arrays or heavy state—scales nicely with window size. The motion feels smooth and unified.
  • Limitations: All squares animate uniformly; interaction is missing. No colors. (In a version, colors follow a fixed formula, so longer viewing gets predictable.)

To be frank, this implementation still lacks the smooth “sea wave” vibe that I was looking for. In particular, I would have liked the edges to transform into non-linear lines like waves. But I would call this a first prototype as a p5.js beginner.

However, I trialed for smaller square size, and I’m surprised that such a minor change created something perceptually different. Finally, I implemented a super cool mouse click effect, which in a way achieved another level of dynamic aesthetics.

Week 2 – Kaleidoscope

Concept

When I was trying to come up with an idea on what art to create, I tried to sketch different styles, 3D, pressable, not pressable, colorful, black and white. But I wasn’t fully satisfied with the end result and couldn’t get myself to code. I was scrolling shorts and I saw one that said “Why do we still have these in a store?” and the first thing he showed was a kaleidoscope (link to short: https://youtube.com/shorts/vnFYE48zqIA?si=TGT7zf0O8515eiQ_).

When I was a child kaleidoscope was my favorite thing ever, but I would also break it eventually to get the pretty stones out of it (lmao). So then I thought, why not recreate that pattern? I also thought that the most interesting art is usually interactive. That was when I came up with the idea to make a kaleidoscope, an individual one, the one that people can customize and interact with.

Production

I didn’t really understand how to make it work and this YouTube tutorial helped me a lot: https://youtu.be/KcHqRAM0sEU?si=AfDeL56RTEBYEjbl . I watched it and customized the code as I wanted.

I first made a usual one:
Press C to clear the canvas. 

But then I wanted to make it more interesting, so I added a function where the sketch disappears every second:

Try drawing simple circles and rectangles in one place along the rhythm of one second

Difficulties and favorite part of code:

The code itself is very short and was’t so difficult to make. What actually challenged me was understanding how to do it initially. And once I got the concept I was able to create without further struggles.

One part of the code that I liked was the function that draws a line in a random color, and then draws a vertically mirrored “reflection” of that line.

function drawReflection(x, y, px, py) {
  strokeWeight(7);
  stroke(random(255), random(255), random(255));
  line(x, y, px, py);

  push();
  scale(1, -1);
  line(x, y, px, py);
  pop();
}

Conclusion:

I really enjoyed seeing how a small code can create very interesting customizable designs, and overall it was really interesting to do.