# How do you generate tileable Perlin noise?

Related:

I'd like to generate tileable Perlin noise. I'm working from Paul Bourke's PerlinNoise*() functions, which are like this:

// alpha is the "division factor" (how much to damp subsequent octaves with (usually 2))
// beta is the factor that multiplies your "jump" into the noise (usually 2)
// n is the number of "octaves" to add in
double PerlinNoise2D(double x,double y,double alpha,double beta,int n)
{
int i;
double val,sum = 0;
double p[2],scale = 1;

p[0] = x;
p[1] = y;
for (i=0;i<n;i++) {
val = noise2(p);
sum += val / scale;
scale *= alpha;
p[0] *= beta;
p[1] *= beta;
}
return(sum);
}

Using code like:

real val = PerlinNoise2D( x,y, 2, 2, 12 ) ; // test

return val*val*skyColor + 2*val*(1-val)*gray + (1-val)*(1-val)*cloudColor ;

Gives sky like

Which isn't tileable.

The pixel values are 0->256 (width and height), and pixel (0,0) uses (x,y)=(0,0) and pixel (256,256) uses (x,y)=(1,1)

How can I make it tileable?

• Just FYI, what you have there isn't Perlin noise; it's fractal noise. Perlin noise is likely the "noise2" function generating each octave of the fractal noise. – Nathan Reed Feb 10 '12 at 6:49

There's two parts to making seamlessly tileable fBm noise like this. First, you need to make the Perlin noise function itself tileable. Here's some Python code for a simple Perlin noise function that works with any period up to 256 (you can trivially extend it as much as you like by modifying the first section):

import random
import math
from PIL import Image

perm = range(256)
random.shuffle(perm)
perm += perm
dirs = [(math.cos(a * 2.0 * math.pi / 256),
math.sin(a * 2.0 * math.pi / 256))
for a in range(256)]

def noise(x, y, per):
def surflet(gridX, gridY):
distX, distY = abs(x-gridX), abs(y-gridY)
polyX = 1 - 6*distX**5 + 15*distX**4 - 10*distX**3
polyY = 1 - 6*distY**5 + 15*distY**4 - 10*distY**3
hashed = perm[perm[int(gridX)%per] + int(gridY)%per]
return polyX * polyY * grad
intX, intY = int(x), int(y)
return (surflet(intX+0, intY+0) + surflet(intX+1, intY+0) +
surflet(intX+0, intY+1) + surflet(intX+1, intY+1))

Perlin noise is generated from a summation of little "surflets" which are the product of a randomly oriented gradient and a separable polynomial falloff function. This gives a positive region (yellow) and negative region (blue)

The surflets have a 2x2 extent and are centered on the integer lattice points, so the value of Perlin noise at each point in space is produced by summing the surflets at the corners of the cell that it occupies.

If you make the gradient directions wrap with some period, the noise itself will then wrap seamlessly with the same period. This is why the code above takes the lattice coordinate modulo the period before hashing it through the permutation table.

The other step, is that when summing the octaves you will want to scale the period with the frequency of the octave. Essentially, you will want each octave to tile the entire just image once, rather than multiple times:

def fBm(x, y, per, octs):
val = 0
for o in range(octs):
val += 0.5**o * noise(x*2**o, y*2**o, per*2**o)
return val

Put that together and you get something like this:

size, freq, octs, data = 128, 1/32.0, 5, []
for y in range(size):
for x in range(size):
data.append(fBm(x*freq, y*freq, int(size*freq), octs))
im = Image.new("L", (size, size))
im.putdata(data, 128, 128)
im.save("noise.png")

As you can see, this does indeed tile seamlessly:

With some small tweaking and color mapping, here's a cloud image tiled 2x2:

Hope this helps!

• im not a python guy, so i ask, how does x*2**o convert to C? is it: x*pow(2,o) or pow(x*2,o) ? – idev Feb 11 '12 at 14:49
• x*pow(2, o), since exponentiation has higher precedence than multiplication. – John Calsbeek Feb 11 '12 at 19:05
• could someone convert this to C? i have huge problems understanding this code, as i have never done anything with python. for example what is a value? and im not sure how the functions convert to C... i get straight lines in output only. – idev Feb 12 '12 at 15:06
• This is definitely the best solution so long as you're fine with the domain of your noise being tied to the shape of your tile. For example, this doesn't allow arbitrary rotations. But if you don't need any such thing, this is the ideal answer. – John Calsbeek Feb 12 '12 at 22:49
• Note: if you want to generate other size than 128, DO NOT change the numeric values on the line im.putdata(data, 128, 128). (For those unfamiliar with python or PIL: they mean scale and offset, not image size.) – Antti Kissaniemi Aug 30 '13 at 3:05

