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();