# [Periodic Randomness using Higher-Dimensional Noise](https://blog.hirnschall.net/perfect-looping-gif/)

author: [Sebastian Hirnschall](https://blog.hirnschall.net/about/)

meta description: Random noise typically doesn't loop cleanly. By sampling higher-dimensional noise on a closed path, we can generate nice looping procedural animations.

meta title: Perfectly Looping Procedural Animations Using Higher-Dimensional Noise

date published: 03.03.2021 (DD.MM.YYYY format)
date last modified: 04.04.2026 (DD.MM.YYYY format)

---

Motivation
----------

Looping animations are everywhere: generative art, shaders, UI animations, game assets, and GIFs.  
But creating perfectly looping procedural animations is surprisingly difficult.

A common approach is to animate some parameter over time:

* scroll a texture
* change a random seed
* animate a value with Perlin noise
* move through procedural space

This usually works, until the animation restarts. Then you get a visible jump.

The beginning and end don't match, because procedural randomness is typically not periodic.

The idea to fix this is simple, we need to create periodic randomness. But how do we create randomness that loops perfectly?

Perlin Noise Example
--------------------

To get a feel for how we can implement periodic noise and how it works we will go through an example using perlin noise in p5js. We will generate a simple 2D terrain and create a perfectly looping gif.

The technique shown is applicable to other procedural animation/generation as well (not just perlin noise gifs).

If you are not familiar with perlin noise, you can take a look at this [article explaining what perlin-noise is](https://blog.hirnschall.net/perlin-noise/) and how we can use it in procedural generation.

### Naive Approach

Lets start with this bad looping gif of a 2d "map" scrolling from right to left:

[


2d perlin noise terrain generation animation
](resources/vid/bad-looping.webm)


Figure 1.1: Naive Approach to Looping Animations

You can clearly see the cut where the gif starts from the beginning which is something we don't want.

You can see side by side which section of perlin noise we are using to create the current animation frame and the corresponding frame in fig. 1.2.

[


2d perlin noise terrain generation animation following a straight line path through noise space. The cut happens because the beginning and end of the path are different.
](resources/vid/how-bad-looping-animations-work.webm)


Figure 1.2: How Bad Looping Animations Work

### Closed Paths

The reason why the animation in fig. 1.1 doesn't loop smoothly is because it follows a straight line path through the noise space. Thus the beginning and end of the path are different.

To alleviate this problem we can move the section of noise we work with in a circle instead of a straight line. By moving on a closed path the noise inherently becomes periodic and we won't have any cuts when the animation restarts.

You can see the result below:

[


2d perlin noise terrain generation animation following a circular (closed) path to avoid cuts when the animation restarts
](resources/vid/how-bad-looping-gisf-without-cut-work.webm)


Figure 1.3: Looping Animation on a Closed Path

Although fig. 1.3 shows that there is no longer an obvious cut when the video restarts, we see another obvious problem. The animation itself has changed. It is no longer scrolling but rather orbiting. We are now scrolling sideways.

### Higher-Dimensional Noise

To overcome this problem we will take the idea of moving in a circle to three dimensions or in general, if we are using \(n\)-dimensional perlin noise in the original animation we'll now use \(n+1\) dimensions.

So what we'll do is map our flat canvas to the outside of a cylinder in 3d space. By doing so we know there won't be any cuts as we are still going in a circle but the animation will stay the same!

You can think of this as if we were to wrap the canvas in fig. 1.1 into a hollow cylinder.

We can do this by mapping the each point \((x,y)\) to \((x',y',z')\):

$$x'=\sin\left(\frac{x\cdot 2\pi}{U}\right)\cdot R$$
$$y'=y$$
$$z'=\begin{cases}
\sqrt{R^2-x'^2} & \text{if $\frac{6\pi}{4} < (\frac{x\cdot 2\pi}{U}$ mod $2\pi) <\frac{2\pi}{4}$} \\
-\sqrt{R^2-x'^2} & \text{otherwise}
\end{cases}
$$

Where \(U\) is the circumference and \(R\) is the radius of the cylinder. By changing the size of the cylinder we can adjust the length of the final animation.

Note that in this example, to stick with the naming of the 2D case, \(y\) represents the height of the cylinder. This might be counter intuitive as we expect \(z\) to represent the height.

As (in p5js at least) \(noise(x)=noise(-x)\) we will offset the center of our cylinder from \(0\). This can be done by adding \(R\) to each coordinate. So, we'll use \(noise(R+x',R+y',R+z')\) instead of \(noise(x,y)\).

You can see the section of noise we are using for the final animation in fig. 1.4 below. However it is hard to visualize 3d noise correctly..

[


Higher dimensional perlin noise section used to create perfectly looping animations. The section of noise is moving in a circle in 3d space which creates periodic noise without changing the animation itself.
](resources/vid/how-perfect-looping-works.webm)


Figure 1.4: Section of 3D Perlin Noise used to Create Periodic Animations (perfectly looping)

And the final animation without any jumps or cuts:

[


perfectly looping terrain generation with 3d perlin noise
](resources/vid/perfect-looping-with-scrolling.webm)


Figure 1.5: Perfectly looping gif

Conclusion
----------

By sampling higher-dimensional noise along a closed path we can create perfectly looping procedural animations. This technique can be applied to any procedural animation that relies on randomness and is not inherently periodic.

All in all I am quite happy with the results. I think the idea of using an extra dimension is quite neat.