skip to content
MusesΜοῦσαιdocs

playing / 08 of 15

Shaders

On Linux, phonto can run a GLSL fragment shader over every frame on its way to the screen.

Running one

shell
phonto ~/Videos/harbour.mp4 --shader ~/.config/phonto/dim.glsl

Your shader replaces phonto’s own, which just draws the frame, and runs on every display. Shaders are Linux only. On macOS, CoreAnimation draws the frames, and they never pass through a shader phonto controls.

Writing one

Shaders are GLSL ES 3.00. Start with #version 300 es, read the frame with texture(), and write the colour to an out vec4. phonto gives the shader:

name type what it is
u_tex sampler2D The video frame.
v_uv vec2 Where this pixel falls on the frame, from 0 to 1.
u_resolution vec2 The screen’s size in pixels. Declare it only if you use it.

This one takes some of the colour out of the video and darkens it, to keep windows and text over it readable:

dim.glsl
#version 300 es
precision mediump float;
uniform sampler2D u_tex;
in vec2 v_uv;
out vec4 frag_color;

void main() {
    vec3 colour = texture(u_tex, v_uv).rgb;
    float grey = dot(colour, vec3(0.299, 0.587, 0.114));
    frag_color = vec4(mix(colour, vec3(grey), 0.4) * 0.7, 1.0);
}

The frame and the screen

v_uv runs across the video, not the screen. With fill, the default, the video overhangs the screen, and the screen’s edges fall somewhere inside 0 to 1. For an effect tied to the screen, like a vignette, work from gl_FragCoord.xy / u_resolution instead:

vignette.glsl
#version 300 es
precision mediump float;
uniform sampler2D u_tex;
uniform vec2 u_resolution;
in vec2 v_uv;
out vec4 frag_color;

void main() {
    vec2 screen = gl_FragCoord.xy / u_resolution;
    float edge = distance(screen, vec2(0.5));
    vec3 colour = texture(u_tex, v_uv).rgb;
    frag_color = vec4(colour * (1.0 - smoothstep(0.4, 0.9, edge)), 1.0);
}

Limits

A shader runs once per frame, with no framebuffers in between, so effects that need several passes, like a separable blur or bloom, aren’t possible. There’s no time uniform either. The shader sees the current frame and nothing else.

If a shader doesn’t compile, phonto exits and prints the compiler’s log.

phonto dump takes --shader too, for a still frame that matches the wallpaper.