var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
'floor|random|round|abs|sqrt|PI|atan2|sin|cos|pow|max|min'
.forEach(function(p) { window[p] = Math[p]; });
function r(n) { return random()*n; }
function rrng(lo, hi) { return lo + r(hi-lo); }
function rint(lo, hi) { return lo + floor(r(hi - lo + 1)); }
function choose(args) { return args[rint(0, args.length-1)]; }
var W, H, frame, t0, time, DPR;
DPR = devicePixelRatio || 1;
canvas.style.width = w+'px';
canvas.style.height = h+'px';
W = canvas.width = w * DPR;
H = canvas.height = h * DPR;
frame = requestAnimationFrame(loop);
cancelAnimationFrame(frame);
frame = frame ? null : requestAnimationFrame(loop);
cancelAnimationFrame(frame);
ctx.clearRect(0, 0, W, H);
frame = requestAnimationFrame(loop);
function Particle(x, y, vx, vy, color) {
Particle.prototype.integrate = function() {
this.vx = this.x - this.px;
this.vy = this.y - this.py;
Particle.prototype.move = function() {
Particle.prototype.gravitateTo = function(x, y) {
var d = sqrt(dx*dx + dy*dy);
Particle.prototype.draw = function() {
ctx.moveTo(this.px, this.py);
ctx.lineTo(this.x, this.y);
return 'rgba('+0+','+n+','+n+', 0.05)';
function spawnParticles() {
for (var x = W/2 - 35; x < W/2 + 35; x += 1)
for (var y = H/2 - 35; y < H/2 + 35; y += 1)
particles[N++] = new Particle(x, y, 0, 0, color(0.5));
for (var i = 0; i < N; i++)
particles[i].friend = choose(particles);
for (var i = 0; i < N; i++) {
ctx.strokeStyle = color((time < 300 ? time : 600 - time) / 300);
if (time == 600) time = 0;
document.onclick = pause;
document.ondblclick = reset;