GRAYSCALE (JOV_GL)
Grayscale with the weights you control — NTSC weights by default, Rec. 709 if you want
- image
- convert
- RGBA
- RGB
- MASK
Desaturating an image is the easiest thing in the world right up until you care what the result looks like. Average the channels and you get muddy grays where the image looked colorful. The reason is that plain averaging weights red, green, and blue equally, when human vision doesn't - green is brightest to us, blue the dimmest. This node is the "do it right" version, with a single VEC3 input that tells it how much each channel contributes. The default is the classic NTSC set - 0.299, 0.587, 0.114 - and you can swap in whatever weights your pipeline needs.
How it works
For each pixel it computes dot(rgb, convert) - the RGB vector dotted against your weight vector - and writes the resulting scalar to all three channels, preserving the input's alpha. It's a one-line fragment shader, which is the whole beauty of it: GPU-fast, deterministic, and trivially easy to reason about. There are no modes, no sliders, no curve controls. Just weights.
The convert VEC3 defaults to NTSC weights, and the field's tooltip says "Scalar for each channel." If you want Rec. 709 luma instead - the standard for modern video - use 0.2126, 0.7152, 0.0722. The difference is subtle on most images and obvious on saturated reds and blues.
The inputs
- image - RGB, RGBA, or MASK input. If you feed it a mask you get a gray mask back.
- convert - the three channel weights, default
(0.299, 0.587, 0.114).
Outputs are the pack's trio: RGBA, RGB, and MASK. The MASK output is quietly the most useful one - a well-weighted grayscale is a great luma mask for compositing, so this node doubles as a "luminance selection" tool.
Install
One install covers the pack. 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. Dependencies: PyOpenGL, glfw, opencv-contrib-python, cozy_comfyui from git. Needs an OpenGL context at runtime. No models, no keys. Under JOVI_GLSL 🌈 → COLOR.
Gotchas
Weights must sum to roughly 1.0 or your grayscale will get brighter or darker than the source - set all three to 1 and you've accidentally made a highlight detector. If your "grayscale" looks off, that's the first thing to check. And one nice property worth using: keep the alpha and this node is a perfectly good way to strip color from an RGBA image without losing your mask.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| imageopt | IMAGE | MASK, RGB or RGBA | |
| convertopt | VEC3 | 0.299,0.587,0.114 | Scalar for each channel |
| FRAGMENTopt | STRING | // name: GRAYSCALE // desc: Convert input to grayscale // category: COLOR // default grayscale using NTSC conversion weights uniform sampler2D image; // | MASK, RGB or RGBA uniform vec3 convert; // 0.299, 0.587, 0.114; 0; 1; 0.01 | Scalar for each channel void mainImage( out vec4 fragColor, vec2 fragCoord ) { vec2 uv = fragCoord / iResolution.xy; vec4 color = texture(image, uv); vec3 gray = vec3(dot(color.rgb, convert)); fragColor = vec4(gray, color.a); } | — |
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. |