Skip to content

Instantly share code, notes, and snippets.

@Flafla2
Last active January 25, 2026 19:43
Show Gist options
  • Select an option

  • Save Flafla2/f0260a861be0ebdeef76 to your computer and use it in GitHub Desktop.

Select an option

Save Flafla2/f0260a861be0ebdeef76 to your computer and use it in GitHub Desktop.
A slightly modified implementation of Ken Perlin's improved noise that allows for tiling the noise arbitrarily.
public class Perlin {
public int repeat;
public Perlin(int repeat = -1) {
this.repeat = repeat;
}
public double OctavePerlin(double x, double y, double z, int octaves, double persistence) {
double total = 0;
double frequency = 1;
double amplitude = 1;
double maxValue = 0; // Used for normalizing result to 0.0 - 1.0
for(int i=0;i<octaves;i++) {
total += perlin(x * frequency, y * frequency, z * frequency) * amplitude;
maxValue += amplitude;
amplitude *= persistence;
frequency *= 2;
}
return total/maxValue;
}
private static readonly int[] permutation = { 151,160,137,91,90,15, // Hash lookup table as defined by Ken Perlin. This is a randomly
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, // arranged array of all numbers from 0-255 inclusive.
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};
private static readonly int[] p; // Doubled permutation to avoid overflow
static Perlin() {
p = new int[512];
for(int x=0;x<512;x++) {
p[x] = permutation[x%256];
}
}
public double perlin(double x, double y, double z) {
if(repeat > 0) { // If we have any repeat on, change the coordinates to their "local" repetitions
x = x%repeat;
y = y%repeat;
z = z%repeat;
}
int xi = (int)x & 255; // Calculate the "unit cube" that the point asked will be located in
int yi = (int)y & 255; // The left bound is ( |_x_|,|_y_|,|_z_| ) and the right bound is that
int zi = (int)z & 255; // plus 1. Next we calculate the location (from 0.0 to 1.0) in that cube.
double xf = x-(int)x; // We also fade the location to smooth the result.
double yf = y-(int)y;i
double zf = z-(int)z;
double u = fade(xf);
double v = fade(yf);
double w = fade(zf);
int aaa, aba, aab, abb, baa, bba, bab, bbb;
aaa = p[p[p[ xi ]+ yi ]+ zi ];
aba = p[p[p[ xi ]+inc(yi)]+ zi ];
aab = p[p[p[ xi ]+ yi ]+inc(zi)];
abb = p[p[p[ xi ]+inc(yi)]+inc(zi)];
baa = p[p[p[inc(xi)]+ yi ]+ zi ];
bba = p[p[p[inc(xi)]+inc(yi)]+ zi ];
bab = p[p[p[inc(xi)]+ yi ]+inc(zi)];
bbb = p[p[p[inc(xi)]+inc(yi)]+inc(zi)];
double x1, x2, y1, y2;
x1 = lerp( grad (aaa, xf , yf , zf), // The gradient function calculates the dot product between a pseudorandom
grad (baa, xf-1, yf , zf), // gradient vector and the vector from the input coordinate to the 8
u); // surrounding points in its unit cube.
x2 = lerp( grad (aba, xf , yf-1, zf), // This is all then lerped together as a sort of weighted average based on the faded (u,v,w)
grad (bba, xf-1, yf-1, zf), // values we made earlier.
u);
y1 = lerp(x1, x2, v);
x1 = lerp( grad (aab, xf , yf , zf-1),
grad (bab, xf-1, yf , zf-1),
u);
x2 = lerp( grad (abb, xf , yf-1, zf-1),
grad (bbb, xf-1, yf-1, zf-1),
u);
y2 = lerp (x1, x2, v);
return (lerp (y1, y2, w)+1)/2; // For convenience we bound it to 0 - 1 (theoretical min/max before is -1 - 1)
}
public int inc(int num) {
num++;
if (repeat > 0) num %= repeat;
return num;
}
public static double grad(int hash, double x, double y, double z) {
int h = hash & 15; // Take the hashed value and take the first 4 bits of it (15 == 0b1111)
double u = h < 8 /* 0b1000 */ ? x : y; // If the most significant bit (MSB) of the hash is 0 then set u = x. Otherwise y.
double v; // In Ken Perlin's original implementation this was another conditional operator (?:). I
// expanded it for readability.
if(h < 4 /* 0b0100 */) // If the first and second significant bits are 0 set v = y
v = y;
else if(h == 12 /* 0b1100 */ || h == 14 /* 0b1110*/)// If the first and second significant bits are 1 set v = x
v = x;
else // If the first and second significant bits are not equal (0/1, 1/0) set v = z
v = z;
return ((h&1) == 0 ? u : -u)+((h&2) == 0 ? v : -v); // Use the last 2 bits to decide if u and v are positive or negative. Then return their addition.
}
public static double fade(double t) {
// Fade function as defined by Ken Perlin. This eases coordinate values
// so that they will "ease" towards integral values. This ends up smoothing
// the final output.
return t * t * t * (t * (t * 6 - 15) + 10); // 6t^5 - 15t^4 + 10t^3
}
public static double lerp(double a, double b, double x) {
return a + x * (b - a);
}
}
@logank8
Copy link

