var w = 75; var h = 50; var scale = 15; var canvas = document.createElement("canvas"); document.body.appendChild( canvas ); canvas.width = w * scale; canvas.height = h * scale; var context = canvas.getContext("2d"); context.scale( scale, scale ); var data = new Int8Array( w * h ); var freeLocations = []; function init( data, w, h, freespacePrecentage ) { for( var i = 0; i < w * h; i++ ) { data[ i ] = 1 + parseInt( Math.random() * 4 - .5 ); if( Math.random() < freespacePrecentage ) { data[ i ] = 0; } } } function iteration( data, sameAsMe ) { var stable = true; var change = []; var freespaces = []; for( var i = 0; i < data.length; i++ ) { if( data[ i ] == 0 ) { freespaces.push( i ); continue; } var type = data[ i ]; var count = 0;//non-empty spaces around pixel count += ( data[ i - w - 1 ] != 0 ) ? 1 : 0; count += ( data[ i - w ] != 0 ) ? 1 : 0; count += ( data[ i - w + 1 ] != 0 ) ? 1 : 0; count += ( data[ i - 1 ] != 0 ) ? 1 : 0; count += ( data[ i + 1 ] != 0 ) ? 1 : 0; count += ( data[ i + w - 1 ] != 0 ) ? 1 : 0; count += ( data[ i + w ] != 0 ) ? 1 : 0; count += ( data[ i + w + 1 ] != 0 ) ? 1 : 0; var sum = 0;//amount of similar pixels sum += ( data[ i - w - 1 ] == type ) ? 1 : 0; sum += ( data[ i - w ] == type ) ? 1 : 0; sum += ( data[ i - w + 1 ] == type ) ? 1 : 0; sum += ( data[ i - 1 ] == type ) ? 1 : 0; sum += ( data[ i + 1 ] == type ) ? 1 : 0; sum += ( data[ i + w - 1 ] == type ) ? 1 : 0; sum += ( data[ i + w ] == type ) ? 1 : 0; sum += ( data[ i + w + 1 ] == type ) ? 1 : 0; if( sum < sameAsMe || count - sum > sum ) { change.push( i ); stable = false; } } freespaces.sort( function( a,b ){return Math.random()> .5 ? - 1 : 1; } ); var tmp, r; for( i = 0; i < change.length; i++ ) { tmp = data[ freespaces[ 0 ] ]; data[ freespaces[ 0 ] ] = data[ change[ i ] ]; data[ change[ i ] ] = tmp; freespaces.shift(); freespaces.push( change[ i ] ); } if( !stable ) { recurse = setTimeout( iteration, 0, data, sameAsMe ); } else { console.log( "stable" ) } render( data ); } function render( data ) { context.fillStyle = "rgba( 48,0,64, 0.5 )"; context.fillRect( 0, 0, w, h ); for( var i = 0; i < data.length; i++ ) { if( data[i] == 0 ) continue; var value = ( ( data[ i ] ) * 0xFF / 4 ) ; context.fillStyle = "#" + parseInt( value << 16 | value << 8 | value).toString( 16 ); context.fillRect( ( i % w ), parseInt( i / w ), 1,1 ); } } window.onmousedown = function() { clearTimeout( recurse ); init( data, w, h, freespacePercentage ); iteration( data, 3 ); render( data ); } var recurse = null; var freespacePercentage = .15; init( data, w, h, freespacePercentage ); render( data ); iteration( data, 3 );