diff --git a/shaders/kik.glsl b/shaders/kik.glsl index 1a35e1f..28ad80f 100644 --- a/shaders/kik.glsl +++ b/shaders/kik.glsl @@ -1,21 +1,24 @@ // Khaos im Kiez: CRT terminal on the stairs (palette from berlin.ccc.de/kik) // -// Output lines scroll along the stairs, the prompt sits on the last active -// step with a blinking cursor, now and then a line is KiK red. +// Slow scenes crossfade into each other every ~26 s: +// terminal output, progress bar, radar sweep, rain, aurora. +// Fast things are rare events on top: glitch, fast scroll dump, red alert. const vec3 BG = vec3(0.008, 0.040, 0.016); // #020a04 const vec3 FG = vec3(0.200, 1.000, 0.400); // #33ff66 const vec3 DIM = vec3(0.122, 0.604, 0.247); // #1f9a3f const vec3 HOT = vec3(1.000, 0.165, 0.165); // #ff2a2a -const float PROMPT_STEP = 12.0; // last step trepped drives -const float SCROLL_SPEED = 1.6; // lines per second +const float LAST = 12.0; // last step trepped drives +const float SCENE_LEN = 26.0; // seconds per scene +const float SCENE_FADE = 2.5; // crossfade at the end of a scene +const float EVENT_SLOT = 45.0; // at most one fast event per slot float hash(float n) { return fract(sin(n * 12.9898) * 43758.5453); } -// What line number n of the output looks like +// Line number n of terminal output vec3 line(float n) { float h = hash(n); if (h < 0.15) return BG; // blank line @@ -24,33 +27,152 @@ vec3 line(float n) { return mix(DIM * 0.5, FG, len * len); } +// --- Slow scenes ----------------------------------------------------------- + +// 0: terminal output, a new line every ~2 s, prompt and cursor at the end +vec3 sceneTerminal(float i, float t) { + if (i < LAST - 1.0) { + float s = t * 0.45; + float scroll = floor(s) + smoothstep(0.5, 1.0, fract(s)); + float n = i + scroll; + return line(n) + 0.10 * (line(n - 1.0) + line(n + 1.0)); + } + if (i < LAST) return DIM * 0.9; // visitor@khaos-im-kiez:~$ + return fract(t / 1.1) < 0.5 ? FG : BG; // blinking cursor, as on the site +} + +// 1: progress bar filling up over ~14 s, then a slow double pulse +vec3 sceneProgress(float i, float t) { + float ph = fract(t / 18.0); + float fill = min(ph / 0.78, 1.0) * (LAST + 1.0); + float d = fill - (i + 0.5); + vec3 col = BG + DIM * 0.06; // empty track + if (d > 0.0) { + col = mix(FG, DIM * 0.7, smoothstep(0.0, 2.0, d)); + } + if (ph > 0.78) { + float p = (ph - 0.78) / 0.22; + col = mix(col, FG, 0.5 + 0.5 * sin(p * 12.566 - 1.57)) * (1.0 - 0.6 * p); + } + return col; +} + +// 2: radar, a blip sweeps back and forth leaving a phosphor trail, +// now and then a red contact appears and fades +vec3 sceneRadar(float i, float t) { + float pos = 6.0 + 6.0 * sin(t * 0.7); + float dir = cos(t * 0.7); + float d = i - pos; + float behind = -sign(dir) * d; + float trail = behind > 0.0 ? exp(-behind * 0.45) : 0.0; + float head = exp(-d * d * 1.2); + vec3 col = BG + DIM * 0.05 + DIM * 0.55 * trail + FG * head; + + float k = floor(t / 7.0); + float cpos = floor(hash(k + 40.0) * (LAST + 1.0)); + float life = sin(3.1416 * fract(t / 7.0)); + col += HOT * 0.8 * life * life * step(abs(i - cpos), 0.5); + return col; +} + +// 3: rain, three drops with fading trails at different speeds +vec3 sceneRain(float i, float t) { + vec3 col = BG + DIM * 0.04; + for (int k = 0; k < 3; k++) { + float fk = float(k); + float speed = 0.9 + 0.5 * fk; + float pos = mod(t * speed + fk * 5.3, 20.0); + float behind = pos - i; + if (behind >= 0.0 && behind < 8.0) { + col += mix(DIM * 0.6, FG, step(behind, 0.7)) * exp(-behind * 0.6); + } + } + return col; +} + +// 4: aurora, slow drifting green waves with a faint red haze +vec3 sceneAurora(float i, float t) { + float w1 = 0.5 + 0.5 * sin(i * 0.5 - t * 0.35); + float w2 = 0.5 + 0.5 * sin(i * 0.31 + t * 0.23); + vec3 col = mix(DIM * 0.35, FG * 0.8, w1) * (0.55 + 0.45 * w2); + float hz = 6.0 + 7.0 * sin(t * 0.11); + col += HOT * 0.30 * exp(-(i - hz) * (i - hz) * 0.06); + return col; +} + +vec3 scene(int id, float i, float t) { + if (id == 0) return sceneTerminal(i, t); + if (id == 1) return sceneProgress(i, t); + if (id == 2) return sceneRadar(i, t); + if (id == 3) return sceneRain(i, t); + return sceneAurora(i, t); +} + +int sceneId(float k) { + // walks through all five scenes, shifted randomly every five slots + return int(mod(k * 3.0 + floor(hash(floor(k / 5.0)) * 5.0), 5.0)); +} + +// --- Rare fast events ------------------------------------------------------ + +vec3 events(vec3 col, float i, float t) { + float k = floor(t / EVENT_SLOT); + float r = hash(k + 17.0); + float local = t - (k * EVENT_SLOT + 5.0 + hash(k + 31.0) * 30.0); + if (local < 0.0) return col; + + if (r < 0.22) { + // glitch, 0.7 s of flickering steps + if (local < 0.7) { + float s = floor(local * 20.0); + float h = hash(i * 7.1 + s * 13.3); + if (h > 0.93) return HOT; + if (h > 0.70) return FG; + if (h < 0.20) return BG * 0.5; + } + } else if (r < 0.60) { + // dump, 2.5 s of fast scrolling output + if (local < 2.5) { + float e = smoothstep(0.0, 0.2, local) * (1.0 - smoothstep(2.2, 2.5, local)); + vec3 l = line(i + floor(local * 14.0) + 100.0); + return mix(col, l * 1.2, e); + } + } else if (r < 0.72) { + // red alert, a red bar sweeps over the stairs + if (local < 1.6) { + float e = sin(3.1416 * local / 1.6); + float d = i - (local / 1.6 * 16.0 - 1.0); + col = mix(col, col * 0.3 + HOT * 0.12, e); + col += HOT * exp(-d * d * 0.5); + } + } + return col; +} + void mainImage(out vec4 fragColor, in vec2 fragCoord) { float i = floor(fragCoord.x); - float s = iTime * SCROLL_SPEED; - float scroll = floor(s) + smoothstep(0.75, 1.0, fract(s)); + float t = iTime; - vec3 col; - if (i < PROMPT_STEP - 1.0) { - // output area, lines move towards step 0 - float n = i + scroll; - col = line(n); - // glow bleeding from neighbouring lines - col += 0.10 * (line(n - 1.0) + line(n + 1.0)); - } else if (i < PROMPT_STEP) { - col = DIM * 0.9; // "visitor@khaos-im-kiez:~$" - } else if (i < PROMPT_STEP + 1.0) { - // cursor, blinks with steps(1) 1.1s like the site - col = fract(iTime / 1.1) < 0.5 ? FG : BG; - } else { - col = vec3(0.0); // not driven by trepped + float k = floor(t / SCENE_LEN); + float f = fract(t / SCENE_LEN); + float m = smoothstep(1.0 - SCENE_FADE / SCENE_LEN, 1.0, f); + + vec3 col = scene(sceneId(k), i, t); + if (m > 0.0) { + col = mix(col, scene(sceneId(k + 1.0), i, t), m); } - // rolling scanline - float band = fract(i / 16.0 * 2.0 - iTime * 0.15); + col = events(col, i, t); + + // rolling scanline, slow + float band = fract(i / 16.0 * 2.0 - t * 0.15); col *= 1.0 - 0.35 * smoothstep(0.0, 0.1, band) * (1.0 - smoothstep(0.1, 0.25, band)); // slight CRT flicker - col *= 0.96 + 0.04 * hash(floor(iTime * 30.0)); + col *= 0.96 + 0.04 * hash(floor(t * 30.0)); + + // steps trepped does not drive + if (i > LAST) col = vec3(0.0); fragColor = vec4(col, 1.0); }