NOISE WORLEY (JOV_GL)
Worley noise — the cellular 'crackle' texture, generated as a node
- iRes
- RGBA
- RGB
- MASK
Perlin and simplex give you smooth gradients. Worley - also called cellular or Voronoi noise - gives you something else entirely: a field of cells, each centered on a random point, with the noise value rising as you get farther from the nearest cell center. It's the "crackle," "leather," and "cell wall" texture in a hundred procedural tutorials, and it's a genuinely different kind of material than smooth noise. This node generates it directly on the GPU with the same fBm controls as the pack's other noise nodes, and a seed so you can reproduce any particular cell pattern.
How it works
The shader scatters points across space and computes, per pixel, the distance to the nearest one - that distance becomes the noise value. Crank the frequency and you get more, smaller cells; drop it and the cells grow into chunky organic plates. The fBm stack then layers these at different scales:
- frequency (default 1) - base cell density.
- amplitude (default 1) - strength of the first layer.
- octaves (default 4, max 12) - stacked cell layers.
- lacunarity (default 2) - frequency multiplier per octave.
- persistence (default 0.5) - amplitude multiplier per octave.
- offset - shifts the field.
- seed - drives
iSeedfor reproducible cell layouts.
Pure generator, no image input, output at iRes (default 512×512). Outputs are the pack's RGBA, RGB, and MASK.
When you'd actually reach for it
Worley is a material node, not a soft-mask node. Use it for: organic camouflage and mottling, crackle/leather detail as a bump or mask, and edge-detection-friendly "cell boundary" patterns when you invert it and grab the walls between cells. It also pairs beautifully with the pack's NORMAL node - feed a Worley field in and you get a cellular normal map for cheap, bumpy materials. If you want smooth clouds, use Perlin or Simplex; if you want structure, use this one.
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.
Gotchas
Same stale tooltip on seed as the other noise nodes - it's the seed, not a frame counter. And one thing that catches people: at default settings Worley reads very dark because most of the image sits at low distance values; raising amplitude and adjusting the range is usually the first move. The output is deterministic per seed, so once you find a cell layout you like, it stays put.
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 WORLEY // desc: Worley noise with the best // 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_WORLEY #define LIB_NOISE_WORLEY #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 WORLEY //------------------------------------------------------------------------------ // 2D Worley noise function (Cellular noise) float noise_worley(vec2 p, int num_cells) { vec2 i = floor(p); vec2 f = fract(p); float d = 1.0; // Initial distance (for min distance to feature points) for (int x = -num_cells; x <= num_cells; ++x) { for (int y = -num_cells; y <= num_cells; ++y) { vec2 cell = vec2(float(x), float(y)); vec2 point = cell + vec2(noise_rand(i + cell), noise_rand(i + cell + vec2(42.0, 17.0))); vec2 offset = point - f; float len = length(offset); d = min(d, len); } } return d; } // 3D Worley noise function (Cellular noise) float noise_worley(vec3 p, int num_cells) { vec3 i = floor(p); vec3 f = fract(p); float d = 1.0; // Initial distance (for min distance to feature points) for (int x = -num_cells; x <= num_cells; ++x) { for (int y = -num_cells; y <= num_cells; ++y) { for (int z = -num_cells; z <= num_cells; ++z) { vec3 cell = vec3(float(x), float(y), float(z)); vec3 point = cell + vec3(noise_rand(i + cell), noise_rand(i + cell + vec3(42.0, 17.0, 23.0)), noise_rand(i + cell + vec3(23.0, 31.0, 51.0))); vec3 offset = point - f; float len = length(offset); d = min(d, len); } } } return d; } // 4D Worley Noise function float noise_worley(vec4 p, int num_cells) { // Grid cell dimensions float cell_size = 1.0 / float(num_cells); // Compute cell coordinates vec4 cell_coords = floor(p / cell_size); // Compute the local position within the cell vec4 local_pos = fract(p / cell_size); float min_dist = 1.0; // Loop over the neighboring cells for (int x = -1; x <= 1; ++x) { for (int y = -1; y <= 1; ++y) { for (int z = -1; z <= 1; ++z) { for (int w = -1; w <= 1; ++w) { vec4 neighbor_cell = vec4(x, y, z, w); vec4 neighbor_coords = cell_coords + neighbor_cell; // Randomize the position within the neighboring cell vec4 random_offset = vec4(fract(sin(dot(neighbor_coords, vec4(12.9898, 78.233, 37.719, 4.581))) * 43758.5453)); // Compute the distance to the random point in the neighboring cell vec4 offset_pos = neighbor_cell * cell_size + random_offset; vec4 diff = p - offset_pos; float dist = length(diff); // Update the minimum distance min_dist = min(min_dist, dist); } } } } return min_dist; } #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 worley = noise_worley(uv, nparam); fragColor = vec4(worley, worley, worley, 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. |