In the second week, we tapped into the world of vectors. I wanted to make something simple that requires speed, velocity, and everything around us with such aspects. Our class starts with bouncing a ball, and I revisited this concept again this time by applying vectors. What I wanted to recreate is a ball object falling into the water and creating this ripple-like effect.
Sketch
How it works
Using vectors, we can have p5.js calculate the speed of the object. The concept of ‘gravity’ here can be attributed to the y-position of the ball. Each time the ball bounces, its y-position should be decreased by cutting it in half. For the bounce to feel smoother, we introduce a bounciness variable that multiplies with the y-position negatively.
// bounce by multiplying the speed by the bounciness
if (position.y + diameter / 2 > height) {
position.y = height - diameter / 2;
speed.y *= -bounciness;
speed.x *= bounciness;
}
Challenges faced
The tutorial that I followed only introduced the concept of ‘gravity’ in scalar methods. Extracting this information, I transformed the modes from scalar to vector. It was quite interesting, and quite the challenge!
As I originally mentioned, I wanted to make the ball form ripples when touching a certain height. While creating the ripples itself is a simple task using for-loops, making sure that it stays without being refreshed by the background is a challenge that I still haven’t overcome yet.
I have been trying to implement a few things, but I do not want my code to become overcomplicated. While I still have the time, I’ll try some stuff up to see if fixing this is possible.
Edit: I finally fixed the ripple problem, check it out here.
For this assignment, I was primarily inspired by the galaxy—a seemingly simple yet profoundly intricate phenomenon in our vast universe. The swirling motion of stars and cosmic dust, the vibrant colors, and the immense scale all evoke a sense of mystery and wonder. Galaxies are both elegant and complex, embodying the balance between chaos and order. This duality inspired me to explore patterns and shapes that reflect the beauty and unpredictability of the cosmos. Through this project, I aimed to capture the essence of the galaxy’s mesmerizing movement and its delicate balance of simplicity and complexity.
In my code, I implemented two key features. The first was the required assessment of movement and acceleration. By pressing the up and down arrow keys, the speed of the particles could be controlled, allowing for a dynamic interaction with the animation. The second feature involved particle interaction through hovering. As the cursor hovers over the particles, they would disperse, creating a sense of disruption and fluid motion in response to user input.
Highlight I’m proud of
Here I was able to create a special class for the particles.
class Particle {
constructor(vx, vy) {
this.vx = vx;
this.vy = vy;
this.num = 255;
this.a = 255;
this.loc = createVector(width / 2, height / 2); // Start from the center
this.vel = createVector(0, 0);
this.acc = createVector(1, 1);
}
update() {
// Apply acceleration using the acceleration factor
this.vel.add(this.acc.mult(accelerationFactor));
this.loc.add(this.vel);
// Reset acceleration and limit the velocity for smoother control
this.acc.mult(0);
this.vel.limit(0.5);
}
repulse() {
// Calculate the distance between the particle and the mouse
let mouse = createVector(mouseX, mouseY);
let dir = p5.Vector.sub(this.loc, mouse);
let distance = dir.mag();
if (distance < repulsionRadius) { // If the particle is within the repulsion radius
dir.normalize(); // Normalize the direction to get just the direction vector
let force = map(distance, 0, repulsionRadius, 5, 0); // Stronger repulsion the closer the particle is
dir.mult(force); // Multiply the direction vector by the repulsion force
this.vel.add(dir); // Apply the repulsion force to the particle's velocity
}
}
isOutside() {
// Check if the particle goes outside the canvas
return (this.loc.x < 0 || this.loc.x > width || this.loc.y < 0 || this.loc.y > height);
}
display() {
// Update acceleration based on the particle's properties and a trigonometric function for spirals
this.acc = createVector(
sin(radians(this.vx + this.num / 2)) / 2,
cos(radians(this.vy - this.num / 2)) / 2
);
// Draw the particle with a fade effect
fill(255, this.a);
var r = map(this.a, 255, 0, 1, 10); // Particle size changes as it fades
ellipse(this.loc.x, this.loc.y, r);
this.num += map(this.a, 255, 0, 1, 0); // Update num for smooth spiral motion
this.a -= 0.1; // Gradually reduce alpha for a fade-out effect
}
}
Rain always brings me peace; therefore, for this project, I created a scene of rainfall to reflect the feeling I get when it rains. I had some ideas before I settled into this one, such as sunset and sunrise, moving waves, and birds. However, replicating rainfall spoke to me the most.
I began this assignment by reviewing the materials for this week and watching inspirational videos of things that move. Then, I read a little about the physics behind rainfall, its average acceleration relative to its size, and velocity. I had to do some conversions and basic math to make it logical on a computer screen. For the scene, I used a PNG image for the mountains, a gradient for the sky, rain audio, and some stars to enhance the theme.
Highlight of code:
I tried to make my code neat and clean for this project by creating a different class file and functions whenever possible. The most challenging part of this project was figuring out logical numbers for the line length, acceleration, and velocity so that the rain looks similar to real life. I also had some trouble figuring out how to make sure the lines were straight and not move in any other direction, so I also experimented with it.
class RainDrop {
constructor(x, y) {
this.position = createVector(x, y);
// make the numbers more logical when it comes to rain in pixcels
this.velocity = createVector(0, random(0.6, 0.9));
this.acceleration = createVector(0.001, 0.9);
this.length = random(10, 20);
this.Colorstrength = random(20, 255); //form od depth
}
show() {
stroke(200, this.Colorstrength);
// subtracting x from the length to create a vertical line if i dont it looks weird
line(
this.position.x,
this.position.y,
this.position.x,
this.position.y - this.length
);
}
update() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
}
}
Embedded sketch:
Future work:
While this project satisfies what I have learned so far, in the future, I want to add other factors that affect the acceleration of a raindrop, like the force of air and its density. I also want to add more depth so that the sketch resembles real life and to enhance the experience. Furthermore, I want to loop the sound and create a button for the user to have the power to play and stop the audio.
Since it is around the end of the dates season in the UAE, I have decided to do this assignment related to that. Upon clicking the down arrow button, you can witness the dates falling from the palm trees in a constant acceleration.
It was quite difficult for me to find a video, so here is a photo of date palm trees instead:
I limited the acceleration to 3 as I did not want it to speed up too fast as it would seem to be unrealistic. Below is the part of code that shows the dates accelerating when the down arrow button is pressed.
For future improvements, I would allow the user to restart it by clicking the mouse or a button. I also would add different kinds of dates and more of them.
To get inspired for this week assignment, I took a walk around the campus. I had many ideas in my mind about what I wanted to do; some of them were about trying to imitate the movement of ants or the light of the sun. Although, something caught my interested when I stumbled upon this view:
That is when I realized what I wanted to do.
The Concept
Once I observed this tree populated by many birds, I wanted to recreate both the movement of the leaves, of the tree, and the path the birds take to reach it. At first, I was intimidated to think about this, since it would be very hard to simulate a tree with moving leaves. Nevertheless, I was intrigued.
To reach a satisfactory conclusion for the project, first I had to make a quick sketch about what I wanted to do:
From what I analyzed in this sketch, I would have to do the following:
Simulate many leaves moving at the same time without lagging the computer.
Make a believable movement using velocity and acceleration.
Populate, randomly, each region and with a different pattern (that is, how they are going to generate according to the range specified).
Understand how the birds will interact after the leaves are populated.
Preparing the scenario
Compared to last week’s assignment, I felt that it was too abstract and needed a bit more of style in order to be more presentable. From this experience, I wanted to avoid what I did previously and improve upon it, thus, I wanted to develop a more “alive” canvas.
For this, I needed to create first the scenario that was in symphony with my then progress done:
Not only that, but in order to create this “alive” canvas, I also realized we would need audio. So, I extracted the audio from the video I recorded, edited it and then added it to the project:
The code
According to what we have seen in class at the moment, for this code it was essential to work with vectors. Essentially, the vectors were used for almost everything: The ranges where the leaves spawn, the leaves and the birds.
In order to understand how this is possible, here is a quick explanation of how it works in a sequential order:
Regions are created, manually, with the help of createVector(x,y). These regions are stored in the class Leaves. The reasoning of creating regions is to simulate, as accurate a possible with the limitations of p5.js, the movement of the leaves without creating lag and with enough diversity to create the illusion.
After the regions are set, the leaves now start to spawn in each region. There are a total of 5 different regions and, by default, 80 leaves by group. The leaves themselves are represented by green circles that walk through transparency with the help of the Perlin movement. Not only that, but the leaves move by accelerating forwards and backwards in their region.
After the regions and leaves are spawned, the birds can start appearing. To avoid creating a lot of circles in the screen, I decided to only draw one black circle for the birds. They will appear one by one from either the left or right part of the screen. The bird will select a region, randomly, as the endpoint. As mentioned, the birds are represented as black circles that will appear as medium-sized and will be further decreasing to create the illusion of the bird going inside the tree. Once the size of the bird reaches 0, it will generate another bird.
Important note: In order to simulate all of this believably, I had to lock the frame rate to 10. Since if it left with a default of 60, it will look that the leaves and birds are moving at super speeds.
What I am mostly proud of
I am proud that I could simulate how leaves move (at least partially). This was the most challenging part of this work and the one that needed to be addressed first. I took some examples from the class in order to understand how to create this movement. Although, sometimes due to not understanding how it fully worked, the leaves would go out of the Canva, move weirdly, extremely fast and unnatural or just not show at all (because they were going so fast that our eyes could not perceive the moment it left the screen).
Here is a quick example of the code that I used for the movement of the leaves:
// ----- Move leaves according to region. -----
for (let i=0; i<amount_of_leaves; i++){
//Taken from the example taught in class.
let dir = p5.Vector.sub(this.range1, this.p1_leaves[i]);
dir.normalize();
dir.mult(0.05);
this.acceleration = dir;
this.velocity.add(this.acceleration);
this.velocity.limit(5);
this.p1_leaves[i].add(this.velocity);
}
for (let i=0; i<amount_of_leaves; i++){
//Taken from the example taught in class.
let dir = p5.Vector.sub(this.range2, this.p2_leaves[i]);
dir.normalize();
dir.mult(0.05);
this.acceleration = dir;
this.velocity.add(this.acceleration);
this.velocity.limit(5);
this.p2_leaves[i].add(this.velocity);
}
for (let i=0; i<amount_of_leaves; i++){
//Taken from the example taught in class.
let dir = p5.Vector.sub(this.range3, this.p3_leaves[i]);
dir.normalize();
dir.mult(0.05);
this.acceleration = dir;
this.velocity.add(this.acceleration);
this.velocity.limit(5);
this.p3_leaves[i].add(this.velocity);
}
Although, I am not very proud by the amount of for statements…
Similarly, for the movement of the birds the code was similar, but easier to understand since I knew how it worked:
fly(rng_number){
//Decide if the bird on the left or right will flight. After that, decide on the final destination to then apply the motion.
if (rng_number == 1){
if (this.final_region_destination == 1){
this.dir = p5.Vector.sub(leaves.range1, this.bird1);
} else if (this.final_region_destination == 2){
this.dir = p5.Vector.sub(leaves.range2, this.bird1);
} else if (this.final_region_destination == 3){
this.dir = p5.Vector.sub(leaves.range3, this.bird1);
} else if (this.final_region_destination == 4){
this.dir = p5.Vector.sub(leaves.range4, this.bird1);
}
this.dir.normalize();
this.dir.mult(0.6);
this.acceleration = this.dir;
this.velocity.add(this.acceleration);
this.velocity.limit(10);
this.bird1.add(this.velocity);
if (this.t <= 0){
this.restart();
}
}
else if (rng_number == 2){
if (this.final_region_destination == 1){
this.dir = p5.Vector.sub(leaves.range1, this.bird2);
} else if (this.final_region_destination == 2){
this.dir = p5.Vector.sub(leaves.range2, this.bird2);
} else if (this.final_region_destination == 3){
this.dir = p5.Vector.sub(leaves.range3, this.bird2);
} else if (this.final_region_destination == 4){
this.dir = p5.Vector.sub(leaves.range4, this.bird2);
}
this.dir.normalize();
this.dir.mult(0.6);
this.acceleration = this.dir;
this.velocity.add(this.acceleration);
this.velocity.limit(10);
this.bird2.add(this.velocity);
if (this.t <= 0){
this.restart();
}
}
Again, not very proud with how copied and pasted it looks…
The Sketch
So, after all of this explanation, what is the final product? Well, there is the final sketch.
Note: The “white background effect” is intentional. Also, this Canva has background audio, no mouse interactions and a default of 10 frames per second.
I am mostly happy to how this assignment concluded. I feel that I learned new things about the programming world as well as how to interpret movements in nature. Although, there are some improvements I want to make for future works:
Even though this sounds nonsensical, I would like to avoid using too many in-class examples for my codes and challenged myself a bit more with what I can find out of the box.
Avoid that the circles accumulate all together if the Canva is left running for a few minutes.
Credits
Some references I used for help are the following:
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.
The concept for this project was to create a rain simulation with lightning. Rain is not something we experience here usually but we can simulate it. I don’t have a specific video or picture in mind; I just went to p5 to simulate raindrops, ripples, and lightning.
Code Highlight
function generateLightningBolt() {
lightningBolt = [];
let x = random(width * 0.25, width * 0.75);
let y = 0;
lightningBolt.push(createVector(x, y));
for (let i = 0; i < 8; i++) {
x += random(-30, 30);
y += random(30, 60);
lightningBolt.push(createVector(x, y));
}
}
function drawLightningBolt() {
stroke(200, 200, 255);
strokeWeight(4);
noFill();
beginShape();
for (let i = 0; i < lightningBolt.length; i++) {
vertex(lightningBolt[i].x, lightningBolt[i].y);
}
endShape();
}
function drawSparkles() {
noStroke();
for (let i = 0; i < 20; i++) {
let sparkleX = lightningBolt[lightningBolt.length - 1].x + random(-40, 40);
let sparkleY = lightningBolt[lightningBolt.length - 1].y + random(-40, 40);
fill(255, 255, 255, random(150, 255));
ellipse(sparkleX, sparkleY, random(3, 8), random(3, 8));
}
}
The part of the code that I’m proud of is the lighting. The lighting has two elements, the bolt and the sparkles. For the bolt, I created a random vector starting from a random position at the top of the screen and started adding random values from fixed intervals to that random vector’s coordinates to get the next subsequent random vectors for the zigzag-like pattern of the lighting bolt. Finally, I plotted those vectors by converting each point to a vertex and using the begin and end shape functions to connect those vertices. For the sparkles, I used 20 ellipses the last vertex of the lightning bolt. Each ellipse has a random major and minor axis length fixed between two values.
Sketch
Future Improvements and Reflection
The simulation is a basic representation of rain with a thunderstorm. However, adding sound might make the experience full and realistic. The sounds are mainly for the rain, ripples, and thunder.
For this week’s assigment, I have decided to get inspired by my favorite season: WINTER (kind of ironic giving the fact that we live in Abu Dhabi). I found myself reminiscing about the snowflakes that I get to see outside my window every time I go back home.
I found this YouTube video for inspiration:
After working for a bit on it, this was the final result:
This is the code:
//SNOWFLAKES ASSIGNMENT 2
//by SP
let snowflakes = [];
let panePadding = 20;
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
setGradient(0, 0, width, height, color(20, 29, 68), color(100, 150, 255));
// new snowflakes
let t = frameCount / 60;
if (random() < 0.05) {
snowflakes.push(new Snowflake());
}
for (let flake of snowflakes) {
flake.update(t);
flake.display();
}
// remove snowflakes that are off the screen
snowflakes = snowflakes.filter((flake) => !flake.offScreen());
drawWindowFrame();
}
class Snowflake {
constructor() {
this.posX = random(width);
this.posY = random(-50, 0); // start slightly off-screen
this.size = random(5, 12); // size of the snowflake
this.speed = random(1, 3); // falling speed
this.drift = random(-0.5, 0.5); // slight drift left or right
this.angle = random(TWO_PI); // random rotation angle
this.rotationSpeed = random(-0.01, 0.01); // slight rotation
}
update(time) {
this.posY += this.speed;
this.posX += this.drift;
this.angle += this.rotationSpeed;
}
display() {
push();
translate(this.posX, this.posY);
rotate(this.angle);
drawSnowflake(this.size);
pop();
}
offScreen() {
return this.posY > height;
}
}
function drawSnowflake(size) {
stroke(255);
strokeWeight(2);
noFill();
// star-like shapes with lines to represent a snowflakes
beginShape();
for (let i = 0; i < 6; i++) {
let x = cos((TWO_PI * i) / 6) * size;
let y = sin((TWO_PI * i) / 6) * size;
line(0, 0, x, y);
// small branches for each main line
for (let j = 0.4; j < 1; j += 0.4) {
let branchX = cos((TWO_PI * i) / 6) * size * j;
let branchY = sin((TWO_PI * i) / 6) * size * j;
line(
branchX,
branchY,
branchX + cos((TWO_PI * (i + 0.5)) / 6) * size * 0.2,
branchY + sin((TWO_PI * (i + 0.5)) / 6) * size * 0.2
);
line(
branchX,
branchY,
branchX + cos((TWO_PI * (i - 0.5)) / 6) * size * 0.2,
branchY + sin((TWO_PI * (i - 0.5)) / 6) * size * 0.2
);
}
}
endShape(CLOSE);
}
// a vertical gradient
function setGradient(x, y, w, h, c1, c2) {
for (let i = y; i <= y + h; i++) {
let inter = map(i, y, y + h, 0, 1);
let c = lerpColor(c1, c2, inter);
stroke(c);
line(x, i, x + w, i);
}
}
// draw the window frame
function drawWindowFrame() {
fill(80, 60, 40);
noStroke();
let paneWidth = (width - panePadding * 3) / 2;
let paneHeight = (height - panePadding * 3) / 2;
rect(0, height / 2 - panePadding / 2, width, panePadding);
rect(width / 2 - panePadding / 2, 0, panePadding, height);
rect(0, 0, panePadding, height); // left
rect(width - panePadding, 0, panePadding, height); // right
rect(0, 0, width, panePadding); // top
rect(0, height - panePadding, width, panePadding); // bottom
}
The code that I am most proud of is the one for the snowflakes. In the beginning, I just created small circles but then I decided to make them look more realistic. It took me a long time but it was worth it because it all worked out in the end.
Reflection and ideas for future work or improvements:
Overall, I think I did a good job. Of course it is not super realistic but we are slowly getting there. For the future, I would like to recreate the entire scene and add a table next to the window.
Concept:
In this assignment, I created an interactive scene where pigeons wander around a gray ground (representing something like a park or city square) searching for food. I added two trees on either side of the canvas, and when I click on them, leaves fall and disappear once they hit the ground. The pigeons move toward randomly placed food, and after one of them reaches the food, a new piece of food appears in a different spot. The goal was to simulate natural pigeon behavior and add a fun, interactive element with the falling leaves, all while using vector-based physics to bring the motion to life.
Code Highlight:
One part of the code I’m really happy with is the falling leaves interaction:
function mousePressed() {
for (let i = 0; i < treePositions.length; i++) {
let treePos = treePositions[i];
// Check if the mouse click is within the tree's area
if (dist(mouseX, mouseY, treePos.x, treePos.y) < 75) {
// Generate falling leaves from the clicked tree
for (let j = 0; j < 10; j++) {
leaves.push({
position: createVector(treePos.x + random(-20, 20), treePos.y),
velocity: createVector(random(-1, 1), random(1, 3)) // Falling leaves
});
}
}
}
}
This part of the code is what handles the falling leaves when you click on a tree. When the user clicks near one of the trees the code checks if the click is within range using the dist() function to measure distance. If it is it triggers a loop that generates 10 leaves each with random positions and velocities so they fall in a natural way. The leaves then get added to an array so they can be drawn and moved across the screen.
I’m really proud of this because it was the part I struggled with the most. Figuring out how to create that random natural-looking motion for the leaves and getting the click interaction to work properly took some effort but it adds a lot of life to the scene!
Embedded Sketch:
Reflection and Ideas for Future Work:
Looking back, I’m really happy with how the pigeons’ movement and the interactive leaves turned out. It’s a fun and dynamic scene, but I have a few ideas for improvements and future work:
Perlin Noise for the Ground: I want to add Perlin noise to the ground to give it texture, making it look like dirt or pavement instead of just flat gray.
More Realistic Pigeon Behavior: I’d like to implement flocking behavior so the pigeons move together in a more natural way, like real pigeons do.
Tree Sway and Better Leaves: I think it would be cool to make the trees sway slightly when the leaves fall and vary the size and speed of the leaves for more realism.
Animated Pigeons: Adding animations for the pigeons, like walking or pecking, would make them feel more alive.
As a true island girl, the beach has always held a special place in my heart. It’s not just one of my favourite places in the world; it’s where I find peace and calm. There’s something really magical about being at the beach at night—when the darkness hides your view, but you can still feel the water, hear the rhythm of the waves, and see the moon reflected on the surface. This inspired me to create an ocean scene for this project, with waves at the height of their movement, each with its own unique personality through acceleration.
In my code, acceleration controls how fast the waves move. You can increase or decrease the speed by pressing the right or left arrow keys:
Pressing the right arrow increases acceleration, making the waves move faster.
Pressing the left arrow decreases acceleration, slowing the waves down.
The movement is smooth because acceleration gradually adjusts the wave speed rather than changing it instantly, making the effect feel more natural. This simulates a build-up or reduction in wave intensity, similar to real ocean waves building up momentum.
Highlight of Code:
One of the most rewarding aspects of this project was simulating the moon’s gravitational pull on the waves. I’m particularly proud of how I captured the subtle way the waves respond to the moon’s slow journey across the sky. The moon’s movement mimics the natural pull of gravity, affecting the height and behaviour of the waves as they interact.
// Slow down the moon's movement, simulating gravitational influence
moonX = map(noise(frameCount * 0.005), 0, 1, 0, width); // Slow, subtle horizontal movement
// Slightly adjust wave height based on moon's horizontal position (gravity effect)
let gravitationalEffect = map(moonX, 0, width, -20, 20); // Subtle effect
let y = noise(frameCount * noiseSpeed + i + j) * noiseHeight + noiseY + offsetY + gravitationalEffect;
Reflection and Future Work:
Though the project didn’t quite reach the full vision I had in mind, I’m proud of how it turned out. I originally hoped to simulate waves crashing onto the shore and to add more elements like sound and depth to the sketch. These are definitely areas I plan to improve on in future iterations, but I feel this piece captures the essence of the ocean that I love so much.