logank8 commented Mar 9, 2025

Here's a (hopefully) bug-free version in HLSL. Sorry if the naming conventions are weird, I only learned HLSL last week

*Also it assumes repeat because I'm lazy

#ifndef PERLIN_INCLUDED
#define PERLIN_INCLUDED

// Original permutation table from Perlin paper
static const int permutation[256] = {
    151, 160, 137,  91,  90,  15, 131,  13, 201,  95,  96,  53, 194, 233,   7, 225,
    140,  36, 103,  30,  69, 142,   8,  99,  37, 240,  21,  10,  23, 190,   6, 148,
    247, 120, 234,  75,   0,  26, 197,  62,  94, 252, 219, 203, 117,  35,  11,  32,
    57, 177,  33,  88, 237, 149,  56,  87, 174,  20, 125, 136, 171, 168,  68, 175,
    74, 165,  71, 134, 139,  48,  27, 166,  77, 146, 158, 231,  83, 111, 229, 122,
    60, 211, 133, 230, 220, 105,  92,  41,  55,  46, 245,  40, 244, 102, 143,  54,
    65,  25,  63, 161,   1, 216,  80,  73, 209,  76, 132, 187, 208,  89,  18, 169,
    200, 196, 135, 130, 116, 188, 159,  86, 164, 100, 109, 198, 173, 186,   3,  64,
    52, 217, 226, 250, 124, 123,   5, 202,  38, 147, 118, 126, 255,  82,  85, 212,
    207, 206,  59, 227,  47,  16,  58,  17, 182, 189,  28,  42, 223, 183, 170, 213,
    119, 248, 152,   2,  44, 154, 163,  70, 221, 153, 101, 155, 167,  43, 172,   9,
    129,  22,  39, 253,  19,  98, 108, 110,  79, 113, 224, 232, 178, 185, 112, 104,
    218, 246,  97, 228, 251,  34, 242, 193, 238, 210, 144,  12, 191, 179, 162, 241,
    81,  51, 145, 235, 249,  14, 239, 107,  49, 192, 214,  31, 181, 199, 106, 157,
    184,  84, 204, 176, 115, 121,  50,  45, 127,   4, 150, 254, 138, 236, 205,  93,
    222, 114,  67,  29,  24,  72, 243, 141, 128, 195,  78,  66, 215,  61, 156, 180
};

int inc(int num) {
    num++;
    if (num >= 512) { 
        return num % 512;
    } 
    return num;
}

float Gradient_float(int hash, float x, float y, float z) {
    int h = hash & 15;
    float u = h < 8 ? x : y;

    float v;

    if (h < 4) {
        v = y;
    } else if (h == 12 || h == 14) {
        v = x;
    } else {
        v = z;
    }

    return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
}

float Fade(float t) {
    return (6 * pow(t, 5)) - (15 * pow(t, 4)) + (10 * pow(t, 3));
}

float Perlin(float x, float y, float z)
{
    // I added this because I'm inputting object space coords, so it helped to scale up a bit
    x *= 10;
    y *= 10;
    z *= 10;

    int p[512];
    for (int i = 0; i<512; i++) {
        p[i] = permutation[i % 256];
    }


    // hash fn index
    int xi = ((int) floor(x)) & 255;
    int yi = ((int) floor(y)) & 255;
    int zi = ((int) floor(z)) & 255;

    // unit coord
    float xf = x - floor(x);
    float yf = y - floor(y);
    float zf = z - floor(z);

    // vals for smoothing lerp
    float u = Fade(xf);
    float v = Fade(yf);
    float w = Fade(zf);

    // Get all the unit cube coordinates surrounding (x,y,z)
    int aaa, aba, aab, abb, baa, bba, bab, bbb;
    aaa = p[p[p[xi] + yi] + zi];
    aba = p[p[p[xi] + inc(yi)] + zi];
    aab = p[p[p[xi] + yi] + inc(zi)];
    abb = p[p[p[xi] + inc(yi)] + inc(zi)];
    baa = p[p[p[inc(xi)] + yi] + zi];
    bba = p[p[p[inc(xi)] + inc(yi)] + zi];
    bab = p[p[p[inc(xi)] + yi] + inc(zi)];
    bbb = p[p[p[inc(xi)] + inc(yi)] + inc(zi)];

    float x1, x2, y1, y2;

    x1 = lerp(Gradient_float(aaa, xf, yf, zf), Gradient_float(baa, xf-1, yf, zf), u);
    x2 = lerp(Gradient_float(aba, xf, yf-1, zf), Gradient_float(bba, xf-1, yf-1, zf), u);
    y1 = lerp(x1, x2, v);
    x1 = lerp(Gradient_float(aab, xf, yf, zf-1), Gradient_float(bab, xf-1, yf, zf-1), u);
    x2 = lerp(Gradient_float(abb, xf, yf-1, zf-1), Gradient_float(bbb, xf-1, yf-1, zf-1), u);
    y2 = lerp(x1, x2, v);

    return ((lerp(y1, y2, w) + 1) / 2);
}

