Nodes/ComfyUI/GLSL Shader
ComfyUI Node Runs on cloud

GLSL Shader

A mini Shadertoy living inside your ComfyUI graph

By Comfy-Org·Created 4 years ago·Updated about 10 hours ago· 130,663
GLSL Shader
  • images
  • floats
  • ints
  • bools
  • curves
  • IMAGE0
  • IMAGE1
  • IMAGE2
  • IMAGE3
fragment_shader#version 300 es precision highp float; uniform sampler2D u_image0; uniform vec2 u_resolution; in vec2 v_texCoord; layout(location = 0) out vec4 fragColor0; void main() { fragColor0 = texture(u_image0, v_texCoord); }
size_mode

The whole image-editing toolbox, in one node

Every custom effect you've ever wanted - a film-grade color grade, a proper vignette, a Gaussian blur that doesn't make you install a pack, per-channel tone curves, depth-based bokeh - can be written as a fragment shader, and this node runs it on your GPU. It's ComfyUI core's answer to the post-processing packs like WAS Node Suite and Impact Pack that everyone historically installed for this stuff: instead of one node per effect, you get an open-ended one that does any effect you can express in GLSL. It landed in core in February 2026 and is still marked experimental, so expect the API to shift under you - but it's real, and it works.

You'll reach for it after VAE decode, before the Save node, or anywhere you'd drop an Image Filter in an img2img or upscaling pass. If you've ever poked at Shadertoy, this is that, wired into your graph instead of a browser tab.

How it works

The node renders your fragment shader over a fullscreen triangle (the vertex shader is fixed; you only write the fragment stage). A fragment shader runs once per output pixel, so you're writing a tiny program that, given a pixel's texture coordinate, decides its color. The fun bit: the same shader runs twice. In your browser it runs live as WebGL 2.0, giving you a real-time preview as you type; when you execute the workflow, the backend spins up a headless OpenGL ES context (via EGL/ANGLE, with a Vulkan fallback for machines with no display server) and renders the real frames. Both environments require GLSL ES 3.00 - that's the compatibility denominator, and it's why the syntax is what it is.

The inputs that matter

The one that matters is the first: fragment_shader, the shader source. This is the default - a passthrough that outputs the image unchanged, and your starting point:

#version 300 es
precision highp float;

uniform sampler2D u_image0;
uniform vec2 u_resolution;

in vec2 v_texCoord;
layout(location = 0) out vec4 fragColor0;

void main() {
    fragColor0 = texture(u_image0, v_texCoord);
}

Everything else is autogrow - click to add more. The mapping is fixed:

  • imagesu_image0u_image4 (sampler2D). Up to 5 inputs, and no, they don't need to match in size - the node scales them into the framebuffer.
  • floatsu_float0u_float19. Your knobs: mix factors, blur radius, strength.
  • intsu_int0u_int19. Great for mode switches (blend mode, filter type).
  • boolsu_bool0u_bool9. Feature toggles.
  • curvesu_curve0u_curve3, 1D LUTs from the core Curve Editor utility node. Sample one with texture(u_curve0, vec2(clamp(x, 0.0, 1.0), 0.5)).r. This is your interactive tone curve - draw the grade, don't hardcode it.
  • u_resolution is always available, and size_mode decides the output size: from_input (first input's dimensions) or custom (manual width/height, up to 16384).

Outputs and multi-pass

You get four IMAGE outputs (IMAGE0IMAGE3), declared as fragColor0fragColor3 at layout locations 0–3 - that's MRT, multiple render targets. The node detects which you actually use; unused ones come back black. A neat pattern: split an image into R, G, B and Bokeh mask in one render, then composite them downstream.

For separable effects like a Gaussian blur, add #pragma passes 2 on the second line and use the u_pass uniform to branch (pass 0 = horizontal, pass 1 = vertical). Each pass reads the previous one's output through u_image0 automatically.

Where people get burned

The big three, from the source and the docs:

  1. Old GLSL won't compile. No gl_FragColor, no varying/attribute. GLSL ES 3.00 only. Compile errors surface as a RuntimeError with the GLSL info log in your console - not in a pretty dialog, so keep the console open.
  2. u_resolution is the output size, not your input's. If they differ, 1.0 / u_resolution gives wrong texel steps. Use textureSize(u_image0, 0) to step one input pixel.
  3. Preview vs. backend drift. Your browser preview is WebGL 2.0; the backend is GLES 3.0 via ANGLE. Keep to the shared subset (that's the whole reason it's ES 3.00) or the two won't match. On a headless box with broken GPU drivers, the backend error lists every EGL strategy it tried before giving up.

It ships with ComfyUI core - no install, no Manager, no model files to fetch. It's a tool for people who think in pixels, and honestly, once you've dialed in one shader you'll never look at the WAS Node Suite the same way again.

Categoryimage/shader

Inputs (7)

NameTypeDefaultDescription
fragment_shaderSTRING#version 300 es precision highp float; uniform sampler2D u_image0; uniform vec2 u_resolution; in vec2 v_texCoord; layout(location = 0) out vec4 fragColor0; void main() { fragColor0 = texture(u_image0, v_texCoord); } GLSL fragment shader source code (GLSL ES 3.00 / WebGL 2.0 compatible)
size_modeCOMBOOutput size: 'from_input' uses first input image dimensions, 'custom' allows manual size
imagesCOMFY_AUTOGROW_V3Images are available as u_image0-4 (sampler2D) in the shader code
floatsCOMFY_AUTOGROW_V3Floats are available as u_float0-19 in the shader code
intsCOMFY_AUTOGROW_V3Ints are available as u_int0-19 in the shader code
boolsCOMFY_AUTOGROW_V3Booleans are available as u_bool0-9 (bool) in the shader code
curvesCOMFY_AUTOGROW_V3Curves are available as u_curve0-3 (sampler2D, 1D LUT) in the shader code. Sample with texture(u_curve0, vec2(x, 0.5)).r

Outputs (4)

NameTypeDescription
IMAGE0IMAGEAvailable via layout(location = 0) out vec4 fragColor0 in the shader code
IMAGE1IMAGEAvailable via layout(location = 1) out vec4 fragColor1 in the shader code
IMAGE2IMAGEAvailable via layout(location = 2) out vec4 fragColor2 in the shader code
IMAGE3IMAGEAvailable via layout(location = 3) out vec4 fragColor3 in the shader code