Fairy Lights
by MusicBar, progress bar
A twisted wire of glass fairy lights swags along the bar, lit up to the playhead and twinkling, with a chase racing down the string on every beat.
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 5a7f67c1761b6291374079361812f8e7a46457eac714d4106332972df7868b58
//! name: Fairy Lights
//! author: MusicBar
//! kind: progress
//! description: A twisted wire of glass fairy lights swags along the bar, lit up to the playhead and twinkling, with a chase racing down the string on every beat.
float hash1(float n)
{
n = frac(n * 0.1031);
n *= n + 33.33;
n *= n + n;
return frac(n);
}
// Lays a color over what is there (premultiplied light, and how much of the pixel it covers).
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);
}
float2 rot(float2 p, float a)
{
float c = cos(a), s = sin(a);
return float2(c * p.x - s * p.y, s * p.x + c * p.y);
}
float boxD(float2 p, float2 b, float r)
{
float2 d = abs(p) - b + r;
return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0) - r;
}
// Bulb colors run through the palette.
float3 tint(float i)
{
float k = frac(i / 3.0) * 3.0;
return k < 0.5 ? colorA.rgb : (k < 1.5 ? colorB.rgb : colorC.rgb);
}
// The wire sags in swags between hooks every P pixels; returns its height and slope.
float2 wire(float x, float P, float H)
{
float f = frac(x / P);
return float2(H * 0.92 - H * 1.36 * f * (1.0 - f), -H * 1.36 * (1.0 - 2.0 * f) / P);
}
// Bulb i: a plastic socket on the wire and a glass bulb hanging under it.
void bulb(inout float3 acc, inout float cover, float2 px, float i, float sp, float P, float H, float head)
{
float xi = (i + 0.5) * sp;
float2 w = wire(xi, P, H);
float h = hash1(i * 3.17 + 0.5);
float ang = atan(w.y) * 0.7 + (h - 0.5) * 0.6 + 0.05 * sin(time * 1.4 + i);
float2 q = rot(px - float2(xi, w.x), -ang);
// Lit behind the playhead (the newest one stutters on), twinkling, and a chase on the beat.
float on = saturate((head - xi) / sp + 0.5);
on *= lerp(0.45 + 0.55 * abs(sin(time * 29.0 + i) * sin(time * 43.0)), 1.0, step(0.999, on));
float tw = 0.75 + 0.25 * sin(time * (2.0 + 3.0 * h) + h * 40.0);
float chase = smoothstep(0.7, 1.0, frac(i * 0.25 - time * 1.7));
float glow = on * tw * (1.0 + (0.2 + 1.8 * beat) * chase + 0.3 * beat) * (0.85 + 0.25 * level);
float3 c = tint(i);
// The glass: an egg that narrows to a tip.
float2 p = q - float2(0.0, -0.27 * H);
float2 r = float2(0.12, 0.17) * H;
float taper = 1.0 + 0.5 * saturate(-p.y / r.y);
float e = length(float2(p.x * taper, p.y) / r);
float sd = (e - 1.0) * r.x;
// Halo first, then the glass with its hot middle and a window-shaped highlight.
float hd = length(p * float2(1.0, 0.8)) / H;
layer(acc, cover, lerp(c, float3(1, 1, 1), 0.1), (exp(-hd * hd * 22.0) * 0.5 + exp(-hd * hd * 70.0) * 0.3) * glow);
float core = exp(-dot(p / r, p / r) * 2.0);
float3 litC = c * (0.55 + 0.7 * core) * glow + core * core * 0.75 * glow;
float3 darkC = c * (0.08 + 0.32 * smoothstep(0.65, 1.0, e)) + 0.02;
float3 glass = lerp(darkC, litC, saturate(on));
float spec = smoothstep(1.0, 0.3, length((p - float2(-0.045, 0.05) * H) / (float2(0.022, 0.055) * H)));
glass += spec * (0.4 + 0.3 * on);
layer(acc, cover, glass, smoothstep(0.5, -0.5, sd) * lerp(0.85, 1.0, on));
// The socket, catching a little of its bulb's light.
float sk = boxD(q - float2(0.0, -0.065 * H), float2(0.055, 0.065) * H, 0.02 * H);
float3 skC = float3(0.05, 0.1, 0.06) + c * 0.25 * on + 0.18 * smoothstep(0.03 * H, -0.04 * H, q.x);
layer(acc, cover, skC, smoothstep(0.5, -0.5, sk));
}
float4 main(float2 uv : TEXCOORD) : COLOR
{
// Pixels, y up the screen, so the bulbs always hang down.
float H = size.y;
float2 px = float2(uv.x * size.x, (1.0 - uv.y) * H);
float sp = 0.78 * H;
float P = sp * 4.0;
float head = progress * size.x;
float played = smoothstep(head + 0.4 * H, head - 0.4 * H, px.x);
float3 acc = float3(0, 0, 0);
float cover = 0;
// The wire: two strands twisted round each other, warmed by the lit bulbs.
float2 w = wire(px.x, P, H);
float n = sqrt(1.0 + w.y * w.y);
float tw = sin(px.x * 6.2832 / (0.16 * H));
float th = max(0.5, 0.014 * H);
float s1 = abs(px.y - w.x - 0.02 * H * tw) / n;
float s2 = abs(px.y - w.x + 0.02 * H * tw) / n;
float3 wc = lerp(float3(0.07, 0.13, 0.08), float3(0.3, 0.26, 0.17), played);
layer(acc, cover, wc * 0.7, smoothstep(th + 0.5, th - 0.5, s2));
layer(acc, cover, wc * (1.0 + 0.6 * step(0.0, tw)), smoothstep(th + 0.5, th - 0.5, s1));
// The two nearest bulbs: the neighbor first, then this one on top.
float i = floor(px.x / sp);
float j = i + (frac(px.x / sp) > 0.5 ? 1.0 : -1.0);
bulb(acc, cover, px, j, sp, P, H, head);
bulb(acc, cover, px, i, sp, P, H, head);
return paint(acc / max(cover, 0.001), cover);
}