Midterm Progress – Khalifa Alshamsi

Concept and Design of Generative Art

The concept for “Evolving Horizons” revolves around the interplay of natural elements in a living landscape. The goal is to create a serene visual experience that captures the passage of time, from dawn to dusk, through smooth transitions of light and shadow. I was inspired by how light can transform landscapes, especially during sunrise and sunset when shadows grow longer, and the colors in the sky shift dramatically.

Key elements in the landscape:

  • Sun: Moves across the sky and influences light and shadows.
  • Hills: Roll across the landscape, with their shadows changing based on the sun’s position.
  • Trees: Stand tall on the hills, casting shadows and swaying gently.
  • Sky: Changes color throughout the day, creating a peaceful and immersive environment.

Sketch

Code

let sun;
let hills = [];
let trees = [];
let numHills = 5;
let numTrees = 30;

function setup() {
  createCanvas(800, 600);
  
  // Create the sun starting at the morning position
  sun = new Sun(100, 100);
  
  // Generate random hills
  for (let i = 0; i < numHills; i++) {
    hills.push(new Hill(random(0, width), random(200, 400), random(100, 200)));
  }
  
  // Generate random trees on the hills
  for (let i = 0; i < numTrees; i++) {
    let hillIndex = floor(random(hills.length));
    trees.push(new Tree(hills[hillIndex].x + random(-50, 50), hills[hillIndex].y - hills[hillIndex].height));
  }
}

function draw() {
  background(135, 206, 235); // Sky blue
  
  // Change the sky color based on sun position
  changeSkyColor(sun.y);
  
  // Move and draw the sun
  sun.moveSun();
  sun.display();
  
  // Draw hills with shadows
  for (let hill of hills) {
    hill.display();
    hill.castShadow(sun);
  }
  
  // Draw trees with shadows
  for (let tree of trees) {
    tree.display();
    tree.castShadow(sun);
  }
}

// Sun class
class Sun {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.diameter = 80;
  }
  
  moveSun() {
    // Sun moves in a circular path from morning to night
    this.x = width / 2 + cos(frameCount * 0.01) * 400;
    this.y = height / 2 + sin(frameCount * 0.01) * 200;
  }
  
  display() {
    fill(255, 204, 0); // Yellow sun
    noStroke();
    ellipse(this.x, this.y, this.diameter);
  }
}

// Hill class
class Hill {
  constructor(x, y, height) {
    this.x = x;
    this.y = y;
    this.height = height;
  }
  
  display() {
    fill(34, 139, 34); // Green hills
    noStroke();
    beginShape();
    vertex(this.x - 100, this.y);
    bezierVertex(this.x, this.y - this.height, this.x + 100, this.y - this.height / 2, this.x + 200, this.y);
    vertex(this.x + 200, this.y);
    endShape(CLOSE);
  }
  
  castShadow(sun) {
    let shadowLength = map(sun.y, 0, height, 100, 400);
    fill(0, 0, 0, 50); // Transparent shadow
    noStroke();
    beginShape();
    vertex(this.x - 100, this.y);
    vertex(this.x - 100 + shadowLength, this.y + shadowLength);
    vertex(this.x + 200 + shadowLength, this.y + shadowLength);
    vertex(this.x + 200, this.y);
    endShape(CLOSE);
  }
}

// Tree class
class Tree {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.trunkHeight = random(40, 80);
    this.trunkWidth = 10;
    this.leafSize = random(30, 50);
  }
  
  display() {
    // Draw tree trunk
    fill(139, 69, 19); // Brown trunk
    noStroke();
    rect(this.x, this.y, this.trunkWidth, -this.trunkHeight);
    
    // Draw leaves
    fill(34, 139, 34); // Green leaves
    ellipse(this.x + this.trunkWidth / 2, this.y - this.trunkHeight, this.leafSize);
  }
  
  castShadow(sun) {
    let shadowLength = map(sun.y, 0, height, 50, 200);
    fill(0, 0, 0, 50); // Transparent shadow
    noStroke();
    beginShape();
    vertex(this.x, this.y);
    vertex(this.x + shadowLength, this.y + shadowLength);
    vertex(this.x + shadowLength + this.trunkWidth, this.y + shadowLength);
    vertex(this.x + this.trunkWidth, this.y);
    endShape(CLOSE);
  }
}

