Appearance
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
TYPE | Becomes | GLSL type |
|---|---|---|
float | A slider (MIN, MAX, DEFAULT) | float |
bool | An on/off switch | bool |
long | A menu (VALUES and LABELS) | int |
event | A switch | bool |
color | Red, green and blue sliders | vec4 |
point2D | X and Y sliders | vec2 |
audio | The live waveform | sampler2D |
audioFFT | The live spectrum | sampler2D |
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:
| Name | Value |
|---|---|
GLOW_BASS, GLOW_MID, GLOW_TREBLE | Band levels, 0–1 |
GLOW_LEVEL | Overall loudness, 0–1 |
GLOW_ONSET | Onset (hit) strength, 0–1 |
GLOW_BEAT_PHASE | Position within the beat, 0–1 (predicted, in sync with what you hear) |
GLOW_BEAT_PULSE | An envelope that peaks on each beat |
GLOW_BAR_PHASE, GLOW_PHRASE_PHASE | Position within the bar and the phrase, 0–1 |
GLOW_BEAT_IN_BAR | Which beat of the bar, 0–3 |
GLOW_BPM | The tempo |
GLOW_ENERGY | The 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" },
{ }
]PERSISTENTkeeps the buffer from frame to frame — the basis of trails, ink and physics simulations. Persistent buffers keep their contents when the window is resized.FLOATstores high-precision values (for simulations).WIDTH/HEIGHTaccept expressions such as$WIDTH/2orfloor($HEIGHT * scale)(using your float inputs).- Up to 8 passes. Branch on
PASSINDEXto 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
whileloops — useforloops. - Every
forloop 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.