Perlin Noise is a simple yet powerful algorithm created by Ken Perlin that is often used in procedurally generated content. It can be utilized to create terrain maps in games, clouds, fire effects, water or waves in an ocean scene etc. but it also can be used as the underlying force to define the direction of movement of particles as in this little experiment. This implementation of the Perlin Noise algorithm is based on the original implementation by Ken Perlin which you can find here here and this modified version.
PerlinNoise = function()
{
var _permutation = [];
function init()
{
// Hash lookup table as defined by Ken Perlin.
// A randomly arranged array of all numbers from 0-255 inclusive.
var table = [
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142,
8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117,
35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71,
134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41,
55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89,
18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217,
226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17,
182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167,
43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246,
97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239,
107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254,
138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180
];
// Doubled permutation to avoid overflow
_permutation = new Array( 512 );
for( var i = 0; i < 512; i++ )
{
_permutation[ i ] = table[ i % 256 ];
}
}
this.fill = function( pixelBuffer, octaves, persistence, frequency )
{
persistence = persistence || 0.5;
frequency = frequency || 0.1;
var width = pixelBuffer.width;
var height = pixelBuffer.height;
var data = pixelBuffer.data;
var numOctaves = octaves.length;
for( var y = 0; y < height; ++y )
{
for( var x = 0; x < width; ++x )
{
var f = frequency;
var total = 0.0;
var amplitude = 1.0;
var maxValue = 0.0; // Used for normalizing result to 0.-1.0
for( var i = 0; i < numOctaves; ++i )
{
var offset = octaves[ i ];
total += this.noise( ( x + offset.x ), ( y + offset.y ), f ) * amplitude;
maxValue += amplitude;
amplitude *= persistence;
f *= 2;
}
var color = ( total / maxValue ) * 255;
var index = ( x + y * pixelBuffer.width ) * 4;
data[ index ] = color;
data[ ++index ] = color;
data[ ++index ] = color;
data[ ++index ] = 255;
}
}
};
this.noise = function( x, y, frequency )
{
x *= frequency;
y *= frequency;
var X = Math.floor( x );
var Y = Math.floor( y );
var xi = X & 255;
var yi = Y & 255;
var xf = x - X;
var yf = y - Y;
// Fade function as defined by Ken Perlin. This eases coordinate values
// so that they will "ease" towards integral values. This ends up smoothing
// the final output. 6t^5 - 15t^4 + 10t^3
var u = xf * xf * xf * (xf * (xf * 6 - 15) + 10);
var v = yf * yf * yf * (yf * (yf * 6 - 15) + 10);
var iy1 = yi + 1;
var pxi = _permutation[ xi ];
var pxi1 = _permutation[ xi + 1 ];
var aaa = _permutation[ _permutation[ pxi + yi ] ];
var aba = _permutation[ _permutation[ pxi + iy1 ] ];
var aab = _permutation[ _permutation[ pxi + yi ] + 1 ];
var abb = _permutation[ _permutation[ pxi + iy1 ] + 1 ];
var baa = _permutation[ _permutation[ pxi1 + yi ] ];
var bba = _permutation[ _permutation[ pxi1 + iy1 ] ];
var bab = _permutation[ _permutation[ pxi1 + yi ] + 1 ];
var bbb = _permutation[ _permutation[ pxi1 + iy1 ] + 1 ];
var x1;
var x2;
var y1;
var y2;
// The gradient function calculates the dot product between a pseudo-random
// gradient vector and the vector from the input coordinate to the 8 surrounding points in its unit cube.
// This is all then lerped together as a sort of weighted average based on the faded (u,v,w)
// values we made earlier.
x1 = lerp( grad( aaa, xf, yf, 0 ), grad( baa, xf - 1, yf, 0 ), u );
x2 = lerp( grad( aba, xf, yf - 1, 0 ), grad( bba, xf - 1, yf - 1, 0 ), u );
y1 = lerp( x1, x2, v );
x1 = lerp( grad( aab, xf, yf, -1 ), grad( bab, xf - 1, yf, -1 ), u );
x2 = lerp( grad( abb, xf, yf - 1, -1 ), grad( bbb, xf - 1, yf - 1, -1 ), u );
y2 = lerp( x1, x2, v );
// bound it to 0 - 1 (theoretical min/max before is -1 - 1)
return ( lerp( y1, y2, 0 ) + 1 ) * 0.5;
};
function grad( hash, x, y, z )
{
// take hashed value and first 4 bits of it (15 == 0b1111)
var h = hash & 15;
// if the most significant bit of the hash is 0 then set u = x, otherwise y.
var u = h < 8 /* 0b1000 */ ? x : y;
var v;
if( h < 4 /* 0b0100 */ )
{
// if the first and second significant bits are 0 set v = y
v = y;
}
else if( h == 12 /* 0b1100 */ || h == 14 /* 0b1110*/ )
{
// if the first and second significant bits are 1 set v = x
v = x;
}
else
{
// if the first and second significant bits are not equal (0/1, 1/0) set v = z
v = z;
}
// Use the last 2 bits to decide if u and v are positive or negative. Then return their addition.
return ( ( h & 1 ) == 0 ? u : -u) + ( ( h & 2 ) == 0 ? v : -v );
}
function lerp( a, b, x )
{
return a + x * ( b - a );
}
init();
};