Here's one rather clever way that uses 4D Perlin noise.

Basically, map the X coordinate of your pixel to a 2D circle, and the Y coordinate of your pixel to a second 2D circle, and place those two circles orthogonal to each other in 4D space. The resulting texture is tileable, has no obvious distortion, and doesn't repeat in the way that a mirrored texture would.

Copy-pasting code from the article:

for x=0,bufferwidth-1,1 do
for y=0,bufferheight-1,1 do
local s=x/bufferwidth
local t=y/bufferheight
local dx=x2-x1
local dy=y2-y1

local nx=x1+cos(s*2*pi)*dx/(2*pi)
local ny=y1+cos(t*2*pi)*dy/(2*pi)
local nz=x1+sin(s*2*pi)*dx/(2*pi)
local nw=y1+sin(t*2*pi)*dy/(2*pi)

buffer:set(x,y,Noise4D(nx,ny,nz,nw))
end
end
• This is definitely the right answer. Add dimensions is an old mathematician trick. Olinde Rodrigues docet (Sir W.R. Hamilton docet too but slightly less) – FxIII Feb 10 '12 at 15:18
• You can use any 4D noise function. Simplex noise would be my recommendation. webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf – John Calsbeek Feb 10 '12 at 20:20
• thanks john! got it working, sweet! nobody said it, but: the x1,y1,x2,y2 seems to be some sort of scaling, the larger distance, the detailed noise. if this helps anyone. – idev Feb 10 '12 at 21:02
• Note that this is topologically equivalent to bobobobo's answer: your mapping embeds a 2-torus into an ℝ⁴, which is possible without the metric distortions you invevitably get when embedding it into ℝ³. – leftaroundabout Feb 11 '12 at 13:21
• @idev: no, it just means that the two methods are more similar than it may seem at first sight. But John Calsbeek's version is quite arguably better, since it avoids the distortions. – leftaroundabout Feb 11 '12 at 14:46

Ok, I got it. The answer is to walk in a torus in 3D noise, generating a 2D texture out of it.

Code:

Color Sky( double x, double y, double z )
{
// Calling PerlinNoise3( x,y,z ),
// x, y, z _Must be_ between 0 and 1
// for this to tile correctly
double c=4, a=1; // torus parameters (controlling size)
double xt = (c+a*cos(2*PI*y))*cos(2*PI*x);
double yt = (c+a*cos(2*PI*y))*sin(2*PI*x);
double zt = a*sin(2*PI*y);
double val = PerlinNoise3D( xt,yt,zt, 1.5, 2, 12 ) ; // torus

return val*val*cloudWhite + 2*val*(1-val)*gray + (1-val)*(1-val)*skyBlue ;
}

Results:

Once:

And tiled:

• It kinda works, but it looks like you're getting a bunch of distortion due to the curvature of the torus. – Nathan Reed Feb 10 '12 at 21:51
• you can really just modulo the position, but I love all the awesome/creative answers to this question. So many different ways to do the same thing. – user10968 Feb 11 '12 at 8:57
• i noticed you actually dont want to use 0-1 values, but 0-0.9999... values! so you would use: x/width, y/height etc. otherwise the seams doesnt match (makes the opposite edges exact same pixels). also it looks like the PerlinNoise3D() function needs clamping for the result value too, or some pixel values overflow. – idev Feb 11 '12 at 15:52
• @idev I believe the way to fix the distortion is to use the 4D method in the top answer of this question. ;) – Nathan Reed Feb 11 '12 at 16:24
• Note that a 2D slice through 3D (or 4D) noise has different properties than native 2D noise. It's less band-limited, which can make it harder to get the right balance between feature detail and aliasing artifacts. See this paper for a more thorough explanation – DMGregory Aug 22 '18 at 17:21

One simple way I can think of would be to take the output of the noise function and mirror/flip it into an image that's twice the size. It's difficult to explain so here's an image:

Now, in this case, it's pretty obvious what you did when you look at this. I can think of two ways to (possibly :-) ) resolve this:

