Generative art experiment 5
April 24, 2013
I’ve played with visualising the forces between particles that repel each other before, but I thought I’d have a little play around with how I could use that to create something that actually looked good (to me at least!)
I put it together on codepen (which is my new favourite tool, by the way) so feel free to fork it and see what you can do with it!
It takes a little while to draw, but it’s quite interesting seeing it building up.
The javascript code
var WIDTH = 800,
HEIGHT = 500,
PARTICLES = [],
STARTING_COUNT = 600,
MAX_PARTICLES = 600,
DAMPING = 0.05,
CURRENT_FRAME = 0,
PADDING = 10;
var canvas = document.getElementsByTagName ('canvas')[0];
canvas.width = WIDTH;
canvas.height = HEIGHT;
var ctx = canvas.getContext ('2d');
function random (min, max) {
return (Math.random() * (max - min) + min);
}
function _particleTimer () {
addParticle (random(0, WIDTH), random(0, HEIGHT));
if (PARTICLES.length < STARTING_COUNT) setTimeout(_particleTimer, 0);
}
function init () {
_particleTimer();
animate();
}
function addParticle (x, y) {
PARTICLES.push ({
position: {
x: x || random(0, WIDTH),
y: y || random (0, HEIGHT)
},
force : {
x: 0,
y: 0
},
vel : {
x: 0,
y: 0
}
});
}
function animate () {
while (PARTICLES.length > MAX_PARTICLES) PARTICLES.shift ();
CURRENT_FRAME++;
var force = {x : 0, y: 0};
for (var i = 0; i < PARTICLES.length; i++) {
var p1 = PARTICLES[i];
for (var j = i + 1; j < PARTICLES.length; j++) {
var p2 = PARTICLES[j];
// length vector
force.x = p2.position.x - p1.position.x;
force.y = p2.position.y - p1.position.y;
var magnitude = mag (force);
var actingDistance = 50-magnitude;
if ((actingDistance > 0) && (magnitude > 0)) {
var red = 0;
var green = 255;
var color = green - actingDistance * (green / 2);
if (color < 0) color = 0;
if (color > 255) color = 255;
if (color < 115 && color > 10) {
ctx.save ();
ctx.strokeStyle = 'hsla(' + color + ', 100%, 50%, 0.05)';
ctx.strokeWidth = 1;
ctx.beginPath ();
ctx.moveTo (p1.position.x, p1.position.y);
ctx.lineTo (p2.position.x, p2.position.y);
ctx.stroke();
ctx.closePath();
ctx.restore ();
}
force.x *= DAMPING * actingDistance / magnitude;
force.y *= DAMPING * actingDistance / magnitude;
p1.force.x -= force.x;
p1.force.y -= force.y;
p2.force.x += force.x;
p2.force.y += force.y;
}
}
draw (p1);
update (p1);
}
if (CURRENT_FRAME > 1500) {
alert('finised drawing!');
document.getElementById("message").innerHTML = 'Done!';
} else {
setTimeout(animate, 0);
}
}
function mag (vector) {
return Math.sqrt (vector.x * vector.x + vector.y * vector.y);
}
function update (particle) {
with (particle) {
vel.x += force.x;
vel.y += force.y;
vel.x *= 0.8;
vel.y *= 0.8;
position.x += vel.x;
position.y += vel.y;
force.x = 0;
force.y = 0;
if (position.x > WIDTH - PADDING) {
position.x = random(PADDING, WIDTH - PADDING);
}
if (position.x < PADDING - 3) {
position.x = random(PADDING, WIDTH - PADDING);
}
if (position.y > HEIGHT - PADDING) {
position.y = random(PADDING, HEIGHT - PADDING);
}
if (position.y < PADDING-3) {
position.y = random(HEIGHT, HEIGHT - PADDING);
}
}
}
function draw (particle) {
ctx.save ();
ctx.globalCompositeOperation = 'xor';
ctx.translate (particle.position.x, particle.position.y);
ctx.fillStyle = "rgba(255,255,255,0.1)";
ctx.fillRect (0, 0, 1, 1);
ctx.restore ();
}
init();