Main content
Computer programming - JavaScript and the web
Two dimensional noise
This idea of noise values living in a one-dimensional space is important because it leads us right into a discussion of two-dimensional space. Let’s think about this for a moment. With one-dimensional noise, we have a sequence of values in which any given value is similar to its neighbor. Because the value is in one dimension, it only has two neighbors: a value that comes before it (to the left on the graph) and one that comes after it (to the right).
Two-dimensional noise works exactly the same way conceptually. The difference of course is that we aren’t looking at values along a linear path, but values that are sitting on a grid. Think of a piece of graph paper with numbers written into each cell. A given value will be similar to all of its neighbors: above, below, to the right, to the left, and along any diagonal.
If you were to visualize this graph paper with each value mapped to the brightness of a color, you would get something that looks like clouds. White sits next to light gray, which sits next to gray, which sits next to dark gray, which sits next to black, which sits next to dark gray, etc.
This is why noise was originally invented. You tweak the parameters a bit or play with color to make the resulting image look more like marble or wood or any other organic texture.
Let’s take a quick look at how to implement two-dimensional noise in ProcessingJS. If you wanted to color every pixel of a window randomly, you would need a nested loop, one that accessed each pixel and picked a random brightness.
To color each pixel according to the
noise()
function, we’ll do exactly the same thing, only instead of calling random()
we’ll call noise()
.var bright = map(noise(x,y), 0, 1, 0, 255);
This is a nice start conceptually—it gives you a noise value for every (
x
,y
) location in our two-dimensional space. The problem is that this won’t have the cloudy quality we want. Jumping from pixel 200 to pixel 201 is too large of a jump through noise. Remember, when we worked with one-dimensional noise, we incremented our time variable by 0.01 each frame, not by 1! A pretty good solution to this problem is to just use different variables for the noise arguments. For example, we could increment a variable called xoff
each time we move horizontally, and a yoff
variable each time we move vertically through the nested loops.We’ve examined several traditional uses of Perlin noise in this tutorial. With one-dimensional noise, we used smooth values to assign the location of an object to give the appearance of wandering. With two-dimensional noise, we created a cloudy pattern with smoothed values on a plane of pixels. It’s important to remember, however, that Perlin noise values are just that—values. They aren’t inherently tied to pixel locations or color. Any example in these tutorials that has a variable could be controlled via Perlin noise. When we model a wind force, its strength could be controlled by Perlin noise. Same goes for the angles between the branches in a fractal tree pattern, or the speed and direction of objects moving along a grid in a flow field simulation, like in the program below.
We began the last tutorial by talking about how randomness can be a crutch. In many ways, it’s the most obvious answer to the kinds of questions we ask continuously—how should this object move? What color should it be? This obvious answer, however, can also be a lazy one.
As we finish off this tutorial, it’s also worth noting that we could just as easily fall into the trap of using Perlin noise as a crutch. How should this object move? Perlin noise! What color should it be? Perlin noise! How fast should it grow? Perlin noise!
The point of all of this is not to say that you should or shouldn’t use randomness. Or that you should or shouldn’t use Perlin noise. The point is that the rules of your system are defined by you, and the larger your toolbox, the more choices you’ll have as you implement those rules. The goal of these tutorials is to help you fill your toolbox. If all you know is one type of randomness, then all of your designs will include only one type of randomness. Perlin noise is another tool for randomness you can use in your programs. After a little practice with Perlin noise we will move on to another type of tool- vectors!
This "Natural Simulations" course is a derivative of "The Nature of Code" by Daniel Shiffman, used under a Creative Commons Attribution-NonCommercial 3.0 Unported License.
Want to join the conversation?
- Could someone please really explain exactly what map is doing? I have looked at others answers to that question and looked it up online, but I still can't get it.(36 votes)
- Using the Fahrenheit scale, we know that water freezes at 32°F and boils at 212°F. Using the Celsius scale, we know that water freezes at 0°C and boils at 100°C. So, what is the Celsius equivalence of 86°F?
var c = map(86, 32, 212, 0, 100);
println(c);
What is the Fahrenheit equivalence of -40°C?var f = map(-40, 0, 100, 32, 212);
println(f);
Exactly, for you Algebra-1 fans,y = map(x, x1, x2, y1, y2)
allows you to specify a linear equation passing through the points(x1, y1)
and(x2, y2)
and then allows you to compute anyy
wherey
= mx
+ b, where m and b are uniquely derived from the two points. Only, you don't have to do any algebra.var map = function(x, x1, x2, y1, y2) {
var m = (y2 - y1) / (x2 - x1);
var b = y1 - m*x1;
return m*x + b;
};(81 votes)
- Why does my program work when the for loop rules are " < 100 " , but when I change that to " <400 " it stops animating?
var zoff = 0.0;
draw = function() {
var xoff = 0.0;
for (var x = 0; x < 100; x++) {
var yoff = 0.0;
for (var y = 0; y < 100; y++) {
var bright = map(noise(xoff, yoff, zoff), 0, 1, 0, 255);
stroke(bright, bright, bright);
point(x, y);
yoff += 0.01;
}
xoff += 0.01;
}zoff += 0.01;
};(6 votes)- Because this code is way too slow. Point is quite slow and you're calling it way too many times.
One way to solve this problem is to change pixels directly, which is a lot faster. I've done a program a while ago quite similar to this one that uses direct pixel manipulation to work.
Check it out and try to adapt it to serve your needs: https://www.khanacademy.org/computer-programming/noise-display/4526645315764224(13 votes)
- I'm trying to understand the noise concept. I get the part about get texture out of the noise function, but I don't see from the examples how the color of the object is defined.(7 votes)
- What am I doing wrong for the challenge: Animated Noise?
It says this: "We want to smoothly animate the noise - so that the noise is moving around over time, not changing completely each time."
This is my code:var xOff = 0.0;
var zOff = 0.0;
draw = function() {
for (var x = 0; x < 100; x++) {
var yOff = 0.0;
for (var y = 0; y < 100; y++) {
var bright = map(noise(xOff, yOff, zOff), 0, 1, 0, 255);
stroke(bright, bright, bright);
point(x, y);
yOff += 0.01;
}
xOff += 0.01;
}
zOff += 0.1;
};(6 votes)- If you move
var xOff = 0.0;
inside your draw function, it'll have thexOff
value reset with each frame, smoothing out the animation.(5 votes)
- Khan Academy said that: We want to smoothly animate the noise - so that the noise is moving around over time, not changing completely each time. Here is my code:
draw = function() {
for (var x = 0; x < 100; x++) {
var yOff = 0.0;
for (var y = 0; y < 100; y++) {
var bright = map(noise(xOff, yOff, zOff), 0, 1, 0, 255);
stroke(bright, bright, bright);
point(x, y);
yOff += 0.01;
}
xOff += 0.01;
}
zOff += 0.01;
};
Does anyone know what is wrong with the code?(7 votes)- Try defining your xOff variable inside your draw function, right above your first for loop. If that doesn't fix it, try indenting everything from "var yOff = 0.0;" to the curly bracket right above the "xOff += 0.01;". That should fix the problem.(3 votes)
- Is there a way to fill the clouds (ellipse) with noisy cloud using point function. Please!! I need your help!.(3 votes)
- Maybe you can only draw the point if it falls inside a circle.
I adjusted the code from this page:var cloudX = 50;
var cloudY = 50;
var cloudRadius = 50;
var xoff = 0.0;
for (var x = 0; x < 100; x++) {
var yoff = 0.0;
for (var y = 0; y < 100; y++) {
if (sq(cloudRadius) > sq(cloudX - x) + sq(cloudY - y)) {
var bright = map(noise(xoff, yoff), 0, 1, 0, 255);
stroke(bright, bright, bright);
point(x, y);
}
yoff += 0.01;
}
xoff += 0.01;
}
Of course, you may want to adjust the if statement to support any ellipse.(11 votes)
- I'm thinking about using noise for terrain generation, and just realized that you can make 2 dimensional noise into a heightmap and use 3 dimensional noise subtractively for caves, ravines, etc. Would that work?(6 votes)
- Yes! A lot of people have alredy done that at Khan. Whenever you see a program that generates terrain, look at the code and you'll likely find noise in there :)(4 votes)
- Hi there! In the Animated noise Challenge, why does putting 'var xOff = 0.0;' outside the draw function cause the frames to change completely each time 'draw' runs instead of move around slowly? And is it for the same reason that 'var yOff = 0.0;' outside the draw function does the same thing but also sort of squishes the cloud image so it's not so smooth anymore? Thank you very much!(6 votes)
- For project mountain range, if anyone knows where to begin that would help a lot because I am unsure as to whether I use noise or set a random variable first.(5 votes)
- You could start using a loop to make different mountain ranges and colouring them with different colors as they go farther away from the observe.
I got stucked there because i'm still not satisfied with the way i'm trying to use noise to generate clouds.(1 vote)
- Is there a way to color noise? I want it to have color besides just shades of gray.(4 votes)
- There is, but how to do it depends on how you're drawing the noise. Can I see your code?(3 votes)