Concept:

For this project, I decided to make a visualization inspired by tension and release with an interactive element as the mouse hovers around the canvas. Initially, the flock is strictly constructed around an attractor in a circular shape, but over time, it changes and moves around the canvas. The mouse interaction enables the Boids to transform shape and size, adding layers to the sketch. The shape of the Boide is inspired by kite-like crystal shapes in the shade of light transparent blue symbolizing shattered glass. Resembling the sketch this way makes sense to me because it shows fragility and the Boids collective movement while remaining individual. This mirrors the sensitivity of how one Boids movement influences others.
Highlight of Code:

For this project, I reviewed the presentation and watched the videos to understand the concept of flocking. I wanted to bring into the sketch things we learned prior to the class, and I decided that having an attractor would be interesting. I initially started playing around with the code we did in class. I played a lot with different classes and ways to make it work. I added three main classes: an attractor, a Boid, and a flock. Even though I had so much debugging to do, I think I am close enough to the visualization I had in my head.

The attractor class pushed the Boids towards it through a force similar to orbiting. It was hard to figure out how to make it work, but as I played with the numbers, it started making sense. The orbiting force is simply rotating the force by 2. The Boid class is affected by flocking and attraction. This helps the body move in groups and respond to the attraction force. The methods used for the Boid class are froce, flock, attraction, update, borders, and the show functions.
for (let i = 0; i < 400; i++) {
//changing this part effects how the overall visulaization is
let angle = map(i, 0, 400, 0, TWO_PI);
let boid = new Boid(
width/1.7 + radious * cos(angle),
height/1.7 + radious * sin(angle)
);
flock.addBoid(boid);
}
The above code is the main effector of the visualization. It took me some time to create a visualization I though was interesting, and it was inspired by the midterm for this semester.
class Boid {
constructor(x, y) {
this.acceleration = createVector(0, 0);
this.velocity = createVector(random(-0.01, 0.001), random(-0.01, 0.01));
this.position = createVector(x, y);
this.r = 3;
//mass
this.mass = 2;
// Maximum speed
this.maxSpeed = 0.7;
// Maximum steering force
this.maxForce = 5;
}
applyForce(force) {
let f = p5.Vector.div(force, this.mass);
//changed how it moves looks nice
this.acceleration.sub(f);
this.acceleration.mult(10);
this.acceleration.mult(f);
}
run(boids, attractor) {
this.flock(boids);
this.applyAttraction(attractor);
this.update();
this.borders();
this.show();
}
applyAttraction(attractor) {
//pull boids
attractor.attract(this);
}
// accumulate acceleration each time based on three rules
flock(boids) {
let sep = this.separate(boids); // Separation
let ali = this.align(boids); // Alignment
let coh = this.cohere(boids); // Cohesion
// Arbitrarily weight these forces
sep.mult(1);
ali.mult(1);
coh.mult(1);
// Add the force vectors to acceleration
this.applyForce(sep);
this.applyForce(ali);
this.applyForce(coh);
}
// update location
update() {
// velocity
this.velocity.add(this.acceleration);
// limit speed
this.velocity.limit(this.maxSpeed);
this.position.add(this.velocity);
// reset accelertion to 0 each cycle
this.acceleration.mult(0);
}
// calculate and apply a steering force towards a target
// STEER = DESIRED MINUS VELOCITY
seek(target) {
// A vector pointing from the location to the target
let desired = p5.Vector.sub(target, this.position);
// Normalize desired and scale to maximum speed
desired.normalize();
desired.mult(this.maxSpeed);
// Steering = Desired minus Velocity
let steer = p5.Vector.sub(desired, this.velocity);
steer.limit(this.maxForce); // Limit to maximum steering force
return steer;
}
show() {
// draw a kite shape
let angle = this.velocity.heading();
fill(random(90, 127), random(150, 200), random(180, 255), 150);
stroke(random(100, 127), random(100, 200), random(200, 255));
push();
translate(this.position.x, this.position.y);
rotate(angle);
let freqX = map(mouseX, 0, width, 1, 15);
let freqY = map(mouseY, 0, height, 1, 15);
beginShape();
let x = this.r * cos(10 * this.r) * freqX;
let y = this.r * cos(10 * this.r) * freqY;
//crystal like shape
//right
vertex(x * 2, 0);
//top right
vertex(x * 0.7, -y);
//top left
vertex(-x * 0.7, -y);
//left
vertex(-x * 2, 0);
//bottom
vertex(0, y);
endShape(CLOSE);
pop();
}
// wraparound
borders() {
if (this.position.x < -this.r) this.position.x = width + this.r;
if (this.position.y < -this.r) this.position.y = height + this.r;
if (this.position.x > width + this.r) this.position.x = -this.r;
if (this.position.y > height + this.r) this.position.y = -this.r;
}
// separation itchecks for nearby boids and steers away
separate(boids) {
let desiredSeparation = 15;
let steer = createVector(0, 0);
let count = 0;
// for every boid in the system, check if it's too close
for (let i = 0; i < boids.length; i++) {
let d = p5.Vector.dist(this.position, boids[i].position);
// ff the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
if (d > 0 && d < desiredSeparation) {
// calculate vector pointing away from neighbor
let diff = p5.Vector.sub(this.position, boids[i].position);
diff.normalize();
// weight by distance
diff.div(d);
steer.add(diff);
// keep track of how many
count++;
}
}
// average -- divide by how many
if (count > 0) {
steer.div(count);
}
// As long as the vector is greater than 0
if (steer.mag() > 0) {
// Implement Reynolds: Steering = Desired - Velocity
steer.normalize();
steer.mult(this.maxSpeed);
steer.sub(this.velocity);
steer.limit(this.maxForce);
}
return steer;
}
// Alignment
// For every nearby boid in the system, calculate the average velocity
align(boids) {
let neighborDistance = 40;
let sum = createVector(0, 0);
let count = 0;
for (let i = 0; i < boids.length; i++) {
let d = p5.Vector.dist(this.position, boids[i].position);
if (d > 0 && d < neighborDistance) {
sum.add(boids[i].velocity);
count++;
}
}
if (count > 0) {
sum.div(count);
sum.normalize();
sum.mult(this.maxSpeed);
let steer = p5.Vector.sub(sum, this.velocity);
steer.limit(this.maxForce);
return steer;
} else {
return createVector(0, 0);
}
}
// Cohesion
// For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location
cohere(boids) {
let neighborDistance = 40;
let sum = createVector(0, 0); // Start with empty vector to accumulate all locations
let count = 0;
for (let i = 0; i < boids.length; i++) {
let d = p5.Vector.dist(this.position, boids[i].position);
if (d > 0 && d < neighborDistance) {
sum.add(boids.position); // Add location
count++;
}
}
if (count > 0) {
sum.div(count);
return this.seek(sum); // Steer towards the location
} else {
return createVector(0, 0);
}
}
}
The Boid class was the most complicated. I had to do a lot of debugging to add the attractor into action so that the Boids are affected by it.
Embedded Sketch:
Future work:
I am satisfied with how the sketch turned out to be. Things I want to experiment further with is how the flock would move around the canvas by creating a path for them to create different shapes and interactions that are strictly influenced by tension and release. Further, I think having an audio sync into the work is also interesting to see. The audio could perhaps be in relation to the acceleration and speed of the flock’s movement. Further, if this could translate into an interactive project, perhaps I can add a motion sensor so that the audience can influence the behavior of the flock through that data.
Resources:
The Coding Train. “Coding Challenge 124: Flocking Simulation.” YouTube, 11 Dec. 2018, www.youtube.com/watch?v=mhjuuHl6qHM.
Arthur Facredyn. “Path Finding in p5.js # 1.” YouTube, 11 June 2018, www.youtube.com/watch?v=Ohcrstxcci4.
Murmuration – Robert Hodgin. roberthodgin.com/project/murmuration.
5. Autonomous Agents. nature-of-code-2nd-edition.netlify.app/autonomous-agents/#vehicles-and-steering.