ComfyUI Node

FL Shadertoy

Paste a Shadertoy GLSL shader straight into ComfyUI

By filliptm·Created 3 years ago·Updated about 15 hours ago· 628
FL Shadertoy
  • channel_0
  • channel_1
  • channel_2
  • channel_3
  • IMAGE
width512
height512
frame_count1
fps1
source void mainImage( out vec4 fragColor, in vec2 fragCoord ) { // Normalized pixel coordinates (from 0 to 1) vec2 uv = fragCoord/iResolution.xy; // Time varying pixel color vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4)); // Output to screen fragColor = vec4(col,1.0); }

If you've ever browsed shadertoy.com and wanted to drop one of those generative GLSL shaders straight into a ComfyUI graph, FL_Shadertoy is that node, more or less literally. It renders Shadertoy-compatible fragment shaders using a real OpenGL context, exposes the same uniforms Shadertoy gives you (iResolution, iTime, iFrame), and can accept up to four images as input channels - so a shader that samples iChannel0 on shadertoy.com can sample one of your ComfyUI images the exact same way here.

It's part of Fill-Nodes' VFX section (filliptm's large grab-bag pack), alongside pixel sorting, halftone, dithering, and glitch effects - this is the most technically deep one of that group, because it's not a fixed algorithm, it's a full shader interpreter.

How it works

You paste GLSL source into source - the default value is a literal working Shadertoy example (a mainImage function that outputs a time-varying color gradient), which is a good sanity check that the node works before you paste in your own shader. The node compiles and runs it via OpenGL, rendering frame_count frames at fps, and returns them as an image batch. If your shader references iChannel0iChannel3, wire images into the matching channel_0channel_3 inputs.

The inputs and outputs that matter

  • source - your GLSL, using the standard void mainImage(out vec4 fragColor, in vec2 fragCoord) entry point Shadertoy uses. Most shaders copied straight off shadertoy.com will drop in with little to no editing, since the uniform names match.
  • width/height (default 512, up to 15360) - render resolution.
  • frame_count (default 1) and fps (default 1) - how many frames to render and at what rate; set frame_count to 1 for a single still, or higher for an animated loop feeding into a video node downstream.
  • channel_0channel_3 - optional IMAGE inputs mapped to the shader's iChannel0iChannel3 texture samplers, for shaders that composite or distort an input image rather than generating pure noise/patterns from scratch.

Output is a single IMAGE batch - one frame per frame_count step, ready for a preview, a save node, or a video compiler elsewhere in the pack.

How to install it

ComfyUI Manager: search "Fill-Nodes", install, restart. Manual:

cd ComfyUI/custom_nodes
git clone https://github.com/filliptm/ComfyUI_Fill-Nodes
pip install -r ComfyUI_Fill-Nodes/requirements.txt

The dependency that actually matters here is GLFW/OpenGL - this node opens a real (typically off-screen) OpenGL context to run the shader, unlike the pack's other image nodes which are pure CPU/numpy work. On a normal desktop install with a GPU driver present that's usually a non-issue; on a headless server or a container without a display or the right GL libraries available, that context creation is exactly the kind of thing that can fail where everything else in the pack works fine. If this specific node errors while the rest of Fill-Nodes runs, look at OpenGL/GLFW availability first, not the shader code.

Common issues & troubleshooting

Node fails to render, but everything else works. Suspect the OpenGL context creation, not your shader - GLFW needs a usable display/driver setup, which a lot of cloud or containerized environments don't provide out of the box.

Shader compiles fine on shadertoy.com but errors here. Shadertoy supports some conveniences (multiple buffer passes, audio input, cubemaps) this node likely doesn't replicate - stick to single-pass mainImage shaders using iResolution/iTime/iFrame and up to four iChannel textures for the best compatibility.

Animation renders but looks choppy or too fast/slow. That's frame_count and fps working against each other - they set total frames and playback rate independently here, not duration directly, so check the math (frame_count / fps = seconds of shader-time covered) matches what you expected.

Category🏵️Fill Nodes/VFX

Inputs (9)

NameTypeDefaultDescription
widthINT51264–15360
heightINT51264–15360
frame_countINT11–262144
fpsINT11–120
sourceSTRING void mainImage( out vec4 fragColor, in vec2 fragCoord ) { // Normalized pixel coordinates (from 0 to 1) vec2 uv = fragCoord/iResolution.xy; // Time varying pixel color vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4)); // Output to screen fragColor = vec4(col,1.0); }
channel_0optIMAGE
channel_1optIMAGE
channel_2optIMAGE
channel_3optIMAGE

Outputs (1)

NameTypeDescription
IMAGEIMAGE