Shadertoy intakes audio at video textures, and currently Isadora has no way to convert audio to a video texture.
So I converted this shader to use 5 fft inputs as seen below (I am not sure this looks right, since this effect would not run for me on shadertoy).

the updated code:
// ------------------------------------------------------------
// Audio-reactive circular visualizer
// Converted from texture-based audio input to 5 numeric bands
//
// Suggested Isadora inputs:
// fft0 = 0 Hz
// fft94 = 94 Hz
// fft188 = 188 Hz
// fft281 = 281 Hz
// fft375 = 375 Hz
//
// Expected input range: 0.0 to 1.0
// ------------------------------------------------------------
// If needed for Isadora custom controls, you can adapt these comments
// to the exact syntax expected by the GLSL Shader actor.
//
// ISADORA_FLOAT_PARAM(fft0, f000, 0.0, 1.0, 0.0, "FFT 0 Hz");
// ISADORA_FLOAT_PARAM(fft94, f094, 0.0, 1.0, 0.0, "FFT 94 Hz");
// ISADORA_FLOAT_PARAM(fft188, f188, 0.0, 1.0, 0.0, "FFT 188 Hz");
// ISADORA_FLOAT_PARAM(fft281, f281, 0.0, 1.0, 0.0, "FFT 281 Hz");
// ISADORA_FLOAT_PARAM(fft375, f375, 0.0, 1.0, 0.0, "FFT 375 Hz");
// ISADORA_FLOAT_PARAM(audioGain, gain, 0.0, 3.0, 1.0, "Audio gain");
uniform float fft0;
uniform float fft94;
uniform float fft188;
uniform float fft281;
uniform float fft375;
uniform float audioGain;
// ------------------------------------------------------------
// Utility: sample a pseudo-spectrum from the 5 numeric bands
// ------------------------------------------------------------
float SampleAudioSpectrum(float normPos)
{
normPos = clamp(normPos, 0.0, 1.0);
// Apply overall gain and clamp
float b0 = clamp(fft0 * audioGain, 0.0, 1.0);
float b1 = clamp(fft94 * audioGain, 0.0, 1.0);
float b2 = clamp(fft188 * audioGain, 0.0, 1.0);
float b3 = clamp(fft281 * audioGain, 0.0, 1.0);
float b4 = clamp(fft375 * audioGain, 0.0, 1.0);
// 5 points distributed evenly across 0..1:
// 0.00, 0.25, 0.50, 0.75, 1.00
if (normPos <= 0.25)
{
float t = normPos / 0.25;
return mix(b0, b1, t);
}
else if (normPos <= 0.50)
{
float t = (normPos - 0.25) / 0.25;
return mix(b1, b2, t);
}
else if (normPos <= 0.75)
{
float t = (normPos - 0.50) / 0.25;
return mix(b2, b3, t);
}
else
{
float t = (normPos - 0.75) / 0.25;
return mix(b3, b4, t);
}
}
float GetIntensityInRange(float beginNorm, float endNorm, float incrementSize)
{
if (incrementSize <= 0.0) return 0.0;
float range = endNorm - beginNorm;
if (range <= 0.0) return 0.0;
float result = 0.0;
float sampleCount = 0.0;
// Fixed maximum iterations for GLSL compatibility
for (int i = 0; i < 64; i++)
{
float lookupCoord = beginNorm + float(i) * incrementSize;
if (lookupCoord >= endNorm) break;
result += SampleAudioSpectrum(lookupCoord) * 2.0;
sampleCount += 1.0;
}
if (sampleCount <= 0.0) return 0.0;
return result / sampleCount;
}
float GetCornerIntensity(vec2 fragCoord, float checkDist, vec2 corner, float beginSample, float endSample)
{
float distanceN = 1.0 - (clamp(distance(fragCoord, corner), 0.0, checkDist) / checkDist);
float intensity = GetIntensityInRange(beginSample, endSample, 0.05);
float color = distanceN * intensity;
return color;
}
bool IsWithin(float val, float minVal, float maxVal)
{
return val >= minVal && val <= maxVal;
}
vec4 innerColour = vec4(0.0, 168.0, 181.0, 255.0) / 255.0;
vec4 outerRingColour = vec4(119.0, 72.0, 152.0, 255.0) / 255.0;
vec4 highsColour = vec4(243.0, 174.0, 75.0, 255.0) / 255.0;
vec4 lowsColour = vec4(222.0, 67.0, 131.0, 255.0) / 255.0;
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord / iResolution.xy;
float smallestDimen = min(iResolution.x, iResolution.y);
vec2 halfDimen = iResolution.xy * 0.5;
float halfSDimen = smallestDimen * 0.5;
vec2 centerOffset = fragCoord - halfDimen;
vec2 nOffset = centerOffset / halfSDimen;
float fragAngle = atan(centerOffset.y, centerOffset.x);
float fragAngleDegrees = fragAngle * 180.0 / 3.14159;
float overAllPulse = GetIntensityInRange(0.0, 0.3, 0.05);
float pixelDistanceToCenter = distance(fragCoord, halfDimen);
float idealRingDistance = halfSDimen * 0.8;
float ringDistance = idealRingDistance + (halfSDimen * overAllPulse * 0.2);
float pixelRingDist = abs(ringDistance - pixelDistanceToCenter);
const float ringWidth = 20.0;
float outerRingIntensity = 1.0 - (pixelRingDist / ringWidth);
if (IsWithin(outerRingIntensity, 0.0, 1.0))
{
outerRingIntensity *= sin(fragAngle * 3.0 + iTime) * 0.5 + 0.5;
fragColor += outerRingIntensity * outerRingColour;
}
// --- Inner ring
// Start = close to center, End = going further away
float innerRingEnd = ringDistance - ringWidth * 0.5;
float lookupAngle = mod(fragAngleDegrees + 180.0, 360.0);
float pixelLookupIndex = lookupAngle / 360.0;
float pixelLookupColor = SampleAudioSpectrum(pixelLookupIndex) * 2.0;
float pulseA = (sin(fragAngle * 3.0 + iTime) * 0.5 + 0.5) * 0.2;
float pulseB = (cos(fragAngle * 6.0 - iTime * 1.5) * 0.5 + 0.5) * 0.2;
float pulse = 0.1 + pulseA + pulseB;
float innerRingStart = innerRingEnd * (1.0 - pixelLookupColor - pulse);
float innerRingLength = max(innerRingEnd - innerRingStart, 0.0001);
float innerRingFragProgress = (pixelDistanceToCenter - innerRingStart) / innerRingLength;
float innerRingIntensity = innerRingFragProgress;
if (pixelDistanceToCenter < innerRingStart || pixelDistanceToCenter > innerRingEnd)
innerRingIntensity = 0.0;
// Inner ring separators
float angleMod = mod(fragAngleDegrees, 3.0);
if (IsWithin(angleMod, 0.0, 1.0))
innerRingIntensity = 0.0;
fragColor += innerColour * innerRingIntensity;
// Corners
float topRightIntensity = GetCornerIntensity(fragCoord, smallestDimen, iResolution.xy, 0.3, 0.65);
float topLeftIntensity = GetCornerIntensity(fragCoord, smallestDimen, vec2(0.0, iResolution.y), 0.0, 0.25) * 2.0;
if (topRightIntensity > 0.0)
{
fragColor += highsColour * topRightIntensity;
}
if (topLeftIntensity > 0.0)
{
fragColor += lowsColour * topLeftIntensity;
}
// Background spectrum wash:
// original shader used texture(iChannel1, vec2(uv.x, 0.25)).x
float texIntensity = SampleAudioSpectrum(uv.x);
fragColor += lowsColour * texIntensity;
fragColor.rgb = clamp(fragColor.rgb, 0.0, 1.0);
}
Looking to use the audio feature in the linked GLSL shader and really struggling. Can anyone help me understand how to use this shader with Isadora's audio features? I
I've linked the Shader and attached my current patch. The cue I'm working on though is only Q4. Any help would be appreciated
Shader: https://www.shadertoy.com/view...
Device: Omen Laptop running Windows 11
Patch: vinyljournal2.izz
I can confirm the behaviour you are seeing. I am recording this as a bug, however, there is a simple work around.
- Set the 'speed' input of your movie player to INIT = 0
- add an Enter Scene Value actor, and set it to 1, and connect it to the speed input.
This ensure playback doesn't start and that the movie is see as active (required for preload).
NOTE: preload is not generally recommended, it requires special care to use it effectively, and due to improved scene loading introduce in Isadora 3 isn't necessary except in extreme cases.
PLEASE read the following support article to better understand the preload actor.
I can confirm this bug seems to exist only in the Windows version of Isadora 4.5.1 VST. It does not exist in Isadora 4.5.1 VST for Mac OS.
Jonathan.
Hello,
Edit: related to this issue?
I am finding that when I preload a scene that contains a Movie Player actor, the movie file immediately begins to play but with the video hidden (e.g. I can hear the audio begin playing, but nothing is sent to the projector), even though the scene has not yet been activated. When I subsequently activate the preloaded scene: (1) the video becomes visible on the stage, and the audio continues in sync with the video; and (2) I can see that the position output parameter of the Movie Player is not zero (e.g. it began playing when I preloaded the scene earlier, not when I just activated the scene).
This is happening only on my Windows laptop. It works as it should on my old Mac laptop (quite old version of Isadora). I don't remember this happening before I just updated to Isadora 4.5.1.
This happens on my Windows laptop even when I create a new, blank Isadora document and add the following three actors:
Scene 1:
[Preload Scene] actor
Scene 2:
[Movie Player] -> [Projector]
The only workaround I can find is to not preload scenes, but this is a problem for my application which requires intricate cueing.
Isadora 4.5.1, Windows 11, Intel i7-13700H. H264 file with stereo audio track.
Many thanks in advance for any advice.
Jonathan.
Try putting another camera (any camera) up on the same pipe to see if it has a bouncy image as well.
What is upstairs from you? If it's something like heavy machinery or a dance studio, that could make your ceiling shake.
Hi all,
I'm making a show and trying to use one singular PTZ camera. It's a Cinetreak PT20. This is my first time using one. Right now I'm just in my studio and I've hung it from a pipe and the image seems to be really...bouncy. Like it actually shakes. And it's not after it PTZ's...just stationary. No one else is in the room and I **barely** move when I'm programming (lol). Has anybody experienced this behaivor. I thought it was maybe some kind of setting that was creating an optical illusion of bounce...wrong hz or something, but everything seems right. This is sort of shot in the dark...but it's bouncy to the point of distraction (for me at least). I even bought a piece of hardware that was supposed to absorb vibration, but that made it even worse. I'm sort of at a loss and wonder if I just bought a bad camera. Any thoughts greatly appreciated.
Jake
Another way is to use the monitor in control panel. Set the ID of the monitor (here 1) to the output of the NDI 5 Watcher. That's it. You can choose the size of the monitor in the control panel as you want. You don't need any extra stage for the preview.


Best regards
Jean-François
@dusx OK I think I now understand what you are saying. Instead of sending to the main screen, I'm sending stage 2 to a display that's not assigned and then forcing preview of that and it shows on the screen like I want it to. Perfect! Thanks for putting me on the right track - FAS