Nodes/Jovi_GLSL/NOISE PERLIN (JOV_GL)
ComfyUI Node

NOISE PERLIN (JOV_GL)

Perlin noise as a node — with octaves, a seed, and GPU speed

By Amorano·Created 2 years ago·Updated about a year ago· 20
NOISE PERLIN (JOV_GL)
  • iRes
  • RGBA
  • RGB
  • MASK
frequency1.00
amplitude1.00
octaves4
lacunarity2.00
persistence0.50
offset0.00
seed0
FRAGMENT// name: NOISE PERLIN // desc: Classic Perlin noise // category: NOISE // control: res, seed #ifndef LIB_NOISE_PARAMS #define LIB_NOISE_PARAMS #ifndef MAX_OCTAVES #define MAX_OCTAVES 16 #endif struct NoiseParams { float frequency; // Base frequency multiplier float amplitude; // Base amplitude multiplier int octaves; // Number of octaves float lacunarity; // Frequency multiplier per octave float persistence; // Amplitude multiplier per octave (same as 'gain' in some functions) float offset; // For ridge noise int seed; // Seed }; NoiseParams defaultNoiseParams() { NoiseParams params; params.frequency = 1.0; params.amplitude = 1.0; params.octaves = 4; params.lacunarity = 2.0; params.persistence = 0.5; params.offset = 1.0; params.seed = 0; return params; } #endif #ifndef LIB_NOISE_PERLIN #define LIB_NOISE_PERLIN #ifndef LIB_NOISE_RAND #define LIB_NOISE_RAND //------------------------------------------------------------------------------ // RANDOM VALUE GENERATORS // These functions generate pseudo-random values using different input dimensions //------------------------------------------------------------------------------ float noise_rand(float x) { return fract(sin(x * 12.9898) * 43758.5453); } float noise_rand(vec2 co) { return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453123); } float noise_rand(vec3 co) { return fract(sin(dot(co, vec3(12.9898, 78.233, 45.678))) * 43758.5453123); } float noise_rand(vec4 co) { return fract(sin(dot(co, vec4(12.9898, 78.233, 45.678, 94.673))) * 43758.5453123); } #endif //------------------------------------------------------------------------------ // NOISE PERLIN //------------------------------------------------------------------------------ // Generate 2D Perlin noise float noise_perlin(vec2 p) { vec2 i = floor(p); vec2 f = fract(p); vec2 u = smoothstep(0.,1.,f); float a = noise_rand(i); float b = noise_rand(i + vec2(1.0, 0.0)); float c = noise_rand(i + vec2(0.0, 1.0)); float d = noise_rand(i + vec2(1.0, 1.0)); return mix(mix(a, b, u.x), mix(c, d, u.x), u.y); } // 3D Perlin Noise function float noise_perlin(vec3 p) { vec3 i = floor(p); vec3 f = fract(p); vec3 u = smoothstep(0.,1.,f); float a = noise_rand(i); float b = noise_rand(i + vec3(1.0, 0.0, 0.0)); float c = noise_rand(i + vec3(0.0, 1.0, 0.0)); float d = noise_rand(i + vec3(1.0, 1.0, 0.0)); float e = noise_rand(i + vec3(0.0, 0.0, 1.0)); float f0 = noise_rand(i + vec3(1.0, 0.0, 1.0)); float g0 = noise_rand(i + vec3(0.0, 1.0, 1.0)); float h0 = noise_rand(i + vec3(1.0, 1.0, 1.0)); return mix(mix(mix(a, b, u.x), mix(c, d, u.x), u.y), mix(mix(e, f0, u.x), mix(g0, h0, u.x), u.y), u.z); } // 4D Perlin Noise function float noise_perlin(vec4 p) { vec4 i = floor(p); vec4 f = fract(p); vec4 u = smoothstep(0.,1.,f); float a = noise_rand(i); float b = noise_rand(i + vec4(1.0, 0.0, 0.0, 0.0)); float c = noise_rand(i + vec4(0.0, 1.0, 0.0, 0.0)); float d = noise_rand(i + vec4(1.0, 1.0, 0.0, 0.0)); float e = noise_rand(i + vec4(0.0, 0.0, 1.0, 0.0)); float f0 = noise_rand(i + vec4(1.0, 0.0, 1.0, 0.0)); float g0 = noise_rand(i + vec4(0.0, 1.0, 1.0, 0.0)); float h0 = noise_rand(i + vec4(1.0, 1.0, 1.0, 0.0)); float i1 = noise_rand(i + vec4(0.0, 0.0, 0.0, 1.0)); float j1 = noise_rand(i + vec4(1.0, 0.0, 0.0, 1.0)); float k1 = noise_rand(i + vec4(0.0, 1.0, 0.0, 1.0)); float l1 = noise_rand(i + vec4(1.0, 1.0, 0.0, 1.0)); float m1 = noise_rand(i + vec4(0.0, 0.0, 1.0, 1.0)); float n1 = noise_rand(i + vec4(1.0, 0.0, 1.0, 1.0)); float o1 = noise_rand(i + vec4(0.0, 1.0, 1.0, 1.0)); float p1 = noise_rand(i + vec4(1.0, 1.0, 1.0, 1.0)); return mix(mix(mix(a, b, u.x), mix(c, d, u.x), u.y), mix(mix(e, f0, u.x), mix(g0, h0, u.x), u.y), u.z); } //------------------------------------------------------------------------------ // PARAMETERS //------------------------------------------------------------------------------ #define NOISE_PERLIN(vec_type) float noise_perlin(vec_type p, NoiseParams params) { \ float value = 0.0; \ float frequency = params.frequency; \ float amplitude = params.amplitude; \ float maxValue = 0.0; \ for(int i = 0; i < min(params.octaves, MAX_OCTAVES); i++) { \ value += amplitude * noise_perlin(p * frequency); \ maxValue += amplitude; \ frequency *= params.lacunarity; \ amplitude *= params.persistence; \ } \ return value / maxValue; \ } NOISE_PERLIN(vec2) NOISE_PERLIN(vec3) NOISE_PERLIN(vec4) #endif uniform float frequency; // 1.; 1.; 100.; 0.01 | Base frequency multiplier uniform float amplitude; // 1.; 1.; 100.; 0.01 | Base amplitude multiplier uniform int octaves; // 4; 1; 12; 1 | Number of octaves uniform float lacunarity; // 2.; 0.; 100.; 0.01 | Frequency multiplier per octave uniform float persistence; // 0.5; 0.; 100.; 0.01 | Amplitude multiplier per octave (same as 'gain' in some functions) uniform float offset; // 0.; 0.; 100.; 0.01 | For ridge noise void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = fragCoord / iResolution.xy; NoiseParams nparam = defaultNoiseParams(); nparam.frequency = frequency; nparam.amplitude = amplitude; nparam.octaves = octaves; nparam.lacunarity = lacunarity; nparam.persistence = persistence; nparam.offset = offset; nparam.seed = iSeed; float perlin = noise_perlin(uv, nparam); fragColor = vec4(perlin, perlin, perlin, 1.); }

