Week 3 – Cosmic Drift

Inspiration:

The idea behind the project came to me after visiting my uncle over the weekend. I sat with him for a while and heard his stories of watching the stars in the sky between his trips around the different emirates, so in turn, I wanted to create the natural beauty of space.

Concept:

Invisible attractors act as gravitational forces, pulling particles toward them like stars or planets pulling in celestial bodies. Adding turbulence (a random force similar to cosmic wind) makes the movement less predictable, introducing a layer of chaos and randomness. The result is a balance between order and disorder, where the attractors constantly influence the movers but never settle into perfect orbits.

Sketch:

Code Structure

The code is structured into different components to keep it organized and easy to manage:

Attractors: These are invisible points on the canvas that exert a gravitational pull on the movers. They are positioned randomly at the start and never move. Their forces influence the movers’ paths.

Movers: These are the visible particles that move based on the forces applied by the attractors and the random turbulence. They leave a fading trail behind them as they move, creating beautiful, fluid patterns.

Turbulence: This force adds an element of unpredictability by introducing small random pushes to the movers, making their motion more organic.

Wrapping Around Edges: When movers reach the canvas’s edge, they reappear on the opposite side, creating the feeling of an infinite space.

Full Code:

let movers = [];
let attractors = [];
let turbulenceForce = 0.01; // Strength of random force (simulating cosmic wind)

function setup() {
  createCanvas(800, 600);
  background(0); 
  
  // Create invisible attractors that represent stars or celestial bodies
  for (let i = 0; i < 4; i++) {
    attractors.push(createVector(random(width), random(height))); // Each attractor is placed randomly on the canvas
  }
  
  // Creates visible movers, which represent comets or particles moving around space
  for (let i = 0; i < 100; i++) {
    movers.push(new Mover(random(width), random(height))); // Starts each mover at a random position
  }
}

function draw() {
  // Creates a fading background effect by drawing a transparent black rectangle over the canvas
  // This keeps a slight trail behind the movers, making the motion smoother and more fluid
  fill(0, 20); // Black rectangles with some transparency
  rect(0, 0, width, height);

  // For every mover, apply forces from each attractor and turbulence, then update and display
  for (let mover of movers) {
    for (let attractor of attractors) {
      mover.applyAttraction(attractor); // Apply gravitational attraction from the invisible attractors
    }
    mover.applyTurbulence(); // Add a random force to create unpredictable motion (like cosmic wind)
    mover.update(); // Update the mover's position based on the forces acting on it
    mover.display(); // Draw the mover on the screen
  }
}

// The Mover class defines the behavior and properties of each particle (mover)
class Mover {
  constructor(x, y) {
    this.position = createVector(x, y); // Mover's position (starts at the given x and y)
    this.velocity = createVector();     // Mover's speed and direction (starts still, no velocity)
    this.acceleration = createVector(); // The force applied to the mover to change its motion
    this.maxSpeed = 3;                  // Limit the maximum speed to keep the movement smooth
    this.color = color(255, 255, 0);    // Initial color is yellow (R, G, B = 255, 255, 0)
  }

  // Function to apply attraction from an invisible attractor (representing stars or planets)
  applyAttraction(attractor) {
    // Calculate the direction and distance between the mover and the attractor
    let force = p5.Vector.sub(attractor, this.position);
    let distance = constrain(force.mag(), 20, 150); // Limits the distance to avoid extreme attraction forces
    force.normalize(); // Normalize the force vector to get direction only, without affecting magnitude
    force.mult(1 / (distance * distance)); // Apply the inverse-square law (force decreases as distance increases)
    this.acceleration.add(force); // Add the attraction force to the mover's acceleration
  }

  // Function to add a random force (like a cosmic wind) to make the motion unpredictable
  applyTurbulence() {
    // Create a small random force in any direction
    let turbulence = createVector(random(-turbulenceForce, turbulenceForce), random(-turbulenceForce, turbulenceForce));
    this.acceleration.add(turbulence); // Add this random force to the mover's acceleration
  }

