If you write fragment shaders often, you’ll notice the same handful of inputs (time, resolution, mouse, frame count) come up again and again. This page is a practical reference for ShaderGif-style workflows, with reusable patterns you can copy into new sketches and adapt when porting code from other shader playgrounds.
What “uniforms” are (and why they matter)
A uniform is a read-only value passed into your shader from the host application (the editor / renderer). Unlike local variables, uniforms don’t change per-pixel within a single draw call: every fragment sees the same uniform values for that frame. This makes uniforms ideal for driving animation (time), adapting to the canvas (resolution / aspect ratio), and adding interaction (mouse).
ShaderGif templates may expose slightly different names depending on the runtime mode (for example GLSL 1.00 vs GLSL 3.00 templates). The patterns below focus on the most common concepts you’ll see. If your template uses different names, the logic is the same: map your available uniforms to the equivalent meaning.
Common uniform “roles” you’ll typically use
Below is a compact reference table of the inputs most shader editors provide. Treat the names as canonical patterns, not strict requirements—always check your current template’s uniform list.
| Concept | Typical name | Type | Units / range | What it’s for |
|---|---|---|---|---|
| Time | u_time | float | Seconds since start (increasing) | Animation, oscillation, procedural motion |
| Resolution | u_resolution | vec2 | Pixels: (width, height) | Aspect correction, pixel-accurate effects |
| Mouse / pointer | u_mouse | vec2 or vec4 | Often pixels; sometimes normalised | Interaction, camera control, painting |
| Frame index | u_frame | int or float | 0, 1, 2… | Deterministic stepping, loop-safe timing |
| Aspect ratio | u_ratio / derived | float | width / height | Keeping circles round, consistent composition |
| Pixel ratio (HiDPI) | u_pixelRatio (optional) | float | Often 1, 2, 3… | Crisp lines and correct pixel-step sizes |
| Date/time | u_date (optional) | vec4 | Varies by host | Seed variation, “daily” generative sketches |
Coordinate basics you’ll reuse constantly
1) Pixel coordinates vs normalised coordinates
Many effects start from the pixel position. In fragment shaders you’ll commonly see gl_FragCoord.xy, which is in pixel space. Pixel space is great for pixel-perfect grids, dithering, and effects that depend on real screen size. For shapes and composition, you usually want normalised coordinates in the range 0–1 (or centred -1..1).
// Pixel coords (origin often at bottom-left in WebGL conventions)
vec2 p = gl_FragCoord.xy;
// Normalised 0..1 (independent of canvas size)
vec2 uv = p / u_resolution;
// Centred -1..1 (nice for radial maths)
vec2 st = (p - 0.5 * u_resolution) / u_resolution.y;
Notice the use of u_resolution.y in the last line. Dividing by height is a common trick for aspect-correct space: it keeps circles circular even on wide canvases.
2) Aspect correction (the “keep circles round” pattern)
If you normalise by width and height independently, you’ll often get stretched geometry. A simple correction is to scale X by the aspect ratio, or to normalise by the smaller dimension.
// Method A: scale x by aspect ratio
float aspect = u_resolution.x / u_resolution.y;
vec2 st = (gl_FragCoord.xy / u_resolution) * 2.0 - 1.0;
st.x *= aspect;
// Method B: normalise by height (compact and common)
vec2 st2 = (gl_FragCoord.xy - 0.5 * u_resolution) / u_resolution.y;
3) Pixel size (useful for lines, grids, and anti-aliasing)
When you want a line that is “one pixel wide”, you need a notion of pixel step in your chosen coordinate space. A practical pattern is:
// One pixel step in UV space
vec2 px = 1.0 / u_resolution;
// Example: soften an edge by ~1px
float edge = smoothstep(0.0, px.x * 1.5, value);
If you’re working in an aspect-correct centred space (normalised by height), the pixel size changes slightly. In that case, compute it in the same space you’re drawing in, or keep your maths in UV and only convert once at the end.
Time patterns: smooth motion, loops, and determinism
1) Simple animation with sine/cosine
u_time is most often used with periodic functions. Sine and cosine give you smooth oscillation and are easy to art-direct.
float t = u_time;
float wobble = sin(t) * 0.25; // -0.25..0.25
float pulse = 0.5 + 0.5*sin(t); // 0..1
2) Making clean, seamless loops
GIFs and short loops look best when the first and last frames match. If your host provides a frame counter (u_frame) or a known recording FPS, you can derive a loop parameter that returns to the same value at the end.
// Loop phase 0..1 repeating every `loopSeconds`
float loopSeconds = 4.0;
float phase = fract(u_time / loopSeconds);
// Convert phase into an angle for perfect periodic motion
float a = phase * 6.28318530718; // TAU
float loopSin = sin(a);
float loopCos = cos(a);
Using phase + angle is often more reliable for loops than relying on raw u_time, especially when you want the same output at exactly phase=0 and phase=1.
3) Stepping time for stylised motion
If you want stop-motion or posterised animation, quantise time:
// 12 “ticks” per second
float ticks = 12.0;
float tStep = floor(u_time * ticks) / ticks;
This can also help when recording: the result is stable, predictable, and less sensitive to tiny timing differences between preview and export.
Mouse / pointer patterns
1) Normalising mouse input
Mouse data is frequently provided in pixels. Convert it into UV early so you can reason about it in the same space as the rest of your shader.
vec2 m = u_mouse / u_resolution; // 0..1
If your mouse uniform is already normalised (0..1), don’t divide again. A quick sanity check: if moving the pointer to the right edge produces values around 1.0, it’s probably normalised.
2) Using mouse as an attractor
A simple interactive pattern is to attract or repel a field around the mouse position:
vec2 uv = gl_FragCoord.xy / u_resolution;
vec2 m = u_mouse / u_resolution;
float d = length(uv - m);
float influence = smoothstep(0.35, 0.0, d); // 1 near mouse, 0 far away
From there you can warp coordinates, change palette parameters, or blend between two looks based on influence.
Reusable helper functions (copy/paste friendly)
These helpers show up in almost every shader codebase. Keeping them in a snippet library saves time and makes your sketches more consistent.
Hash / pseudo-random
// 1D hash to 0..1
float hash11(float p) {
p = fract(p * 0.1031);
p *= p + 33.33;
p *= p + p;
return fract(p);
}
// 2D hash to 0..1
float hash21(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * 0.1031);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
Smooth minimum (for blending SDF shapes)
// Smooth min between distances a and b
float smin(float a, float b, float k) {
float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0);
return mix(b, a, h) - k * h * (1.0 - h);
}
Rotation
mat2 rot(float a) {
float c = cos(a), s = sin(a);
return mat2(c, -s, s, c);
}
With these three utilities alone (hash, smin, rot) you can build a huge range of animated procedural scenes quickly.
Porting notes: ShaderToy-style uniforms vs “u_*” uniforms
If you’re adapting code from ShaderToy or other playgrounds, you’ll often see different uniform names. The mapping is conceptually simple:
iTime(ShaderToy) →u_time(time in seconds)iResolution(vec3) →u_resolution(vec2); ignore or emulate Z if needediMouse(vec4) →u_mouse(often vec2); if you need click state, you may need to add your own control
The biggest “gotcha” is coordinate assumptions: ShaderToy examples often use fragCoord and do their own normalisation. If your template already provides UVs, decide whether to use the template UV or compute your own from gl_FragCoord and u_resolution. Consistency matters more than which approach you pick.
Sharing and reusing code responsibly (SPDX quick tip)
Shaders are frequently shared as snippets, gists, and embedded examples. If you’re reusing code (especially when porting from community sources), it’s good practice to include a clear licence note in the header. A lightweight way to do this is to use an SPDX identifier comment, which is short, standardised, and easy to scan. You can browse the list of recognised identifiers on the SPDX licence list.
// SPDX-License-Identifier: MIT
// Short note: adapted from my own study sketch, 2025-12-30
This doesn’t replace reading the original terms, but it helps you keep your own snippets organised and reduces ambiguity when others learn from (or build on) your work.