Procedural noise is the secret ingredient in half the interesting ComfyUI work out there - cloud masks, water distortion, texture displacement, that subtle organic wobble on otherwise clean renders. But the usual path is "generate noise with a random node, then blur it until it looks smooth," which is a terrible way to build real Perlin noise. This node just generates it, properly, with the standard fBm controls (octaves, lacunarity, persistence) and a seed, in a single GPU pass. If you've ever wanted "texture-generator, but correct," this is it.

How it works

It's a fragment shader implementing classic Perlin noise, and it deserves the word "classic" - gradient noise over a lattice, with a fractional Brownian motion stack on top. The inputs map directly to the standard fBm vocabulary:

  • frequency (default 1) - the base scale of the noise features.
  • amplitude (default 1) - how strong the first octave is.
  • octaves (default 4, max 12) - how many layers of noise get summed.
  • lacunarity (default 2) - the frequency multiplier per octave.
  • persistence (default 0.5) - the amplitude multiplier per octave (the "gain" in some noise libraries).
  • offset - a shift control on the noise field.
  • seed - drives the iSeed value the shader reads, so you can reproduce a specific noise field.

There's no image input - this is a pure generator producing a grayscale image at iRes (default 512×512). Outputs are the pack's RGBA, RGB, and MASK; the grayscale noise is easiest to work with via the MASK or RGB output, wired into compositing, masking, or as a distortion map for a warp node.

Install

One-time pack install. Via ComfyUI Manager, search Jovi_GLSL; or:

