templates
These templates are built from the project examples so you can copy, paste, and adapt them in your own sets. Open Studio, create a shader item, then paste any fragment/vertex pair below.
Basic Fragment Starter
A clean full-screen fragment baseline you can customize quickly.
Basic Fragment Starter
A clean full-screen fragment baseline you can customize quickly.
Fragment shader
example.fraguniform float iTime;
uniform vec2 iResolution;
uniform float uGlow;
uniform float uInvert;
varying vec2 vUv;
// Rings + glow; uGlow scales the halo (0–10), uInvert flips colors.
void main() {
vec2 uv = (vUv - 0.5) * 2.0;
uv.x *= iResolution.x / iResolution.y;
float r = length(uv);
float a = atan(uv.y, uv.x);
float rings = 0.5 + 0.5 * sin(14.0 * r - iTime * 2.0);
float swirl = 0.5 + 0.5 * sin(a * 3.0 + iTime);
float glow = exp(-3.0 * r) * (uGlow / 2.5);
vec3 col = vec3(rings * swirl) * vec3(0.1, 1.0, 0.6) + glow * vec3(0.2, 0.8, 1.0);
if (uInvert > 0.5) {
col = vec3(1.0) - col;
}
gl_FragColor = vec4(col, 1.0);
}
Wave Pair (Fragment + Vertex)
A matched pair for motion-heavy visuals with explicit vertex control.
Wave Pair (Fragment + Vertex)
A matched pair for motion-heavy visuals with explicit vertex control.
Fragment shader
wave.fraguniform float iTime;
uniform vec2 iResolution;
varying vec2 vUv;
void main() {
vec2 uv = (vUv - 0.5) * 2.0;
uv.x *= iResolution.x / iResolution.y;
float r = length(uv);
float a = atan(uv.y, uv.x);
float rings = 0.5 + 0.5 * sin(16.0 * r - iTime * 2.2);
float swirl = 0.5 + 0.5 * sin(a * 4.0 + iTime * 0.9);
float glow = exp(-3.2 * r);
vec3 col = vec3(rings * swirl) * vec3(0.85, 0.15, 0.55) + glow * vec3(0.25, 0.95, 0.75);
gl_FragColor = vec4(col, 1.0);
}
Vertex shader
wave.vert// Optional vertex shader: must declare `varying vec2 vUv` if the fragment uses it.
// `iTime` is provided by the studio preview (same uniforms as fragment).
varying vec2 vUv;
uniform float iTime;
void main() {
vec2 u = uv;
u += 0.04 * vec2(sin(u.y * 12.0 + iTime * 1.7), cos(u.x * 11.0 + iTime * 1.3));
vUv = u;
gl_Position = vec4(position, 1.0);
}
RGB Trails
Color-trail style look useful for reactive, layered live sets.
RGB Trails
Color-trail style look useful for reactive, layered live sets.
Fragment shader
rgb-trails-3.fraguniform float iTime;
uniform vec2 iResolution;
uniform float uRWeight;
uniform float uGWeight;
uniform float uBWeight;
uniform float uAWeight;
uniform float uTrailTime;
varying vec2 vUv;
vec4 sourceColor(vec2 uv, float t) {
vec2 p = (uv - 0.5) * 2.0;
p.x *= iResolution.x / max(iResolution.y, 1.0);
float r = length(p);
float a = atan(p.y, p.x);
float wave = 0.5 + 0.5 * sin(10.0 * r - t * 2.4 + sin(a * 3.0 + t * 0.8));
float swirl = 0.5 + 0.5 * sin(a * 5.0 - t * 1.3 + r * 6.0);
float pulse = 0.5 + 0.5 * sin(t * 1.7 + r * 18.0);
vec3 col = vec3(
0.35 + 0.65 * wave,
0.25 + 0.75 * swirl,
0.2 + 0.8 * pulse
);
col *= 0.35 + 0.65 * exp(-1.4 * r);
return vec4(col, 1.0);
}
void main() {
float trail = max(uTrailTime, 0.0);
vec4 fresh = sourceColor(vUv, iTime);
vec4 stale = vec4(
sourceColor(vUv, iTime - trail * 1.0).r,
sourceColor(vUv, iTime - trail * 1.3).g,
sourceColor(vUv, iTime - trail * 1.7).b,
sourceColor(vUv, iTime - trail * 2.1).a
);
gl_FragColor = vec4(
mix(fresh.r, stale.r, clamp(uRWeight, 0.0, 1.0)),
mix(fresh.g, stale.g, clamp(uGWeight, 0.0, 1.0)),
mix(fresh.b, stale.b, clamp(uBWeight, 0.0, 1.0)),
mix(fresh.a, stale.a, clamp(uAWeight, 0.0, 1.0))
);
}
Vertex shader
rgb-trails-3.vertvarying vec2 vUv;
uniform float iTime;
void vv_vertShaderInit() {
vec2 st = uv * 2.0 - 1.0;
float r2 = dot(st, st);
// Keep full-screen background where the sphere does not exist.
if (r2 > 1.0) {
vUv = uv;
gl_Position = vec4(position, 1.0);
return;
}
// Map disk -> sphere (upper hemisphere), then rotate over time.
float z = sqrt(max(0.0, 1.0 - r2));
vec3 p = vec3(st.x, st.y, z);
float ay = iTime * 0.8;
float ax = iTime * 0.45;
mat3 rotY = mat3(
cos(ay), 0.0, sin(ay),
0.0, 1.0, 0.0,
-sin(ay), 0.0, cos(ay)
);
mat3 rotX = mat3(
1.0, 0.0, 0.0,
0.0, cos(ax), -sin(ax),
0.0, sin(ax), cos(ax)
);
p = rotY * rotX * p;
float perspective = 1.35 + p.z;
vec2 projected = p.xy / perspective;
// Reproject sphere coordinates to keep fragment animation coherent.
vUv = projected * 0.5 + 0.5;
gl_Position = vec4(projected, 0.0, 1.0);
}
void main() {
vv_vertShaderInit();
}
Crystal Pearl
A textured style template for softer crystalline/polished moods.
Crystal Pearl
A textured style template for softer crystalline/polished moods.
Fragment shader
crystal-pearl.fraguniform float iTime;
uniform vec2 iResolution;
uniform float uRotSpeed;
uniform float uDispAmp;
uniform float uEdgeWidth;
uniform float uPearlMix;
uniform float uGrain;
varying vec2 vUv;
const float PI = 3.14159265;
mat2 rot(float a) {
float c = cos(a), s = sin(a);
return mat2(c, s, -s, c);
}
float map(vec3 p) {
p.xz *= rot(p.y + iTime * uRotSpeed);
float displacement =
sin(p.x * 2.0 + iTime) * sin(p.y * 3.0) * sin(p.z * 2.0) * uDispAmp;
return length(p) - 1.0 + displacement;
}
vec3 calcNormal(vec3 p) {
vec2 e = vec2(0.0015, 0.0);
float dx = map(p + vec3(e.x, e.y, e.y)) - map(p - vec3(e.x, e.y, e.y));
float dy = map(p + vec3(e.y, e.x, e.y)) - map(p - vec3(e.y, e.x, e.y));
float dz = map(p + vec3(e.y, e.y, e.x)) - map(p - vec3(e.y, e.y, e.x));
return normalize(vec3(dx, dy, dz));
}
float raymarch(vec3 ro, vec3 rd, out vec3 pos, out float edge) {
float t = 0.0;
edge = 0.0;
pos = ro;
for (int i = 0; i < 64; i++) {
pos = ro + rd * t;
float d = map(pos);
edge = 1.0 - smoothstep(0.0, max(0.0005, uEdgeWidth), d);
if (d < 0.001 || t > 8.0)
break;
t += clamp(d * 0.75, 0.015, 0.2);
}
return t;
}
vec3 cosinePalette(float t, vec3 a, vec3 b, vec3 c, vec3 d) {
return a + b * cos(6.28318 * (c * t + d));
}
void main() {
vec2 fragCoord = vUv * iResolution;
vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
vec3 bg = vec3(0.95, 0.95, 0.97);
vec3 ro = vec3(0.0, 0.0, -3.0);
vec3 rd = normalize(vec3(uv, 1.2));
vec3 p;
float edge;
float t = raymarch(ro, rd, p, edge);
vec3 col = bg;
if (t < 8.0) {
vec3 N = calcNormal(p);
vec3 lightDir = normalize(vec3(-0.15, 0.95, 0.25));
float fresnel = pow(1.0 - max(dot(N, -rd), 0.0), 3.0);
vec3 baseColor = vec3(1.0);
vec3 irid =
0.5 + 0.5 * cos(6.28318 *
(vec3(0.0, 0.33, 0.67) + fresnel + p.y * 0.5));
float diff = 0.5 + 0.5 * max(dot(N, lightDir), 0.0);
vec3 spec =
pow(max(dot(reflect(rd, N), lightDir), 0.0), 32.0) * vec3(1.0);
float band1 = sin(p.y * 4.5 + p.x * 2.0 - iTime * 0.7);
float band2 = sin((p.y + p.z) * 6.0 + p.x * 1.4 + iTime * 0.5);
float flow = 0.5 + 0.5 * (0.6 * band1 + 0.4 * band2);
vec3 pearlTint = cosinePalette(
flow + fresnel * 0.35, vec3(0.96), vec3(0.05, 0.06, 0.08), vec3(1.0),
vec3(0.15, 0.45, 0.75));
col = mix(baseColor, irid, fresnel * 0.8);
col *= mix(vec3(0.97, 0.98, 1.0), pearlTint, 0.35 * clamp(uPearlMix, 0.0, 1.0));
col *= mix(0.92, 1.08, diff);
col += spec * 1.25;
col += fresnel * fresnel * irid * 0.12;
float rim = pow(1.0 - max(dot(N, -rd), 0.0), 2.5);
col += vec3(0.9, 0.95, 1.0) * rim * 0.08;
col = mix(bg, col, edge);
float halo = exp(-22.0 * abs(map(p + N * 0.02)));
col += irid * fresnel * halo * 0.05;
}
float vignette = 1.0 - dot(uv, uv) * 0.12;
col *= vignette;
float grain = fract(sin(dot(fragCoord + iTime * 37.0, vec2(12.9898, 78.233))) *
43758.5453);
col += (grain - 0.5) * uGrain;
col = clamp(col, 0.0, 1.0);
col = pow(col, vec3(0.95));
gl_FragColor = vec4(col, 1.0);
}
Multi-Window
Template foundation for synchronized multi-display output.
Multi-Window
Template foundation for synchronized multi-display output.
Fragment shader
multi-window.frag// Multi-window shaders can use `uScreenId` (1 = left, 2 = right, ...).
// Some render paths inject it automatically, but declaring it keeps the shader portable.
uniform int uScreenId;
uniform float iTime;
uniform vec2 iResolution;
uniform float uColorR;
uniform float uColorG;
uniform float uColorB;
uniform float uStripeR;
uniform float uStripeG;
uniform float uStripeB;
uniform float uMoveSpeed;
uniform float uShapeMorph; // 0.0 = circle, 1.0 = triangle
float sdEquilateralTriangle(vec2 p) {
const float k = 1.7320508;
p.x = abs(p.x) - 1.0;
p.y = p.y + 0.57735027;
if (p.x + k * p.y > 0.0) {
p = vec2(p.x - k * p.y, -k * p.x - p.y) / 2.0;
}
p.x -= clamp(p.x, -2.0, 0.0);
return -length(p) * sign(p.y);
}
void main() {
// Normalize pixel coordinates (from 0 to 1) for the LOCAL screen
vec2 uv = gl_FragCoord.xy / iResolution.xy;
// ---- MULTI-SCREEN COORDINATE SLICING ----
// Let's assume we have 2 screens side-by-side.
// We want to create one massive global UV space that spans both.
vec2 globalUv = uv;
// If this is screen 2 (the right screen), shift its X coordinate over by 1
if (uScreenId == 2) {
globalUv.x += 1.0;
}
// Scale the X coordinate down by the number of total screens (2)
// so the total width spans from 0.0 to 1.0 globally.
globalUv.x /= 2.0;
// To make a perfect circle instead of an oval, we MUST correct for the aspect ratio!
// The global aspect ratio is (2 * screenWidth) / screenHeight
float globalAspect = (2.0 * iResolution.x) / iResolution.y;
// Shift coordinates so (0,0) is the center of the global screens
globalUv -= 0.5;
// Multiply X by aspect ratio so X and Y use the exact same physical scale
globalUv.x *= globalAspect;
// Morph between a circle and an equilateral triangle in the shared global space.
float circleDist = length(globalUv) - 0.55;
float triangleDist = sdEquilateralTriangle(globalUv / 0.62) * 0.62;
float shapeDist = mix(circleDist, triangleDist, clamp(uShapeMorph, 0.0, 1.0));
// Add pulsing rings based on the morphed shape distance.
float ring = sin(shapeDist * 50.0 - uMoveSpeed);
ring = smoothstep(0.0, 0.1, ring);
vec3 baseColor = vec3(uColorR, uColorG, uColorB);
vec3 stripeColor = vec3(uStripeR, uStripeG, uStripeB);
vec3 color = baseColor;
color += stripeColor * ring;
gl_FragColor = vec4(color, 1.0);
}