Skip to content

Instantly share code, notes, and snippets.

@aaronsnoswell
Created February 21, 2012 12:42
Show Gist options
  • Save aaronsnoswell/1876320 to your computer and use it in GitHub Desktop.
Save aaronsnoswell/1876320 to your computer and use it in GitHub Desktop.
Better Canvas Perlin Noise
// l33t codes go here
var canvas = document.querySelector("#canvas"),
ctx = canvas.getContext("2d");
/**
* The general idea with perlin noise is to generate
* noise at several resolutions and overlay them
* with varying amplitudes and opacities.
*
* Rather than do all that in memory, I'm going to
* exploit the way <canvas> elements currently render
* stretched images - letting the GPU handle
* smoothing and blending
*/
var randcache = [];
function noise(x, y) {
if(typeof(randcache[x])=="undefined")
randcache[x] = [];
if(typeof(randcache[x][y])=="undefined")
randcache[x][y] = Math.random();
return randcache[x][y];
}
/**
* Generates a 2D noise Image of dimensions
* width x height with the given period.
* n = 1 equates to highest frequency
* n = 32 equates to low frequency
*/
function noise_2d(width, height, n) {
width = width/n;
height = height/n;
var imagedata = ctx.createImageData(width, height);
for(var x=0; x<imagedata.width; x++) {
for(var y=0; y<imagedata.height; y++) {
var val = noise(x, y);
val *= 255;
putpix(imagedata, x, y, val, val, val, 255);
}
}
var tmp_canvas = document.createElement("canvas"),
tmp_ctx = tmp_canvas.getContext("2d");
tmp_canvas.width = width;
tmp_canvas.height = height;
tmp_ctx.putImageData(imagedata, 0, 0);
var img = new Image();
img.src = tmp_canvas.toDataURL();
delete tmp_canvas;
delete tmp_ctx;
return img;
}
/**
* Generates perlin noise with the given
* number of octaves
*/
function perlin_noise(octaves, width, height, threshold) {
var images = {},
loaded_count = 0,
arr = [];
for(var i=0; i<octaves; i++) {
var n = Math.pow(2, i),
alpha = 1 / Math.pow(2, octaves-1-i);
arr.push(1 / Math.pow(2, i));
var img = noise_2d(width, height, n);
img.onload = function() {
loaded_count++;
if(loaded_count == octaves) {
for(var j in arr) {
var alpha = arr[j];
ctx.globalAlpha = alpha;
ctx.drawImage(images[alpha], 0, 0, width, height);
}
if(threshold) postprocess(width, height, threshold);
}
}
images[alpha] = img;
}
}
function postprocess(width, height, func) {
var imdata = ctx.getImageData(0, 0, width, height);
for(var x=0; x<width; x++) {
for(var y=0; y<height; y++) {
var offset = (x + y*width)*4;
imdata.data[offset + 0] = 255*func(x, y, imdata.data[offset + 0]/255);
imdata.data[offset + 1] = 255*func(x, y, imdata.data[offset + 1]/255);
imdata.data[offset + 2] = 255*func(x, y, imdata.data[offset + 2]/255);
imdata.data[offset + 3] = 255*func(x, y, imdata.data[offset + 3]/255);
}
}
clear();
ctx.putImageData(imdata, 0, 0);
}
perlin_noise(6, 256, 256, function(x, y, d) {
return d;
});
/**
* Utility method. Blits an rgba pixel to an
* ImageData object
*/
function putpix(imagedata, x, y, r, g, b, a) {
var offset = (x + y*imagedata.width)*4;
imagedata.data[offset + 0] = r;
imagedata.data[offset + 1] = g;
imagedata.data[offset + 2] = b;
imagedata.data[offset + 3] = a;
}
/**
* Utility method to clear the canvas
*/
function clear() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment