Assignment 3 – Kaleidoscope

This sketch is a kaleidoscopic illustration of movers and attractors. My main inspiration is a combination of the picture above and a mix of effects I used in one of my motion graphics/VFX projects. I wanted to recreate a sample that captures both feelings at once.

Black White Kaleidoscope Background Abstract Animated

 

 

I started this sketch by placing circular attractors as shown in the image.

I wrote code that creates a circular arrangement of attractor points around a center position.

  for (let i = 0; i < numAttractors; i++) {
    let angle = (TWO_PI / numAttractors) * i;
    let x = centerX + cos(angle) * attractorRadius;
    let y = centerY + sin(angle) * attractorRadius;
    attractors.push(new Attractor(x, y, 20));
  }
}

This loop divides the circle into segments, each iteration multiplies by i to get the angle for that specific attractor. For x and y positions, I use polar-to-cartesian coordinate conversion as shown in the code.

Then I added movers and I applied the forces to the movers such that their position is affected by the attractors.

For this effect I take inspiration from this code.

So basically, the kaleidoscope effect happens because every single object gets drawn (numAttractors) times instead of just once. The trick is that before drawing each copy, the code rotates the entire coordinate system around the center of the canvas by increments of 60 degrees (since 360 deg ÷ 6 = 60 deg). It does this by moving the origin to the canvas center, rotating, then moving it back, and finally drawing the object at its actual position. But because the whole coordinate system has been rotated, what you actually see is the object appearing in six different spots arranged symmetrically around the center, like looking through a kaleidoscope. The cool part is that the physics simulation itself, only happens once for each object, but visually you see six reflections of everything. So when a mover orbits an attractor, all six of its mirror images orbit simultaneously.

 

For example, the number of movers in this sketch is 1, but it gets reflected 5 times

 

Here’s the code responsible for the kaliedoscope effect

for (let i = 0; i < symmetryFold; i++) {
  push();
  translate(width / 2, height / 2);      // move origin to center
  rotate((TWO_PI / symmetryFold) * i);   // rotate by incremental angle
  translate(-width / 2, -height / 2);    // move origin back
  circle(this.position.x, this.position.y, size);  // draw at original position
  pop();
}

I then made some really interesting changes here that make the pattern way more complex and controlled. I added this clever touch of four “corner attractors” placed at each corner of the canvas with higher mass , which act like gravitational anchors that influence the overall flow and create an effect of repulsion. I also added a visibility toggle (showAttractorsRepeller) so I can hide the attractors if I just want to see the trails. Speaking of trails, I changed the background to fade super slowly with background(0, 1) instead of clearing completely each frame, which creates these motion trails that build up over time. And finally, I made the movers wrap around the screen edges, if one goes off the right side it reappears on the left, which keeps the pattern continuous and prevents movers from escaping the canvas entirely.

Then I hit the jackpot. By changing one of the corner attractors mass, I create really interesting kaleidoscopic effects as shown in the gif below
Then by playing with the number of attractors I achieve really compelling visual effects like this

Sketch

To get the most out of this sketch, please manipulate the parameters I indicated at the top of the code. There’s something so super satisfying with watching those patterns slowly get created.

Assignment 2 documentation – Mustafa Bakir

What inspired this project is my fish, Abdulfattah, a Betta fish that was gifted to me from a very dear person to my heart.

This sketch mimics natural fish movement when it spots food. The user moves the mouse around and upon clicking you essentially drop a fish food pellet, and the fish mimics natural movement and acceleration towards food.

I first started t project by adding a background I found online

Blue water surface template in cartoon style illustration

then I made a rectangle that will later be a fish to start simulating it’s  movement.

then I wrote very simple code with if statements just as a base for my fish movement.

 

then I updated my code to lerp (linear interpolation) towards the mouse.

new_pos= current_pos + (target_pos – current_pos) * speed

 

After that I started implementing dropping fish pellets, I started with creating a simple function to spawn pellets with an array of circles.

let circles[];

  //in draw
  for (let circle of circles) {
    fill(139, 69, 19); // brown
    noStroke();
    ellipse(circle.x, circle.y, 20, 20);

function mousePressed() {
  circles.push({x: mouseX, y: mouseY});
}

Then I added a sinking motion for the pellets with a slight drift using the sin function to simulate natural waves pushing pellets around as they are sinking with a random angle for variation.

// draw food pellets
for (let i = circles.length - 1; i >= 0; i--) {
  let circle = circles[i];
  
  // make circle float downwards
  circle.y += circle.speed;
  
  // increment the angle for sin
  circle.angle += 0.05;
  
  // subtle drift for food pelletes
  let drift = sin(circle.angle) * 20;
  
  // draw circles
  fill(139, 69, 19);
  noStroke();
  ellipse(circle.x + drift, circle.y, 20, 20);

 

then I edited the code to make the rectangle follow the pellets instead of the mouse. For error handling, the pellets are put in a stack such that the rectangle eats the pellets using a queue as a data structure following the FIFO (first in last out) principle.

 

A challenge I faced:

As shown in the previous gif, there was a problem where the rectangle can’t eat the food because the pellets are drifting and sinking. This is happening because the rectangle is trying to go the position of that circle but it doesn’t account for the drifting and sinking, therefore, its more like it’s tracing the pellet rather than trying to catch it.

The way I tackled this challenge is also a highlight of the code that I’m proud of.

.I fixed this by predicting where each pellet would be 10 frames ahead, accounting for sinking and horizontal drift. I calculated the direction vector and euclidean distance to this predicted target, then normalized it to apply acceleration forces. The velocity builds gradually but it’s not fully smooth yet but I’ll get to that later. I multiplied velocity by 0.92 each frame for friction, cap the maximum speed, then update position. When the fish gets within 30 pixels of a pellet, it gets removed from the queue with shift() and reduce velocity by 70%. This creates a deceleration effect before accelerating toward the next pellet.

function followCircles(){
  // if there are circles, follow the first one following FIFO principle
  if (circles.length > 0) {
    let target = circles[0];
    
    // predict where the pellet will be
    let futureY = target.y + target.speed * 10;
    let futureAngle = target.angle + 0.03 * 10;
    let futureDrift = sin(futureAngle) * 1.5;
    
    let targetX = target.x + futureDrift; // i add the predicted position here so the fish can catch the food
    let targetY = futureY;
    
    // calculate direction to predicted position
    let dx = targetX - x;
    let dy = targetY - y;
    let distance = dist(x, y, targetX, targetY); // calculate the eucilidian distance of the fish and the pellet
    
    // normalize direction and apply acceleration
    if (distance > 0) {
      vx += (dx / distance) * acceleration;
      vy += (dy / distance) * acceleration;
    }
    
    // apply friction for more natural movement
    vx *= friction;
    vy *= friction;
    
    // limit speed
    let speed = dist(0, 0, vx, vy);
    if (speed > maxSpeed) {
      vx = (vx / speed) * maxSpeed;
      vy = (vy / speed) * maxSpeed;
    }
    
    // update position with velocity
    x += vx;
    y += vy;
    
    // check if rectangle is touching the circle 
    let actualDistance = dist(x, y, target.x + target.drift, target.y);
    if (actualDistance < 30) {
      circles.shift(); // eat the circle
      // reduce velocity when eating 
      vx *= 0.3;
      vy *= 0.3;
    }
  } else {
    // slow down when no target
    vx *= 0.9;
    vy *= 0.9;
  }
}

another problem I faced was the fish was getting stuck around the corners but I easily fixed that but implementing a function that lets the fish wander using perlin noise (Thanks professor Jack!!)

Then I added the fish’s sprite from this website.

Future improvements and reflection:

I want to have a simulation of the background such that it actually simulates fluids and looks like water. I also have 3 more fish in the tank that are Abdulfattah’s friends that I want to add, they always steal his food so it would be more realistic that way. Moreover I want to have a bigger canvas so I can add object you naturally find in the ocean or the one’s I have in my fish tank, and an algorithm that makes the fish occasionally hide behind those objects. Lastly, I want an algorithm where  sometimes the fish just rests in the tank doing nothing, which would add more realism to the sketch.

Reading response – Mustafa

This chapter hit me hard. Flake asks the question I’ve been circling around for years: why can’t we predict everything if we understand the parts? The ant colony example nails it. You can study individual ants all day long. You can catalog their behaviors, map their neural pathways, document their castes. You still won’t predict the emergent sophistication of millions of them working together.

I love how Flake frames computation as nature’s language. We’ve been so focused on dissecting things that we forgot to ask “what does this do?” instead of “what is this?” That shift feels enormous. A duck isn’t just feathers and bones. A duck migrates, mates, socializes, adapts. The doing matters as much as the being.

The pdf’s structure fascinates me. Fractals lead to chaos, chaos leads to complex systems, complex systems lead to adaptation. Everything connects. I’ve read excerpts on chaos theory. I’ve read excerpts on evolution. I’ve never seen someone draw the lines between them so clearly. Simple rules, iterated over time, create everything from snowflakes to stock markets.

The timing matters too. Flake wrote this in 1998, right when computers stopped being tools and became laboratories. We could finally simulate systems too complex to solve analytically. That changed everything. Meteorologists, economists, biologists: they all started speaking the same computational language.

What gets me is the humility baked into this approach. Reductionism promised we could know everything by breaking it down far enough. Flake shows us the limits. Some things are incomputable. Some systems defy long-term prediction. Understanding quarks doesn’t help a doctor diagnose patients. Understanding individual neurons doesn’t explain consciousness.

Mustafa Bakir – Assignment 1 – QU4NT0M W4LK

This sketch was inspired by Quantum Cloud by Antoney Gormley 

Sources utilized: (514) Coding Challenge #112: 3D Rendering with Rotation and Projection – YouTube

Concept

I was talking to my friend Youssab William about decision trees, specifically binary decision trees and the thought lingered in my head for a little bit. Then when I started reading the assignment prompt, I read about the 3D walk and say Quantum Cloud. I got an idea of merging the logarithmic growth of binary trees with the random walk on a 3D plane in quadrantal angles. This is where QU4NT0M W4LK was born.

Also, im a big big fan of glitching and RGB offsets as a motion designer and video editor. I include RGB offsets (appropriately) in almost all my projects, so I also loved to add it here.

function draw() {
  background(255);

  // track rotation changes from dragging
  if (mouseIsPressed) {
    let deltaX = mouseX - pmouseX; // built in coords
    let deltaY = mouseY - pmouseY;
    rotY += deltaX * 0.01; // have to manually track rotation cus its not built in
    rotX += deltaY * 0.01;
  }
  
  // calculate rotation delta for RGB offset
  let deltaRotX = rotX - prevRotX;
  let deltaRotY = rotY - prevRotY;
  let deltaZoom = zoom - prevZoom;
  
  // update RGB offsets based on camera rotation and zoom
  blueOffset = lerp(blueOffset, deltaRotY * 500, 0.15);   // horizontal drag -> Blue
  greenOffset = lerp(greenOffset, deltaRotX * 500, 0.15); // vertical drag -> Green
  redOffset = lerp(redOffset, deltaZoom * 100, 0.15);     // zoom -> Red
  
  // decay offsets back to zero when not moving
  blueOffset *= 0.92;
  greenOffset *= 0.92;
  redOffset *= 0.92;
  
  // store previous values
  prevRotX = rotX;
  prevRotY = rotY;
  prevZoom = zoom;

  // apply camera transformations
  scale(zoom);
  rotateX(rotX);
  rotateY(rotY);

I am particularly proud of this segment of the code as everything else was something I have done before. But this camera movement was something I thought was extremely complex. What motivated me to embark on this idea is my recent project in motion graphics with camera movements that I genuinely was proud of, so I wanted to make camera movements I’m proud of using P5 as well. Both instances were something I was dreading before but once I started working on them they turned out to be much much simpler than I thought.

 

Future considerations would optimization. because of the fast growth of the segments, I had to decrease the number of segments. it looks much cooler with more segments, but I assume, i dont know yet, but I assume that the RGB channels are always drawn which technically *3’s the amount of processing. Another thing I wanna try but i’m dreading ins implementing dynamic 3d lighting with shadows and highlights, but maybe in future assignments.

 

Resubmission

 

In this resubmission, I edited the walk such that x walks in the red channel, y walks in the green channel, and blue walks in the blue channel.