//A slight improvement upon my first javascript noise implementation. It uses gradient noise, with the vectors originating from the points of a poisson disk sample, to generate a variety of noise. var now= new Date()//Just used for debug. console.log(`${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`); //Sets up for drawing function setup(id)//Initializes drawing space { //Change these two values to change size of image var w = 160//Math.min(320,window.innerWidth-16);//Width of image var h = 120//Math.max(320,window.innerHeight-40);//Height of image canvas=document.getElementById(id);//Gets the canvas from HTML canvas.width=w;//Sets canvas width canvas.height=h;//Sets canvas height var ctx=canvas.getContext('2d')//Gets the 'context'. This is where drawing actually happens. return [ctx,w,h];//Returns the context, width, and height. } //makes blank grid filled with one value. function gridBlank(w,h,fill) { if (fill==null){fill=0;}//In case variable is not set var array=[]//Declares array for(x=0;x<w;x++)//Cycles columns { array.push([]);//Adds a column for(y=0;y<h;y++)//Cycles positions of columsn { array[x].push(fill)//Makes an entry } } return array; } //The inner compnent of the noise function. Fills a circular area with values. function circleNoise(pnt,array,x,y,r,grad,base,linearity) { //for array, [0] is grid for poisson check, [1] gradient noise output, [2] minimum output, [3] is second minimum output, [4] is third minimum output, [5] is a voronoid //grad and base are special parameters. At defaults, [1] will be normal gradient noise, [2] and [3] will be somewhat cellular blobs. At grad=0,base=-1,linearity=0, [1] will be metaballs. At grad=0,base=1,linearity=1, [2] and [3] will be almost identical to normal Worley noise. if (grad==null){grad=1;}//Influence of dot product if (base==null){base=0;}//Base value subtracted from function if (linearity==null){linearity=0;}//Modifies distance function. Only 0 is truly differentiable. var w=array[0].length;//Height of width var h=array[0][0].length;//Height of image var array2=array;//Copies array. var theta=Math.random()*2*Math.PI;//Angle for gradient vector var vectx=Math.cos(theta);//X compnent of vector var vecty=Math.sin(theta);//Y component of vector for(cx=-r;cx<r;cx++)//Cycles all possible integer x values of a circlewith radius r { var r2=Math.floor(Math.sqrt(Math.abs(r*r-cx*cx)));//How tall the circle is at that x value for(cy=-r2;cy<r2;cy++)//Cycles all possible y values at that x value { var nx=cx/r;//Scales x and y coordinates by the radius var ny=cy/r; var dist=Math.sqrt(nx*nx+ny*ny);//Euclidean distance of pixel to center of circle var dot=nx*vectx+ny*vecty;//Dot product ith gradient vector var scale=(1-dist*Math.sqrt(Math.abs(linearity)))/(1+Math.exp(11.1*dist*dist*dist*dist));//A highly differentiable fade function for noise value. var output=scale*(grad*dot-base);//The value that will be output to image var arrx=x+cx-w*Math.floor((x+cx)/w);//Coordinates of pixel in image var arry=y+cy-h*Math.floor((y+cy)/h); if (dist<1/3){array2[0][arrx][arry]=1;}//Creates a circle of values used for poisson disk sampling array2[1][arrx][arry]+=output;//Outputs to a noisemap created by summation. //These output to noisemaps creted by taking the minimum, second minimum, or third minimum value generated for a given pixel. These are necessary for Worley and Voronoi noise. if (output<array2[2][arrx][arry]||array2[2][arrx][arry]==null)//If new value is less than previous least value... { array2[4][arrx][arry]=array[3][arrx][arry];//3rd least replaced array2[3][arrx][arry]=array[2][arrx][arry];//2nd least replaced array2[2][arrx][arry]=output;//Least replaced array[5][arrx][arry]=pnt;//For Voronoi } else if (output<array2[3][arrx][arry])//Less than 2nd least... { array2[4][arrx][arry]=array[3][arrx][arry];//3rd least array2[3][arrx][arry]=output;//2nd least } else if (output<array2[4][arrx][arry])//Less than 3rd least... { array2[4][arrx][arry]=output;//3rd least } } } return array2; } //Finds an empty spot for poisson disk sample function placePoint(pnt,array,r,grad,base,linearity) { if (grad==null){grad=1;}//Defaults if these variables aren't set if (base==null){base=0;} if (linearity==null){linearity=0;} var w=array[0].length;//DImensions of output image var h=array[0][0].length; var array2=array;//Copies array //console.log(`placePoint check1 width=${array2[0].length}, height=${array2[0][0].length}`); var x=Math.floor(Math.random()*w)//Randomly chosen coordinates for a point var y=Math.floor(Math.random()*h) var attempts=0;//Will keep track of how many times it was unable to place a point var full=false while (array2[0][x][y]!=0)//Repeats until it finds a valid place to put a point in the poisson disk sample { var x=Math.floor(Math.random()*w)//Random coordinates var y=Math.floor(Math.random()*h) attempts+=1;//Number of attempts if (attempts>64){full=true;break}//If too many attempts, disk sample is probably mostly full } array2=circleNoise(pnt,array2,x,y,r,grad,base,linearity);//Once a valid point is found, will perform a noise function on a circle of pixels. //console.log(`placePoint check2 width=${array2[0].length}, height=${array2[0][0].length}`); return [array2,full]; } //Fills a grid with noise function noise(array,r,grad,base,linearity) { if (grad==null){grad=1;}//Default if not set. if (base==null){base=0;} if (linearity==null){linearity=0;} var full=false;//Will keep track of if disk sample is full var array2=array;//Copies array //console.log(`noise check1 width=${array2[0].length}, height=${array2[0][0].length}`); var pntInd=0;//Index of point. Needed for Voronoi noise. while (full==false)//WHile there is still room for points { var gotten=placePoint(pntInd,array2,r,grad,base,linearity);//Places a point array2=gotten[0];//Replaces array with output array from placing a point in the Poisson disk sample full=gotten[1];//Gets value that reveals if there is no more room for points pntInd++;//Increments point index } //console.log(`noise check2 width=${array2[0].length}, height=${array2[0][0].length}`); return array2; } //Rescale a grid to a range of 0 to 1 function gridRescale(array) { var w=array.length;//Dimensions var h=array[0].length; var array2=array;//Copies array var gmin=1000000; var gmax=0; for(x=0;x<w;x++)//Cycles all pixels finding the max and min { for(y=0;y<h;y++) { gmin=Math.min(gmin,array[x][y]); gmax=Math.max(gmax,array[x][y]); } } for(x=0;x<w;x++)//Cycles all pixel, rescaling so that the new minimum is 0 and the new maximum is 1 { for(y=0;y<h;y++) { array2[x][y]=(array[x][y]-gmin)/(gmax-gmin); } } //console.log(`max=${gmax}, min=${gmin}`); return array2 } //Draws a grayscale image to the canvas function drawGridGrayscale(array,context) { var w=array.length;//Dimensions var h=array[0].length; for(x=0;x<w;x++)//Cycles pixels { for(y=0;y<h;y++) { var shade=Math.floor(255*array[x][y]);//Value for image context.fillStyle=`rgb(${shade},${shade},${shade})`//Grayscale shade context.fillRect(x,y,1,1);//Sets the pixel } } } function drawGridPlanet(array,context) { var w=array.length;//Dimensions var h=array[0].length; for(x=0;x<w;x++)//Cycles pixels { for(y=0;y<h;y++) { var height=array[x][y]; var latt=Math.abs(2*y/h-1); if(height>.6) { if(latt>(.9-Math.pow(height,6))) { context.fillStyle=`hsl(177,33%,${Math.floor(80+height*20)}%)` } else { if(latt>(2.5-3.5*height*height)) { context.fillStyle=`hsl(104,9%,${Math.floor(20+30*height)}%)` } else { context.fillStyle=`hsl(108,55%,${Math.floor(10+50*height)}%)` } } } else { if(latt>(.9-.8*Math.pow(height,6))) { context.fillStyle=`hsl(177,33%,${Math.floor(90+height*20)}%)` } else { context.fillStyle=`hsl(221,100%,${Math.floor(height*100)}%)` } } context.fillRect(x,y,1,1);//Sets the pixel } } } function octave(w,h,r,grad,base,linearity)//Initializes the noise function { if (grad==null){grad=1;}//Defaults if (base==null){base=0;} if (linearity==null){linearity=0;} var array=[gridBlank(w,h),gridBlank(w,h),gridBlank(w,h,null),gridBlank(w,h,null),gridBlank(w,h,null),gridBlank(w,h,null)];//Initializes all of the grids needed array=noise(array,r,grad,base,linearity);//Performs noise functions array[1]=gridRescale(array[1]);//Rescales the first output return [array[1],array[2],array[3],array[4],array[5]] } function combine(equ,w,h)//Intended to generate image given a combine function string. Sometimes doesn't work... { var array2=gridBlank(w,h); for (x=0;x<w;x++) { for(y=0;y<h;y++) { array2[x][y]=eval(equ); } } return array2 } function distort(source,distort,mag)//Domain distortion { var w=Math.min(source.length,distort.length);//Dimensions var h=Math.min(source[0].length,distort[0].length); var output=gridBlank(w,h);//new grid for (x=0;x<w;x++) { for(y=0;y<h;y++) { var nx=x+32+Math.floor(mag*Math.cos(distort[x][y]));//New coordinates (uses 'distort' array as angle for a vector) nx-=w*Math.floor(nx/w);//Wraps edges of grid var ny=y+Math.floor(mag*Math.sin(6.28*distort[x][y])); ny-=h*Math.floor(ny/h); output[x][y]=source[nx][ny]; } } return output; } var val=setup("canvas"); var draw=val[0]; var width=val[1]; var height=val[2]; function example1(w,h) { //All tthe noise we'll need var noiseMap1=octave(w,h,128,1,1,0)[0]; var noiseMap2=octave(w,h,64,1,1,0)[0]; var noiseMap3=octave(w,h,32,1,1,0)[0]; var noiseMap4=octave(w,h,16,1,1,0)[0]; var equation="noiseMap1[x][y]+noiseMap2[x][y]/2+noiseMap3[x][y]/4+noiseMap4[x][y]/8"//Combine equation //var noiseMap5=gridRescale(combine(equation,w,h)) var noiseMap5=[]//gridBlank(w,h); for (x=0;x<w;x++)//Combines to an output mgrid { noiseMap5.push([]); for(y=0;y<h;y++) { noiseMap5[x].push(eval(equation)); } } console.log(noiseMap5[0]); noiseMap5=distort(noiseMap5,noiseMap5,32);//Distorts it by itself drawGridPlanet(gridRescale(noiseMap5),draw); } function example2(w,h) { var noiseMap1=octave(w,h,128,0,1,1); var noiseMap2=octave(w,h,64,1,1,1); var noiseMap3=octave(w,h,32,1,0,0); var noiseMap4=octave(w,h,16,1,0,0); var equation="Math.pow(noiseMap1[3][x][y]-noiseMap1[2][x][y]+1/32,1/4)*(1+noiseMap2[1][x][y]+noiseMap3[0][x][y]/2+noiseMap4[0][x][y]/4)" //var noiseMap5=gridRescale(combine(equation,w,h)) var noiseMap5=gridBlank(w,h) for (x=0;x<w;x++) { for(y=0;y<h;y++) { noiseMap5[x][y]=eval(equation); } } noiseMap5=gridRescale(noiseMap5); drawGridGrayscale(noiseMap5,draw); //drawGridGrayscale(noiseMap5,draw); } function example3(w,h) { var noiseMap1=octave(w,h,128,0,1,1); var noiseMap2=octave(w,h,64,1,1,1); var noiseMap3=octave(w,h,32,1,0,0); var noiseMap4=octave(w,h,16,1,0,0); var equation="noiseMap1[0][x][y]+noiseMap2[0][x][y]/2+noiseMap3[0][x][y]/4+noiseMap4[0][x][y]/8" var noiseMap5=gridBlank(w,h) for (x=0;x<w;x++) { for(y=0;y<h;y++) { noiseMap5[x][y]=eval(equation); } } noiseMap5=distort(noiseMap1[4],noiseMap5,48); drawGridGrayscale(gridRescale(noiseMap5),draw); //drawGridGrayscale(noiseMap5,draw); } example1(width,height);

!