A re-implementation of http://www.barryvan.com.au/demos/swarms/swarms.html, which in turn is a reimplementation of a Processing script that I wrote. :)
a=Math,b=a.random,c=[],j=document.getElementById("c"),k=j.getContext("2d"),m=window,n=(W=m.innerWidth)/2,q=(H=m.innerHeight)/2,r=M=1;function s(o,p,u,v){k.fillStyle=o;k.beginPath();k.arc(p,u,v,0,a.PI*2,0);k.closePath();k.fill()}function t(o,p){for(g=40;g--;)if(!c[g]){c[g]={x:o||n,y:p||q,d:o?b()*4-2:0,e:p?b()*4-2:0,b:1,a:0,c:b()>.5};return}}with(j.style){position="fixed";top=left=0}j.width=W;j.height=H;for(f=40;f--;)c[f]=0;for(f=9;f--;)t(); m.setInterval(function(){k.fillStyle="rgba(9,9,9,.2)";k.fillRect(0,0,W,H);for(f=40;f--;)if(d=c[f])if(d.a>1E3)c[f]=0;else{l=d.a/1E3;h=l*7;d.d+=b()*.1*(n-d.x>1?1:n-d.x<-1?-1:n-d.x);d.e+=b()*.1*(q-d.y>1?1:q-d.y<-1?-1:q-d.y);d.x+=d.d;d.y+=d.e;d.a++;i=d.c?"rgba(49,153,255,":"rgba(255,49,166,";h=1-l;s(i+h+")",d.x,d.y,1+l*12);s(i+h/2+")",d.x,d.y,1+l*16);d.b&&d.b--;if(d.b==0&&d.a>250&&d.a<750&&b()>.9)for(g=40;g--&&!d.b;)if((e=c[g])&&e!=d&&e.a>=d.a){i=a.sqrt(d.x-e.x,d.y-e.y);if(i>-5&&i<5&&d.c!=e.c){t(d.x, d.y);d.b=e.b=10}}}r=n>W||n<0?-r:r;M=q>H||q<0?-M:M;n+=r;q+=M},40);
YT1NYXRoLGI9YS5yYW5kb20sYz1bXSxqPWRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJjIiksaz1qLmdldENvbnRleHQoIjJkIiksbT13aW5kb3csbj0oVz1tLmlubmVyV2lkdGgpLzIscT0oSD1tLmlubmVySGVpZ2h0KS8yLHI9TT0xO2Z1bmN0aW9uIHMobyxwLHUsdil7ay5maWxsU3R5bGU9bztrLmJlZ2luUGF0aCgpO2suYXJjKHAsdSx2LDAsYS5QSSoyLDApO2suY2xvc2VQYXRoKCk7ay5maWxsKCl9ZnVuY3Rpb24gdChvLHApe2ZvcihnPTQwO2ctLTspaWYoIWNbZ10pe2NbZ109e3g6b3x8bix5OnB8fHEsZDpvP2IoKSo0LTI6MCxlOnA/YigpKjQtMjowLGI6MSxhOjAsYzpiKCk+LjV9O3JldHVybn19d2l0aChqLnN0eWxlKXtwb3NpdGlvbj0iZml4ZWQiO3RvcD1sZWZ0PTB9ai53aWR0aD1XO2ouaGVpZ2h0PUg7Zm9yKGY9NDA7Zi0tOyljW2ZdPTA7Zm9yKGY9OTtmLS07KXQoKTsgbS5zZXRJbnRlcnZhbChmdW5jdGlvbigpe2suZmlsbFN0eWxlPSJyZ2JhKDksOSw5LC4yKSI7ay5maWxsUmVjdCgwLDAsVyxIKTtmb3IoZj00MDtmLS07KWlmKGQ9Y1tmXSlpZihkLmE+MUUzKWNbZl09MDtlbHNle2w9ZC5hLzFFMztoPWwqNztkLmQrPWIoKSouMSoobi1kLng+MT8xOm4tZC54PC0xPy0xOm4tZC54KTtkLmUrPWIoKSouMSoocS1kLnk+MT8xOnEtZC55PC0xPy0xOnEtZC55KTtkLngrPWQuZDtkLnkrPWQuZTtkLmErKztpPWQuYz8icmdiYSg0OSwxNTMsMjU1LCI6InJnYmEoMjU1LDQ5LDE2NiwiO2g9MS1sO3MoaStoKyIpIixkLngsZC55LDErbCoxMik7cyhpK2gvMisiKSIsZC54LGQueSwxK2wqMTYpO2QuYiYmZC5iLS07aWYoZC5iPT0wJiZkLmE+MjUwJiZkLmE8NzUwJiZiKCk+LjkpZm9yKGc9NDA7Zy0tJiYhZC5iOylpZigoZT1jW2ddKSYmZSE9ZCYmZS5hPj1kLmEpe2k9YS5zcXJ0KGQueC1lLngsZC55LWUueSk7aWYoaT4tNSYmaTw1JiZkLmMhPWUuYyl7dChkLngsIGQueSk7ZC5iPWUuYj0xMH19fXI9bj5XfHxuPDA/LXI6cjtNPXE+SHx8cTwwPy1NOk07bis9cjtxKz1NfSw0MCk7
/* SWARMS (v1.7)
* by Barry van Oudtshoorn (www.barryvan.com.au)
* A re-implementation of http://www.barryvan.com.au/demos/swarms/swarms.html,
* which in turn is a reimplementation of a Processing script that I wrote. :)
*
* Male and female particles move around a central point, gradually aging.
* When they reach maturity, they may interact with a particle of the opposite
* sex to spawn a new particle. As they grow old, they become larger and slower
* until they eventually die.
*
* Revisions (really, these version numbers are all but meaningless!)
* 1.0 (1023B)
* - Initial code.
* 1.5 (1017B)
* - Moved to Closure compiler to reduce size
* - Some slight tweaks to particle movement
* - Canvas now positioned to get rid of annoying border (try fullscreen!)
* 1.7 (1011B)
* - The swarm centre now moves around (simple bouncing). This allows the
* particles to pick up a bit more speed, and move in more interesting ways;
* where before they tended to just 'bubble' in the middle of the screen,
* the swarm, as an entity, now appears more fluid and organic.
* - Bugfix to spawning: particles now actually have to be near to each other
* in BOTH axes to spawn, and both particles have to be of suitable age. This
* prevents billions of particles from spawning from a single one.
* - Reduced the total number of particles for aesthetic reasons
* - Did some more "by-hand" optimisations after running it through the closure
* compiler, like removing global variable declarations when I'm not
* instantiating the variables there and then and removing the leading 0 on
* decimal values to conserve precious bytes. With each revision, despite
* adding functionality each time, the size is going down! :D
*
* Copyright 2010 Barry van Oudtshoorn.
*/
var z = function(a, b, c) { // Normalise a to between b and c
return (a / (c - b));
},
h = function(a, b, c) { // Limx a to between b and c
return (a > b ? b : (a < c ? c : a));
},
// I strip out all of the uninitialised declarations after compilation;
// they're globals anyway. I know it's not stylish, but it saves bytes!
r,s, // Particles that we're currently interested in
i,j, // Iterators
u,d, // Working variables
m = Math, // Alias
v = m.random, // Alias
p = [], // The particles
e = document.getElementById('c'), // The canvas element
t = e.getContext('2d'), // The context
n, // The normalised age of a particle
Q = window, // Alias
X = (W = Q.innerWidth)/2, // The swarm's COG
Y = (H = Q.innerHeight)/2,
N = M = 1, // The components of the velocity of the swarm's COG
q = function(a, b, c, f) { // Draw a blob
t.fillStyle = a;
t.beginPath();
t.arc(b, c, f, 0, m.PI*2, 0);
t.closePath();
t.fill();
},
x = function() { // Global iterator
//t.globalCompositeOperation = 'source-over'; // This is prettier, but "globalCompositeOperation" takes too many bytes! :(
t.fillStyle = 'rgba(9,9,9,.2)';
t.fillRect(0,0,W,H);
//t.globalCompositeOperation = 'lighter';
for (i = 40; i--;) { // Loop over all of the particles
if (r = p[i]) {
if (r.a > 1E3) {
p[i] = 0; // Kill off old particles; use 0 because it's shorter than false, null, or using delete.
} else {
// Move the particle
n = z(r.a, 0, 1E3);
u = n * 7;
r.X += v() * .1 * h(X - r.x, 1, -1); // Update the particle's velocity.
r.Y += v() * .1 * h(Y - r.y, 1, -1);
r.x += r.X; // Update the particle's coordinates based on its velocity.
r.y += r.Y;
r.a++; // Increment the particle's age
// Draw the particle
d = r.s ? 'rgba(49,153,255,' : 'rgba(255,49,166,'; // The colour of the particle
u = 1 - n; // The opacity of the particle; n is the normalised age
q(d + u + ')', r.x, r.y, 1 + n * 12); // Draw the primary particle blob
q(d + (u/2) + ')', r.x, r.y, 1 + n * 16); // We draw a lighter, larger blob for a 'glow' effect
r.f && r.f--; // If we've spawned, decrement the cooldown.
if (r.f == 0 && r.a > 250 && r.a < 750 && v() > 0.9) { // If we're the right age, check if we can spawn!
for (j = 40; j-- && !r.f;) { // We have to loop over the particles again, and see if we're close enough to spawn.
if ((s = p[j]) && s != r && s.a >= r.a) {
d = m.sqrt(r.x - s.x, r.y - s.y); // Two if statements to avoid this calculation if possible
if (d > -5 && d < 5 && r.s != s.s) { // Collision detection? What's that? :P
y(r.x, r.y); // Spawn a new particle!
r.f = s.f = 10; // Enforce the spawning cooldown
}
}
}
}
}
}
}
// Bounce the swarm's COG around the canvas
N = (X > W || X < 0 ? -N : N);
M = (Y > H || Y < 0 ? -M : M);
X += N;
Y += M;
},
// Spawn a new particle. If sx and sy are defined, they will be its initial
// coordinates; otherwise, we just the COG. If sx and sy are defined, we also
// give the particle a random velocity; this means that they're 'flung out'
// from their parents; at startup, though, we want them all nicely bunched.
y = function(sx,sy) {
for (j = 40; j--;) {
if (!p[j]) {
p[j] = {
x: sx || X, // The particle's coords
y: sy || Y,
X: sx ? v() * 4 - 2: 0, // The particles velocity
Y: sy ? v() * 4 - 2 : 0,
f: 1, // Cooldown after spawning
a: 0, // The age of the particle
s: v() > .5 // The sex of the particle
}
return;
}
}
};
// Make the canvas fill the entire screen
with (e.style) {
position = 'fixed';
top = left = 0;
}
e.width = W; // Set the canvas width and height
e.height = H;
for (i = 40; i--;) p[i] = 0; // Populate initial particles
for (i = 9; i--;) y(); // Spawn eight particles. A number < 10 saves a byte! :P
Q.setInterval(x, 40); // Make it go.