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