Skip to content

Writing your own shaders ​

You can add your own visualizations as ISF shaders — the Interactive Shader Format used by VJ apps such as VDMX, Resolume and Millumin. An ISF file is a GLSL fragment shader with a JSON header describing its controls.

Upload shaders in the Visuals tab, drop them onto the page, or import them from the Presets tab. See Managing visualizations.

A first shader ​

glsl
/*{
  "DESCRIPTION": "Rings that pulse with the bass.",
  "CREDIT": "You",
  "ISFVSN": "2",
  "CATEGORIES": ["Generator"],
  "INPUTS": [
    { "NAME": "rings", "LABEL": "Rings", "TYPE": "float", "MIN": 2.0, "MAX": 40.0, "DEFAULT": 12.0 },
    { "NAME": "spin", "LABEL": "Spin", "TYPE": "bool", "DEFAULT": true }
  ]
}*/

void main() {
  vec2 p = (gl_FragCoord.xy - 0.5 * RENDERSIZE) / RENDERSIZE.y;
  float r = length(p);
  float a = spin ? atan(p.y, p.x) + TIME : 0.0;
  float wave = 0.5 + 0.5 * sin(r * rings * 6.2831 - TIME * 3.0 + a);
  vec3 colour = glowPalette(r + GLOW_BAR_PHASE * 0.2);
  gl_FragColor = vec4(colour * wave * (0.4 + 1.5 * GLOW_BASS + GLOW_BEAT_PULSE), 1.0);
}

Each input becomes a control in the layer — sliders, switches or menus — and can be oscillated and modulated like any other.

Inputs ​

TYPEBecomesGLSL type
floatA slider (MIN, MAX, DEFAULT)float
boolAn on/off switchbool
longA menu (VALUES and LABELS)int
eventA switchbool
colorRed, green and blue slidersvec4
point2DX and Y slidersvec2
audioThe live waveformsampler2D
audioFFTThe live spectrumsampler2D

Image inputs aren't supported.

Standard ISF uniforms ​

TIME, TIMEDELTA, RENDERSIZE, FRAMEINDEX, PASSINDEX and DATE, and the macros IMG_NORM_PIXEL, IMG_PIXEL, IMG_THIS_PIXEL, IMG_THIS_NORM_PIXEL and IMG_SIZE. isf_FragNormCoord gives the pixel's position from 0 to 1. Older GLSL (gl_FragColor, texture2D, varying) is translated automatically.

Music and colour extensions ​

On top of standard ISF, every shader can read the music and the scene's palette:

NameValue
GLOW_BASS, GLOW_MID, GLOW_TREBLEBand levels, 0–1
GLOW_LEVELOverall loudness, 0–1
GLOW_ONSETOnset (hit) strength, 0–1
GLOW_BEAT_PHASEPosition within the beat, 0–1 (predicted, in sync with what you hear)
GLOW_BEAT_PULSEAn envelope that peaks on each beat
GLOW_BAR_PHASE, GLOW_PHRASE_PHASEPosition within the bar and the phrase, 0–1
GLOW_BEAT_IN_BARWhich beat of the bar, 0–3
GLOW_BPMThe tempo
GLOW_ENERGYThe song's energy, slowly changing, 0–1
glowPalette(t)The scene's (or layer's) palette colour at t (wraps)
glowSpectrum(x)The spectrum at x, from 0 (bass) to 1 (treble)

Using glowPalette means your shader follows whatever palette the user picks.

Multiple passes and feedback ​

Use PASSES for multi-pass effects and simulations. A pass with a TARGET renders into a named buffer that later passes (and the next frame) can read:

json
"PASSES": [
  { "TARGET": "state", "PERSISTENT": true, "FLOAT": true, "WIDTH": "$WIDTH/2", "HEIGHT": "$HEIGHT/2" },
  { }
]
  • PERSISTENT keeps the buffer from frame to frame — the basis of trails, ink and physics simulations. Persistent buffers keep their contents when the window is resized.
  • FLOAT stores high-precision values (for simulations).
  • WIDTH / HEIGHT accept expressions such as $WIDTH/2 or floor($HEIGHT * scale) (using your float inputs).
  • Up to 8 passes. Branch on PASSINDEX to do different work in each.

The built-in Ripple Tank, Fluid Smoke, Double Pendulum and Ink Feedback are ISF shaders written this way — good examples to learn from.

Shapes on black ​

If your shader draws shapes on a black background (meant to sit over another layer), add "Element" to CATEGORIES. It's then listed under shapes & particles and Surprise me uses it as an overlay.

Safety rules ​

So that a shader can't freeze the graphics card (browsers block sites that do), uploads are checked first:

  • No while loops — use for loops.
  • Every for loop must compare against a constant number, at most 1024.

Anything that breaks a rule is refused with a message saying why. If a shader is still too heavy for your computer, the app switches that layer off and tells you, rather than letting the screen stall.

Sharing shaders ​

Uploaded shaders are stored in your browser. Export everything in the Presets tab includes them, so importing that file elsewhere brings them along. Share links don't include shaders.

Everything runs in your browser: no account, no uploads, no tracking.