Shader
Run a custom WGSL fragment shader — written, pasted, or AI-generated — as a node. Acts as a generator, filter, or compositor depending on how many image inputs you declare.
Category: Code Menu path: Code > Shader
Ports
Ports are dynamic — they're derived from the //! input pragmas in your source.
| Port | Type | Direction | Description |
|---|---|---|---|
in0, in1, … | imageRgba16f | input | One per //! input you declare. Sample them with textureSample(in0, samp, uv). |
out | imageRgba16f | output | The shaded result. |
- 0 inputs → generator (procedural pattern, like Noise/Gradient).
- 1 input → filter (processes the upstream image, like Blur/Levels).
- 2+ inputs → compositor (combines layers, like Merge/Mix).
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
source | string | starter generator | The shader source. Edited in the code editor in Properties (not a normal field). |
format | enum | WGSL | Shader language: WGSL (Caddis's native contract) or GLSL (Shadertoy-style). Switching swaps in that language's starter. |
Plus dynamic params — every //! param you declare becomes a node param (slider / color / vector) you can keyframe like any other. (GLSL is builtins-only in v1 — see below.)
GLSL / Shadertoy
Set Format → GLSL to write (or paste) Shadertoy-style shaders. You write a standard Shadertoy entry point:
glsl
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
fragColor = vec4(0.5 + 0.5 * cos(iTime + uv.xyx + vec3(0,2,4)), 1.0);
}The engine wraps it in a Shadertoy-compatible prelude (declaring iResolution, iTime, iFrame, iMouse), flips Y to Shadertoy's bottom-left origin, transpiles GLSL → WGSL via naga, and renders it. Don't write #version, precision, uniform declarations, or main() — those are synthesized. iResolution is the composition size, so output is resolution-independent (identical at Draft/Preview/Full).
This makes paste-a-Shadertoy work for the huge class of single-pass procedural shaders — including raymarching and fractals (they're just mainImage with a loop; nothing special is needed).
v1 limitations: builtins-only — no iChannel texture inputs, no custom //! param knobs, and no multipass (Buffer A–D). Drive everything from iTime / iResolution. A pasted shader that samples iChannel0 won't compile yet; the AI assistant (set to GLSL) can rework it to be procedural.
How It Works (WGSL)
How It Works
You write a single shade(uv) function (plus any helper functions) and a small pragma header that declares your inputs and params. The engine generates the surrounding WGSL — the texture/sampler/uniform bindings, the param struct, and the @fragment entry point — compiles your shader at runtime (cached by source hash), and runs it as a fullscreen GPU pass at the working precision (RGBA 16-bit float).
The authoring contract:
wgsl
//! input in0 label="Source"
//! param intensity float default=0.5 range=0,1 label="Intensity"
//! param tint color default=1,0,0,1 label="Tint"
fn shade(uv: vec2<f32>) -> vec4<f32> {
let src = textureSample(in0, samp, uv);
return vec4<f32>(src.rgb * params.tint.rgb * params.intensity, src.a);
}Available inside shade:
uv—vec2<f32>in[0,1], origin top-left.samp— a linearsamplerfor your input textures.in0,in1, … — the textures you declared with//! input.params.<name>— your declared params (float→f32,vec2/vec3,color→vec4<f32>).builtins.iResolution(vec2<f32>, composition size),builtins.iTime(f32, seconds),builtins.iFrame(f32).
Return straight-alpha RGBA — the engine premultiplies. Don't write @group/@binding lines, the param/builtins structs, or @fragment fn fs_main — the engine generates those (declaring them yourself is a duplicate-definition compile error).
Pragma grammar:
//! input <id> [label="..."]—<id>isin0,in1, … in order.//! param <name> <type> [default=<comma,sep>] [range=<min>,<max>] [label="..."]—<type>isfloat | vec2 | vec3 | color. Forcolor,defaultisr,g,b,a.label=must be the last field on the line.
Compile errors never crash the engine: they surface as inline markers on the failing line in the editor, and the node keeps rendering the last shader that compiled.
AI authoring (the Assistant panel)
You don't have to know WGSL. With a Shader node selected, open the Assistant panel and describe the effect — "a VHS glitch with chromatic aberration and rolling scanlines" — and the AI writes the shader into the node. Refine iteratively: "thinner scanlines", "add an Intensity knob". Generated code is compiled before it's applied; if it doesn't compile, the error is fed back automatically (a couple of retries) so you only ever get working shaders.
Two ways to power it (Assistant → Settings):
- Caddis AI — uses Caddis' service and your token balance.
- Use my own key — calls your own Claude or OpenAI account directly. No tokens are used and your prompts stay on your account. Your key is stored encrypted on your device.
Usage Examples
Basic: Procedural generator
Create a Shader node with no //! input lines. The starter source is an animated rainbow gradient driven by builtins.iTime — a self-contained generator you can drop on a layer and animate immediately.
Filter: Tint + intensity
Add //! input in0 and sample it. Wire an image (or any rendered chain) into in0; the shader processes whatever flows through. Add //! param lines to expose live controls.
Compositor: Blend two inputs
Declare in0 and in1, sample both, and mix — e.g. mix(a, b, params.amount). The node now has two image input ports.
Tips
- Typing is the primary path — the editor has line numbers, WGSL highlighting, and inline error markers. The AI is optional.
- The code editor in Properties is resizable — drag its bottom edge to give yourself more room.
builtins.iResolutionis the composition size, so coordinate math (uv * iResolution) is identical at Draft / Preview / Full quality.- Animate with
builtins.iTime(seconds) rather thaniFramefor frame-rate-independent motion. - WGSL is strongly typed: write
0.0, not0, where a float is expected, and use explicit constructors likevec3<f32>(1.0, 0.0, 0.0). - To feed a Shader's output into a field (for point/shape work), pass it through ImageSample.