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 3_Electrons

Concept:

For this week’s assignment, I used forces to control electrons moving around their nucleus. In this case, the electrons are the visible movers and the nucleus (with neutrons and protons) is the invisible attractor.

 

Atomic structure explained: The building blocks of matter - Times of India
This image shows the structure of an Atom for more clarification.

Embedded Sketch:

Code Highlight:

// Showing and Updating the movers
for (let i = 0; i < 4; i++) {
  movers[i].update();
  movers[i].show();
  attractor.attract(movers[i]);
}

In this part of the code, I added a for loop to keep showing the movers and updating them, as well as make them go towards the attractor which is centered in the middle of the canvas. I added a seperate class for each, the mover and the attractor.

By changing the value 4 into a lower value, you can see less electrons moving towards the invisible nucleus.

Reflections & Improvement:

I faced a few difficulties while placing the movers in a nice pattern and giving them specific velocity values. For that, I referred to this video which helped me figure that out.

If I were to improve this assignment, I would definitely add more movers and give the user the option to select how many electrons they want to see moving according to whatever chemical element they want to visualize.

Week #3 – Magnetic Waves

Introduction

For this week, I initially planned to create marbles falling from a platform and landing on a magnetic field, where they would accumulate over time and collide with each other. At first, this idea seemed interesting to implement, but once I realized it was going to take more time than I originally planned, I had to scale down my ambition.

However, while trying to implement the original idea, I discovered an interesting behavior between three attractors. That’s when I decided to create the following sketch:

Note: You can interact by holding the left mouse button in the Canva.

The concept

As mentioned, the idea transitioned from a fun physics simulation with marbles and magnetic fields to a set of movers accumulating around randomly defined attractors.

There are 3 attractors in total, each with a random X and Y position determined by the width and range of the canvas. Additionally, the movers can collide with the borders of the scenario, and they also have friction as an extra feature.

Initially, the 3 attractors were fixed, as shown in the following full-screen sketch:  Test #1 of Assignment #3. However, this setup was not dynamic enough, and the movers never collided with the borders of the screen. Therefore, it was necessary to make the attractors’ position constantly random.

As an additional note, the colors of each mover are defined by Perlin noise.

Highlight of the code I am proud of

One segment of code I am most proud of is the following:

This part of the code is found in the file sketch.js, inside the draw() function.

if (attractors[0].g > 0){
        attractors[0].g -= 1;
        attractors[1].g += 1;
        
    } else if (attractors[1].g > 0){
        attractors[1].g -= 1;
        attractors[2].g += 1;
        

    } else if (attractors[2].g > 0){
        attractors[2].g -= 1;
        
      
    //Reset first value and the rest of positions of the attractors.
    } else {
        attractors[0].position.x = random(width);
        attractors[0].position.y = random(height);


        attractors[1].position.x = random(width);
        attractors[1].position.y = random(height);

        attractors[2].position.x = random(width);
        attractors[2].position.y = random(height);

        attractors[0].g = 70;
        attractors[2].g = 0;
    }
}

What this process does is, depending on the movers’ positions, increase the universal gravitational constant (G) of the next attractor while decreasing that of the current one, creating a smooth transition between attractors. When the final attractor is reached and its gravitational constant is reduced to 0, the process restarts by randomly assigning new positions to each attractor and redefining their gravitational constants to prevent any issues.

Reflection and ideas for future improvements

I initially thought it would take me more time to grasp the concepts. While there are interesting patterns I would love to create in the future with the movers and attractors, for now, I am glad I am understanding the concepts presented in class, which allows me to experiment with them. That said, here are some improvements I would like to explore in the future:

      • Experiment with parallel attractors and movers. In other words, have two sets of attractors and movers that can interact with each other, instead of a single group of movers interacting with two attractors at a time.
      • Use the collisions and gravity more creatively to add complexity to the designs.
      • Add more user interaction to manipulate the canvas, along with clear visual cues.

References

Most of the code that I used as a guide is from the following video (which is also provided in the weekly schedule):  Mutual Attraction by The Coding Train

Week 3 – Assignment

Visualise slow, sine-wave courses being travelled by unseen attractors, creating faint forces comparable to whirlpools that attract and release visible particles (movers) floating around the canvas. These particles leave behind soft glowing trails, and the closer they are to the attractors, the more varied their colours are. The entire landscape has the appearance of a serene, lively garden with petals or lights floating in a mild wind.

Breakdown of the Sketch

Invisible Attractors:  These generate soft swirling forces as they follow sine-wave pathways.

The movers (or particles): Are softly shimmering as they leave traces behind and swirl around the attractors. The invisible attractors affect their movement, and the closer they are to the attractors, the more their colour changes.

Visual Appeal: The doodle has a soothing and attractive appearance thanks to its soft gradients, shimmering particles, vibrant colours, and delicate trails.

let particles = [];
let attractors = [];
let numAttractors = 3;
let numParticles = 200;

function setup() {
  createCanvas(800, 800);
  noStroke();
  
  // Create particles
  for (let i = 0; i < numParticles; i++) {
    particles.push(new Particle(random(width), random(height)));
  }
  
  // Create attractors (moving along sine waves)
  for (let i = 0; i < numAttractors; i++) {
    attractors.push(new Attractor(i * TWO_PI / numAttractors));
  }
}

function draw() {
  background(10, 10, 30, 25); // Fading background for trails
  
  // Update and display attractors
  for (let attractor of attractors) {
    attractor.move();
    attractor.display();
  }
  
  // Update and display particles
  for (let particle of particles) {
    particle.applyAttractors(attractors);
    particle.update();
    particle.display();
  }
}

// Particle class representing each mover
class Particle {
  constructor(x, y) {
    this.pos = createVector(x, y);
    this.vel = createVector(0, 0);
    this.acc = createVector(0, 0);
    this.maxSpeed = 2;
  }
  
  applyAttractors(attractors) {
    for (let attractor of attractors) {
      let force = attractor.getAttractionForce(this.pos);
      if (force) {
        this.acc.add(force);
      }
    }
  }
  
  update() {
    this.vel.add(this.acc);
    this.vel.limit(this.maxSpeed);
    this.pos.add(this.vel);
    this.acc.mult(0);
  }
  
  display() {
    let glow = map(this.vel.mag(), 0, this.maxSpeed, 150, 255);
    fill(100, 150, 255, glow);
    ellipse(this.pos.x, this.pos.y, 8, 8);
  }
}

// Attractor class
class Attractor {
  constructor(offset) {
    this.offset = offset;  // Offset for sine wave motion
    this.pos = createVector(0, 0);
    this.speed = 0.005;
  }
  
  move() {
    let time = frameCount * this.speed;
    this.pos.x = width / 2 + sin(time + this.offset) * 300;
    this.pos.y = height / 2 + cos(time + this.offset) * 300;
  }
  
  getAttractionForce(particlePos) {
    let force = p5.Vector.sub(this.pos, particlePos);
    let distance = force.mag();
    
    if (distance < 150) { // Attraction radius
      let strength = map(distance, 0, 150, 0.1, 0);
      force.setMag(strength);
      return force;
    }
    return null; // No attraction if too far away
  }
  
  display() {
    noStroke();
    fill(255, 50, 50, 50);
    ellipse(this.pos.x, this.pos.y, 20, 20); // Faint circle for visualization (optional)
  }
}

Highlights
Smooth Attractor Motion + Dynamic Glow: The sketch has a softer, more natural flow since the moving attractors follow a smooth sine-wave pattern. A lovely, gentle visual impression is produced by the glowing effect on the particles, where their brightness varies according to their velocity.

The particles’ colour changes create a mystical atmosphere as they float around, while the invisible attractors generate the whirling forces that draw the particles in and out of their orbits.

fill(100, 150, 255, glow);
ellipse(this.pos.x, this.pos.y, 8, 8);

Thoughts and Suggestions for Future Improvements:

This sketch produces a lovely, lively image that is evocative of drifting petals in a peaceful garden or fireflies. A serene image with flowing patterns is produced by the interaction of the gently lighting particles and the smooth attractor motion.

Future Improvements:

To produce a wider variety of swirling patterns, introduce more intricate movement for the attractor, such as spiral or circular routes.
To provide even more visual depth, adjust the particle’s size and glow intensity according to its distance from the attractor and speed.
For an ethereal, more immersive effect, add minor backdrop gradient transitions (from dark blue to purple, for example).

Difficulties: The task at hand involved harmonising the visual components by achieving a smooth and glowing particle motion without causing visual disarray.

End Result:

 

Week 3: Long Distance

Concept:

The idea behind my code is very personal and meaningful to me. This week’s task was about exploring attraction, repulsion, and movement, but as I worked through it, these concepts began to reflect something much deeper. I wanted to use this project to represent the experience of being far away from my loved ones back home. It’s not just the physical distance or the different time zones that can sometimes make things feel disconnected—it’s also that constant push and pull of being apart but still feeling so closely tied together.

To express this, I created clusters of moving particles. They act as symbols for the intangible but powerful connection I have with the people I care about. Even though we may be separated by distance, the bond we share remains strong and evident, just like the attraction forces at play in the code.

How it Works:

The code features two clusters of 500 particles each, influenced by invisible attractors to mimic gravitational effects. The clusters each represent a person, while the moving particles aim to give them the essence of human beings. The particles move smoothly and realistically, reflecting the attraction forces at play, like love, sadness, and sometimes anger. A connecting line between the clusters adds a sense of unity, and provides an extra touch of visual interest, but it also represents the otherwise invisible attraction and connection between the two people.

Embedded Sketch:

Code I’m Proud Of:

One aspect of the code I’m particularly proud of is the attracted function. This function employs vector math to simulate attraction between particles and attractors. It calculates the force vector, adjusts it based on distance, and applies it as acceleration. This function not only demonstrates a solid understanding of forces but also showcases my ability to translate these principles into code effectively.

class Particle {
  constructor(x, y) {
    this.pos = createVector(x, y);
    this.vel = createVector(0, 0);
    this.acc = createVector(0, 0);
    this.mass = 1; // Added mass to simulate the same behavior as the "Mover"
  }

  // Function to apply attraction toward an invisible attractor
  attracted(target) {
    let force = p5.Vector.sub(target, this.pos);
    let distance = constrain(force.mag(), 5, 25); // Limit the distance just like in the original Mover code
    let strength = (1 * this.mass) / (distance * distance);
    force.setMag(strength);
    this.acc.add(force);
  }

  // Update position and velocity
  update() {
    this.vel.add(this.acc);
    this.vel.limit(2); // Limit the speed just like in the original Mover code
    this.pos.add(this.vel);
    this.acc.mult(0); // Reset acceleration after each frame
  }

  // Display the particle as a point
  display() {
    point(this.pos.x, this.pos.y);
  }
}

Reflections and Future Work:

Looking back, I’m proud of how the attraction forces in my project brought the particle clusters to life, creating a dynamic and personal representation of connection despite distance. The way the clusters interact feels meaningful, and the organized code made refining the project easier. For future improvements, I want to explore adding forces like friction, make the experience more interactive with user controls, and experiment with different visual elements.

Solar System: Invisible Attractors and Moving Objects – Week 3

Concept
For this weeks assignment, I wanted to explore how invisible forces like gravity can affect objects in motion. I was inspired by the movement of planets around the sun and how they interact with each other through invisible forces (image below). The sketch creates a solar system-like scene where planets and moons orbit around a sun. I also added some extra forces, like a little bit of randomness (turbulence) to make things feel more alive and natural.

Image Inspiration

Code Highlight
One part of the code I’m really happy with is how I made the sun glow. I used different shades of color and layered circles to make the sun look like it’s shining and radiating light, just like in real life. Here’s the code for that:

// Draw gradient layers for the sun
for (let i = gradientColors.length - 1; i >= 0; i--) {
  fill(gradientColors[i]);
  ellipse(this.pos.x, this.pos.y, this.radius * 2 * (1 - i * 0.2)); // Each layer is slightly smaller
}

// Add outer glow to the sun
for (let i = 0; i < 10; i++) {
  let alpha = map(i, 0, 10, 50, 0); // Make the glow fade out
  fill(255, 153, 51, alpha);
  ellipse(this.pos.x, this.pos.y, this.radius * 2 + i * 15); // Expand glow with each layer
}

Embedded Sketch

Reflection and Future Work
Working on this sketch was really fun, but I did face some challenges. It was tricky to make sure the planets and their moons didn’t overlap or move too fast. For the future, I’d like to add a “repulsion” force so that planets and moons don’t get too close to each other. I also think it would be cool to let people interact with the sketch by clicking on planets or moving them around.

Week 2 – Algae Simulation by Dachi

Sketch:

 

Concept: My project is an interactive digital artwork that simulates the movement and appearance of algae in a swamp environment. Inspired by what I have seen in my home country many times, it aims to capture the flow of algae in a river. I used different methodologies to create a dynamic, visually interesting scene that mimics the organic, flowing nature of algae. By incorporating various elements such as multiple algae clusters, water particles, and background rocks, I tried to recreate a cohesive river like ecosystem.

Inspiration: The inspiration for this project came from my trip to Dashbashi Mountain in Georgia. I saw algae flowing in a river near the waterfall, and it was very pretty, almost from a fantasy world. This happened very recently so it was the first thing that came to mind when I was thinking about this project. This brief encounter with nature became the foundation for my work, as I tried to translate the organic movement of algae and water into an interactive digital format.

IMG_7042 – Short clip of moving Algae

Process of Development: I developed this project iteratively, adding various features and complexities over time:

At first I visualized the algae movement. I realized it had to do something with waves and sinusoidal shapes were first thing that came to my mind. Unfortunately, few hours in implementation I realized assignment specifically asked for acceleration. Soon after implementing acceleration, I realized this movement alone limited me in creating algae flow so I had to go back and forth multiple times to get at result that somewhat resembled the movement while using acceleration. Unfortunately, I did not find any definitive tutorials of such movement. As such this is more of a strand like simulation which I slowly built up, line by line, also looking at other works like hair movement for inspiration, I will mention them in references.

These screenshots are from earlier development of the simulations:

As you can see by adding various parameters to each strand as well as overall cluster, we are able to achieve similar wavy pulsating pattern that algae have. I also added particle systems and noise-based algorithms for water movement (you can see inspiration of this from reference). To enhance the environment, I included rock formations and a sky.  I integrated sliders and toggles for user interaction. Finally, I kept refining values till I achieved desire perfomance and visuals. The simulation is pretty heavy to run and you can expect drastic FPS drops, based on number of strands we are running. Water simulation is a significant culprit here despite multiple scaling that was needed to achieve even that. 

How It Works:

Algae Simulation: I created multiple clusters of algae strands, each with unique properties. I animate each strand using sine waves and apply tapering effects and clustering forces for a more natural-looking movement. I also calculate acceleration and velocity for each strand to simulate fluid dynamics.

Water Effects: I used a particle system to create the illusion of flowing water, with Perlin noise for natural-looking movement. I applied color gradients to enhance the swamp-like appearance. There is also background audio of waterfall that helps the immersion.

Environmental Elements: I drew rocks using noise-based shapes with gradients and added a toggleable sky for depth.

Interactivity: I included multiple sliders that allow users to adjust various parameters in real-time.

If you want to know more about in depth working and how everything is related, it will be better to check out my code as it is commented thoroughly.

Code: 

One piece of code I’m particularly proud of is the function that generates and animates individual algae strands:

function algae(strandPhase, strandLength, strandAmplitude, clusterEndX, clusterPulsePhase) {
  beginShape();
  
  let taperingPoint = taperingPointSlider.value() * strandLength;
  let taperingStrength = taperingStrengthSlider.value();
  
  for (let i = 0; i <= strandLength; i += 10) {
    let x = i;
    let y = 0;
    
    let progress = i / strandLength;
    
    let taperingFactor = 1;
    if (i > taperingPoint) {
      taperingFactor = pow(map(i, taperingPoint, strandLength, 1, 0), taperingStrength);
    }
    
    let currentAmplitude = strandAmplitude * (1 - progress * 0.8) * taperingFactor;
    
    let movementFactor = sin(map(i, 0, strandLength, 0, PI));
    let movement = sin(strandPhase + i * 0.02) * currentAmplitude * movementFactor;
    
    let angle = map(i, 0, strandLength, 0, PI * 2);
    x += cos(angle) * movement;
    y += sin(angle) * movement;
    
    let curvature = sin(i * 0.05 + phase + clusterPulsePhase) * 5 * (1 - progress * 0.8) * taperingFactor;
    y += curvature;
    
    let clusteringForce = map(i, 0, strandLength, 0, 1);
    let increasedClusteringFactor = clusteringFactor + (progress * 0.5);
    x += (clusterEndX - x) * clusteringForce * increasedClusteringFactor;
    
    vertex(x, y);
  }
  endShape();
}

This code incorporates acceleration and velocity calculations to simulate realistic fluid dynamics, creating more natural and unpredictable movement. The function also creates a tapering effect along the strand’s length, generates wave-like movement using sine functions, and applies a clustering force to mimic how algae clumps in water. I’m especially pleased with how it combines mathematical concepts like acceleration, sine waves, and mapping with artistic principles to create a visually appealing and believable representation of algae in motion. The integration of user controls allows for real-time adjustment of parameters like acceleration strength, making the simulation highly interactive and customizable.

Challenges

Balancing visual quality with smooth performance was tricky, especially when animating multiple elements at once. Getting the algae to move naturally in response to water currents took a lot of tweaking. The water particle system was also tough to optimize – I had to find ways to make it look good without slowing everything down. Another challenge was making the user controls useful but not overwhelming.

Reflection:

This project was a good learning experience for me. I enjoyed turning what I saw at Dashbashi Mountain into a digital artwork. It was challenging at times, especially when I had to figure out how to make the algae move realistically. I’m happy with how I combined math and art to create something that looks pretty close to real algae movement. The project helped me improve my coding skills and while it’s not perfect, I’m pleased with how finished product looks.

Future Improvements:

Speed it up: The simulation can be slow, especially with lots of algae strands. I’d try to make it run smoother.
Better water: The water effect is okay, but it could look more realistic.
Add more stuff: Maybe include some fish or bugs to make it feel more like a real ecosystem.

References:

p5.js Web Editor | Blade seaweed copy copy (p5js.org)

p5.js Web Editor | sine wave movement (hair practice) (p5js.org)

p5.js Web Editor | Water Effect (p5js.org)

YouTube

Internet

 

 

 

 

 

 

 

 

 

Week 2 Sketch

Concept:

I recently took on the challenge of simulating the motion of a falling feather in nature using p5.js. The goal was to go beyond a simple straight-down fall and imbue the feather with a sense of personality through its movement, all while controlling it solely through acceleration. I envisioned a feather that gently drifts and rocks side to side as it descends, mimicking the playful dance of a real feather caught in a gentle breeze.

Code Highlight:

One of the aspects I’m particularly proud of is how I implemented the rocking motion coupled with a horizontal boost:

// Apply wind force (horizontal acceleration) with Perlin noise and angle influence
let windDirection = noise(noiseOffset) * 2 - 1; // -1 to 1
featherAx = windForce * windDirection + abs(sin(featherAngle)) * 0.5 * windDirection;

This snippet shows how the feather’s horizontal acceleration (featherAx) is influenced not just by random wind (using Perlin noise) but also by its current angle (featherAngle). The abs(sin(featherAngle)) term provides a boost in the direction of the rotation, making the feather sway more pronouncedly as it rocks. This simple addition significantly enhances the visual appeal and realism of the motion.

