@dexamusx
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);
}