cd ComfyUI/custom_nodes
git clone https://github.com/Amorano/Jovi_GLSL.git
cd Jovi_GLSL
pip install -r requirements.txt

Restart ComfyUI. Standard pack deps (PyOpenGL, glfw, opencv-contrib-python, cozy_comfyui from git), needs an OpenGL context. No models, no keys. Under JOVI_GLSL 🌈 → NOISE, alongside Simplex and Worley.

Where people get tripped up

The seed tooltip in the current build says "Number of frames to generate. 0 (continuous mode)…" - that's a copy-paste from the batch control on other nodes; seed is the noise seed, plain and simple. And if you're feeding this into a warp or displacement step, start with octaves around 2–3 rather than the default 4; fewer octaves give smoother, more readable flow while 4+ gets busy fast. One more: the noise updates when you change the seed, so don't be surprised that "randomness" is fully deterministic and reproducible - that's the point.

CategoryJOV_GL 🌈/NOISE

Inputs (9)

NameTypeDefaultDescription
frequencyoptFLOAT1.001–100Base frequency multiplier
amplitudeoptFLOAT1.001–100Base amplitude multiplier
octavesoptINT41–12Number of octaves
lacunarityoptFLOAT2.000–100Frequency multiplier per octave
persistenceoptFLOAT0.500–100Amplitude multiplier per octave (same as 'gain' in some functions)
offsetoptFLOAT0.000–100For ridge noise
iResoptVEC2INT512,512Width and Height as a Vector2 Integer (x, y)
seedoptINT00–9223372036854776000Number of frames to generate. 0 (continuous mode) means continue from the last queue generating the next single frame based on iFrameRate.
FRAGMENToptSTRING// name: NOISE PERLIN // desc: Classic Perlin noise // category: NOISE // control: res, seed #ifndef LIB_NOISE_PARAMS #define LIB_NOISE_PARAMS #ifndef MAX_OCTAVES #define MAX_OCTAVES 16 #endif struct NoiseParams { float frequency; // Base frequency multiplier float amplitude; // Base amplitude multiplier int octaves; // Number of octaves float lacunarity; // Frequency multiplier per octave float persistence; // Amplitude multiplier per octave (same as 'gain' in some functions) float offset; // For ridge noise int seed; // Seed }; NoiseParams defaultNoiseParams() { NoiseParams params; params.frequency = 1.0; params.amplitude = 1.0; params.octaves = 4; params.lacunarity = 2.0; params.persistence = 0.5; params.offset = 1.0; params.seed = 0; return params; } #endif #ifndef LIB_NOISE_PERLIN #define LIB_NOISE_PERLIN #ifndef LIB_NOISE_RAND #define LIB_NOISE_RAND //------------------------------------------------------------------------------ // RANDOM VALUE GENERATORS // These functions generate pseudo-random values using different input dimensions //------------------------------------------------------------------------------ float noise_rand(float x) { return fract(sin(x * 12.9898) * 43758.5453); } float noise_rand(vec2 co) { return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453123); } float noise_rand(vec3 co) { return fract(sin(dot(co, vec3(12.9898, 78.233, 45.678))) * 43758.5453123); } float noise_rand(vec4 co) { return fract(sin(dot(co, vec4(12.9898, 78.233, 45.678, 94.673))) * 43758.5453123); } #endif //------------------------------------------------------------------------------ // NOISE PERLIN //------------------------------------------------------------------------------ // Generate 2D Perlin noise float noise_perlin(vec2 p) { vec2 i = floor(p); vec2 f = fract(p); vec2 u = smoothstep(0.,1.,f); float a = noise_rand(i); float b = noise_rand(i + vec2(1.0, 0.0)); float c = noise_rand(i + vec2(0.0, 1.0)); float d = noise_rand(i + vec2(1.0, 1.0)); return mix(mix(a, b, u.x), mix(c, d, u.x), u.y); } // 3D Perlin Noise function float noise_perlin(vec3 p) { vec3 i = floor(p); vec3 f = fract(p); vec3 u = smoothstep(0.,1.,f); float a = noise_rand(i); float b = noise_rand(i + vec3(1.0, 0.0, 0.0)); float c = noise_rand(i + vec3(0.0, 1.0, 0.0)); float d = noise_rand(i + vec3(1.0, 1.0, 0.0)); float e = noise_rand(i + vec3(0.0, 0.0, 1.0)); float f0 = noise_rand(i + vec3(1.0, 0.0, 1.0)); float g0 = noise_rand(i + vec3(0.0, 1.0, 1.0)); float h0 = noise_rand(i + vec3(1.0, 1.0, 1.0)); return mix(mix(mix(a, b, u.x), mix(c, d, u.x), u.y), mix(mix(e, f0, u.x), mix(g0, h0, u.x), u.y), u.z); } // 4D Perlin Noise function float noise_perlin(vec4 p) { vec4 i = floor(p); vec4 f = fract(p); vec4 u = smoothstep(0.,1.,f); float a = noise_rand(i); float b = noise_rand(i + vec4(1.0, 0.0, 0.0, 0.0)); float c = noise_rand(i + vec4(0.0, 1.0, 0.0, 0.0)); float d = noise_rand(i + vec4(1.0, 1.0, 0.0, 0.0)); float e = noise_rand(i + vec4(0.0, 0.0, 1.0, 0.0)); float f0 = noise_rand(i + vec4(1.0, 0.0, 1.0, 0.0)); float g0 = noise_rand(i + vec4(0.0, 1.0, 1.0, 0.0)); float h0 = noise_rand(i + vec4(1.0, 1.0, 1.0, 0.0)); float i1 = noise_rand(i + vec4(0.0, 0.0, 0.0, 1.0)); float j1 = noise_rand(i + vec4(1.0, 0.0, 0.0, 1.0)); float k1 = noise_rand(i + vec4(0.0, 1.0, 0.0, 1.0)); float l1 = noise_rand(i + vec4(1.0, 1.0, 0.0, 1.0)); float m1 = noise_rand(i + vec4(0.0, 0.0, 1.0, 1.0)); float n1 = noise_rand(i + vec4(1.0, 0.0, 1.0, 1.0)); float o1 = noise_rand(i + vec4(0.0, 1.0, 1.0, 1.0)); float p1 = noise_rand(i + vec4(1.0, 1.0, 1.0, 1.0)); return mix(mix(mix(a, b, u.x), mix(c, d, u.x), u.y), mix(mix(e, f0, u.x), mix(g0, h0, u.x), u.y), u.z); } //------------------------------------------------------------------------------ // PARAMETERS //------------------------------------------------------------------------------ #define NOISE_PERLIN(vec_type) float noise_perlin(vec_type p, NoiseParams params) { \ float value = 0.0; \ float frequency = params.frequency; \ float amplitude = params.amplitude; \ float maxValue = 0.0; \ for(int i = 0; i < min(params.octaves, MAX_OCTAVES); i++) { \ value += amplitude * noise_perlin(p * frequency); \ maxValue += amplitude; \ frequency *= params.lacunarity; \ amplitude *= params.persistence; \ } \ return value / maxValue; \ } NOISE_PERLIN(vec2) NOISE_PERLIN(vec3) NOISE_PERLIN(vec4) #endif uniform float frequency; // 1.; 1.; 100.; 0.01 | Base frequency multiplier uniform float amplitude; // 1.; 1.; 100.; 0.01 | Base amplitude multiplier uniform int octaves; // 4; 1; 12; 1 | Number of octaves uniform float lacunarity; // 2.; 0.; 100.; 0.01 | Frequency multiplier per octave uniform float persistence; // 0.5; 0.; 100.; 0.01 | Amplitude multiplier per octave (same as 'gain' in some functions) uniform float offset; // 0.; 0.; 100.; 0.01 | For ridge noise void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = fragCoord / iResolution.xy; NoiseParams nparam = defaultNoiseParams(); nparam.frequency = frequency; nparam.amplitude = amplitude; nparam.octaves = octaves; nparam.lacunarity = lacunarity; nparam.persistence = persistence; nparam.offset = offset; nparam.seed = iSeed; float perlin = noise_perlin(uv, nparam); fragColor = vec4(perlin, perlin, perlin, 1.); }

Outputs (3)

NameTypeDescription
RGBAIMAGEFull channel [RGBA] image. If there is an alpha, the image will be masked out with it when using this output.
RGBIMAGEThree channel [RGB] image. There will be no alpha.
MASKMASKSingle channel mask output.