FL Shadertoy
Paste a Shadertoy GLSL shader straight into ComfyUI
- channel_0
- channel_1
- channel_2
- channel_3
- IMAGE
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 iChannel0–iChannel3, wire images into the matching channel_0–channel_3 inputs.
The inputs and outputs that matter
source- your GLSL, using the standardvoid 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) andfps(default 1) - how many frames to render and at what rate; setframe_countto 1 for a single still, or higher for an animated loop feeding into a video node downstream.channel_0–channel_3- optional IMAGE inputs mapped to the shader'siChannel0–iChannel3texture 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.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| width | INT | 51264–15360 | — |
| height | INT | 51264–15360 | — |
| frame_count | INT | 11–262144 | — |
| fps | INT | 11–120 | — |
| source | STRING | 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_0opt | IMAGE | — | |
| channel_1opt | IMAGE | — | |
| channel_2opt | IMAGE | — | |
| channel_3opt | IMAGE | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |