Spend a month making one beautiful thing per day, given a bunch of prompts. A month late, but as they say, ’the second best time is now'.
Let’s do it!
5) Debug view
I like Boids. Here are some Boids with debug vectors drawn showing the three forces acting on them (red to stay away from one another, green to move in the same direction as their friends, blue to move towards the center of their friends).
Use the ’toggle clear’ button to clear the screen between each frames to see the simulation versus not to get some twisty spaghetti ‘art’. 😄
Also, the forces for each are randomized each time. Click ‘reload’ to get new random values.
let gui;
let params = {
count: 100,
countMin: 10,
countMax: 1000,
maxSpeed: 3.0,
maxSpeedMin: 0.0,
maxSpeedMax: 10.0,
maxSpeedStep: 0.1,
visionDistance: 50.0,
visionDistanceMin: 1.0,
visionDistanceMax: 400.0,
noiseForce: 1.0,
noiseForceMin: 0.0,
noiseForceMax: 2.0,
noiseForceStep: 0.1,
wallAvoidanceForce: 1.0,
wallAvoidanceForceMin: 0.0,
wallAvoidanceForceMax: 2.0,
wallAvoidanceForceStep: 0.1,
separationForce: 1.0,
separationForceMin: 0.0,
separationForceMax: 2.0,
separationForceStep: 0.1,
alignmentForce: 1.0,
alignmentForceMin: 0.0,
alignmentForceMax: 2.0,
alignmentForceStep: 0.1,
cohesionForce: 1.0,
cohesionForceMin: 0.0,
cohesionForceMax: 2.0,
cohesionForceStep: 0.1,
debug: true,
drawBackground: true,
};
let boids;
class Boid {
constructor(p) {
this.position =
p ||
createVector(
params.visionDistance + random(width - params.visionDistance * 2),
params.visionDistance + random(height - params.visionDistance * 2)
);
this.velocity = p5.Vector.random2D().mult(params.maxSpeed * random());
}
update() {
// Apply noise
this.velocity.add(p5.Vector.random2D().mult(params.noiseForce));
// Avoid the walls
if (this.position.x < params.visionDistance) {
this.velocity.x += params.wallAvoidanceForce;
}
if (this.position.x > width - params.visionDistance) {
this.velocity.x -= params.wallAvoidanceForce;
}
if (this.position.y < params.visionDistance) {
this.velocity.y += params.wallAvoidanceForce;
}
if (this.position.y > height - params.visionDistance) {
this.velocity.y -= params.wallAvoidanceForce;
}
// Actual BOID forces
this.separation_force = createVector(0, 0);
this.alignment_force = createVector(0, 0);
this.cohesion_force = createVector(0, 0);
// Calculate average of local neighborhood
this.friend_count = 0;
this.friend_position = createVector(0, 0);
this.friend_velocity = createVector(0, 0);
// Check all boids, ignore those not close enough
for (let other of boids) {
if (this == other) continue;
let v = p5.Vector.sub(other.position, this.position).normalize();
let d = v.mag();
if (d > params.visionDistance) continue;
// Add separation force
this.separation_force.add(
p5.Vector.mult(v, -1 * d / params.visionDistance * params.separationForce)
);
// Alignment and cohesion
this.friend_count += 1;
this.friend_position.add(other.position);
this.friend_velocity.add(other.velocity);
}
this.velocity.add(p5.Vector.mult(this.separation_force, 0.1));
if (this.friend_count > 0) {
// Alignment force, move towards local heading
this.friend_velocity.mult(1 / this.friend_count);
this.alignment_force = p5.Vector.mult(
this.friend_velocity,
params.alignmentForce
);
this.velocity.add(p5.Vector.mult(this.alignment_force, 0.1));
// Cohesion force, move towards center of local group
this.friend_position.mult(1 / this.friend_count);
this.cohesion_force = p5.Vector.mult(
p5.Vector.sub(this.friend_position, this.position).normalize(),
params.cohesionForce
);
this.velocity.add(p5.Vector.mult(this.cohesion_force, 0.1));
}
// Clamp to maximum speed
if (this.velocity.mag() > params.maxSpeed) {
this.velocity = this.velocity.normalize().mult(params.maxSpeed);
}
// Update position based on current velocity
this.position = this.position.add(this.velocity);
if (this.position.x < 0) {
this.position.x = 0;
this.velocity.x = abs(this.velocity.x);
}
if (this.position.x > width) {
this.position.x = width;
this.velocity.x = -1 * abs(this.velocity.x);
}
if (this.position.y < 0) {
this.position.y = 0;
this.velocity.y = abs(this.velocity.y);
}
if (this.position.y > width) {
this.position.y = height;
this.velocity.y = -1 * abs(this.velocity.y);
}
}
draw() {
stroke(0);
circle(this.position.x, this.position.y, 5);
if (params.debug) {
stroke(255, 0, 0);
line(
this.position.x,
this.position.y,
this.position.x + 10.0 * this.separation_force.x,
this.position.y + 10.0 * this.separation_force.y
);
stroke(0, 255, 0);
line(
this.position.x,
this.position.y,
this.position.x + 10.0 * this.alignment_force.x,
this.position.y + 10.0 * this.alignment_force.y
);
stroke(0, 0, 255);
line(
this.position.x,
this.position.y,
this.position.x + 10.0 * this.cohesion_force.x,
this.position.y + 10.0 * this.cohesion_force.y
);
}
}
}
function setup() {
params.noiseForce = random() / 5.0;
params.wallAvoidanceForce = random() / 5.0;
params.separationForce = random() / 5.0;
params.alignmentForce = random() / 5.0;
params.cohesionForce = random() / 5.0;
createCanvas(400, 400);
boids = [];
for (let i = 0; i < params.count; i++) {
boids.push(new Boid());
}
gui = createGuiPanel("params");
gui.addObject(params);
gui.setPosition(420, 0);
background(255);
}
function draw() {
if (params.drawBackground) {
background(255);
}
for (let boid of boids) {
boid.update();
}
for (let boid of boids) {
boid.draw();
}
}
Posts in Genuary 2023:
- Genuary 2023.01: Perfect loop
- Genuary 2023.02: Made in 10 minutes
- Genuary 2023.03: Glitch art
- Genuary 2023.04: Intersections
- Genuary 2023.05: Debug view
- Genuary 2023.06: Steal like an artist
- Genuary 2023.07: Sample a color palette
- Genuary 2023.08: Signed Distance Functions
- Genuary 2023.09: Plants
- Genuary 2023.10: Generative Music
- Genuary 2023.11: Suprematism
- Genuary 2023.12: Tessellation
- Genuary 2023.13: Something you've always wanted to learn
- Genuary 2023.14: Asemic Writing
- Genuary 2023.15: Sine Waves
- Genuary 2023.16: Reflections of a Reflection
- Genuary 2023.17: A grid inside a grid inside a grid
- Genuary 2023.18: Definitely not a grid
- Genuary 2023.19: Black and white
- Genuary 2023.20: Art Deco
- Genuary 2023.21: Persian Carpet
- Genuary 2023.22: Shadows
- Genuary 2023.23: Moiré
- Genuary 2023.24: Textile
- Genuary 2023.25: Yayoi Kusama
- Genuary 2023.26: My kid could have made that
- Genuary 2023.27: In the style of Hilma Af Klint
- Genuary 2023.28: Generative poetry
- Genuary 2023.29: Maximalism
- Genuary 2023.30: Minimalism
- Genuary 2023.31: Break a previous image