Shadertoy
Run any GLSL shader as video inside ComfyUI
- channel_0
- channel_1
- channel_2
- channel_3
- IMAGE
ComfyUI is not a video editor, but with this node it becomes a shader player. Paste in any GLSL fragment shader written in Shadertoy's mainImage style, and it renders the thing frame by frame, handing you a normal ComfyUI IMAGE batch at the end - ready to preview, save, or push into an img2vid pipeline. If you've ever browsed shadertoy.com, you know how much raw visual mileage sits in those shader galleries. This node is how you get it into a graph, which is why it's effectively the payoff node of the whole pack.
How it works: the node wraps your fragment source in a header defining the standard Shadertoy uniforms - iResolution, iTime, iTimeDelta, iFrameRate, iFrame, iMouse, and iChannel0–iChannel3 - then compiles it against a fullscreen-triangle vertex shader and renders offscreen with PyOpenGL. Each frame it updates the uniforms from your fps and frame index, samples the optional channel textures, draws, and reads the pixels back with glReadPixels (including the vertical flip your shader expects). The default source is Shadertoy's own starter shader, the cosine-gradient thing, so the node produces output the instant you add it.
Rendering context: on a desktop with a display it uses GLFW with a hidden window; on a headless Linux box it switches to EGL automatically. Either way you need a working OpenGL driver - this is the one node in the pack that's GPU-dependent, unlike the audio analysis.
Inputs worth setting: source is the entire shader as multiline text. width/height run 64 to 16384 in steps of 8, default 512. frame_count and fps are your video timeline - and both default to 1, which is the classic trap: a fresh node renders one frame at 1 fps until you tell it otherwise. The optional channel_0–channel_3 IMAGE inputs become iChannel samplers, which is exactly how you build audio-reactive shaders: wire Audio Frame Transform (Shadertoy)'s output into a channel and your shader can texture() the spectrum.
Output: IMAGE, [frame_count, height, width, 3] - a batched tensor any video node downstream can consume.
The honest troubleshooting list:
- Headless server throwing "No EGL display connection available"? That means no working GL stack. Install Mesa (
libegl1and friends) or run somewhere with a real GPU. iMouseis always zeros. Mouse-reactive shaders will sit still. Accept it.- Readback is CPU-side, so resolution × frames multiplies fast. 512² for a few hundred frames is fine; 16384² for thousands is an excellent way to discover your GPU's thermal limits. Most shader "wallpapers" run beautifully at 1024² or less.
- Most gallery shaders work as-is, but anything needing Shadertoy's extra buffer passes (bufferA/B, multipass) won't - there's a single pass here, and channels only accept what you feed in as IMAGE inputs.
Install is shared across the pack: Manager search "ComfyUI-AudioReactor," or clone, pip install -r requirements.txt, restart - which pulls in PyOpenGL, glfw, and librosa. No models.
The take: load audio, analyze it, drop a music-reactive shader in here, and you have a generative music video without ever leaving the graph.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| width | INT | 51264–16384 | — |
| height | INT | 51264–16384 | — |
| 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 | — |