Embedded Sketch:

Reflection and Future Work:

I’m quite pleased with how this simulation turned out. The feather’s motion feels natural and captures the essence of its lightness and susceptibility to air currents. However, there’s always room for improvement! Here are some ideas for future exploration:

  • More Realistic Feather Shape: The current U-shape is a simplification. Implementing a more detailed feather shape, perhaps with multiple segments, could add visual interest.

  • Interaction with Wind: It would be interesting to allow the user to interact with the wind, perhaps by blowing on the feather through a microphone or using the mouse to create gusts.

  • 3D Simulation: Extending the simulation to 3D space could open up new possibilities for exploring the feather’s movement in a more immersive environment.

  • Multiple Feathers: Simulating multiple feathers interacting with each other and the wind could create a mesmerizing visual experience.

Raindrop Simulator with Wind

For my project this week I knew I wanted to do something related to water. I decided to pick rain, something I miss when in this country. Below you can see the program I created, and you can view the full code on my GitHub here.

The program is simple, it generates raindrops that will fall down the screen. There are sliders to modify the rain’s density and the wind’s speed “blowing” the rain either left or right. To do this, there is an array containing a Drop object that stores a position, velocity, and acceleration vector, that will update itself when step() is called. In the draw() function, there is a loop to iterate through the rain array to update the position of each raindrop. To simulate wind, the acceleration vector is given an x-value based on the current value of the wind slider. This makes the vector move along the x-axis to emulate wind blowing the rain particles. After updating the position, it will draw a line based on the length of the raindrop and its previous x position. The Drop class can be seen here:

class Drop {
  constructor(vel, acc, angle) {
    this.len = random(10, 15);
    this.pos = createVector(random(width), -this.len);
    //slightly varying velocity so they all dont fall at the same exact speed
    this.vel = createVector(0, random(0.5, vel));
    this.acc = createVector(angle, acc);
  }
  step() {
    this.vel.add(this.acc);
    this.pos.add(this.vel);
    //make sure the drops stay in bounds of screen
    if (this.pos.x > width) {
      this.pos.x = -this.len;
    }
    if (this.pos.x < -this.len) {
      this.pos.x = width;
    }
  }
  show() {
    //draw raindrop
    stroke(209, 255, 251);
    line(
      this.pos.x,
      this.pos.y,
      this.pos.x - this.vel.x,
      this.pos.y - this.len
    );
  }
}

Overall, I don’t think there is much else I can do to improve this program. One idea I had was to try to have an effect such as a ripple when the drop would hit the ground. Perhaps it would also be interesting to try to emulate the rain in three dimensions, which would allow for wind to blow in two different dimensions.

Sketch – Week 2

Concept

I wanted to create realistic clouds as I was inspired by the beautiful skies and sunsets I grew up seeing in Vancouver. I was not able to capture a video myself as Abu Dhabi is relatively cloudless, so I used a video from the internet as a reference point. The clouds were not moving at a constant rate and did not move in only one directions. I used acceleration to achieve this random effect.

Code Snippet

noiseVector = createVector(0, 0);          // Starting position
noiseVelocity = createVector(0.03, 0);     // Initial velocity moves from left to right
noiseAcceleration = createVector(random(-0.005, 0.005), random(-0.005, 0.005)); // Small acceleration for drift


// Update the noise field
noiseVelocity.add(noiseAcceleration);  
noiseVector.sub(noiseVelocity);       
  
// Move noise field slightly in all directions 
noiseAcceleration.x = random(-0.0005, 0.0005);  
noiseAcceleration.y = random(-0.0005, 0.0005);

 Embedded Sketch

Reflections

I had a lot of trial and error with the cloud movement. Using Perlin noise helped create the cloud like shape, but in the video I was referencing, the clouds were moving and changing constantly. I think modifying the acceleration helped create the cloud movement and make it look more natural/random, but I was not able to modify the acceleration a lot as this would make the movement erratic, so the result ended up being more subtle. In the future, I want to look into other ways to modify Perlin noise.