Simple water simulation based on cellular automata, with refraction and reflection rendering. Move mouse to make ripples.
var c=document.body.children.c,d=c.getContext('2d'),w=100,z=p=5E3,x,y,t=[],m=[],n=[],F=Math.floor,R=Math.random;for(d.globalAlpha=0.8;z--;)t[z]=m[z]=n[z]=z%2E3<1E3?z%20<10:z%20>9;setInterval(function(){for(x=z=p;x--;)n[x]=(m[(p+x-1)%p]+m[(x+1)%p]+m[(p+x-w)%p]+m[(x+w)%p]>>1)-n[x],n[x]-=n[x]>>5;for(;z--;)x=m[(p+z-1)%p]-m[(z+1)%p]>>9,y=m[(p+z-w)%p]-m[(z+w)%p]>>9,y=t[((w*y)+x+z+p)%p]*9,x=(x<0?8:x>7?15:x+8).toString(16),d.fillStyle='#'+x+y+x+'ff'+y,d.fillRect(z%w*3,F(z/w)*3,3,3);x=m;m=n;n=x},20);function W(i){m[i+1]=m[i-1]=m[i+w]=m[i-w]=m[i]-=p}c.onmousemove=function(e){W(F(e.offsetX/3)+w*F(e.offsetY/3))};(function _(){W(F(R()*p));setTimeout(_,F(R()*p))})()
dmFyIGM9ZG9jdW1lbnQuYm9keS5jaGlsZHJlbi5jLGQ9Yy5nZXRDb250ZXh0KCcyZCcpLHc9MTAwLHo9cD01RTMseCx5LHQ9W10sbT1bXSxuPVtdLEY9TWF0aC5mbG9vcixSPU1hdGgucmFuZG9tO2ZvcihkLmdsb2JhbEFscGhhPTAuODt6LS07KXRbel09bVt6XT1uW3pdPXolMkUzPDFFMz96JTIwPDEwOnolMjA+OTtzZXRJbnRlcnZhbChmdW5jdGlvbigpe2Zvcih4PXo9cDt4LS07KW5beF09KG1bKHAreC0xKSVwXSttWyh4KzEpJXBdK21bKHAreC13KSVwXSttWyh4K3cpJXBdPj4xKS1uW3hdLG5beF0tPW5beF0+PjU7Zm9yKDt6LS07KXg9bVsocCt6LTEpJXBdLW1bKHorMSklcF0+PjkseT1tWyhwK3otdyklcF0tbVsoeit3KSVwXT4+OSx5PXRbKCh3KnkpK3greitwKSVwXSo5LHg9KHg8MD84Ong+Nz8xNTp4KzgpLnRvU3RyaW5nKDE2KSxkLmZpbGxTdHlsZT0nIycreCt5K3grJ2ZmJyt5LGQuZmlsbFJlY3QoeiV3KjMsRih6L3cpKjMsMywzKTt4PW07bT1uO249eH0sMjApO2Z1bmN0aW9uIFcoaSl7bVtpKzFdPW1baS0xXT1tW2krd109bVtpLXddPW1baV0tPXB9Yy5vbm1vdXNlbW92ZT1mdW5jdGlvbihlKXtXKEYoZS5vZmZzZXRYLzMpK3cqRihlLm9mZnNldFkvMykpfTsoZnVuY3Rpb24gXygpe1coRihSKCkqcCkpO3NldFRpbWVvdXQoXyxGKFIoKSpwKSl9KSgp
// Simple cellular-automata based water simulation,
// http://iobound.com/2010/08/js1k/
// Based on Hugo Elias' algorithm at:
// http://freespace.virgin.net/hugo.elias/graphics/x_water.htm
// Variable lookup:
//
// c - Canvas
// d - 2D Context
// w - Pixels across (100)
// p - Total pixels (100*50)
// m - First heightmap buffer
// n - Second heightmap buffer
// t - Texture buffer
// x,y,z - Temp vars
// Function lookup:
//
// W - Perturb water
// F - Math.floor
// R - Math.random
// To strip comments/whitespace (hat tip to Paul Hammond, http://phmmnd.me/1k):
// perl -p -0 -e 's!(//[^\n]*)?\n\s*!!g;s!;}!}!g;s!;+!;!g' <script.js >compressed.js
// Initialise variables
var c=document.body.children.c,
d=c.getContext('2d'),
w=100,
z=p=5E3,
x,y,
t=[],m=[],n=[],
F=Math.floor,
R=Math.random;
// Use alpha to take edge off pixellyness
for(d.globalAlpha=0.8;z--;)
// Initialise background checkerboard texture, and clear water
// buffers (using texture vals, as close enough to zero)
t[z]=m[z]=n[z]=z%2E3<1E3?z%20<10:z%20>9;
// Tick the simulation and render
setInterval(function(){
// Update water height map - see algorithm for explanation
for(x=z=p;x--;)
// Calculate new value based on previous 2 frames
n[x]=(m[(p+x-1)%p]+
m[(x+1)%p]+
m[(p+x-w)%p]+
m[(x+w)%p]>>1)-n[x],
// Apply damping
n[x]-=n[x]>>5
;
// Render pixels
for(;z--;)
// Get X and Y wave gradient by comparing height with neighbours
x=m[(p+z-1)%p]-m[(z+1)%p]>>9,
y=m[(p+z-w)%p]-m[(z+w)%p]>>9,
// Use X and Y gradients as offsets into texture array
// (Fake refraction)
y=t[((w*y)+x+z+p)%p]*9,
// Use X gradient to pick a reflection map value
// (Fake environment map)
x=(x<0?8:x>7?15:x+8).toString(16),
// Use these components to make a bluish hex value
d.fillStyle='#'+x+y+x+'ff'+y,
// Draw a 3x3 pixel
d.fillRect(z%w*3,F(z/w)*3,3,3)
;
// Swap water buffers over
x=m;
m=n;
n=x
},20);
// Perturb water at position i
function W(i){
// Push down a cross of pixels centered on i
m[i+1]=m[i-1]=m[i+w]=m[i-w]=m[i]-=p
}
// Register mouse handler to stir water
c.onmousemove=function(e){
W(F(e.offsetX/3)+w*F(e.offsetY/3))
};
// Make it rain
(function _(){
W(F(R()*p));
setTimeout(_,F(R()*p))
})()