ComfyUI Node

Custom Shader

Run any GLSL fragment shader on your images, GPU-speed

By Quasimondo·Created 2 years ago·Updated about a year ago· 15
Custom Shader
  • texture_0
  • mask_0
  • texture_1
  • mask_1
  • texture_2
  • mask_2
  • texture_3
  • mask_3
  • images
  • mask
  • help
frames1
output_size
width1024
height1024
v00.0000
v10.0000
v20.0000
v30.0000
v40.0000
v50.0000
fragment_codevoid main() { vec2 uv_text = gl_FragCoord.xy/u_resolution.xy; gl_FragColor = texture(texture_0,uv_text); }
sampling
border
timefactor1.000
shadertoyfalse

If you've ever wanted a post-processing effect that no ComfyUI node offers - a kaleidoscope, a pixel-sorting vibe, chromatic aberration done properly, a displacement wobble - and you know your way around GLSL, Custom Shader is the escape hatch. You paste in a fragment shader, wire in up to four textures, and it renders the result on your GPU. The author's own line in the README - "often quicker to write my own solution than finding an existing one" - basically is this node.

How it works

Under the hood it uses moderngl to spin up an offscreen OpenGL context (EGL backend), compiles your fragment shader, draws a fullscreen quad, and reads the framebuffer back into a torch tensor. Your shader gets a standard set of uniforms for free:

  • u_time (0→1 over the run, scaled by timefactor)
  • u_resolution (output size in pixels)
  • u_frame (current frame index)
  • texture_0texture_3 (whatever you connected, as sampler2Ds)
  • v0v5 (six user floats you can animate)

You write to gl_FragColor. The default fragment code is literally just "sample texture_0 and pass it through," which is the right starting point before you break it.

Two features make it more than a toy. First, the Shadertoy toggle (added June 2024) does basic translation of Shadertoy variables like iResolution into the node's uniforms, so many shaders from shadertoy.com paste in and mostly work. Second, since July 2024 the texture inputs accept image batches, so you can feed a video sequence frame-by-frame and get a per-frame shader pass - full video postprocessing on the GPU.

Masks are merged into their texture's alpha channel, and the alpha comes back out as the mask output. So you can write a shader that produces a matte and wire it straight into a mask consumer.

The inputs that matter

  • fragment_code - the shader itself. This is 90% of the node.
  • frames - how many frames to render (1 for a still, more for animation).
  • output_size - Custom (uses width/height) or match one of your connected textures.
  • v0v5 - the animatable float uniforms.
  • shadertoy - compatibility toggle for Shadertoy code.
  • sampling / border - texture filter (LINEAR/NEAREST) and wrap mode per axis (REPEAT/CLAMP).
  • texture_0texture_3 and mask_0mask_3 - the inputs.

There's also a help output (a STRING) that lists every uniform - genuinely handy when you forget the exact name of something.

Install

From the Quasimondo pack. ComfyUI Manager → search ComfyUI-QuasimondoNodes, or by hand:

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

This is one of the two nodes in the pack that actually needs moderngl, the pack's headline dependency. It also needs a working OpenGL context - the code uses the egl backend, which normally works headless on Linux but has been the main source of "Custom Shader won't initialize" reports on Windows or remote setups. If you get an OpenGL/EGL error at execution, that's the wall you've hit.

Where people get burned

  • The OpenGL wall. No context, no render. If moderngl.create_context(standalone=True, backend="egl") fails, the node fails - before your shader is even compiled. Driver issues here are a you-problem, not a shader problem.
  • Version mismatches. The node prepends #version 330, so Shadertoy code that assumes 440 or uses newer syntax will throw compile errors that appear as cryptic messages in the console.
  • Shadertoy code is not guaranteed. The toggle handles the common variables, but shaders that rely on multiple render passes or buffers (common on Shadertoy) won't run - it's a single-pass fragment shader runner.
  • Cost. High frames × large output on the CPU-readback path can get slow; the GPU does the shading, but shuttling frames back is not free.
Categoryimage/postprocessing

Inputs (23)

NameTypeDefaultDescription
framesINT11–9999
output_sizeCOMBO5 options: Custom, texture_0, texture_1, texture_2, texture_3
widthINT10241–65536
heightINT10241–65536
v0FLOAT0.0000-65536–65536
v1FLOAT0.0000-65536–65536
v2FLOAT0.0000-65536–65536
v3FLOAT0.0000-65536–65536
v4FLOAT0.0000-65536–65536
v5FLOAT0.0000-65536–65536
fragment_codeSTRINGvoid main() { vec2 uv_text = gl_FragCoord.xy/u_resolution.xy; gl_FragColor = texture(texture_0,uv_text); }
samplingCOMBO2 options: LINEAR, NEAREST
borderCOMBO4 options: REPEAT/REPEAT, CLAMP/CLAMP, CLAMP/REPEAT, REPEAT/CLAMP
timefactorFLOAT1.000-65536–65536
shadertoyBOOLEANfalse
texture_0optIMAGE
mask_0optMASK
texture_1optIMAGE
mask_1optMASK
texture_2optIMAGE
mask_2optMASK
texture_3optIMAGE
mask_3optMASK

Outputs (3)

NameTypeDescription
imagesIMAGE
maskMASK
helpSTRING