Storm Arc

by MusicBar, progress bar

Jagged lightning crackles from a small terminal at the start of the bar to a glowing electrode orb at the playhead, striking faster when the music is loud and flashing on every beat.

14 downloads Download file, 6.7 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 5b14fa2edbe245380844f4e87fd26dc13a33496cafad8d687e4e42f4766735bd

//! name: Storm Arc
//! author: MusicBar
//! kind: progress
//! description: Jagged lightning crackles from a small terminal at the start of the bar to a glowing electrode orb at the playhead, striking faster when the music is loud and flashing on every beat.

float hash1(float n)
{
    n = frac(n * 0.1031);
    n *= n + 33.33;
    n *= n + n;
    return frac(n);
}

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

// Jagged noise: straight runs between random points (value and slope).
float2 jag(float x, float seed)
{
    float i = floor(x);
    float a = hash1(i + seed);
    float b = hash1(i + 1.0 + seed);
    return float2(lerp(a, b, frac(x)) * 2.0 - 1.0, (b - a) * 2.0);
}

// A bolt's offset from its centre line at x, in bar heights, with its slope: four jagged octaves.
float2 bolt(float x, float seed)
{
    float2 v = jag(x * 0.9, seed) * float2(0.24, 0.24 * 0.9);
    v += jag(x * 2.1, seed + 13.1) * float2(0.12, 0.12 * 2.1);
    v += jag(x * 4.7, seed + 27.7) * float2(0.06, 0.06 * 4.7);
    v += jag(x * 10.3, seed + 41.3) * float2(0.03, 0.03 * 10.3);
    return v;
}

