Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

shader

Runs a fragment shader from the config over the node’s box: a glow, an animated gradient, a procedural pattern. It reads no textures and takes no input. To run a shader between two pictures, use an image transition.

A band that glows in over 400 ms when pulse_on turns true:

local pulse_on = state("pulse_on", false)

local glow = shader {
    width = 200,
    height = 40,
    source = mantle.config_dir .. "/shaders/glow.frag",
    progress = pulse_on:map(function(on) return on and 1 or 0 end),
    params = { tint = { 0.54, 0.71, 0.98 } },
    animate = { progress = 400 },
}

return glow

shaders/glow.frag in the config directory:

uniform vec3 tint;

void main() {
    // Distance from the horizontal centre line, 0 at the middle, 1 at the edges.
    float edge = abs(v_uv.y - 0.5) * 2.0;
    float alpha = (1.0 - edge) * u_progress;
    fragColor = vec4(tint * alpha, alpha); // premultiplied
}

Properties

shader takes the common properties, plus:

PropertyTypeDefaultBehaviour
sourcestring|Bound""Absolute .frag path; relative is refused, "" draws nothing. Compiling, errors and reloads: the .frag file
progressnumber|Bound, [-8192, 8192]0Becomes u_progress. There is no clock uniform: animate this for motion; the wide range lets a spring overshoot
paramstable<string, number|number[]>|Bound{}Uniforms by name: a finite number for float, 2-4 numbers for vec2-vec4. Missing ones are 0. Not tweened

It has no intrinsic size: without width and height it draws nothing. opacity, transforms, shadow_* and content_blur apply to it.

The .frag file

GLSL ES 3.00 without the header. The engine prepends #version 300 es, precision highp float and the declarations below, then compiles the file as written. Error line numbers count from the file’s first line.

NameTypeWhat
v_uvin vec2Box coordinate, 0..1, top-left origin, y down
fragColorout vec4Premultiplied RGBA. The engine multiplies it by the node’s opacity afterwards
u_progressfloatThe node’s progress
u_sizevec2The node’s size in logical px
uniform float, vec2, vec3, vec4 of your ownSet from params by name, 0 when params leaves one out. A params name with no uniform is ignored; a wrong component count is padded or truncated and logged once

Write void main(). params never sets a uniform named u_* or mantle_*. A uniform the shader reads of any other type, such as an int or a sampler2D, refuses the whole shader.

EventResult
Compile or link failsLogged once, draws nothing until the file changes
A .frag under the config directory is savedThe config reloads, which recompiles it. A file elsewhere recompiles at the surface’s next pass
mantle checkPasses: it has no GPU and compiles no GLSL. The first compile is in the running shell
The shader hangs the GPUThe session hangs. It is config code, as trusted as process.run

How do I…

TaskAnswer
Fade an effect in and outBind progress to 0 or 1 and tween it with animate, as above
Loop an animationanimate = { progress = { keyframes = { 0, 1 }, duration = 2000, loops = "Infinite" } } (keyframes)
Pass a colourA vec3 or vec4 uniform, params = { tint = { r, g, b } } in 0..1
Work in pixelsv_uv * u_size is the fragment’s position in logical px
Click a shaderWrap it in a button
Round its cornersWrap it in a rect with radius and clip = "Rounded" (clip)

Gotchas

TrapFix
Draws nothing, and check passedRead mantle log for the compile error. Check the node has a size and an absolute source
The shader is staticThere is no time uniform. Animate progress
A uniform int refuses the shaderDeclare it float and pass the integer as a number
Colours glow too bright where alpha is lowfragColor is premultiplied: multiply RGB by alpha
A params change jumpsparams is not tweened. Drive the change through progress

See also: image transitions, animation, paint.

Source: vocabulary, content parsers, params, shader stage, .frag reloads.