Create audio-reactive GLSL visualizers for Bice-Box. Provides templates, audio uniforms (iRMSOutput, iRMSInput, iAudioTexture), coordinate patterns, and common shader functions.
Scanned 2/10/2026
Install via CLI
openskills install majiayu000/claude-skill-registry---
name: glsl-shader
description: Create audio-reactive GLSL visualizers for Bice-Box. Provides templates, audio uniforms (iRMSOutput, iRMSInput, iAudioTexture), coordinate patterns, and common shader functions.
---
# GLSL Shader Development
Create audio-reactive GLSL visualizers for Bice-Box.
## Essential Structure
- **GLSL ES 3.00** only
- **DO NOT DECLARE** `#version 300 es` or `precision` directives (system adds these automatically)
- **Main function**: `void mainImage(out vec4 fragColor, in vec2 fragCoord)`
- **File types**:
- Single-pass: `name.glsl`
- Multi-pass: `name_bufferA.glsl`, `name_image.glsl`
## Standard Uniforms (DO NOT redeclare)
```glsl
uniform vec3 iResolution; // Screen resolution (width, height, aspect)
uniform float iTime; // Shader time (seconds)
uniform vec4 iMouse; // Mouse coordinates (xy = current, zw = click)
uniform sampler2D iChannel0; // Buffer A output (in image pass)
uniform sampler2D iChannel1; // Buffer B output (in image pass)
```
## Audio Uniforms
```glsl
uniform float iRMSInput; // Real-time input audio (0.0-1.0+) - USE FOR REACTIVITY
uniform float iRMSOutput; // Real-time output audio (0.0-1.0+) - USE FOR REACTIVITY
uniform float iRMSTime; // Cumulative time - NOT for reactivity, grows with audio
uniform sampler2D iAudioTexture; // FFT/waveform data
```
### Audio Reactivity - Important Notes
**Use iRMSOutput/iRMSInput for real-time reactivity:**
```glsl
float intensity = 0.3 + 0.7 * iRMSOutput; // Pulse with audio
float scale = 1.0 + iRMSOutput * 0.5; // Scale with audio
```
**Do NOT use iRMSTime for reactivity** - it's cumulative time that grows infinitely with audio input.
## Audio Texture Sampling
The `iAudioTexture` provides two rows of data:
```glsl
// FFT (frequency spectrum) - Row 0
// u = 0.0 (bass) to 1.0 (treble), y = 0.25
float bass = texture(iAudioTexture, vec2(0.1, 0.25)).x;
float mid = texture(iAudioTexture, vec2(0.5, 0.25)).x;
float treble = texture(iAudioTexture, vec2(0.9, 0.25)).x;
// Waveform (time domain) - Row 1
// u = time position (0.0 to 1.0), y = 0.75
float waveVal = texture(iAudioTexture, vec2(0.5, 0.75)).x;
float waveValSigned = (waveVal * 2.0) - 1.0; // Convert 0-1 to -1,1
// Loop over frequencies
for(float i = 0.0; i < 1.0; i += 0.01) {
float fftMag = texture(iAudioTexture, vec2(i, 0.25)).x;
// Use fftMag for frequency-based visualization
}
```
## Common Coordinate Patterns
```glsl
// Normalized coordinates (0-1)
vec2 uv = fragCoord.xy / iResolution.xy;
// Centered coordinates (-0.5 to 0.5)
vec2 uv_centered = (fragCoord.xy - 0.5 * iResolution.xy) / iResolution.xy;
// Aspect-corrected coordinates (for perfect circles)
vec2 uv_centered = fragCoord.xy - 0.5 * iResolution.xy;
vec2 uv_aspect = uv_centered / iResolution.y;
// Polar coordinates
vec2 centered = fragCoord.xy - 0.5 * iResolution.xy;
float r = length(centered) / iResolution.y;
float theta = atan(centered.y, centered.x);
```
## Single-Pass Template
```glsl
// Optional resolution scaling (0.5 = half res for performance)
// resolution: 0.5
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Normalized coordinates
vec2 uv = fragCoord.xy / iResolution.xy;
// Audio reactivity
float audioLevel = iRMSOutput;
// Sample audio texture
float bass = texture(iAudioTexture, vec2(0.1, 0.25)).x;
// Your visualization here
vec3 col = vec3(uv.x, uv.y, audioLevel);
// Output
fragColor = vec4(col, 1.0);
}
```
## Multi-Pass Template
**Buffer A** (`effect_name_bufferA.glsl`) - Feedback/persistence:
```glsl
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord.xy / iResolution.xy;
// Read previous frame from iChannel0 (self-reference)
vec4 prev = texture(iChannel0, uv);
// Fade previous frame
prev *= 0.95;
// Add new content
float audioLevel = iRMSOutput;
vec3 newContent = vec3(0.0);
// ... your drawing code ...
// Combine
vec3 col = prev.rgb + newContent;
fragColor = vec4(col, 1.0);
}
```
**Image Pass** (`effect_name_image.glsl`) - Final output:
```glsl
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord.xy / iResolution.xy;
// Read from Buffer A via iChannel0
vec4 bufferA = texture(iChannel0, uv);
// Apply post-processing
vec3 col = bufferA.rgb;
col = pow(col, vec3(1.0/2.2)); // Gamma correction
fragColor = vec4(col, 1.0);
}
```
**JSON Configuration** (`effect_name.json`):
```json
{
"shader": "shaders/effect_name"
}
```
## Multi-Pass Setup Notes
- **Buffer A** self-references via `iChannel0` (previous frame)
- **Image pass** reads buffers via `iChannel0` (Buffer A), `iChannel1` (Buffer B), etc.
- Use for: trails, feedback, persistence, fluid simulation
- JSON uses base name without `_bufferA` or `_image` suffix
## Audio-Reactive Patterns
### Pulsing/Scaling
```glsl
float pulse = 0.5 + 0.5 * iRMSOutput;
float scale = 1.0 + iRMSOutput * 0.3;
```
### Color Modulation
```glsl
vec3 col = vec3(0.5) + 0.5 * vec3(
sin(iTime + iRMSOutput * 2.0),
sin(iTime + iRMSOutput * 3.0 + 2.0),
sin(iTime + iRMSOutput * 4.0 + 4.0)
);
```
### Frequency-Based Visualization
```glsl
// Spectrum analyzer style
for(float i = 0.0; i < 1.0; i += 0.02) {
float fft = texture(iAudioTexture, vec2(i, 0.25)).x;
if(abs(uv.x - i) < 0.01 && uv.y < fft) {
col = vec3(1.0, 0.5, 0.0);
}
}
```
### Waveform Visualization
```glsl
float wave = texture(iAudioTexture, vec2(uv.x, 0.75)).x;
wave = (wave * 2.0) - 1.0; // Convert to -1,1
float dist = abs(uv.y - 0.5 - wave * 0.3);
if(dist < 0.01) {
col = vec3(0.0, 1.0, 0.5);
}
```
## MCP Workflow
**Workflow for creating/updating shaders:**
1. **Create shader file(s)** in `shaders/` directory
- Single-pass: `shaders/my_shader.glsl`
- Multi-pass: `shaders/my_shader_bufferA.glsl`, `shaders/my_shader_image.glsl`
- Use Write tool to create files directly
2. **Activate visualizer**
```
mcp__bice-box__set_visualizer(visualizerName: "my_shader")
```
3. **Link to audio effect** - Add shader comment in `.sc` file
```supercollider
// shader: my_shader
(
var defName = \my_effect;
// ... rest of effect code
)
```
This auto-loads the shader when the effect activates.
4. **List available visualizers**
```
mcp__bice-box__list_visualizers()
```
Returns p5.js sketches and GLSL shaders
5. **Iterate** - Edit and save, hot-reload handles the rest
- Changes auto-reload when files are saved
- No need to manually reload
## Common Shader Functions
```glsl
// Smooth minimum (blend shapes)
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);
}
// Rotate 2D
vec2 rotate(vec2 v, float angle) {
float c = cos(angle);
float s = sin(angle);
return vec2(v.x * c - v.y * s, v.x * s + v.y * c);
}
// Hash function (pseudo-random)
float hash(vec2 p) {
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}
// Smooth step
float smoothstep(float edge0, float edge1, float x) {
float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t * t * (3.0 - 2.0 * t);
}
```
## Performance Tips
- **Use resolution scaling** for complex shaders:
```glsl
// resolution: 0.5 // Half resolution
```
- **Minimize texture lookups** - Cache results when possible
```glsl
vec4 prev = texture(iChannel0, uv); // Once per fragment
```
- **Avoid loops when possible** - Unroll or use noise functions
- **Use built-in functions** - `smoothstep`, `mix`, `clamp` are optimized
- **Test on target hardware** - Raspberry Pi has different performance characteristics
## Common Gotchas
- **Don't declare `#version 300 es`** - System adds it automatically
- **Don't declare `precision`** - System handles this
- **iRMSTime is NOT for reactivity** - Use iRMSOutput/iRMSInput instead
- **Audio texture Y coordinates** - FFT at 0.25, waveform at 0.75 (not 0.0 and 1.0)
- **Multi-pass naming** - Must use `_bufferA`, `_image` suffixes exactly
- **Aspect ratio** - Divide by `iResolution.y` for circular shapes, not `iResolution.x`
## Debugging
- **Check shader compilation** - Look for errors in console logs
```
mcp__bice-box__read_logs(lines: 100, filter: "shader")
```
- **Test with simple output** - Start with solid colors to verify structure
```glsl
fragColor = vec4(1.0, 0.0, 0.0, 1.0); // Red
```
- **Visualize UVs** - Debug coordinates
```glsl
fragColor = vec4(uv, 0.0, 1.0);
```
- **Check audio uniforms** - Verify audio is flowing
```glsl
fragColor = vec4(vec3(iRMSOutput), 1.0); // Should pulse with audio
```
No comments yet. Be the first to comment!