float4 main(float2 uv : TEXCOORD) : COLOR
{
    float H = size.y;
    float2 px = float2(uv.x * size.x, rise(uv) * size.y);   // pixels: along the bar, up from the screen edge
    float yc = H * 0.5;
    float head = max(progress * size.x, 1.6 * H);
    float ro = 0.23 * H * (1.0 + 0.12 * beat);
    float x0 = 0.42 * H;
    float x = px.x / H;

    // A new strike many times a second, faster when loud; each one a little brighter or dimmer.
    float tick = floor(time * 16.0);
    float strike = 0.75 + 0.25 * hash1(tick * 3.7);
    float span = smoothstep(0.0, 0.7, (px.x - x0) / H) * smoothstep(0.0, 0.5, (head - ro - px.x) / H) * step(px.x, head);
    float wide = 0.1 * H * (1.0 + 0.6 * beat + 0.3 * level);
    float cw = max(0.55, 0.022 * H);

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

    // A faint wire for the rest of the song.
    float wire = step(head + ro + 0.2 * H, px.x) * step(frac(px.x / (0.2 * H)), 0.5) * smoothstep(1.0, 0.0, abs(px.y - yc));
    layer(acc, cover, colorB.rgb, wire * 0.18);

    // The bloom over the whole arc, flashing on the beat.
    // (Distance to a bolt: across the slope close to it, straight up and down further out so steep runs do not smear.)
    float2 m = bolt(x, tick * 7.13);
    float my = abs(px.y - yc - m.x * H * span);
    float dm = my * lerp(rsqrt(1.0 + m.y * m.y * span * span), 1.0, saturate(my / (0.05 * H)));
    layer(acc, cover, colorA.rgb, exp(-dm / (0.45 * H)) * (0.12 + 0.35 * beat) * step(px.x, head) * smoothstep(x0 - 0.1 * H, x0 + 0.3 * H, px.x));

    // A second, dimmer filament, struck on its own rhythm.
    float2 g = bolt(x, floor(time * 9.0) * 3.31 + 500.0);
    float gy = abs(px.y - yc - g.x * H * span * 0.8);
    float dg = gy * lerp(rsqrt(1.0 + g.y * g.y * span * span * 0.64), 1.0, saturate(gy / (0.05 * H)));
    layer(acc, cover, colorB.rgb, exp(-dg / (wide * 0.7)) * 0.4 * span);
    layer(acc, cover, lerp(colorB.rgb, float3(1, 1, 1), 0.5), smoothstep(cw * 0.8 + 0.5, cw * 0.8 - 0.5, dg) * 0.6 * span);

    // Side branches forking forward off the main bolt.
    float L = 1.3 * H;
    float bi = floor(px.x / L);
    float bh = hash1(bi * 1.37 + tick * 5.1);
    float bx0 = (bi + 0.08 + 0.3 * bh) * L;
    float blen = (0.3 + 0.28 * hash1(bi + tick * 2.9)) * L;
    float bspan = smoothstep(0.0, 0.7, (bx0 - x0) / H) * smoothstep(0.0, 0.5, (head - ro - bx0) / H);
    float by0 = yc + bolt(bx0 / H, tick * 7.13).x * H * bspan;
    float bt = (px.x - bx0) / blen;
    float bdir = (hash1(bi * 3.3 + tick) < 0.5 ? -1.0 : 1.0) * (0.35 + 0.4 * bh);
    float2 bj = jag(bt * 5.0, bi + tick * 9.7);
    float byy = by0 + bdir * (px.x - bx0) + bj.x * 0.07 * H * bt;
    float slope = bdir + bj.y * 0.07 * H * 5.0 / blen;
    float dbr = abs(px.y - byy) / sqrt(1.0 + slope * slope);
    float bmask = step(0.0, bt) * step(bt, 1.0) * (1.0 - bt) * step(bh, 0.65) * step(bx0 + blen, head - ro) * step(x0 + 0.4 * H, bx0);
    layer(acc, cover, colorB.rgb, exp(-dbr * dbr / (wide * wide * 0.3)) * 0.6 * bmask * smoothstep(0.0, 0.3, bt));
    layer(acc, cover, lerp(colorB.rgb, float3(1, 1, 1), 0.6), smoothstep(cw * 0.7 + 0.5, cw * 0.7 - 0.5, dbr) * bmask);

    // The main bolt: a colored glow and a white core.
    float onArc = smoothstep(x0 - 0.1 * H, x0 + 0.1 * H, px.x) * step(px.x, head);
    layer(acc, cover, colorA.rgb, exp(-dm / wide) * 0.85 * strike * onArc);
    layer(acc, cover, lerp(float3(1, 1, 1), colorA.rgb, 0.12), smoothstep(cw + 0.5, cw - 0.5, dm) * strike * onArc);

    // Short arcs licking forward off the orb.
    float2 rel = px - float2(head, yc);
    float a = atan2(rel.y, rel.x);
    float ab = floor((a + 3.14159) / 6.28318 * 10.0);
    float ah = hash1(ab * 1.9 + tick * 3.3);
    float ang = (ab + 0.15 + 0.7 * ah) / 10.0 * 6.28318 - 3.14159;
    float2 dir = float2(cos(ang), sin(ang));
    float alen = H * (0.18 + 0.3 * hash1(ab + tick * 1.7)) * (0.7 + 0.6 * level + 0.6 * beat);
    float al = dot(rel, dir) - ro;
    float2 aj = jag(al / H * 9.0, ab * 11.0 + tick * 4.3);
    float ac = dot(rel, float2(-dir.y, dir.x)) - aj.x * 0.05 * H * saturate(al / (0.1 * H));
    float lick = smoothstep(cw + 0.5, cw - 0.5, abs(ac) / sqrt(1.0 + aj.y * aj.y * 0.2)) * step(0.0, al) * step(al, alen) * (1.0 - al / alen);
    float fwd = step(0.0, cos(ang) + 0.35) * step(ah, 0.75);
    layer(acc, cover, colorA.rgb, exp(-abs(ac) / (wide * 0.5)) * step(0.0, al) * step(al, alen) * (1.0 - al / alen) * fwd * 0.5);
    layer(acc, cover, lerp(colorA.rgb, float3(1, 1, 1), 0.7), lick * fwd);

    // The electrode orb: a glass ball full of plasma, rim lit, with a highlight; and a small terminal at the start.
    float od = length(rel);
    layer(acc, cover, colorA.rgb, exp(-max(od - ro, 0.0) / (0.2 * H)) * (0.45 + 0.4 * beat));
    float3 orb = lerp(colorA.rgb * 0.55, lerp(colorA.rgb, float3(1, 1, 1), 0.85), exp(-od / (0.45 * ro)) * (0.8 + 0.2 * strike));
    orb += lerp(colorB.rgb, float3(1, 1, 1), 0.3) * smoothstep(ro * 0.7, ro, od) * 0.6;
    orb += smoothstep(0.32 * ro, 0.12 * ro, length(rel - float2(-0.35, 0.4) * ro)) * 0.8;
    layer(acc, cover, orb, smoothstep(ro + 0.6, ro - 0.6, od));
    float2 tr = px - float2(x0 - 0.12 * H, yc);
    float td = length(tr);
    float rt = 0.13 * H;
    float n = sqrt(saturate(1.0 - td * td / (rt * rt)));
    float3 term = float3(0.25, 0.27, 0.31) + float3(0.6, 0.62, 0.66) * n * smoothstep(-0.5, 0.6, tr.y / rt) + colorA.rgb * 0.35 * (1.0 - n);
    layer(acc, cover, colorA.rgb, exp(-max(td - rt, 0.0) / (0.12 * H)) * 0.4 * strike);
    layer(acc, cover, term, smoothstep(rt + 0.6, rt - 0.6, td));

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