Skip to content
Sample of Shane Organic Noise — layered Perlin with rotation and twist

shader_legends · ShaneOrganicNoise

Shane Organic Noise — layered Perlin with rotation and twist

Shane Olsson's 'organic noise' Shadertoy technique: a base Perlin-style gradient noise is sampled in multiple octaves, with each successive layer rotated by an angle proportional to the layer index and twisted by sinusoidal warping about the canvas centre. The summed scalar field is mapped through a cosine palette (Quilez) for the biomorphic, flesh-like textures Shane uses across his Shadertoy catalogue.

Original: https://www.shadertoy.com/view/3fcyz8

▶ Try the live demo (JS port) ▶ Run reference.py via Pyodide 10 formulas have instant JS ports. The Pyodide path runs the actual Python in your browser — first load is ~6 MB, then instant.

Family
shader_legends
Source
shader legends
Evolution
noise_field
Render
scalar_field
Complexity
medium
Agents
0
Year
2017
Author
Shane Olsen (after Ken Perlin, 2002)

Math essence

Per layer k: rotate (x, y) by angle k*twist_per_layer, displace by sin/cos of the polar angle for an organic warp, then sample Perlin gradient noise at scale*(x', y') + offset_k. Layers are summed with amp = 0.62^k. A radial vignette (1 - r*0.85) dampens the field near the edges. The final scalar is fed through the IQ cosine palette a + b*cos(2π*(c*t + d)) with offset drift over time.

Parameters

Name Type Default Range Description
dim int 360 120–800 Square output size in pixels.
scale float 2.6 0.5–8 Perlin input scale per layer (larger = finer features).
octaves int 4 1–8 Number of stacked layers.
twist_per_layer float 0.6 -1.4–1.4 Per-layer rotation in radians added to the polar angle.
distortion float 0.45 0–2.5 Sinusoidal warp strength applied to each layer.
contrast float 1.25 0.25–3 Post-field contrast multiplier.
palette_offset float 0.55 0–1 Phase offset for the IQ cosine palette.
time_speed float 0.04 -0.4–0.4 Drift speed of the warp and palette offset per step.

Reference

First 60 lines of reference.py. Full source: library_external/shader_legends/ShaneOrganicNoise/reference.py

from __future__ import annotations

from typing import ClassVar

import numpy as np


_PERMUTATION = (
    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,
)
_P = np.zeros(512, dtype=np.int32)
for _i in range(256):
    _P[_i] = _PERMUTATION[_i]
    _P[256 + _i] = _PERMUTATION[_i]


def _fade(t: np.ndarray) -> np.ndarray:
    return t * t * t * (t * (t * 6.0 - 15.0) + 10.0)


def _lerp(t: np.ndarray, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    return a + t * (b - a)


def _grad2(hash_idx: np.ndarray, x: np.ndarray, y: np.ndarray) -> np.ndarray:
    h = hash_idx & 7
    u = np.where(h < 4, x, y)
    v = np.where(h < 4, y, x)
    sign_u = np.where((h & 1) == 0, 1.0, -1.0)
    sign_v = np.where((h & 2) == 0, 1.0, -1.0)
    return sign_u * u + sign_v * v


def _perlin2(x: np.ndarray, y: np.ndarray, seed: int) -> np.ndarray:
    """Vectorised Ken-Perlin improved noise in 2D (z=0)."""
    xi = np.floor(x).astype(np.int32) & 255
    yi = np.floor(y).astype(np.int32) & 255
    xf = x - np.floor(x)
    yf = y - np.floor(y)
    u = _fade(xf)
    v = _fade(yf)

    perm = _P if seed == 0 else np.roll(_P, seed & 255)

    A = (perm[xi] + yi) & 255

Try running it locally

The reference implementation is self-contained Python with numpy. Install the package and run:

python library_external/shader_legends/ShaneOrganicNoise/reference.py

Need help? See Learn for the standard environment setup, or Playground for an in-browser sketchbook.

More from shader legends

See all shader_legends artworks →