shimga

GLSL Shaders for Music Visualization: A Beginner's Guide

Advanced 📅 May 2026 ⏱ 11 min read

The most visually striking music videos on YouTube right now — Star Nest, Octave Meatballs, audio-reactive fractals, tunnel zooms — are all built from a single technique: a GLSL fragment shader driven by audio data. Shaders run on the GPU at 60+ frames per second, can produce effects no canvas API can match, and are surprisingly accessible once you understand the structure. This guide is a from-zero introduction to writing custom GLSL shaders for music visualization.

What a Fragment Shader Actually Does

A fragment shader is a tiny program that runs once per pixel on the screen, every frame. It receives the pixel's coordinates, returns a color. That's it. With 1080p × 30fps you're running this little program 62 million times per second. The GPU eats that for breakfast.

The program has access to "uniforms" — variables the host application sets each frame, identical for every pixel. For a music visualizer, the uniforms are things like the current time, the screen resolution, and audio analysis data.

Shadertoy Convention (And Why Shimga Follows It)

Shadertoy.com is the de-facto standard for GLSL shader sharing. It defines a fixed set of uniforms every shader has access to. Shimga's custom shader layer uses the same names, so you can paste Shadertoy code in with minimal edits.

The standard uniforms:

Plus shimga-specific helpers:

The Audio Texture Trick

The big unlock for audio-reactive shaders is iChannel0 — a 2-row texture where:

So to draw a frequency-spectrum bar at pixel x:

float h = texture2D(iChannel0, vec2(uv.x, 0.75)).x;

That returns 0..1, the volume at frequency bin x. Map it to a bar height and you've got a spectrum analyzer in three lines.

Your First Shader: Bass-Reactive Radial Pulse

The shimga custom-shader starter is this:

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
  vec2 uv = fragCoord / iResolution.xy;
  vec2 ce = uv - 0.5;
  ce.x   *= iResolution.x / iResolution.y;

  // Spectrum bars (bottom-up)
  float spec = texture2D(iChannel0, vec2(uv.x, 0.75)).x;
  float bar = step(1.0 - 0.05 - spec * 0.65, uv.y);

  // Waveform line through the middle
  float wave = texture2D(iChannel0, vec2(uv.x, 0.25)).x - 0.5;
  float waveLine = smoothstep(0.005, 0.0, abs(wave - (uv.y - 0.5)));

  // Bass-driven radial pulse
  float r = length(ce);
  float pulse = smoothstep(0.4 + iAudioBass * 0.4, 0.38 + iAudioBass * 0.4, r);

  // Color blend
  vec3 c1 = 0.5 + 0.5 * cos(iTime * 0.5 + vec3(0.0, 2.094, 4.189) + iAudioMid * 3.0);
  vec3 col = mix(vec3(0.02), c1, bar);
  col = mix(col, vec3(1.0), waveLine);
  col += (vec3(1.0) - c1) * pulse * 0.3;

  fragColor = vec4(col, 1.0);
}

Three independent effects composed in one pass: a spectrum bar chart, a center waveform line, and a bass-driven radial pulse. The hue shifts with mid-band energy, so the whole frame breathes with the song.

The Mental Model: UV Coordinates and Effects

Every fragment shader starts with the same dance: convert fragCoord (raw pixel) to uv (0..1 across the screen). From there:

Audio Reactivity Patterns That Work

Pattern 1: "Punch on the bass"

Use iAudioBass as a scale factor on something that already moves. Don't start a motion on bass — that looks twitchy. Amplify an existing slow motion.

float radius = 0.3 + 0.05 * iAudioBass;  // base radius, bass boosts

Pattern 2: "Hue rotation with mid"

Mid frequencies are where vocals + melodic content live. Rotating hue with mid gives the strongest "feels alive" reaction.

vec3 hueColor = 0.5 + 0.5 * cos(iTime + iAudioMid * 3.0 + vec3(0.0, 2.094, 4.189));

Pattern 3: "Shimmer with highs"

High frequencies are hi-hats and cymbals. Use them on tiny accents — sparkle, noise grain, edge glints — not on large shapes.

float sparkle = fract(sin(uv.x * 999.0 + iTime * 5.0) * 43758.5453);
sparkle = step(0.985 - iAudioHigh * 0.05, sparkle);  // brighter sparkle on high energy

Borrowing From Shadertoy

Most Shadertoy audio-reactive shaders work in shimga with two edits:

  1. Replace any iChannel code that fetches video frames or images — shimga's iChannel0 is audio only.
  2. Replace any uniforms not in the standard list (some authors define their own).

Beyond that, the GLSL ES 1.0 dialect is identical between Shadertoy and shimga, so the math just works. More on how FFT data is structured.

Performance Tips

Try the custom shader layer now

Add a "Custom GLSL Shader" element in the studio, paste any shader, hit Apply.

Open Shimga Studio →

Related Reading