Snake Byte

by MusicBar, progress bar

A blocky LCD snake winds back and forth to the playhead, its body as long as the song so far, flicking its tongue on every beat while the empty cells ahead light up with the music.

9 downloads Download file, 5.6 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 29b0ab284de6625a11f07a94b19424c6e407d624633495a88cc32c6cb78259e5

//! name: Snake Byte
//! author: MusicBar
//! kind: progress
//! description: A blocky LCD snake winds back and forth to the playhead, its body as long as the song so far, flicking its tongue on every beat while the empty cells ahead light up with the music.

// The snake's path snakes up one column, over the top, down the next, over the bottom, and so on.
// Step i of the path, as (column, row) with 4 rows; every 5 steps cover 2 columns.
float2 pathCell(float i)
{
    float k = floor(i / 5.0), j = i - 5.0 * k;
    float up = 1.0 - step(0.25, frac(k * 0.5));
    float row = j < 3.5 ? lerp(3.0 - j, j, up) : up * 3.0;
    return float2(2.0 * k + step(3.5, j), row);
}

// Which way the path goes after step i, and where it came from.
float2 nextDir(float i)
{
    float k = floor(i / 5.0), j = i - 5.0 * k;
    float up = 1.0 - step(0.25, frac(k * 0.5));
    return j < 2.5 ? float2(0.0, up * 2.0 - 1.0) : float2(1.0, 0.0);
}

float2 prevDir(float i)
{
    float k = floor(i / 5.0), j = i - 5.0 * k;
    float up = 1.0 - step(0.25, frac(k * 0.5));
    return (j < 0.5 || j > 3.5) ? float2(-1.0, 0.0) : float2(0.0, 1.0 - 2.0 * up);
}

float sdBox(float2 p, float2 b) { float2 d = abs(p) - b; return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0); }

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
{
    // The LCD grid: 4 rows of square cells, with a quarter cell of margin top and bottom.
    float s = size.y / 4.5;
    float2 px = float2(uv.x * size.x, rise(uv) * size.y - 0.25 * s);
    float2 cell = floor(px / s);
    float2 f = px / s - cell - 0.5;                            // -0.5..0.5 inside the cell
    float aa = 1.0 / s;
    float inRows = step(0.0, cell.y) * step(cell.y, 3.0);
    float headX = progress * size.x;
    float3 acc = float3(0, 0, 0);
    float cover = 0;

    // How long the snake is: the whole path across the bar times the progress.
    float total = floor(floor(size.x / s) * 0.5) * 5.0;
    float H = floor(progress * total);

    // This cell's place on the path (odd columns only hold the turn at the top or bottom).
    float k = floor(cell.x * 0.5);
    float odd = cell.x - 2.0 * k;
    float up = 1.0 - step(0.25, frac(k * 0.5));
    float j = odd > 0.5 ? 4.0 : lerp(3.0 - cell.y, cell.y, up);
    float onPath = odd > 0.5 ? step(abs(cell.y - up * 3.0), 0.1) : 1.0;
    float idx = 5.0 * k + j;
    float lit = onPath * step(idx, H) * inRows;

    // Unlit cells: a faint ghost grid; ahead of the snake they light up like an equalizer.
    float cx = (cell.x + 0.5) * s;
    float eq = spectrum(saturate((cx - headX) / max(size.x - headX, 1.0)) * 0.9);
    float bar = step(cell.y + 0.5, eq * 4.6) * step(headX + s, cx);
    float ghost = smoothstep(aa, -aa, sdBox(f, float2(0.3, 0.3)) - 0.03);
    layer(acc, cover, colorB.rgb, ghost * inRows * (1.0 - lit) * (0.18 + 0.32 * bar));

    // The body: one square per segment, bridged to its neighbors so it reads as one snake, fading toward the tail.
    float2 nd = nextDir(idx), pd = prevDir(idx);
    float bulge = saturate(1.0 - abs(idx - (H - (1.0 - beat) * 26.0)) / 1.5) * step(0.05, beat);   // a swallow wave on the beat
    float g = 0.3 + 0.1 * bulge;
    float dSeg = sdBox(f, float2(g, g)) - 0.04;
    float dNext = sdBox(f - nd * 0.42, abs(nd) * 0.12 + abs(nd.yx) * 0.2);
    float dPrev = sdBox(f - pd * 0.42, abs(pd) * 0.12 + abs(pd.yx) * 0.2);
    dNext += 9.0 * (1.0 - step(idx + 1.0, H));
    dPrev += 9.0 * (1.0 - step(1.0, idx));
    float dBody = min(dSeg, min(dNext, dPrev));
    float age = 0.5 + 0.5 * exp(-(H - idx) / 70.0);
    float3 skin = colorA.rgb * lerp(1.0, 0.8, step(0.25, frac(idx * 0.5))) * age;
    skin = lerp(skin, float3(1, 1, 1), 0.22 * smoothstep(-0.1, -0.3, f.x - f.y) + 0.35 * bulge);    // a lit bevel on each square
    layer(acc, cover, colorA.rgb * 0.12, lit * smoothstep(aa, -aa, dBody - 1.5 * aa));
    layer(acc, cover, skin, lit * smoothstep(aa, -aa, dBody) * (0.55 + 0.45 * age));

    // The head at the playhead, seen from above: two eyes facing where it goes, and a forked tongue that flicks on the beat.
    float2 hc = (pathCell(H) + 0.5) * s;
    float2 hn = nextDir(H);
    float2 hq = (px - hc) / s;
    float2 side = float2(-hn.y, hn.x);
    float along = dot(hq, hn), across = dot(hq, side);
    float dHead = sdBox(float2(along + 0.03, across), float2(0.44, 0.4)) - 0.05;
    layer(acc, cover, colorA.rgb * 0.12, smoothstep(aa, -aa, dHead - 1.5 * aa));
    layer(acc, cover, lerp(colorA.rgb, float3(1, 1, 1), 0.3), smoothstep(aa, -aa, dHead));
    float eyes = sdBox(float2(along - 0.14, abs(across) - 0.2), float2(0.09, 0.09));
    layer(acc, cover, float3(0.04, 0.07, 0.03), smoothstep(aa, -aa, eyes));
    float flick = saturate(beat * 1.6 + step(0.85, frac(time * 0.9)));
    float tlen = 0.5 + 0.55 * flick;
    float stem = step(0.44, along) * step(along, tlen) * step(abs(across), 0.05);
    float forkT = step(tlen - 0.18, along) * step(along, tlen + 0.06) * step(abs(abs(across) - (along - tlen + 0.18) * 0.7), 0.05);
    layer(acc, cover, lerp(colorC.rgb, float3(1.0, 0.25, 0.35), 0.6), saturate(stem + forkT) * step(0.05, flick));

    // Food a few cells ahead, blinking.
    float F = (floor(H / 9.0) + 1.0) * 9.0 + 3.0;
    float2 fq = (px - (pathCell(F) + 0.5) * s) / s;
    float plus = min(sdBox(fq, float2(0.3, 0.1)), sdBox(fq, float2(0.1, 0.3)));
    float blink = step(0.35, frac(time * 2.2));
    layer(acc, cover, colorC.rgb, exp(-max(length(fq) - 0.2, 0.0) / 0.25) * 0.35 * blink);
    layer(acc, cover, lerp(colorC.rgb, float3(1, 1, 1), 0.3 * beat), smoothstep(aa, -aa, plus) * blink);

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