NOISE PERLIN (JOV_GL)
Perlin noise as a node — with octaves, a seed, and GPU speed
- iRes
- RGBA
- RGB
- MASK
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
iSeedvalue 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.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| frequencyopt | FLOAT | 1.001–100 | Base frequency multiplier |
| amplitudeopt | FLOAT | 1.001–100 | Base amplitude multiplier |
| octavesopt | INT | 41–12 | Number of octaves |
| lacunarityopt | FLOAT | 2.000–100 | Frequency multiplier per octave |
| persistenceopt | FLOAT | 0.500–100 | Amplitude multiplier per octave (same as 'gain' in some functions) |
| offsetopt | FLOAT | 0.000–100 | For ridge noise |
| iResopt | VEC2INT | 512,512 | Width and Height as a Vector2 Integer (x, y) |
| seedopt | INT | 00–9223372036854776000 | Number of frames to generate. 0 (continuous mode) means continue from the last queue generating the next single frame based on iFrameRate. |
| FRAGMENTopt | STRING | // 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)
| Name | Type | Description |
|---|---|---|
| RGBA | IMAGE | Full channel [RGBA] image. If there is an alpha, the image will be masked out with it when using this output. |
| RGB | IMAGE | Three channel [RGB] image. There will be no alpha. |
| MASK | MASK | Single channel mask output. |