# Composing noise functions for more complex / looping noise

The `orx-noise` OPENRNDR extension received new capabilities in this and this commits. This post describes the new functionality and was posted by Edwin on Slack in July 2021.

## It starts with functions

We know that `simplex3D` is a

``````(Int, Double, Double, Double) -> Double
``````

Here `(Int, Double, Double, Double) -> Double` is our commonly used function signature for noise functions that given an `Int` seed and a 3d coordinate returns a scalar noise value. `perlin3D`, `value3D` etc. use that same signature.

`orx-noise` already had a `fbm` function that is much like

``````fun fbm(lacunarity:Double, gain:Double,
seed:Int, x:Double, y:Double, z:Double,
noise: (Int, Double, Double, Double) -> Double): Double
``````

So that too is a scalar noise function, however with a different signature.

orx-`noise` also has an `fbmFunc3D` function that instead of returning a scalar returns a function with our noise function signature.

``````inline fun fbmFunc3D(
crossinline noise: (Int, Double, Double, Double) -> Double,
octaves: Int = 8,
lacunarity: Double = 0.5,
gain: Double = 0.5
): (Int, Double, Double, Double) -> Double
``````

You’d use `fbmFunc3D` to compose a new function like this:

``````val fbmSimplex = fbmFunc3D(simplex3D)
``````

Which can be used like any other function.

``````val noise = fbmSimplex(432, 0.0, 1.0, 2.0)
``````

Since our fbmSimplex has the `(Int, Double, Double, Double) -> Double` signature we can apply `fbmFunc3D` on it again if we want to:

``````val fbmFbmSimplex = fbmFunc3D(fbmSimplex, lacunarity = 1.3222)
``````

Or alternatively, to demonstrate how quickly this becomes hard to read:

``````val fbmFbmSimplex = fbmFunc3D(fbmFunc3D(simplex3D), lacunarity = 1.3222)
``````

To improve readability I introduce the use of extension functions, I assume the reader knows what those are. The `fbm` function is an extension function on functions with the `(Int, Double, Double, Double) -> Double)` signature:

``````    fun ((Int, Double, Double, Double) -> Double).fbm :
(Int, Double, Double, Double) -> Double
``````

It does the same thing as fbmFunc3D, which is to return a function.So now we can compose our previous noise functions without clutter:

``````val fbmSimplex = simplex3D.fbm()
val fbmFbmSimplex = simplex3D.fbm().fbm(lacunarity = 1.3222)
``````

Now let’s look into some of the additional tooling I made.

## `.crossFade()`

`crossFade` is used for seamless noise looping animations and assumes the z axis of 3D noise functions is used for time. (Perhaps it makes sense to have generalized version of this in which the axis/axes can be specified). The `crossFade` function uses the receiver noise function (`this`) to calculate a blend. The `start`, `end` and `width` arguments are used to define the loop of the `z` value.

``````fun ((Int, Double, Double, Double) -> Double).crossFade(
start: Double, end: Double, width: Double = 0.5):
(Int, Double, Double, Double) -> Double {
return { seed, x, y, z ->
val a = z.map(start, end, 0.0, 1.0).mod_(1.0)
val f = (a / width).coerceAtMost(1.0)
val o = this(seed, x, y, a.map(0.0, 1.0, start, end)) * f
val s = this(seed, x, y, (a + 1.0).map(0.0, 1.0, start, end) * (1.0 - f))
o + s
}
}
``````

## `.withVector2Output()`

The `withVector2Output` function changes the function signature to return `Vector2` instead of `Double`. So the noise function outputs 2d noise, classically you’d pass in different seeds and juggle a bit with your input coordinates to get uncorrelated but similarly distributed noise for x and y. `withVector2Output` does that for you by xor-ing the seed and rotating x,y by 90 degrees. Also here we assume the z axis is used for time.

``````fun ((Int, Double, Double, Double) -> Double).withVector2Output():
(seed: Int, x: Double, y: Double, z: Double) -> Vector2 =
{ seed, x, y, z -> Vector2(this(seed, x, y, z),
this(seed xor 0x7f7f7f7f, y, -x, z)) }
``````

## `.gradient()`

And finally, we have gradient function. Which returns a function that approximates the gradient of the receiver function, also known as (curl noise):

``````fun ((Int, Double, Double, Double) -> Vector2).gradient(epsilon: Double = 1e-2 / 2.0):
(Int, Double, Double, Double) -> Vector2 =
{ seed, x, y, z ->
val dfdx = (this(seed, x + epsilon, y, z) - this(seed, x - epsilon, y, z)) / (2 * epsilon)
val dfdy = (this(seed, x, y + epsilon, z) - this(seed, x, y - epsilon, z)) / (2 * epsilon)
dfdx + dfdy
}
``````

## Notes and references

Original text by Edwin, formatted and edited by Abe, reference links by Yann.

1 Like

Now we need some images to show how those concepts actually look like! Any volunteers? 