home/ modules/ glsl-debugging-and-recovery-in-the-rig

GLSL debugging and live recovery: gotchas, error reading, and the panic shader

  • learner can port a Shadertoy shader to glslViewer by replacing all incompatible uniform names and the entry-point signature
  • learner can identify and fix GLSL type-system and syntax errors — int/float mixing, integer division truncation, wrong vector constructor arity, undeclared uniforms, uniform-bounded loops on ES 1.00, mismatched #version syntax, and missing main()/gl_FragColor — from the compiler's error line before a set
  • learner can diagnose a black, blown-out, or visually-degraded frame by tracing the likely cause (NaN from undefined math, clamped channel, unbounded/mediump time precision loss, feedback gain ≥ 1, an unfilled feedback buffer, or step-edge aliasing) and apply the targeted fix (smoothstep+fwidth, highp/bounded time, #ifdef guard, or gain < 1)
  • learner can recover from any GLSL compile or runtime failure mid-set using the error-line/bisect procedure and the canonical panic shader

Port and repair a deliberately broken glslViewer shader, then recover from it live. The shader ships with every fault class seeded at once: Shadertoy uniforms and entry-point (iTime/iResolution/iMouse/mainImage) that must be replaced with rig names and a u_mouse pixel-to-0..1 normalization; a missing precision block plus mediump banding in large u_time / fine gradients; the full strict-typing set — an int/float mixed expression, an integer-division truncation, a wrong-arity vector constructor, an undeclared uniform, a for-loop bounded by a uniform on ES 1.00, mixed #version syntax, and an absent void main()/gl_FragColor; a hard step() edge that aliases; and the runtime failures — a NaN-producing pow()/normalize call that renders black, a feedback pass whose render code lacks its #ifdef guard so the buffer never fills, and a feedback gain ≥ 1 that blows to white. Diagnose and fix each fault reading only the compiler error line (bisecting to a known-good body when the message is ambiguous) and the visual frame — relying on the fact that glslViewer keeps the last good frame while you work — replacing the step edge with smoothstep+fwidth and bounding u_time so precision holds over a long set. Confirm the rig has no audio bridge, so all motion is driven by u_time, then swap in the canonical cosine-palette panic shader from memory in under thirty seconds: it must compile cleanly, define main()/gl_FragColor, output a clamped colour, and loop correctly over unbounded u_time.

Live visual coding with glslViewer in this rig means one thing above all else: you are one bad save away from a compile error, and the audience sees the last good frame while you race to fix it. That safety net is the first thing to internalise — glslViewer never swaps in a broken shader — and the second thing is the repair workflow: read the line number, fix the named token, and bisect if the message is unclear. This module builds the reflex for both halves: the preventive knowledge that keeps you from landing in trouble, and the recovery procedures that get you out when you do.

The preventive layer covers two clusters of failure. The first is the rig-incompatibility cluster: Shadertoy shaders look runnable but every uniform name is wrong (iTimeu_time, iResolutionu_resolution, iMouseu_mouse, mainImagemain() writing gl_FragColor), the mouse uniform is in pixels rather than 0..1, and the rig has no audio bridge — a.fft simply does not exist, so any reactivity must come from u_time. The second is the GLSL type-system cluster: the compiler is strict in ways JavaScript is not — every float literal needs a decimal point, integers cannot appear in float expressions without a cast, integer division silently truncates, vector constructors require the exact right number of components, uniforms must be declared before use, every shader must assign gl_FragColor from a void main(), and #version syntax must be internally consistent.

The runtime failure layer is about pixels that compile but look wrong: NaN from pow(negative, x), 1.0/0.0, or normalize(vec3(0)) spreads black clusters; feedback buffers without their #ifdef guard never fill; gain ≥ 1 in a double-buffer loop saturates to white within frames; unbounded u_time in float32 loses precision over minutes, creating jitter in periodic animations; and hard step() edges alias while smoothstep with fwidth is the per-pixel-correct alternative.

The arc runs from gotcha inventory to drill to live repair. Work through each failure category on a scaffold shader: first introduce and fix the Shadertoy porting errors on a known-good template, then mutate the type system one error at a time and read the compiler messages, then exercise the runtime failures (feedback, NaN, precision) and read the frame. The L5 atoms arrive in the second half: practice the error-line/bisect procedure on an ambiguous message, then memorise and type the panic shader from scratch — not copy-paste, from memory — until it takes under thirty seconds. The capstone is a gauntlet that presents all five fault classes at once: you diagnose from error messages and visual evidence alone, fix each in situ, then execute the panic-shader swap as if the set were live.

Supporting atoms for grounding: glsl-standard-uniforms is the authoritative list of what glslViewer provides without declaration; glsl-float-decimal-requirement and glsl-coordinate-normalization are from the prerequisite module but recur constantly in debugging sessions — having them here as supporting gives you fast look-up context without re-gating the capstone on material already drilled.

Atoms in this module

Required — these gate the capstone

Shadertoy uniform names (`iTime`, `iResolution`, etc.) are undefined in glslViewer and must be replaced
Misconception L1 Foundations G
glslViewer's `u_mouse` is in pixels, not 0..1 — normalize by `u_resolution` before use
Misconception L1 Foundations G
glslViewer in this rig has no audio input — `a.fft` does not exist and reactivity must use `u_time`
Fact L1 Foundations GJ
GLSL ES requires a precision mediump float block at the top of every shader
Fact L1 Foundations G
GLSL `mediump` precision makes large `u_time` and fine gradients visibly step — use `highp`
Principle L1 Foundations G
GLSL does not implicitly convert `int` to `float` — mixing them in an expression is a compile error
Misconception L1 Foundations G
GLSL integer division truncates: `1/2` is `0`, not `0.5`
Misconception L1 Foundations G
GLSL vector constructors are exact — you cannot assign a `vec2` to a `vec3`, and swizzles must exist
Concept L1 Foundations G
On GL ES 1.00, `for` loop bounds must be compile-time constants, not uniforms
Fact L1 Foundations G
Unbounded `u_time` loses float precision over minutes, causing visible jitter in periodic motion
Fact L1 Foundations G
A hard `step` edge aliases in GLSL — use `smoothstep` with a `fwidth` width for anti-aliasing
Principle L1 Foundations G
GLSL operations with undefined results produce NaN/Inf that spread and turn pixels black
Principle L1 Foundations G
A GLSL double-buffer feedback pass with gain ≥ 1 saturates to white within a few frames
Principle L1 Foundations G
A glslViewer feedback buffer only fills if its render code is wrapped in the matching `#ifdef`
Fact L1 Foundations G
GLSL syntax must match the `#version` directive — mixing ES 1.00 and 3.00 syntax fails to compile
Fact L1 Foundations G
glslViewer keeps the last successfully-compiled shader on screen when a save fails to compile
Concept L5 Voice G
Debug a GLSL compile error by reading its line number, then bisecting back to a known-good body
Procedure L5 Voice G
Shadertoy shaders use different uniform names from glslViewer and will not run as-is
Misconception L5 Voice G
GLSL requires explicit float literals and exact vector sizes; int/float mixing is a compile error
Concept L5 Voice G
Every GLSL fragment shader must define void main() and assign gl_FragColor at least once
Fact L5 Voice G
GLSL uniforms must be declared before use; only glslViewer-provided names are available without declaration
Fact L5 Voice G
A GLSL shader that compiles but outputs values outside 0..1 or produces NaN renders black or blown-out
Misconception L5 Voice G
The terminal-rig glslViewer has no audio-reactivity bridge; use u_time for motion instead
Fact L5 Voice GJ
The canonical GLSL panic shader is the hello.frag cosine palette driven by u_time with a clamped output
Procedure L5 Voice G

Supporting — enrichment, not gating

The Book of Shaders defines three standard uniforms: u_resolution, u_mouse, and u_time
Fact L1 Foundations G
GLSL requires a decimal point on all floating-point literals
Fact L2 First instrument G
Dividing gl_FragCoord by u_resolution maps pixel coordinates to the [0,1] UV range
Procedure L1 Foundations G