Shader import + reactivity
Paste a shader, drive it from the music. Three layers, in the order to reach for them: the knobs that already exist, your own knobs from one comment block, then the audio uniforms read straight from your code. None of it needs an AI key.
Layer one: the knobs you already have
Open the Console. These act on whatever is on screen right now, imported shaders included, with no editing and no reload.
They stack, and the stacking is the part most people miss:
The Intensity fader reads 1 to 11 on screen but drives 0 to 2 underneath: display 6 is 1.0×, display 11 is 2.0×, and the default display 8 is 1.4×. A factory-fresh IKANDY is already feeding your shader 1.4 times what it was written for.
- Want more? Bass at 2 with Intensity at 11 gives you 4× the raw band, roughly three times the default. That is the ceiling from the Console alone.
- Want it dead still? Intensity at 1 sends every band to exactly zero. That is deliberate: it is the defeat switch, not a low setting.
- Bass, Mid and Treble are saved per engine, so your imported-shader settings do not follow you into MilkDrop. Intensity is one global value.
The Reactivity section
Two of its four knobs act on an imported shader:
- Light-Sync (starts at 85%) lets the beat drive brightness and glow in the post chain.
- FX Pulse (starts at 50%) lets hits pump the bloom, chromatic aberration and vignette for a moment.
- Motion Warp and Transform start at zero and stay inert on GLSL and WebGPU by design. See Colour, zoom, and the motion rule.
Layer two: your own knobs, one comment block
Put a single line comment in your pasted source and IKANDY builds you real knobs in the Console's SCENE section: sliders, toggles, dropdowns, a proper colour picker. They persist with the scene and come back when it reloads.
// ikandy-controls: [
// {"id":"glow", "label":"Glow", "type":"float", "range":[0,2], "default":0.8, "mapsTo":"u_glow"},
// {"id":"tint", "label":"Tint", "type":"color", "default":"#66ccff", "mapsTo":"u_tint"},
// {"id":"spin", "label":"Spin", "type":"bool", "default":true, "mapsTo":"u_spin"}
// ]
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
vec3 col = u_tint * smoothstep(0.8, 0.0, length(uv - 0.5));
col *= 1.0 + u_glow * u_bassS; // your knob, times the music
fragColor = vec4(col, 1.0);
}Put it anywhere in the pasted source. On a multipass shader it goes in the Common tab, or the top of Image if you have no Common tab, and every pass can read the uniform.
Four rules that will bite you
- Do not declare the uniform yourself. IKANDY writes
uniform float u_glow;for you. Adding your own is a redefinition error, and you get a message telling you to delete the line. - Read it or lose it. A control that is declared but never actually read in the code is dropped, and the Load status names it. No dead knobs. Reading it inside a comment does not count, because comments are stripped before the check.
- One block per shader. A second block is an error, not a merge.
- A malformed block stops the Load and tells you the block line and the source line. It never quietly gives you zero knobs.
Fields
| Field | Needed | What it does |
|---|---|---|
mapsTo | yes | The uniform name: u_ plus letters, digits, underscore. Cannot be a built-in like u_bass. |
label | should | The name on the knob. |
type | should | float (knob, the default), int, color (picker, arrives as a vec3), bool (toggle), enum (dropdown, needs options). |
range | float / int | [min, max] |
default | should | Starting value. Colours take "#rrggbb" or [r,g,b] in 0–1. |
id | should | Short key used for saving. Defaults to mapsTo. |
options | enum | [{"value":0,"label":"Soft"}, ...] |
audioDefault | no | Wires the knob to a band. See below. |
Up to 12 controls. Duplicate or reserved mapsTo names are dropped, with the reason logged to the developer console.
uc.glow inside fs_main, which is your mapsTo with the u_ prefix removed. Everything else is identical.Point a knob at a band
This is the direct answer to “how do I put the bass on something else?” Add audioDefault to any float or int control and that knob is modulated by the band every frame, without touching your shader code.
// ikandy-controls: [
// {"id":"zoom", "label":"Zoom", "type":"float", "range":[0.5,3], "default":1.0,
// "mapsTo":"u_zoom", "audioDefault": {"band":"bass", "gain":1.5}},
//
// {"id":"hue", "label":"Hue", "type":"float", "range":[0,1], "default":0.2,
// "mapsTo":"u_hue", "audioDefault": {"band":"treble", "gain":0.6}}
// ]Bands are bass, mid, treble and volume. Gain is clamped to 0–8. The knob you set by hand stays the base, so you keep a manual trim on top of the music.
Repointing it live, without editing
You can move a control from one band to another while the shader runs:
- Load the shader, then Save it.
- Reopen it from the My Scenes dropdown with Open, in the same panel.
- Every float and int control now shows a small pill under its name: off, bass, mid, treble, volume. Pick one. It applies immediately and is remembered.
The pill appears only on a shader the panel itself opened, which is why the save-then-open step matters. Loading a fresh paste gives you the knobs but not the pills. One more thing worth knowing: if you later edit the ikandy-controls block in the saved file, the file wins over the remembered pill settings on the next load.
Layer three: straight into the source
The fastest loop. The paste box is the editor: type in it and press Ctrl+Enter to recompile. IKANDY declares all of these for you, so just read them.
| Uniform | What it carries |
|---|---|
u_bass u_mid u_treble | The three bands, after your Console knobs and Intensity. Fast and spiky. |
u_bassS u_midS u_trebS | The same bands, envelope-smoothed. Use these for anything that should glide. |
u_kickTight | The kick drum transient, 50–110 Hz. The structural pulse. |
u_snare | Backbeat body and crack. Good for colour and saturation flares. |
u_hihat | 6–16 kHz tick. Fast, fine sparkle. |
u_subBass | 20–60 Hz rumble. Slow glow and depth. |
u_air | 16–22 kHz sheen. Slow shimmer. |
u_bassHit u_midHit u_trebHit | Raw onset envelopes per band. |
u_buildup u_peak u_calm u_release u_sustain u_arc | Song shape. These rise and fall over seconds, not beats. Stage the drop with them. |
u_beat u_beatPhase u_barPhase u_bpm | The live beat clock. |
u_drive | The Intensity fader itself, 0–2. Multiply your reactivity by it so your shader goes fully still at zero. |
u_volume u_intensity | Overall loudness. u_volume is the RMS of the bands, u_intensity is their mean. |
spectrumAt(x) waveformAt(x) | Helper functions. Sample the full spectrum or waveform; x and the result are both 0–1. |
Shadertoy shaders work unchanged. Every iChannel resolves to IKANDY's live audio in the exact 512×2 texture format Shadertoy uses: FFT on one row, waveform on the other. A shader that already samples a music channel reacts the moment it loads, with nothing to press. iResolution, iMouse, iDate, iFrame and the rest are wired too. Shadertoy library textures do not travel with an export, so bind your own image or the shader renders without them.
u_bass is the most common reason an import looks flat.Colour, zoom, and the motion rule
Colour, brightness, glow, sparkle, saturation and contrast are open season. Drive any of it from any signal, as hard as you like.
Zoom, camera and whole-frame motion are different. Your hand-written shader can do it, because a fresh paste renders exactly as written and nothing rewrites your code. But IKANDY's own automatic tools will not wire audio into motion for you, and that is a decision rather than an oversight:
- Motion Warp and Transform on the Console ship at zero, and Transform is inert on GLSL and WebGPU entirely.
- The Basic button injects brightness, warmth and glow only. It used to warp time; that was removed.
- IGNITE, the AI path, refuses to propose audio-driven camera, zoom or geometry bindings.
The reason is motion comfort. Repetitive whole-field movement in roughly the 0.08 to 0.5 Hz band is what makes people queasy, and music sitting at 100 to 140 BPM puts a bass-driven zoom uncomfortably close to it. If you want breathing zoom, write it yourself, keep the amplitude small, and watch it on a big screen for a few minutes before you put it in front of a room.
Two things people ask
Does an AI key change any of this?
No. Every layer above is free of provider cost and works offline. A key buys three things: IGNITE, which reads a non-reactive shader and proposes audio bindings for you to review before anything applies; Tune, which looks at the running frame and balances the gains; and AI Create, which writes whole scenes from a prompt. If you already write your own reactivity, a key mostly buys you the gain-balancing pass. See AI scene generation.
Can I build .milk presets in IKANDY?
Not today. IKANDY reads them very well: drop a folder of .milk files and they convert in the app, get security-screened, and land in rotation, tested with ten thousand presets at once. But nothing in the app writes a .milk file, so authoring still needs MilkDrop itself or a preset editor. See Visualizers + presets.