// Change sky color based on sun's height
function changeSkyColor(sunY) {
  let morning = color(135, 206, 235); // Morning sky
  let dusk = color(255, 99, 71);      // Dusk sky
  let t = map(sunY, 0, height, 0, 1);
  let skyColor = lerpColor(morning, dusk, t);
  background(skyColor);
}

Designing the Code: Functions, Classes, and Interactivity

To break the project into manageable parts, I designed the following core components:

Classes:

  • Sun: The sun manages its position and movement across the sky. The sun also affects the length of shadows.
  • Hill: Represents rolling hills with a bezier curve that gives each hill a natural shape. The hill class also handles the casting of shadows.
  • Tree: Trees are placed on the hills, swaying slightly to simulate a breeze, and casting their own shadows based on the sun’s position.

Functions:

  • movesun(): Moves the sun in a circular motion across the sky. The position of the sun changes dynamically with each frame, mimicking the progression of time.
  • castShadow(): Calculates the length of shadows based on the sun’s position in the sky. This adds realism by changing the shape and size of shadows as the sun moves.
  • changeSkyColor(): Shifts the sky color gradually throughout the day, using the lerpColor() function to transition smoothly from morning to dusk.

Interactivity:

  • Time of Day: I’m considering adding controls where users can manually change the time of day by dragging the sun or pressing a key to speed up the passage of time. This would allow users to explore the landscape at different times of the day and see how light and shadows behave.

The combination of these functions creates a seamless, flowing environment where every aspect of the landscape interacts with the light in real-time.

Identifying Complexities and Minimizing Risks

The most complex part of this project was implementing the shadow-casting algorithm. Calculating shadows based on the sun’s position required careful mapping of angles and distances. I needed to ensure that the shadows grew longer as the sun lowered in the sky and became shorter when the sun was overhead.

Steps to Reduce Risk:

To tackle this complexity, I isolated the problem by writing a simple test sketch that calculated shadow lengths based on different sun positions. I used the map() function in p5.js to dynamically adjust shadow length relative to the sun’s y position. By experimenting with this small feature, I was able to fine-tune the shadow casting before incorporating it into the full project.

Additionally, the sky color transitions were another challenge. I experimented with lerpColor() to create smooth transitions from morning to dusk, ensuring the visual experience was gradual and not abrupt.

Next Steps:

Now that the core features are working, I plan to:

  • Fix the positioning of the trees that are randomly generated.
  • Implement wind simulation for the trees, making them sway in response to virtual wind.
  • Enhance user interactivity by allowing users to manipulate the sun’s position, controlling the time of day directly.
  • To make the scene feel more immersive, add finer details to the landscape, such as moving clouds and birds.

Week 4 – Harmony in the Cosmos

Inspiration

Memo Akten’s work on Simple Harmonic Motion made me drawn to the rhythmic, cyclical patterns that form the foundation of so much of our world. Akten’s focus on symmetry and motion led me to think about the natural world—and nothing reflects cosmic symmetry and harmonic motion quite like a galaxy. I drew inspiration from Akten to code a visual representation of a spiral galaxy, exploring the underlying harmony that often governs the universe itself.

The Concept

A galaxy, particularly a spiral galaxy, is a perfect example of natural harmony. Galaxies are bound by gravity, with billions of stars moving in circular or elliptical orbits around a central massive core, often a black hole. While galaxies appear chaotic at first glance, they actually follow harmonious patterns of motion. The stars within them move predictably, the spiral arms wind in beautiful formations, and the entire system evolves slowly over time.

