This is halftone.js, a port of my xscreensaver hack Halftone from 2002. Read more in the source code. It's my first try at using canvas and any feedback is very welcome!
S=document.body.style,S.overflow="hidden",S.margin="0px",M=Math,r=M.random,n=M.min,D=Date,s=9,m=[];for(i=0;i<3;m[i++]={x:r(),y:r(),dx:r()*.03,dy:r()*.03,m:r()*.5});setInterval(function(){t=+(new D),W=window,w=W.innerWidth,h=W.innerHeight,c.width=w,c.height=h;for(i=0;i<3;){g=m[i++],g.x+=g.dx,g.y+=g.dy;if(g.x>1||g.x<0)g.dx=-g.dx;if(g.y>1||g.y<0)g.dy=-g.dy}for(x=0;x<=w;x+=s)for(y=0;y<=h;y+=s){I=0;for(i=0;i<3;)g=m[i++],X=x-g.x*w,Y=y-g.y*h,d=M.sqrt(X*X+Y*Y)/(h+w),I+=d?n(g.m/(d*d*20),2):2;a.beginPath(),a.arc(x+.5,y+.5,n(I,9)*s*.5,0,7,0),a.fill()}e=+(new D)-t,e<20&&(s/=2),e>70&&(s+=8)},50)
Uz1kb2N1bWVudC5ib2R5LnN0eWxlLFMub3ZlcmZsb3c9ImhpZGRlbiIsUy5tYXJnaW49IjBweCIsTT1NYXRoLHI9TS5yYW5kb20sbj1NLm1pbixEPURhdGUscz05LG09W107Zm9yKGk9MDtpPDM7bVtpKytdPXt4OnIoKSx5OnIoKSxkeDpyKCkqLjAzLGR5OnIoKSouMDMsbTpyKCkqLjV9KTtzZXRJbnRlcnZhbChmdW5jdGlvbigpe3Q9KyhuZXcgRCksVz13aW5kb3csdz1XLmlubmVyV2lkdGgsaD1XLmlubmVySGVpZ2h0LGMud2lkdGg9dyxjLmhlaWdodD1oO2ZvcihpPTA7aTwzOyl7Zz1tW2krK10sZy54Kz1nLmR4LGcueSs9Zy5keTtpZihnLng+MXx8Zy54PDApZy5keD0tZy5keDtpZihnLnk+MXx8Zy55PDApZy5keT0tZy5keX1mb3IoeD0wO3g8PXc7eCs9cylmb3IoeT0wO3k8PWg7eSs9cyl7ST0wO2ZvcihpPTA7aTwzOylnPW1baSsrXSxYPXgtZy54KncsWT15LWcueSpoLGQ9TS5zcXJ0KFgqWCtZKlkpLyhoK3cpLEkrPWQ/bihnLm0vKGQqZCoyMCksMik6MjthLmJlZ2luUGF0aCgpLGEuYXJjKHgrLjUseSsuNSxuKEksOSkqcyouNSwwLDcsMCksYS5maWxsKCl9ZT0rKG5ldyBEKS10LGU8MjAmJihzLz0yKSxlPjcwJiYocys9OCl9LDUwKQ==
/*
* halftone.js
*
* Author: Peter Jaric (@peterjaric, http://javahacker.com)
*
* Port of my xscreensaver hack Halftone from 2002.
* (two for and one against at http://www.jwz.org/blog/2006/03/screen-savers-that-suck/).
*
* Features:
* . Adapts to full size of window when it is resized.
* . Dynamically changes number of dots depending on time
* spent drawing to keep a good user experience.
*/
// We want to go full screen
S = document.body.style;
S.overflow = "hidden";
S.margin = "0px";
// Some shortcuts
M = Math;
r = M.random;
n = M.min;
D = Date;
// Spacing between dots
s = 9;
// Moving mass centers (3 of them)
m = [];
for (i = 0; i < 3; m[i++] = {x: r(), y:r(), // position
dx: r() * 0.03, dy: r() * 0.03, // speed
m:r() * 0.5 // mass
});
// Call main function every 50 ms
setInterval(function() {
// Record timestamp
t = +new D();
// Resize (to full screen) and clear canvas
W = window;
w = W.innerWidth,
h = W.innerHeight;
c.width = w;
c.height = h;
// Move mass centers
for (i = 0; i < 3;) {
g = m[i++];
g.x += g.dx;
g.y += g.dy;
if (g.x > 1 || g.x < 0)
g.dx = -g.dx;
if (g.y > 1 || g.y < 0)
g.dy = -g.dy;
}
// Draw all dots
for (x = 0; x <= w; x+=s)
for (y = 0; y <= h; y+=s) {
// Intensity in this dot
I = 0;
// Sum influence from all mass centers
for (i = 0; i < 3;) {
g = m[i++];
X = x - g.x * w; // x distance
Y = y - g.y * h; // y distance
d = M.sqrt(X * X + Y * Y) / (h + w); // dance between current mass center and current dot
I += !d ? 2 : n(g.m / (d * d * 20), 2); // Add influence from current mass center (but not too much!)
}
// Draw the dot: a circle drawn with Context2D.arc and then filled
a.beginPath();
a.arc(x + .5, y + .5, n(I, 9) * s * .5, 0, 7, 0);
a.fill();
}
// Calculate the elapsed time for this call
e = +new D() - t;
// If the time was too short, half the spacing between dots
if (e < 20)
s /= 2;
// If the time was too long, increase spacing by 8 pixels
if (e > 70)
s += 8;
}, 50);