Hi-Fi Spectrum

by MusicBar, visualizer

A classic stereo LED spectrum analyzer behind glossy glass, bass in the middle, its bars jumping from green through yellow to red with the music.

59 downloads Download file, 3.4 KB
How it installs, its SHA-256 and its code

Click Apply: MusicBar downloads it, checks its SHA-256 and puts it on the taskbar, or just puts it on if it already has it. Needs MusicBar 1.8 or newer.

SHA-256 b12224214c3f43ddd19729c99e4741e74585158174cf3c017bdef384574207f3

//! name: Hi-Fi Spectrum
//! author: MusicBar
//! kind: visualizer
//! description: A classic stereo LED spectrum analyzer behind glossy glass, bass in the middle, crisp segments climbing from the main color through the second to the accent.

void layer(inout float3 acc, inout float cover, float3 rgb, float a)
{
    a = saturate(a);
    acc = rgb * a + acc * (1.0 - a);
    cover = a + cover * (1.0 - a);
}

float4 main(float2 uv : TEXCOORD) : COLOR
{
    float2 px = float2(uv.x * size.x, rise(uv) * size.y);   // pixels: along the bar, up from the screen edge
    float H = size.y;
    float halfW = size.x * 0.5;

    // The grid snaps to whole pixels so every LED has hard edges: 8 rows, columns mirrored from the middle.
    float rows = 8.0;
    float segP = max(3.0, floor(H * 0.105));
    float gapY = max(1.0, floor(segP * 0.3));
    float base = max(1.0, floor((H - rows * segP) * 0.35));
    float colP = max(5.0, floor(H * 0.34));
    float gapX = max(1.0, floor(colP * 0.18));
    float d = floor(abs(px.x - halfW));
    float ci = floor(d / colP);
    float lx = d - ci * colP;
    float yI = floor(px.y) - base;
    float si = floor(yI / segP);
    float ly = yI - si * segP;
    float inCol = step(floor(gapX * 0.5), lx) * step(lx, colP - 1.0 - ceil(gapX * 0.5));
    float inRow = step(ly, segP - 1.0 - gapY) * step(0.0, si) * step(si, rows - 1.0);
    float led = inCol * inRow;

    // This column's band: bass at the middle, treble at the ends; the two channels drift apart a little.
    float f = (ci + 0.5) * colP / halfW;
    float side = step(px.x, halfW);
    float v = saturate((spectrum(f * 0.92) * 1.75 - 0.22) * (1.0 + 0.08 * sin(time * 3.1 + f * 9.0 + side * 2.0)) + 0.14 * beat);
    float fill = saturate(v * rows - si);
    float on = step(0.35, fill);
    float tip = on * step(v * rows - 1.0, si + 0.65);

    // The color climbs from main to second to accent; LEDs show a bright core and a lit top edge.
    float t = si / (rows - 1.0);
    float3 c = lerp(lerp(colorA.rgb, colorB.rgb, saturate(t * 1.8)), colorC.rgb, saturate(t * 2.4 - 1.4));
    float segH = segP - gapY;
    float core = 1.0 - 0.35 * abs((lx - colP * 0.5) / (colP * 0.5)) - 0.15 * (1.0 - ly / max(segH - 1.0, 1.0));
    float3 lit = c * core * (0.95 + 0.25 * fill) + 0.25 * step(segH - 1.5, ly) * step(1.5, segH);

    // Glass over the panel: a reflection with a crisp lower edge across the upper part, each LED's
    // upper half a little glossier, and a slow diagonal glint gliding past.
    float gloss = step(0.6 * H, px.y) * lerp(0.26, 0.08, saturate((px.y - 0.6 * H) / (0.4 * H)));
    gloss += step(segH * 0.5, ly) * 0.1;
    float glint = exp(-abs(frac((px.x + px.y * 2.0) / (16.0 * H) - time * 0.05) - 0.5) * 24.0) * 0.4;

    float3 acc = float3(0, 0, 0);
    float cover = 0;

    // Light bleeding from the lit run into the gaps, then unlit LEDs, then lit ones.
    float topY = base + floor(v * rows + 0.65) * segP - gapY;
    float dx = max(abs(lx - colP * 0.5) - colP * 0.4, 0.0);
    float dy = max(px.y - topY, 0.0) + max(base - px.y, 0.0);
    float bleed = exp(-length(float2(dx, dy)) / (0.05 * H + 0.6)) * step(px.y, base + rows * segP + 0.08 * H);
    layer(acc, cover, c, bleed * 0.3 * (0.8 + 0.4 * level));
    layer(acc, cover, c * 0.3 + 0.05, led * (1.0 - on) * 0.32);
    layer(acc, cover, lit * lerp(1.0, 1.3, tip), led * on);
    layer(acc, cover, float3(1, 1, 1), led * saturate(gloss + glint) * (0.12 + 0.88 * on));

    return paint(acc / max(cover, 0.001), cover);
}