float OctavePerlin(float x, float y, float z, int octaves, float persistence) {
    float total = 0;
    float frequency = 1;
    float amplitude = 1;
    float maxVal = 0;

    for (int i = 0; i < octaves; i++) {
        total += Perlin(x * frequency, y * frequency, z * frequency) * amplitude;

        maxVal += amplitude;

        amplitude *= persistence;
        frequency *= 2;
    }

    return total/maxVal;
}


#endif

@DavidAntliff
Copy link

DavidAntliff commented Mar 9, 2025

@sakura-ice

Fixed the bug in the code.

What was the bug you fixed?

@sakura-ice
Copy link

@sakura-ice

Fixed the bug in the code.

What was the bug you fixed?

It wasn't a bug that I fixed; I integrated the code issues mentioned in other responses.

@743211258
Copy link

743211258 commented Jan 17, 2026

The implementation is very helpful for me to learn Perlin Noise, I appreciate it.

However, as I was trying to implement the Perlin Noise in C, I didn't think that it was necessary to introduce the increment function while trying to simply add by one. At the beginning of the perlin function, variables x, y, z had already been taken modulo to make the noise repeats,

x = x%repeat;
y = y%repeat;
z = z%repeat;

(Line 52 to line 54), applying another modular arithmetic in the increment function may cause errors, especially when repeat = 1. I believe

xi++

is sufficient as opposed to

inc(xi)

@743211258
Copy link

2D Perlin Noise in C language.

#include <math.h>

const int PERMUTATION_TABLE[512] =
{151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7,
225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247,
120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33,
88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134,
139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220,
105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80,
73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86,
164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38,
147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189,
28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101,
155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232,
178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12,
191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181,
199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236,
205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180,
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7,
225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247,
120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33,
88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134,
139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220,
105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80,
73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86,
164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38,
147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189,
28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101,
155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232,
178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12,
191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181,
199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236,
205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180};

float two_dim_grad(int hash, float x, float y) {
  switch (hash & 0x7) {
    case 0x0: return  x;
    case 0x1: return -x;
    case 0x2: return  y;
    case 0x3: return -y;
    case 0x4: return  x + y;
    case 0x5: return  x - y;
    case 0x6: return -x + y;
    case 0x7: return -x - y;
    default: return 0;
  }
}

float lerp(float a, float b, float x) {
  return a + x * (b - a);
}

float fade(float t) {
  return t * t * t * (t * (t * 6 - 15) + 10);
}

float perlin_noise(int repeat, float x, float y) {
  x = fmod(x, repeat);
  y = fmod(y, repeat);

  int x_i = (int) x & 255;
  int y_i = (int) y & 255;
  float x_f = x - (int) x;
  float y_f = y - (int) y;

  float u = fade(x_f);
  float v = fade(y_f);

  int aa, ab, ba, bb;
  int inc_x_i = x_i + 1;
  int inc_y_i = y_i + 1;
  aa = PERMUTATION_TABLE[PERMUTATION_TABLE[x_i] + y_i];
  ab = PERMUTATION_TABLE[PERMUTATION_TABLE[x_i] + inc_y_i];
  ba = PERMUTATION_TABLE[PERMUTATION_TABLE[inc_x_i] + y_i];
  bb = PERMUTATION_TABLE[PERMUTATION_TABLE[inc_x_i] + inc_y_i];

  double x1, x2, y1;
  x1 = lerp(two_dim_grad(aa, x_f, y_f),
            two_dim_grad(ba, x_f - 1, y_f),
            u);
  x2 = lerp(two_dim_grad(ab, x_f, y_f - 1),
            two_dim_grad(bb, x_f - 1, y_f - 1),
            u);
  y1 = lerp(x1, x2, v);

  return (y1 + 1) / 2;
}

float octave_perlin(int repeat, float x, float y, int octaves, float persistance) {
  float total = 0.0;
  float frequency = 1.0;
  float amplitude = 1.0;
  float max_value = 0.0;

  for (int i = 0; i < octaves; ++i) {
    total += perlin_noise(repeat, x * frequency, y * frequency) * amplitude;
    max_value += amplitude;
    amplitude *= persistance;
    frequency *= 2;
  }
  return total / max_value;
}

@743211258
Copy link

What is the license of this code?

Quoted from his GitHub page:

"As far as copyright goes, you can pretty much do whatever you want, provided it is within the rights granted by the Creative Commmons 4.0 International Attribution Licensee. Most notably you must attribute me (Adrian Biagioli) before sourcing any of my work. Thank you for understanding."

I believe as long as you give him credits, you will be fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment