par-term 0.38.0

Cross-platform GPU-accelerated terminal emulator with inline graphics support (Sixel, iTerm2, Kitty)
Documentation
/*! par-term shader metadata
name: convergence
author: null
description: null
version: 1.0.0
defaults:
  animation_speed: 0.5
  channel0: ''
  channel1: null
  channel2: null
  channel3: null
  cubemap: ''
  cubemap_enabled: false
  use_background_as_channel0: null
  uniforms:
    bolt_color: '#cce6ff'
    divider_position: 0.5
    glow_width: 0.00049999997
    left_color: '#00eeff'
    noise_scale: 6.0
    noise_speed: 5.0
    right_color: '#ff9494'
    split_intesity: 0.010000001
*/

// Convergence shader - Optimized
// Adapted from https://www.shadertoy.com/view/wXyyRD
// Reference: https://www.shadertoy.com/view/WXycRW

// control slider min=0.1 max=24 step=0.1 scale=log label="Noise Scale"
uniform float noise_scale;
// control slider min=0 max=20 step=0.1 label="Noise Speed"
uniform float noise_speed;
// control slider min=0 max=0.08 step=0.001 label="Split Intensity"
uniform float split_intesity;
// control slider min=0.00005 max=0.01 step=0.00005 scale=log label="Glow Width"
uniform float glow_width;
// control slider min=0.05 max=0.95 step=0.001 label="Divider Position"
uniform float divider_position;
// control color label="Bolt Color"
uniform vec3 bolt_color;
// control color label="Left Color"
uniform vec4 left_color;
// control color label="Right Color"
uniform vec4 right_color;

const vec4 LEFT_COLOR_GAIN = vec4(17.0, 17.0, 17.0, 1.0);
const vec4 RIGHT_COLOR_GAIN = vec4(19.0, 19.0, 19.0, 1.0);

float hash(float n) {
    return fract(sin(n) * 43758.5453123);
}

float noise(vec2 x) {
    vec2 p = floor(x);
    vec2 f = fract(x);
    f = f * f * (3.0 - 2.0 * f);
    float n = p.x + p.y * 57.0;
    return mix(mix(hash(n), hash(n + 1.0), f.x),
               mix(hash(n + 57.0), hash(n + 58.0), f.x), f.y);
}

float fbm(vec2 p) {
    float f = 0.0;
    float w = 0.5;
    for (int i = 0; i < 5; i++) {
        f += w * noise(p);
        p *= 2.0;
        w *= 0.5;
    }
    return f;
}

// Original voronoise - kept intact for visual accuracy
float voronoise_proc(vec2 p) {
    float d = 1000.0;
    vec2 q = fract(p);
    vec2 f = floor(p);

    for (float x = -0.5; x <= 0.5; x++)
    for (float y = -0.5; y <= 0.5; y++) {
        vec2 h = vec2(x, y);
        vec2 noiseVal = vec2(
            hash(dot(f + h, vec2(127.1, 311.7))),
            hash(dot(f + h, vec2(269.5, 183.3)))
        );
        d = min(d, length(q - h - noiseVal));
    }
    return d;
}

mat2 rot(float x) {
    float c = cos(x), s = sin(x);
    return mat2(c, s, -s, c);
}

// BufferA effect - left side with inward time flow
vec4 calcBufferA(vec2 fragCoord, vec2 r) {
    vec2 p = (fragCoord + fragCoord - r) / sqrt(r.x * r.y);

    if (length(p) < 0.1) {
        return vec4(0.0);
    }

    p /= dot(p, p) * 2.1;
    p *= rot(pow(dot(p, p), 1.5) * 0.4);

    vec4 c = vec4(0.0);
    float t = -iTime * 7.0;

    for (float n = fract(t); n < 24.0; n++) {
        float noiseOffset = hash(floor(n - floor(t)) * 127.1) * 256.0;
        c += pow(voronoise_proc(p * n * 0.4 + noiseOffset) * 1000.0 + 10.0, -1.0)
             * clamp(1.0 - n / 22.0, 0.0, 1.0)
             * clamp(n, 0.0, 1.0);
    }

    c += dot(p, p) * 0.07;
    c *= left_color * LEFT_COLOR_GAIN;
    c -= dot(p, p) * 0.6 * vec4(0.9, 1.2, 1.4, 0.0);
    c = max(c, 0.0);
    c *= smoothstep(1.0, 0.0, length(p * 0.6) - 0.5);
    c *= c;
    return tanh(c);
}

// BufferB effect - right side with outward time flow
vec4 calcBufferB(vec2 fragCoord, vec2 r) {
    vec2 p = (fragCoord + fragCoord - r) / sqrt(r.x * r.y);

    p /= dot(p, p) * 2.1;
    p *= rot(pow(dot(p, p), 1.5) * 0.4);

    vec4 c = vec4(0.0);
    float t = iTime * 7.0;

    for (float n = fract(t); n < 24.0; n++) {
        float noiseOffset = hash(floor(n - floor(t)) * 127.1) * 256.0;
        c += pow(voronoise_proc(p * n * 0.4 + noiseOffset) * 1000.0 + 10.0, -1.0)
             * clamp(1.0 - n / 22.0, 0.0, 1.0)
             * clamp(n, 0.0, 1.0);
    }

    c += dot(p, p) * 0.07;
    c *= right_color * RIGHT_COLOR_GAIN;
    c -= sin(dot(p, p) * 0.5 * vec4(p.x - 1.0, length(p.y * 0.01), 0.5, 0.0));
    c += length(p * 0.01) - 0.5;
    c *= smoothstep(1.0, 0.0, length(p * 0.6) - 0.5);
    c *= c;
    return tanh(c);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 r = iResolution.xy;
    float splitPos = divider_position;

    // Lightning displacement
    vec2 noiseUV = (fragCoord - 0.5 * r) / r.y;
    float noiseTime = iTime * noise_speed;
    float displacement = fbm(noiseUV * noise_scale + vec2(noiseTime, noiseTime * 0.5));
    displacement = (displacement - 0.5) * 2.0;
    float dist = (fragCoord.x / r.x) - splitPos + displacement * split_intesity;

    // KEY OPTIMIZATION: Only calculate the buffer we actually need
    vec4 color = (dist < 0.0) ? calcBufferA(fragCoord, r) : calcBufferB(fragCoord, r);

    // Lightning glow
    float glow = glow_width / abs(dist);
    glow = pow(clamp(glow, 0.0, 1.0), 1.2);
    glow *= (0.8 + 0.2 * sin(iTime * 20.0));
    color.rgb += bolt_color * glow * 4.0;

    fragColor = color;
}