What fascinated me about Akten’s work was his ability to capture movement that is predictable yet fluid, a concept that parallels the dynamics of galaxies. Inspired by his approach to harmonic motion, I wanted to create my own representation of a galaxy, shifting and evolving while remaining within the boundaries of harmonic symmetry.

Sketch

The Core Code Idea

  1. Condensed Spiral Galaxy: I focused on creating a galaxy that’s small enough to fit within the canvas while still representing the vastness and depth of space. A maximum radius of 150 pixels keeps the stars tight and visually coherent.
  2. Stars on Spiral Arms: The stars are distributed along several spiral arms (in this case, five) and move in circular orbits around the center. Their speed is dependent on how far they are from the center, mimicking how stars in real galaxies behave.
  3. Slow Shape Shifting: Inspired by Akten’s harmonic motion, I added a subtle shifting mechanism where the spiral arms slowly evolve over time, creating a dynamic and living representation of a galaxy.

Full Code

let stars = [];
let numStars = 400; // Number of stars
let spiralArms = 5; // Number of spiral arms
let galaxyRadius = 150; // Maximum radius of the galaxy (more condensed)
let timeFactor = 0.001; // Factor for the shape-shifting over time

function setup() {
  createCanvas(600, 500); 
  noStroke();

  // Create stars
  for (let i = 0; i < numStars; i++) {
    let angle = random(TWO_PI); // Random starting angle
    let radius = sqrt(random()) * galaxyRadius; // Random radius within the galaxy radius
    let star = {
      angle: angle, // Initial angle
      radius: radius, // Distance from the center
      speed: map(radius, 0, galaxyRadius, 0.002, 0.015), // Stars closer to the center move faster
      size: map(radius, 0, galaxyRadius, 2, 4), // Smaller stars are further out
      twinkleOffset: random(TWO_PI) // Random phase for twinkle effect
    };
    stars.push(star);
  }
}

function draw() {
  background(10, 10, 30, 80); 
  translate(width / 2, height / 2); // Moves origin to the center of the canvas

  // Slowly shift the shape of the spiral galaxy over time
  let spiralShift = sin(frameCount * timeFactor) * 0.5;

  for (let i = 0; i < stars.length; i++) {
    let star = stars[i];
    
    // Calculates star position with spiral shifting
    let armOffset = (i % spiralArms) * TWO_PI / spiralArms + spiralShift; // Spiral arm offset with time-based shift
    let x = star.radius * cos(star.angle + armOffset);
    let y = star.radius * sin(star.angle + armOffset);
    
    // Updates star's angle to make it rotate around the center
    star.angle += star.speed;
    
    // Twinkle effect: stars slowly change brightness
    let twinkle = map(sin(star.twinkleOffset + frameCount * 0.05), -1, 1, 180, 255);
    fill(twinkle, twinkle, 255); // Soft white-blue color for stars
    
    // Draws the star
    ellipse(x, y, star.size, star.size);
  }

    // Draws the central black hole 
  fill(0);
  ellipse(0, 0, 20, 20); // Central black hole at the center
}

Explanation: 

  • Cos() and Sin() Functions calculate the stars’ X and Y positions based on their angle around the galaxy’s center.
  • The formula x = radius * cos(angle) calculates the horizontal (X) position of the star.
  • Similarly, y = radius * sin(angle) calculates the vertical (Y) position.
  • Angle Update: Each star’s angle property increases slightly in every frame, causing them to move in circular orbits. This simulates the motion of stars around the galaxy’s core.
  • Spiral Shift: The spiralShift variable gradually changes over time, slowly altering the spiral arms’ positions, mimicking a galaxy’s slow evolution.

This code allows us to simulate stars moving in elliptical paths around the galaxy’s center, which is key to creating the harmonic motion of a spiral galaxy.

What Can Be Improved

  • Interactive Zoom and Pan: Allow users to zoom in and out of the galaxy to simulate a sense of scale, as galaxies are vast and complex. Panning around the galaxy would also help users explore different parts of it.
  • Nebulae and Star Clusters: Adding glowing clouds or star clusters would enhance the visual complexity.

 

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)