1. You could take that larger image and then generate some more noise on top of it but (and I'm not sure if this is possible) focused towards the middle (so the edges stay the same). It could add the extra bit of difference that would make your brain think it's not just mirror images.

2. (I'm also not sure if this is possible) You could try fiddling with the inputs to the noise function to generate the initial image differently. You'd have to do this by trial and error, but look for features that draw your eye when you tile/mirror it and then try and get it not to generate those.

Hope this helps.

• Very nice but too symmetrical! – bobobobo Feb 10 '12 at 18:18
• @bobobobo That's what I was thinking the other steps would alleviate. So, you could generate a "base" using this method, and then add some more details over the whole thing to make it look like it's not (so) mirrored. – Richard Marskell - Drackir Feb 10 '12 at 18:29
• You start to get some weird patterns when you do this kind of thing. This one in particular looks kind of like a butterfly. Easy solution, though. – notlesh Feb 10 '12 at 21:04
• This was my first approach too, but it has a problem, visible here: dl.dropbox.com/u/6620757/noise_seam.png As you cross a flip boundary you cause a disjoint in the noise function by instantly inverting the slope of the function. Even if you apply a second noise function on top, that may still be visible in the output. – Jherico Nov 25 '12 at 22:57
• Great idea. This can be easily done in a pixel shader using triangle wave function : tex2d(abs(abs(uv.x)%2.0-1.0), abs(abs(uv.y)%2.0-1.0)) – tigrou May 16 '16 at 12:13

First version of this answer was actually wrong, I've updated it

A method I used successfully is make noise domain tiled. In other words, make your base noise2() function periodical. If noise2() is periodic and beta is integer, resulting noise will have the same period as noise2().

How can we make noise2() periodic? In most implementations, this function uses some kind of lattice noise. That is, it gets random numbers at integer coordinates, and interpolates them. For example:

function InterpolatedNoise_1D(float x)

integer_X    = int(x)
fractional_X = x - integer_X

v1 = SmoothedNoise1(integer_X)
v2 = SmoothedNoise1(integer_X + 1)

return Interpolate(v1 , v2 , fractional_X)

end function

This function can be trivially modified to become periodic with integer period. Simply add one line:

integer_X = integer_X % Period

before calculating v1 and v2. This way, values at integer coordinates will repeat every Period units, and interpolation will ensure that resulting function is smooth.

Note, however, that this only works when Period is more than 1. So, to actually use this in making seamless textures, you'd have to sample a Period x Period square, not 1x1.

• But how do you make noise2 periodic (with a short period such as 1 unit)? I think that's what the question is ultimately asking. Standard Perlin noise is periodic with a period of 256 on each axis but you want a modified noise with a smaller period. – Nathan Reed Feb 10 '12 at 6:52
• @Nathan Reed If you call noise2 as suggested, you will get periodic results, whether the function itself is periodic or not. Because the arguments wrap around every 1 unit. – Nevermind Feb 10 '12 at 7:01
• But then you get seams at the grid lines, don't you? Since there's no guarantee that noise2(0, 0.999) is anything near noise2(0, 0), unless I've missed something. – Nathan Reed Feb 10 '12 at 17:30
• @Nathan Reed That's a good point. In fact, I just re-checked my old code and it turns out I was wrong. I'll edit the answer now. – Nevermind Feb 10 '12 at 18:57
• Great! This is actually a good answer now. +1 :) – Nathan Reed Feb 10 '12 at 19:31

Another alternative is to generate noise using libnoise libraries. You can generate noise over a theoretical infinite amount of space, seamlessly.

Take a look at the following: http://libnoise.sourceforge.net/tutorials/tutorial3.html#tile

There is also an XNA port of the above at: http://bigblackblock.com/tools/libnoisexna

If you end up using the XNA port, you can do something like this:

Perlin perlin = new Perlin();
perlin.Frequency = 0.5f;                //height
perlin.Lacunarity = 2f;                 //frequency increase between octaves
perlin.OctaveCount = 5;                 //Number of passes
perlin.Persistence = 0.45f;             //
perlin.Quality = QualityMode.High;
perlin.Seed = 8;

//Create our 2d map
Noise2D _map = new Noise2D(CHUNKSIZE_WIDTH, CHUNKSIZE_HEIGHT, perlin);

//Get a section
_map.GeneratePlanar(left, right, top, down);

GeneratePlanar is the function to call to get the sections in each direction that will connect seamlessly with the rest of the textures.

Of course, this method is more costly than simply having a single texture that can be used across multiple surfaces. If you are looking to create some random tileable textures, this may be something that interests you.

Though there are some answers here that would work, most of them are complicated, slow and problematic.

All you really need to do is use a periodic noise generation function. That's it!

An excellent public domain implementation based on Perlin's "advanced" noise algorithm can be found here. The function you need is pnoise2. The code was written by Stefan Gustavson, who has made a pointed comment here about exactly this issue, and how others have taken the wrong approach. Listen to Gustavson, he knows what he's talking about.

Regarding the various spherical projections some here have suggested: well, they in essence work (slowly), but they also produce a 2D texture that is a flattened sphere, so that the edges would more condensed, likely producing an undesired effect. Of course, if you intend for your 2D texture to be projected onto a sphere, that's the way to go, but that's not what was being asked for.

Here's a much much simpler way to do tiled noise:

You use a modular wrap around for each scale of the noise. These fit the edges of the area no matter what frequency scale you use. So you only have to use normal 2D noise which is a lot faster. Here is the live WebGL code which can be found at ShaderToy: https://www.shadertoy.com/view/4dlGW2

The top three functions do all the work, and fBM is passed a vector x/y in a 0.0 to 1.0 range.

// Tileable noise, for creating useful textures. By David Hoskins, Sept. 2013.
// It can be extrapolated to other types of randomised texture.

#define SHOW_TILING
#define TILES 2.0

//----------------------------------------------------------------------------------------
float Hash(in vec2 p, in float scale)
{
// This is tiling part, adjusts with the scale...
p = mod(p, scale);
return fract(sin(dot(p, vec2(35.6898, 24.3563))) * 353753.373453);
}

//----------------------------------------------------------------------------------------
float Noise(in vec2 x, in float scale )
{
x *= scale;

vec2 p = floor(x);
vec2 f = fract(x);
f = f*f*(3.0-2.0*f);
//f = (1.0-cos(f*3.1415927)) * .5;
float res = mix(mix(Hash(p,                  scale),
Hash(p + vec2(1.0, 0.0), scale), f.x),
mix(Hash(p + vec2(0.0, 1.0), scale),
Hash(p + vec2(1.0, 1.0), scale), f.x), f.y);
return res;
}

//----------------------------------------------------------------------------------------
float fBm(in vec2 p)
{
float f = 0.4;
// Change starting scale to any integer value...
float scale = 14.0;
float amp = 0.55;
for (int i = 0; i < 8; i++)
{
f += Noise(p, scale) * amp;
amp *= -.65;
// Scale must be multiplied by an integer value...
scale *= 2.0;
}
return f;
}

//----------------------------------------------------------------------------------------
void main(void)
{
vec2 uv = gl_FragCoord.xy / iResolution.xy;

#ifdef SHOW_TILING
uv *= TILES;
#endif

// Do the noise cloud (fractal Brownian motion)
float bri = fBm(uv);

bri = min(bri * bri, 1.0); // ...cranked up the contrast for no reason.
vec3 col = vec3(bri);

#ifdef SHOW_TILING
vec2 pixel = (TILES / iResolution.xy);
// Flash borders...
if (uv.x > pixel.x && uv.y > pixel.y                                        // Not first pixel
&& (fract(uv.x) < pixel.x || fract(uv.y) < pixel.y) // Is it on a border?
&& mod(iGlobalTime-2.0, 4.0) < 2.0)                 // Flash every 2 seconds
{
col = vec3(1.0, 1.0, 0.0);
}
#endif
gl_FragColor = vec4(col,1.0);
}
• Your image link went dead. I took a best guess & replaced it with a screen shot of the output from the shadertoy code you posted. If that's not correct, please re-upload your intended image directly to the Stack Exchange server. – Pikalek Aug 22 '18 at 14:26

I had some not-bad results interpolating near the edges of the tile (edge-wrapped), but it depends on what effect you're trying to achieve and the exact noise parameters. Works great for somewhat blurry noise, not so good with spikey/fine-grained ones.

I was checking this thread in search for an answer to a similar issue, then I got a clean and compact solution from the developer of this python code to generate fractal noise out of perlin/simplex noise. The updated code is provided in this (closed) issue and can be resumed in setting the gradients for the right side of the "generator" equal to those on the left side (and same for top and bottom), such as in