Perlin Noise§

My implementation is based on the Perlin code from “Simplex noise demystified” by Gustavson, and written using JavaScript.

Instead of using a predefined permutation table, I opted to use a randomly generated one. To do this, I filled a 256 element array with values corresponding with their indicies, then performed a Fisher-Yates shuffle.

const p = [...new Array(256).keys()]
    .map((v, _, a, j = random(255)) => [a[j], a[j] = v][0])

const perm = new Array(512)
    .fill(0)
    .map((_, i) => p[i & 255])

The random(N) function generates a uniformly random integer between 0 and N, inclusive. Here, I used crypto to generate a random integer; one consideration to keep in mind is to not use the modulo operator to generate a number between 0 and N - 1 as it does not preserve uniformity.

My implementation of the Perlin noise algorithm has a maximum throughput of about 8 million calls a second; which is less than noise.js’ 10M/s.


Fractional Brownian Motion (fBm)§

Fractional Brownian motion is used to modulate Perlin noise, it works by superimposing the noise at a certain point with a number of octaves with increasing frequency and amplitude.

The Book of Shaders has a good tutorial on how fBm can be used to modulate waves, and its implementation. Another good resource is this article by Inigo Quilez.


Curl Noise§

Notes from “Curl-Noise for Procedural Fluid Flow” by Bridson et al.

Given a potential field $\Psi = \langle \Psi_x, \Psi_y, \Psi_z \rangle$, its curl in $\mathbb{R}^3$ is given by:

$$ \vec{v}(x, y, z) = \nabla \times \Psi = \left( \frac{\partial\Psi_z}{\partial y} - \frac{\partial\Psi_y}{\partial z}, \frac{\partial\Psi_x}{\partial z} - \frac{\partial\Psi_z}{\partial x}, \frac{\partial\Psi_y}{\partial x} - \frac{\partial\Psi_x}{\partial y} \right). $$

The motivation for taking the curl of a vector field is that the resultant curl field, $\vec{v}$, has the following property:

$$ \nabla \cdot \vec{v} = \nabla \cdot (\nabla \times \Psi) = 0. $$

The divergence of curl of the vector field (at least in $\mathbb{R^3}$) is always zero. Thus, the curl field contains neither sources nor drains, and is incompressible. These properties are desirable as they ensure uniformity in the field, which is good for noise functions.

In this demo we’re working with a vector field in $\mathbb{R}^2$, then if $\Psi$ takes the form $ \langle \Psi_x, \Psi_y\rangle$, then the curl is defined as

$$ \nabla \times \Psi = \frac{\partial \Psi_y}{\partial x} -\frac{\partial \Psi_x}{\partial y}. $$

Because $\nabla \times \Psi: \mathbb{R}^2 \to \mathbb{R}$, I created a vector function $\vec{v}$ by splitting the scalar as follows

$$ \vec{v} = \left\langle \frac{\partial \Psi_y}{\partial x}, -\frac{\partial \Psi_x}{\partial y} \right\rangle. $$

This made animation individual particles easier, as I could just set the velocity of the particle to the value of the field at that position. As opposed to making the angle some sort of function based on the scalar value. And the visual much more appealing — in my opinion.

Considerations§

  1. The partial derivatives are calculated using finite diference approximations, Bridson recommends a step value of $10^{-4}$ times the domain.
  2. Perlin noise, $N(x, y)$, can be used to construct the potential field, in $\mathbb{R}^2, \Psi = N$.
  3. Adding octaves of different scales can produce fields similar to physical turbulance.
  • See: Kolmogorov turbulance spectrum on how to reduce the speed of small vortices.
  1. Ideally, the field should vary with time, possibly by using FlowNoise.

Process§

I ended up combining all three of the aforementioned methods. First, I generate Perlin noise, then modulate the field using Fractional Brownian Motion, then apply the Curl noise method over the Perlin + fBm field.

For the fBm, I ended up using 3 octaves, a lacunarity of 2, and gain of 0.5.

To calculate the partial derivatives for Curl noise, I used finite differences in $\mathbb{R}^2$ using the approximations listed on Wikipedia.

$$ f_x(x, y) \approx \frac{f(x + h, y) - f(x - h, y)}{2h}, $$

$$ f_y(x, y) \approx \frac{f(x, y + k) - f(x, y - k)}{2k}, $$

where the constants $h$ and $k$ were $10^{-4}$ times the domain as suggested.

To evolve the field over time, I took the delta between the current and start time of animation and put it through a sine function multiplied by a random constant. This number was then put into a 3D Perlin noise function as the third (z) component, and the x/y components were the usual screen coordiates transformed using a function that linearly generates a number within some specified range (in the Curl Noise demo from 0 to 8).


Process Visualization§

Step 1: Generate Perlin noise
Step 2: Modulate using Fractional Brownian motion
Step 3: Calculate 2D curl using finite differences.
To visualize the curl field, I sampled a grid of points and computed the angle of flow at the point. Using HSL, I translated the angle into the line's color.


Demonstrations§

In the end, I used web workers in order to offload the computation for noise generation from the main thread, and to parallelize it. But, a much better option would be using WebGL shaders to take advantage of parallelization on the graphics processor.

The demonstrations below are rendered in real time, using particles originating in random positions on the xy-plane with velocities determined by the corresponding noise field.

Perlin Noise§

Perlin yields a scalar instead of a vector, to get around this, I generate a 2D vector by combining two Perlin calculations, one at the (x, y) position of the particle, and another at some constant offset from the particle.

Curl Noise§

This curl noise demo superimposes the path of every particle, the brightness of a spot is directly correlated to how many times a particle has visited that spot.
The above demo shows how particles travel within the velocity field generated by curl noise. The color of the particle's path is dependent on the HSL color at the angle it is travelling.