For this week’s assignment, I want to use the Air Friction code from matter.js. In the code, there are three shares that move down at different rates, simulating how different air friction affects different objects. From here, I wanted to create beach ball objects in different sizes, with the biggest beach ball falling at the slowest rate due to it’s larger surface area creating increased air friction, and with the same logic, the smallest ball falling the fastest.
function spawnBeachBalls() {
let ballSizes = [20, 30, 40];
let airFrictions = [0.001, 0.02, 0.05];
for (let i = 0; i < ballSizes.length; i++) {
let ball = Bodies.circle(random(50, width - 50), -30, ballSizes[i], { frictionAir: airFrictions[i] });
beachBalls.push(ball);
Composite.add(engine.world, ball);
}
Embedded Sketch
Reflections
Visually, I like how my code turned out. However, I wanted the beach balls to interact with the person (the mouse) differently. When the person touches the beach ball, the beach ball should float upwards with the same velocity it is moving downwards. I tried to reverse the velocity in the code, but it did not work. As I am working with matter.js, I will have to spend more time figuring out how to get the beach balls to behave the way I want.
For this week’s assignment, I was inspired by Ryoichi Kurokawa’s “syn_mod.n” artwork. I liked how fluid the artwork looked, and the intricacy of the webs. I intended to create similar visual shapes and manipulate them through separation and alignment.
Code Snippet
class Boid {
constructor(x, y) {
this.position = createVector(x, y);
this.velocity = p5.Vector.random2D();
this.acceleration = createVector(0, 0);
// Define maximum speed and steering force for the boid
this.maxSpeed = 3;
this.maxForce = 0.05;
// Set initial weights for flocking behaviors
this.setBehaviorWeights(20, 1, 1);
}
run(boids) {
// Control boid's behavior, movement, and visual display
this.flock(boids);
this.update();
this.wrapAround();
this.showConnections(boids); // Draw connecting lines between boids
}
setBehaviorWeights(sepWeight, aliWeight, cohWeight) {
// Set weights for separation, alignment, and cohesion behaviors
this.sepWeight = sepWeight;
this.aliWeight = aliWeight;
this.cohWeight = cohWeight;
}
flock(boids) {
// Calculate the three main flocking behaviors
let sep = this.separate(boids).mult(this.sepWeight); // Separation: avoid crowding
let ali = this.align(boids).mult(this.aliWeight); // Alignment: steer towards average heading
let coh = this.cohere(boids).mult(this.cohWeight); // Cohesion: move towards group center
// Apply the combined steering forces to the boid
this.applyForce(sep.add(ali).add(coh));
}
Embedded Sketch
Reflections
I enjoyed learning the flocking behaviours. To improve my code, I would add interaction to it. I wanted to add a mousePressed interaction but I was struggling to get the visual effect I wanted. I also wanted the web to look more organic, and I would change the code using perlin noise to achieve this effect.
For this week’s assignment, I wanted to investigate the code for the wandering behaviour that we covered in class. I found it interesting how the direction of wander isn’t completely random, rather guided in one direction for a bit and then another. When I thought of what in nature wanders, I thought of tadpoles. I wanted to create a realistic tadpole, with its realism enhanced by the wandering behaviour. Working of the code from class, I tweaked some of the numbers and added edges, so that the tadpole can’t escape the bounds of the canvas.
Code Snippet
edges() {
let pushed = false;
// Check each boundary, reverse direction, and add a force toward the center if needed
if (this.pos.x > width - this.r) {
this.pos.x = width - this.r;
this.vel.x *= -1;
pushed = true;
} else if (this.pos.x < this.r) {
this.pos.x = this.r;
this.vel.x *= -1;
pushed = true;
}
if (this.pos.y > height - this.r) {
this.pos.y = height - this.r;
this.vel.y *= -1;
pushed = true;
} else if (this.pos.y < this.r) {
this.pos.y = this.r;
this.vel.y *= -1;
pushed = true;
}
// If a boundary was hit, apply a slight push toward the canvas center
if (pushed) {
let center = createVector(width / 2, height / 2);
let directionToCenter = p5.Vector.sub(center, this.pos);
directionToCenter.setMag(0.5); // Set the magnitude of the push
this.applyForce(directionToCenter);
}
}
Embedded Sketch
Reflections
I like the visual effect of my code and I enjoyed learning more about how to modify the wandering behaviour. I struggled the most with creating the boundary for the canvas, and making sure the tadpole didn’t get stuck and keep bouncing on the boundary. With more time, I would incorporate more into the code, such as mousePressed(), to make the project more interactive and experiment more with wander.
At first, I was inspired by the concept of identity and was attempting to create fingerprint patterns. However, I couldn’t get the shape to look the way I wanted to. Here is my sketch.
Although I am proud of the work I put into the fingerprints, but they did not have the organic shape I wanted, and I couldn’t figure out how to generative movement while still making the sketch feel natural and organic. In brainstorming other ideas, I revisited my other sketches for this class. Many of my sketches were of natural elements. I think it’s interesting to use computation to create more fluid, nature-related objects. So, I re-imagined my midterm to show rain falling on a lake, and the creation of the ripples to be the main artistic element of my sketch. Here are some of the images I was inspired by.
Luckily, I was able to draw from my previous draft in creating the new sketch, as both shared the same logic in creating the circular shapes and in modifying these shapes with Perlin noise.
Coding Translation and Logic
I planned the sketch by first modifying my fingerprint sketch to create the ripples. I created a class for the ripples and a class for the raindrops, as these were my main two elements
class Raindrop {
constructor(x, y) {
this.pos = createVector(x, y); // Initial position
this.size = 15; // Size of the raindrop
this.speed = random(initialRaindropSpeedRange[0], initialRaindropSpeedRange[1]); // Random speed
this.acceleration = 1.2; // Acceleration (gravity)
this.targetY = random(height / 6, height); // Random point at which raindrop will stop
}
update() {
this.speed += this.acceleration; // Increase speed due to acceleration (gravity)
this.pos.y += this.speed; // Move the raindrop down by updating its y-coordinate
// If mouse is pressed, make the raindrop fall faster
if (mouseIsPressed) {
this.speed = random(10, 15);
} else {
this.speed = constrain(this.speed, initialRaindropSpeedRange[0], initialRaindropSpeedRange[1]);
}
}
display() {
fill(201, 243, 255, 100); // Light blue, semi-transparent fill
stroke(255); // White stroke
strokeWeight(0.2); // Thin stroke weight
// Draw a bezier curve to represent the raindrop's shape
beginShape();
vertex(this.pos.x, this.pos.y);
bezierVertex(this.pos.x - this.size / 2, this.pos.y + this.size, this.pos.x + this.size / 2, this.pos.y + this.size, this.pos.x, this.pos.y);
endShape(CLOSE);
}
hitsBottom() {
return this.pos.y >= this.targetY; // Checks if the raindrop has reached the target Y position
}
}
In the Raindrop class, I initialized all the elements to the raindrop. I made the speed random and added acceleration as each raindrop falls so it creates a more natural effect. When the mouse is pressed, the raindrops fall faster, creating a rainstorm-like effect. The shape of the raindrops are created with bezierVertex so that they look fluid. If the raindrop returns true that it hit the ‘ground’, then a ripple is created.
class Ripple {
constructor(x, y) {
this.pos = createVector(x, y);
this.size = 0;
this.alpha = 230; // Initial transparency
this.growthRate = 2; // How fast the ripple grows
this.fadeRate = 4; // How fast the ripple fades
this.layers = 6; // Number of ripple layers
this.wideningFactor = 1.1; // Factor to widen the ripple's oval shape
}
update() {
// Increase ripple size
this.size += this.growthRate;
// Decrease transparency so ripple fades out
this.alpha -= this.fadeRate;
}
display() {
noFill();
stroke(201, 243, 255, this.alpha);
strokeWeight(2);
// Draw multiple layers of ripples
for (let i = 0; i < this.layers; i++) {
let layerSize = this.size - i * 10;
let widening = pow(this.wideningFactor, i); // Widen each subsequent ripple
if (layerSize > 0) {
this.drawOvalRipple(layerSize * 2 * widening, layerSize * widening); // Draw each ripple layer
}
}
}
// Draw ripples with distortion using Perlin noise
drawOvalRipple(ovalWidth, ovalHeight) {
beginShape();
for (let angle = 0; angle < TWO_PI; angle += 0.1) {
let xoff = cos(angle) * 5; // X offset for Perlin noise
let yoff = sin(angle) * 5; // Y offset for Perlin noise
// Apply Perlin noise to distort the ripple shape
let distortion = map(noise(xoff + this.size * noiseScale, yoff + this.size * noiseScale), 0, 1, 0.95, 1.05);
// Calculate the X and Y coordinates for the ripple based on the oval's size and the distortion
let x = this.pos.x + (ovalWidth / 2 * cos(angle)) * distortion;
let y = this.pos.y + (ovalHeight / 2 * sin(angle)) * distortion;
vertex(x, y); // Add vertex to the shape
}
endShape(CLOSE);
}
// Check if the ripple is fully faded
isDone() {
return this.alpha <= 0;
}
}
In the Ripple class, I initialized all the elements to the ripple. I updated the ripple’s size and level of opacity. I drew multiple layers of ripples, with each growing larger. Each ripple has Perlin noise applied to it to distort the shape and make it look more organic. If the ripple is fully faded, it is removed from the simulation. In the draw function, I used ‘splice’ to remove each ripple and raindrop from their respective arrays.
Challenges
I wanted to make the background more interesting and fluid. Instead of just having a one colour background, I want to make a gradient background of two colours. Although this is usually a simple process that I have worked with in the past, I found it hard to implement into my code as my code worked with objects that changed and moved. This meant I needed to find a way to the gradient to be drawn as the background without affecting the raindrop or ripple objects (both objects have transparency to them and thus are visually effected) To do this, I drew the gradient on PGraphics (gradientBG) rather than directly onto the canvas. This way, the gradient is stored and I don’t need to redraw it every frame. In the draw function the gradient is applied using “image(gradientBG, 0, 0)”.
Pen Plotting
I did not have to change my sketch for the pen plotting. The process was enjoyable and informative, as it was my first time using a pen plotter. One improvement that I think could have been made is drawing different layers, and using a light colour for some of the individual ripples, as in the code the ripples become lighter in colour as they fade out. Here is my pen plot and the video of the process.
Future Improvements
To improve this project, I would add rainfall sounds and music in the back to create a calming ambiance. I would create other objects to add to the ambiance, such as lily pads or lake animals. I would also create a boundary for each ripple, and modify the ripples when they collide with each other, such as increasing the Perlin noise. I was attempting to do this, but kept running into errors/having glitches.
For my midterm, I wanted to work with the concept of identity. Using techniques we learned in class, I want to create intricate, morphing shapes. Identities are defined in numerous ways, such as fingerprints, phenotypes, and personality. I want to create shapes that morph and change when mouse is pressed.
So far, I have made a pattern on p5.js. It changes when mouse is pressed, but I haven’t made the transition seamless yet. I think the hardest part of my project will be creating the shapes for the different identities that I have in mind, and well as successfully making the transitions.
I wanted to create a lines and circles that moved together, similar to one of Memo Akten’s art pieces. Once I created the basic shapes and movement, I added to the aesthetic by making the sketch resemble flowers in a field.
Code Snippet
let elapsedTime = millis() - startTime;
// adjust speed using lerp over time
if (elapsedTime < 10000) {
speedFactor = lerp(0.05, 0.5, elapsedTime / 10000);
} else if (elapsedTime < 20000) {
speedFactor = 0.5;
} else {
speedFactor = lerp(0.5, 0.05, (elapsedTime - 20000) / 10000);
}
Embedded Sketch
Reflections
I struggled with making the petals for the flower, and I couldn’t get the petals to touch the circumference of flower. With more time, I would tweak the code and experiment more. I would also try to achieve more interesting/complex visual effects, as Memo Akten’s works are very flashy and colourful.
For this week’s assignment I wanted to create fish swimming towards ripples in the water. The ripple would create attraction for the fish, and the fish would slow down as they reached the ripple. I used force and drag to create these effects.
Code Snippet
if (this.target != null) {
let force = p5.Vector.sub(this.target, this.pos); // Force towards target
let dist = force.mag(); // Distance to target
force.setMag(map(dist, 0, 100, 0, 1)); // Adjust force based on distance
this.applyForce(force);
let drag = this.vel.copy().mult(-1.6).mult(0.1); // Create drag force
this.applyForce(drag); // Apply drag to slow down near target
if (dist < 10) this.target = null; // Clear target if close
} else {
// If no target, allow fish to move randomly
// Add slight randomness to velocity
if (this.vel.mag() < 1) {
this.vel.x += random(-0.1, 0.1);
this.vel.y += random(-0.1, 0.1);
}
this.vel.limit(6); // Limit speed to ensure natural movement
}
this.vel.add(this.acc); // Update velocity
this.pos.add(this.vel); // Update position
this.acc.mult(0); // Reset acceleration
Embedded Code
Reflections
If I had more time, I would have the fish flow more seamlessly. Additionally, I think the drag continues to slow the fish down indefinitely, and I would trouble shoot that. I would also try to gamify the sketch a bit more, but having a start screen and an animation of fish food entering the water.
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.
I wanted to create a self-avoiding walk and when the walk ended there would be a morphing shape displayed.
Embedded Sketch
Code Highlight
function displayShape() {
background(50);
// Rotate the shape around the y-axis
rotateY(frameCount * 0.05);
// Draw shape using the curve function
fill(167, 250, 250);
curve(-500, 500, 0, 0, 20, 20, 20, 20, 0, 500, 500, 0);
}
Future Improvements
The self-avoiding walk was time-consuming to figure out, and it was my first time using curve(). I had difficulty implementing it into my code as I haven’t worked with “WEBGL”, and thus had to modify my code accordingly. In the future, I want to try to add text to the displayShape screen as well, as I couldn’t figure out how to add it with “WEBGL” interfering.
For this week, I read the introduction of the book Computational Beauty of Nature. The text was centered around reductionism, which is the theory of reducing complex things into basic parts or components. The author points to flaws in reductionism, particularly in observing nature in his example of ants. According to the author, if ants were studied individually as opposed to studied collectively as a colony, scientists would lack a crucial understanding of the complexities of ants and their colonies due to the intricate societies ants form. Through analogies like this one, the author argues that many systems and structures are connected. Thus, the author argues for understanding the interactions between agents being one of the three ways of analysis, with the other two being reductionism and a more holistic perspective. From here, the book is introduced as a way of learning about computer systems and structures.
I am intrigued by the introduction of the book and I look forward to reading more. I did not know about the reductionism theory, and it made me question if this theory can be applied to computers and technology more successfully than it can be applied to nature. As computers are built through smaller parts and components coming together, shouldn’t they be easily dissected and analyzed from their parts and components? Computers are meant to be predictable, while nature is not.