def perlin(x, y, seed=0):
    np.random.seed(seed)
p = np.arange(256, dtype=int)
np.random.shuffle(p)
p = np.stack([p, p]).flatten()
xi = x.astype(int)
yi = y.astype(int)
xf = x - xi
yf = y - yi
u = fade(xf)
v = fade(yf)
n00 = gradient(p[p[xi] + yi], xf, yf)
n01 = gradient(p[p[xi] + yi + 1], xf, yf - 1)
n11 = gradient(p[p[xi + 1] + yi + 1], xf - 1, yf - 1)
n10 = gradient(p[p[xi + 1] + yi], xf - 1, yf)
x1 = lerp(n00, n10, u)
x2 = lerp(n01, n11, u)
return lerp(x1, x2, v)

def lerp(a, b, x):
    """linear interpolation"""
return a + x * (b - a)

def fade(t):
    """6t^5 - 15t^4 + 10t^3"""
return 6 * t ** 5 - 15 * t ** 4 + 10 * t ** 3

def gradient(h, x, y):
    """grad converts h to the right gradient vector and return the dot product with (x,y)"""
vectors = np.array([[0, 1], [0, -1], [1, 0], [-1, 0]])
g = vectors[h % 4]
return g[:, :, (0)] * x + g[:, :, (1)] * y
lin = np.linspace(0, 5, 100, endpoint=False)
x, y = np.meshgrid(lin, lin)
plt.imshow(perlin(x, y, seed=2), origin='upper')
Producing 2D perlin noise with numpy