  // Updates the mover's position based on its current velocity and acceleration
  update() {
    this.velocity.add(this.acceleration); // Add the acceleration to the velocity (changing the speed/direction)
    this.velocity.limit(this.maxSpeed);   // Make sure the velocity doesn't exceed the maximum speed
    this.position.add(this.velocity);     // Update the position based on the velocity
    this.acceleration.mult(0);            // Reset acceleration after each frame so forces don't accumulate

    // Changes the color of the mover based on its speed (faster movers turn blue, slower ones stay yellow)
    let speed = this.velocity.mag();      // Calculates how fast the mover is going
    let r = map(speed, 0, this.maxSpeed, 255, 0);  // Red decreases as speed increases
    let g = map(speed, 0, this.maxSpeed, 255, 0);  // Green decreases as speed increases
    let b = map(speed, 0, this.maxSpeed, 0, 255);  // Blue increases as speed increases
    this.color = color(r, g, b); // The color will change from yellow to blue as the speed changes

    // Wrapping around the edges of the screen
    if (this.position.x > width) this.position.x = 0;  // If mover goes off the right edge, wrap to left
    if (this.position.x < 0) this.position.x = width;  // If mover goes off the left edge, wrap to right
    if (this.position.y > height) this.position.y = 0; // If mover goes off the bottom, wrap to top
    if (this.position.y < 0) this.position.y = height; // If mover goes off the top, wrap to bottom
  }

  // Function to display (draw) the mover on the screen
  display() {
    stroke(this.color);  // Set the stroke (outline) color of the mover based on its current color
    strokeWeight(2);     // Set the thickness of the mover's stroke
    point(this.position.x, this.position.y); // Draw the mover as a small point on the screen
  }
}

Key Feature in the Code

// Change the color of the mover based on its speed (faster movers turn blue, slower ones stay yellow)
let speed = this.velocity.mag(); // Calculate how fast the mover is going
let r = map(speed, 0, this.maxSpeed, 255, 0);  // Red decreases as speed increases
let g = map(speed, 0, this.maxSpeed, 255, 0);  // Green decreases as speed increases
let b = map(speed, 0, this.maxSpeed, 0, 255);  // Blue increases as speed increases
this.color = color(r, g, b); // Transition from yellow to blue based on speed

As movers speed up, they change from yellow (slow) to blue (fast), creating a clear and beautiful visual representation of their velocity. This color change adds another layer of artistic depth to the simulation, making the movers look like celestial objects glowing as they move faster.

Challenges and Solutions

One of the challenges was balancing the forces so that movers didn’t either get stuck near the attractors or fly off the canvas too quickly. Tuning the strength of both attraction and turbulence forces was the solution. I also implemented the edge wrapping mechanism to prevent movers from disappearing off-screen, which greatly improved the overall flow of the design.

The most rewarding part of the project was seeing how the movers interacted with the invisible attractors and how the random turbulence affected their paths. This gave the final design an organic, flowing feel—something I had aimed for from the beginning.

The only thing I am still unclear about accomplishing is the color change due to my colorblindness; I have zero clue if that function works. But hopefully, it looks as beautiful to you as it does to me.

Future Improvements:

  1. Interactivity: In the future, I would like to add interactivity by allowing users to click and place new attractors on the canvas in real time, changing the flow of the movers.
  2. Sound Design: Pairing the visual elements with generative ambient sounds based on the movement and speed of the movers would create an immersive experience.

Resource: 

https://p5js.org/reference/#/p5/map

Week 2 – Khalifa Alshamsi

Concept:

My concept goes back to Dory from Finding Nemo. She swims around and tries to find Nemo, but she will never find him in this universe I created. With a constant restart whenever she goes off the screen.

Code:

let fish; // Declaring a variable for the fish object

function setup() {
  createCanvas(600, 400); 
  fish = new Fish(); // Initializes the fish object
}

function draw() {
  background(0, 150, 255); // Ocean blue background for a feeling of thr sea
  fish.update(); // Updates the fish's position and movement
  fish.display(); // Display the fish on the canvas
}

class Fish {
  constructor() {
    // Initialize the fish's properties: position, velocity, and acceleration
    this.position = createVector(width / 2, height / 2); // Starts the fish in the middle of the canvas
    this.velocity = createVector(2, 0); // Give the fish an initial velocity to move it to the right
    this.acceleration = createVector(0.01, 0); // Adds a small acceleration to simulate swimming
    this.angle = 0; // Angle for tail movement
  }

  update() {
    // This function updates the fish's position and behavior each frame

    // Simulate natural swimming by adding small random changes to the fish's acceleration
    this.acceleration = createVector(random(-0.01, 0.01), random(-0.01, 0.01));
    
    // Add the acceleration to the velocity to change the fish's speed
    this.velocity.add(this.acceleration);

    // Limits the maximum speed of the fish to make the movement smoother
    this.velocity.limit(3);

    // Updates the fish's position by adding the velocity to it
    this.position.add(this.velocity);

    // If the fish moves off one edge of the canvas, make it reappear on the opposite edge
    if (this.position.x > width) this.position.x = 0; // Reappear on left side if off right
    if (this.position.x < 0) this.position.x = width; // Reappear on right side if off left
    if (this.position.y > height) this.position.y = 0; // Reappear on top if off bottom
    if (this.position.y < 0) this.position.y = height; // Reappear on bottom if off top

    // Creates an oscillating angle for the fish's tail using the sine function
    // The angle swings back and forth, making the tail move naturally
    this.angle = sin(frameCount * 0.1) * PI / 6;
  }

  display() {
    // This function draws the fish on the canvas

    // Set the fill color to blue for Dory's body
    fill(0, 100, 255); // Blue body color
    stroke(0); // Black outlines
    strokeWeight(1); // Set the line thickness for the outline

    push(); // Starts a new drawing state to control the position and rotation of the fish
    translate(this.position.x, this.position.y); // Moves the fish to its current position
    rotate(this.velocity.heading()); // Rotates the fish in the direction of its movement

    // Draws the fish's body as an ellipse
    ellipse(0, 0, 50, 20); // The fish's body (50 pixels wide, 20 pixels tall)

    // Draws the fish's tail as a triangle that moves back and forth
    // Move the tail to the back of the body and rotate it based on the oscillating angle
    push(); // Starts a new drawing state for the tail
    translate(-25, 0); // Moves the tail to the back end of the fish
    rotate(this.angle); // Rotates the tail back and forth
    fill(255, 215, 0); // Yellow color for Dory's tail
    triangle(0, -10, -20, 0, 0, 10); // Draw the tail as a triangle
    pop(); // Restore the previous drawing state for the body

    // Draw Dory's fins (small yellow triangles)
    fill(255, 215, 0); // Yellow fin color
    triangle(10, -10, 20, 0, 10, 10); // Top fin
    triangle(0, 5, -10, 10, -5, 5);   // Bottom fin

    // Draw black markings on the body
    fill(0); // Black for markings
    beginShape(); // Start shape for the black marking
    vertex(0, -10);
    vertex(10, -5);
    vertex(0, 5);
    vertex(-10, 0);
    endShape(CLOSE); // Create a simple black marking shape

    // Draws the fish's eye as a small white circle with a black pupil
    fill(255); // White for the eye
    ellipse(15, -5, 8, 8); // Draw the eye (8x8 pixel circle)
    fill(0); // Black pupil
    ellipse(15, -5, 4, 4); // Draw the pupil (4x4 pixel circle)

    pop(); // Restores the previous drawing state
  }
}

In this simulation, the fish moves by updating its position based on velocity and acceleration, which makes its motion feel natural and fluid, like a real fish swimming. The velocity determines how fast and in which direction the fish swims, while the acceleration adds subtle variations, simulating the fish’s changing speed and slight adjustments in its path. Creating a decent-looking fish was tricky because I had to balance the simplicity of shapes (an ellipse for the body, triangles for the tail, and fins) while ensuring it still resembled a fish, particularly Dory from Finding Nemo. I also added some black markings and a wiggling tail to make the movement more realistic.

One fun complication was ensuring the fish didn’t just swim off the canvas and disappear forever.

// If the fish moves off one edge of the canvas, make it reappear on the opposite edge
if (this.position.x > width) this.position.x = 0; // Reappear on the left side if off the right edge
if (this.position.x < 0) this.position.x = width; // Reappear on the right side if off the left edge
if (this.position.y > height) this.position.y = 0; // Reappear at the top if off the bottom edge
if (this.position.y < 0) this.position.y = height; // Reappear at the bottom if off the top edge

Horizontal boundaries:

If the fish’s x position goes beyond the right edge (this.position.x > width), it reappears at the left edge by setting this.position.x = 0.
Similarly, if it swims past the left edge (this.position.x < 0), it reappears on the right by setting this.position.x = width.

Vertical boundaries:

If the fish swims off the bottom (this.position.y > height), it reappears at the top by setting this.position.y = 0.
If it goes above the top (this.position.y < 0), it reappears at the bottom by setting this.position.y = height.

This wrapping behavior is achieved by checking if the fish’s position exceeds the canvas boundaries and resetting its position to the opposite side, ensuring Dory never swims too far away!

Sketch:

Reflection:

I would love to know more about how to create better-looking shapes without that much struggle; while the end results look like fish, they aren’t the best out there. Also, I would like to add a little controller to make the fish move around.

Resources:

  • https://p5js.org/reference/p5/p5.Vector/
  • https://p5js.org/reference/p5/sin/
  • https://p5js.org/reference/p5/frameCount/#:~:text=A%20Number%20variable%20that%20tracks,in%20draw()%20finishes%20executing.
  • https://forum.processing.org/two/discussion/16493/daniel-shiffman-s-nature-of-code-exercise-1-5.html

Reading Response Week 1 – Khalifa Alshamsi

According to Gary Flick, breaking down systems into their simplest parts makes sense, but doing so often overlooks important details. An excellent example of an ant colony is where individual ants follow simple rules yet achieve remarkably complex collective behavior. It made me think about how similar cycles occur in ecosystems and human societies, where actions that seem simple have more complex consequences than they appear to be. This concept contradicts the reductionist viewpoint and encourages me to appreciate the beauty of interconnectedness, akin to witnessing neurons firing in harmony to create consciousness. I understand the analysis of simple actions piece by piece, but is it possible to understand such complete systems through piecemeal analysis?

The chapter focuses on computational models and notes that nature often creates immense complexities by following basic rules. This claim is interesting and somewhat controversial at the same time. Although Flick’s perspective is supported by the beauty of fractals in trees and the predictability of chaos in weather systems, I couldn’t help but wonder whether all phenomena, especially social systems, and human behaviors, can be reduced to algorithms. Is it very idealistic to believe that all complex systems can be reduced to a few fundamental principles? The boundaries between reductionism and holistic knowledge pique my curiosity, especially Flick’s emphasis on mathematical beauty, which raises more questions for me rather than providing answers.

Week 1 – Khalifa Alshamsi

Concept:

The look I was going for was to follow this random game that keeps popping up in my ads, which is a knockoff version of Venom, I think… But yeah, this shape moving around randomly was the look I was going for.

Code:

// Defines the variables for the creature's position
let posX, posY;
let offsets = [];
// Number of points (vertices) to create the creature
let numVertices = 10;
let radius = 50;

function setup() {
  createCanvas(400, 400);
  
  // Starts the creature's position in the center of the canvas
  posX = width / 2;
  posY = height / 2;

  // Fills the offsets array with random numbers
  // These random numbers will change the shape's vertices over time
  for (let i = 0; i < numVertices; i++) {
    offsets.push(random(10)); // Adds a random number between 0 and 10
  }
}

function draw() {
  background(220);
  
  // Update the creature's position
  updatePosition();
  fill(0);
  noStroke();
  
  // Begin drawing the shape
  beginShape();
  
  // Loops through each vertex to create the creature
  for (let i = 0; i < numVertices; i++) {
    // Calculates the angle for each point (vertex) on the shape
    // This spreads the points around a circle
    let angle = map(i, 0, numVertices, 0, TWO_PI); // Spread angles evenly

    // Calculates the X and Y position of each vertex based on the angle
    // I used sine and cosine to make the points go around in a circle
    // Also, added some randomness to the position using offsets
    let x = cos(angle) * (radius + sin(frameCount * 0.05 + offsets[i]) * 20);
    let y = sin(angle) * (radius + sin(frameCount * 0.05 + offsets[i]) * 20);
    
    // Places the vertex at the calculated X and Y position
    // While also adding posX and posY to move the shape to the creature's position
    vertex(posX + x, posY + y);
  }
  
  endShape(CLOSE);

  // Draws a border around the canvas so we can see the boundary
  noFill();
  stroke(0);
  strokeWeight(2);
  rect(0, 0, width, height);
}

// Functions to update the creature's position
function updatePosition() {
  // Moves towards the mouse if it's on the canvas
  posX = posX + (mouseX - posX) * 0.05; // Moves X position closer to mouse
  posY = posY + (mouseY - posY) * 0.05; // Moves Y position closer to mouse

  // Constrains the creature's position so it doesn't leave the canvas
  // By subtract/add 'radius' to stop the creature before it goes out completely
  posX = constrain(posX, radius, width - radius);
  posY = constrain(posY, radius, height - radius);
}

 

Sketch:

The proud work from the inspirational randomness work of a weird-looking venom game.

Reflection:

While reflecting on the code, I would’ve liked to create a better-looking creature, and by that, I mean the curves and smoothness of its edges instead of the sharp edges of the creature. But overall, I am happy with the work.

Sources that helped:

https://natureofcode.com/book/chapter-3-oscillation/

The Coding Train on Youtube:

Random Walker in p5.js (Coding Challenge 52) & Drawing Shapes in p5.js for beginners (1.3)