xxxxxxxxxx
106
// Submission for Sableraph's Birb's Nest weekly challenge
// Theme: Getting Closer
let dots;
function setup() {
const size = min(windowWidth, windowHeight); // make sure canvas fits screen
createCanvas(size, size);
colorMode(RGB, 1); // normalize colour values between 0 - 1
noStroke();
setupDots();
}
const count = 500;
function setupDots() {
dots = [];
for (let i = 0; i < count; i++) {
const x = random();
const y = random();
const v = dirFromCenter(x, y);
dots.push({
x, y, v,
velX: 0,
velY: 0,
});
}
}
function dirFromCenter(x, y) {
const vec = createVector(x - 0.5, y - 0.5);
return vec.heading() / TWO_PI; // divide by 2 * PI to normalize angle beteen 0 - 1
}
const friction = 0.99;
const attractionFactor = 0.001;
// flocking behaviour, but instead of checking each dot for each dot you pick 1 target dot at random
function updateDots() {
dots.forEach(d => {
d.x = fract(d.x + d.velX); // fract loops x between 0 - 1 (wrap around)
d.y = fract(d.y + d.velY);
d.velX *= friction;
d.velY *= friction;
const d2 = dots[floor(random() * dots.length)];
const vec = createVector(d2.x - d.x, d2.y - d.y);
const dist = vec.mag();
const attraction = (1 - dist) * attractionFactor;
const repulsion = -attraction * cosn(t); // repulsion waves up and down over time
// chance to get attracted is higher when further away
// chance to get pushed away is inverse of chance to get attracted
const move = random() < dist
? attraction
: repulsion
d.velX += vec.x * move;
d.velY += vec.y * move;
d2.velX -= vec.x * move;
d2.velY -= vec.y * move;
});
}
// irrational number (also golden ratio)
const PHI = (1 + Math.sqrt(5)) / 2
let t;
const frames = 1000;
function draw() {
t = (frameCount / frames);
updateDots();
// normalize x and y coordinates between 0 - 1
scale(width, height);
// fade black background for tracers
background(0,0,0,0.1);
dots.forEach(d => {
// blend v value to new direction so the colours get mingled a little, but not too much
const targetV = dirFromCenter(d.x, d.y);
d.v += (targetV - d.v) * 0.01;
// cosine colour palettes: https://www.iquilezles.org/www/articles/palettes/palettes.htm
// using PI and PHI to avoid looping (should show all possible cosine colours :)
// (although... I use full amplituteds, so there are more cosine colours ;)
fill(
cosn(d.v + t * 0),
cosn(d.v + 0.33 + t * (PI - 3) * 0),
cosn(d.v + 0.66 + t * (PHI - 1) * 0)
);
circle(d.x, d.y, 0.005);
});
}
// normalized cos function (normalized phase and amplitude between 0 - 1)
function cosn(v) {
return cos(v * TWO_PI) * 0.5 + 0.5;
}