initial commit

This commit is contained in:
mr0xb 2026-07-13 15:10:55 -04:00
commit f1374f54c4
46 changed files with 4121 additions and 0 deletions

View file

@ -0,0 +1,96 @@
#define PI 3.141592653589793
float hash12(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * 0.1031);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
float diamondSparkle(vec2 uv, float density, float threshold, float speed) {
vec2 g = uv * vec2(density, density * 0.55);
vec2 id = floor(g);
vec2 f = fract(g) - 0.5;
float n = hash12(id);
float gate = step(threshold, n);
float twinkle = pow(max(0.0, sin(iTime * speed + n * 6.2831853)), 36.0);
float diaD = abs(f.x) + abs(f.y);
float diamond = 1.0 - smoothstep(0.00, 0.10, diaD);
float vertical = (1.0 - smoothstep(0.00, 0.012, abs(f.x))) *
(1.0 - smoothstep(0.00, 0.38, abs(f.y)));
float horizontal = (1.0 - smoothstep(0.00, 0.012, abs(f.y))) *
(1.0 - smoothstep(0.00, 0.38, abs(f.x)));
return gate * twinkle * (diamond + 0.40 * (vertical + horizontal));
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord.xy / iResolution.xy;
vec2 px = 1.0 / iResolution.xy;
// Tiny VHS-ish horizontal wobble.
float wobble = sin(uv.y * 82.0 + iTime * 2.1) * 0.0008
+ sin(uv.y * 11.0 - iTime * 0.35) * 0.0012;
vec2 uvR = clamp(uv + vec2(wobble + px.x * 1.25, 0.0), vec2(0.001), vec2(0.999));
vec2 uvG = clamp(uv + vec2(wobble, 0.0), vec2(0.001), vec2(0.999));
vec2 uvB = clamp(uv + vec2(wobble - px.x * 1.25, 0.0), vec2(0.001), vec2(0.999));
vec4 base = texture(iChannel0, uvG);
vec3 src = vec3(
texture(iChannel0, uvR).r,
base.g,
texture(iChannel0, uvB).b
);
float lum = dot(src, vec3(0.299, 0.587, 0.114));
float ink = smoothstep(0.03, 0.22, lum);
float text = smoothstep(0.20, 0.78, lum);
// Cash-money palette: velvet black, dark leather brown, hot gold, pale diamond gold.
vec3 velvet = vec3(0.014, 0.008, 0.003);
vec3 brown = vec3(0.085, 0.044, 0.012);
vec3 gold = vec3(1.00, 0.66, 0.13);
vec3 paleGold = vec3(1.00, 0.90, 0.55);
vec3 bg = mix(
velvet,
brown,
uv.y + 0.20 * sin(uv.x * 3.0 + iTime * 0.10)
);
vec3 goldText = mix(gold * 0.75, paleGold, pow(lum, 0.75));
vec3 col = mix(bg, mix(src, goldText + src * 0.50, 0.48), ink);
// Jewelry-store light sweep / chrome-grille reflection.
float sweepA = pow(max(0.0, sin((uv.x * 1.8 + uv.y * 0.75) * PI * 2.0 - iTime * 0.60)), 22.0);
float sweepB = pow(max(0.0, sin((uv.x * -1.1 + uv.y * 1.25) * PI * 2.0 + iTime * 0.42)), 30.0);
col += (sweepA * 0.16 + sweepB * 0.11) * vec3(1.0, 0.83, 0.42) * (0.35 + text);
// Thin old-video scan shimmer, subtle enough to keep text readable.
float scan = 0.965 + 0.035 * sin(fragCoord.y * PI);
float vhs = 0.98 + 0.02 * hash12(vec2(floor(uv.y * 360.0), floor(iTime * 24.0)));
col *= scan * vhs;
// Random diamond/star hits.
float spark = diamondSparkle(uv, 74.0, 0.978, 4.5);
spark += 0.55 * diamondSparkle(uv + vec2(0.17, -0.11), 43.0, 0.970, 3.2);
col += spark * vec3(1.0, 0.88, 0.48) * (0.85 + 0.65 * text);
// Camera-flash blobs in the corners.
vec2 aspect = vec2(iResolution.x / iResolution.y, 1.0);
float flashL = 1.0 - smoothstep(0.00, 0.28, length((uv - vec2(0.14, 0.82)) * aspect));
float flashR = 1.0 - smoothstep(0.00, 0.34, length((uv - vec2(0.88, 0.18)) * aspect));
col += (flashL * 0.10 + flashR * 0.07) * vec3(1.0, 0.72, 0.22);
// Music-video edge falloff.
float vig = 1.0 - smoothstep(0.20, 0.82, distance(uv, vec2(0.5)));
col *= 0.80 + 0.20 * vig;
// Legibility save: blend some original terminal text back in.
col = mix(col, src, 0.18 * text);
fragColor = vec4(clamp(col, 0.0, 1.0), base.a);
}

View file

@ -0,0 +1,52 @@
// source: https://gist.github.com/qwerasd205/c3da6c610c8ffe17d6d2d3cc7068f17f
// credits: https://github.com/qwerasd205
// Golden spiral samples, [x, y, weight] weight is inverse of distance.
const vec3[24] samples = {
vec3(0.1693761725038636, 0.9855514761735895, 1),
vec3(-1.333070830962943, 0.4721463328627773, 0.7071067811865475),
vec3(-0.8464394909806497, -1.51113870578065, 0.5773502691896258),
vec3(1.554155680728463, -1.2588090085709776, 0.5),
vec3(1.681364377589461, 1.4741145918052656, 0.4472135954999579),
vec3(-1.2795157692199817, 2.088741103228784, 0.4082482904638631),
vec3(-2.4575847530631187, -0.9799373355024756, 0.3779644730092272),
vec3(0.5874641440200847, -2.7667464429345077, 0.35355339059327373),
vec3(2.997715703369726, 0.11704939884745152, 0.3333333333333333),
vec3(0.41360842451688395, 3.1351121305574803, 0.31622776601683794),
vec3(-3.167149933769243, 0.9844599011770256, 0.30151134457776363),
vec3(-1.5736713846521535, -3.0860263079123245, 0.2886751345948129),
vec3(2.888202648340422, -2.1583061557896213, 0.2773500981126146),
vec3(2.7150778983300325, 2.5745586041105715, 0.2672612419124244),
vec3(-2.1504069972377464, 3.2211410627650165, 0.2581988897471611),
vec3(-3.6548858794907493, -1.6253643308191343, 0.25),
vec3(1.0130775986052671, -3.9967078676335834, 0.24253562503633297),
vec3(4.229723673607257, 0.33081361055181563, 0.23570226039551587),
vec3(0.40107790291173834, 4.340407413572593, 0.22941573387056174),
vec3(-4.319124570236028, 1.159811599693438, 0.22360679774997896),
vec3(-1.9209044802827355, -4.160543952132907, 0.2182178902359924),
vec3(3.8639122286635708, -2.6589814382925123, 0.21320071635561041),
vec3(3.3486228404946234, 3.4331800232609, 0.20851441405707477),
vec3(-2.8769733643574344, 3.9652268864187157, 0.20412414523193154)
};
float lum(vec4 c) {
return 0.299 * c.r + 0.587 * c.g + 0.114 * c.b;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord.xy / iResolution.xy;
vec4 color = texture(iChannel0, uv);
vec2 step = vec2(1.414) / iResolution.xy;
for (int i = 0; i < 24; i++) {
vec3 s = samples[i];
vec4 c = texture(iChannel0, uv + s.xy * step);
float l = lum(c);
if (l > 0.2) {
color += l * s.z * c * 0.2;
}
}
fragColor = color;
}

View file

@ -0,0 +1,43 @@
#define PI 3.141592653589793
float diamondAt(vec2 uv, vec2 c, float r) {
vec2 q = (uv - c) * vec2(iResolution.x / iResolution.y, 1.0);
return 1.0 - smoothstep(r * 0.20, r, abs(q.x) + abs(q.y));
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord.xy / iResolution.xy;
vec4 base = texture(iChannel0, uv);
vec3 col = base.rgb;
float edgeD = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y));
float frame = 1.0 - smoothstep(0.0, 0.055, edgeD);
float chrome = pow(
0.5 + 0.5 * sin((uv.x * 5.0 + uv.y * 3.0) * PI * 2.0 - iTime * 2.0),
3.0
);
float bead = pow(
0.5 + 0.5 * sin((uv.x + uv.y) * 120.0 + iTime * 1.7),
14.0
);
vec3 darkGold = vec3(0.23, 0.11, 0.02);
vec3 hotGold = vec3(1.00, 0.82, 0.30);
vec3 frameColor = mix(darkGold, hotGold, chrome);
col = mix(col, col + frameColor * (0.20 + 0.35 * bead), frame * 0.55);
// Big fake diamonds in the corners.
float corners = 0.0;
corners += diamondAt(uv, vec2(0.035, 0.045), 0.040);
corners += diamondAt(uv, vec2(0.965, 0.045), 0.040);
corners += diamondAt(uv, vec2(0.035, 0.955), 0.040);
corners += diamondAt(uv, vec2(0.965, 0.955), 0.040);
float twinkle = 0.75 + 0.25 * sin(iTime * 5.0);
col += corners * vec3(1.0, 0.90, 0.55) * twinkle * 0.80;
fragColor = vec4(clamp(col, 0.0, 1.0), base.a);
}

View file

@ -0,0 +1,114 @@
// credits: https://github.com/rymdlego
const float speed = 0.2;
const float cube_size = 1.0;
const float cube_brightness = 1.0;
const float cube_rotation_speed = 2.8;
const float camera_rotation_speed = 0.1;
mat3 rotationMatrix(vec3 m,float a) {
m = normalize(m);
float c = cos(a),s=sin(a);
return mat3(c+(1.-c)*m.x*m.x,
(1.-c)*m.x*m.y-s*m.z,
(1.-c)*m.x*m.z+s*m.y,
(1.-c)*m.x*m.y+s*m.z,
c+(1.-c)*m.y*m.y,
(1.-c)*m.y*m.z-s*m.x,
(1.-c)*m.x*m.z-s*m.y,
(1.-c)*m.y*m.z+s*m.x,
c+(1.-c)*m.z*m.z);
}
float sphere(vec3 pos, float radius)
{
return length(pos) - radius;
}
float box(vec3 pos, vec3 size)
{
float t = iTime;
pos = pos * 0.9 * rotationMatrix(vec3(sin(t/4.0*speed)*10.,cos(t/4.0*speed)*12.,2.7), t*2.4/4.0*speed*cube_rotation_speed);
return length(max(abs(pos) - size, 0.0));
}
float distfunc(vec3 pos)
{
float t = iTime;
float size = 0.45 + 0.25*abs(16.0*sin(t*speed/4.0));
// float size = 2.3 + 1.8*tan((t-5.4)*6.549);
size = cube_size * 0.16 * clamp(size, 2.0, 4.0);
//pos = pos * rotationMatrix(vec3(0.,-3.,0.7), 3.3 * mod(t/30.0, 4.0));
vec3 q = mod(pos, 5.0) - 2.5;
float obj1 = box(q, vec3(size));
return obj1;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
float t = iTime;
vec2 screenPos = -1.0 + 2.0 * fragCoord.xy / iResolution.xy;
screenPos.x *= iResolution.x / iResolution.y;
vec3 cameraOrigin = vec3(t*1.0*speed, 0.0, 0.0);
// vec3 cameraOrigin = vec3(t*1.8*speed, 3.0+t*0.02*speed, 0.0);
vec3 cameraTarget = vec3(t*100., 0.0, 0.0);
cameraTarget = vec3(t*20.0,0.0,0.0) * rotationMatrix(vec3(0.0,0.0,1.0), t*speed*camera_rotation_speed);
vec3 upDirection = vec3(0.5, 1.0, 0.6);
vec3 cameraDir = normalize(cameraTarget - cameraOrigin);
vec3 cameraRight = normalize(cross(upDirection, cameraOrigin));
vec3 cameraUp = cross(cameraDir, cameraRight);
vec3 rayDir = normalize(cameraRight * screenPos.x + cameraUp * screenPos.y + cameraDir);
const int MAX_ITER = 64;
const float MAX_DIST = 48.0;
const float EPSILON = 0.001;
float totalDist = 0.0;
vec3 pos = cameraOrigin;
float dist = EPSILON;
for (int i = 0; i < MAX_ITER; i++)
{
if (dist < EPSILON || totalDist > MAX_DIST)
break;
dist = distfunc(pos);
totalDist += dist;
pos += dist*rayDir;
}
vec4 cubes;
if (dist < EPSILON)
{
// Lighting Code
vec2 eps = vec2(0.0, EPSILON);
vec3 normal = normalize(vec3(
distfunc(pos + eps.yxx) - distfunc(pos - eps.yxx),
distfunc(pos + eps.xyx) - distfunc(pos - eps.xyx),
distfunc(pos + eps.xxy) - distfunc(pos - eps.xxy)));
float diffuse = max(0., dot(-rayDir, normal));
float specular = pow(diffuse, 32.0);
vec3 color = vec3(diffuse + specular);
vec3 cubeColor = vec3(abs(screenPos),0.5+0.5*sin(t*2.0))*0.8;
cubeColor = mix(cubeColor.rgb, vec3(0.0,0.0,0.0), 1.0);
color += cubeColor;
cubes = vec4(color, 1.0) * vec4(1.0 - (totalDist/MAX_DIST));
cubes = vec4(cubes.rgb*0.02*cube_brightness, 0.1);
}
else {
cubes = vec4(0.0);
}
vec2 uv = fragCoord/iResolution.xy;
vec4 terminalColor = texture(iChannel0, uv);
vec3 blendedColor = terminalColor.rgb + cubes.rgb;
fragColor = vec4(blendedColor, terminalColor.a);
}

View file

@ -0,0 +1,221 @@
// sRGB -> Linear conversion (needed because Ghostty passes sRGB values but the shader pipeline operates in linear color space)
vec3 sRGBToLinear(vec3 c) {
return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(vec3(0.04045), c));
}
// --- CONFIGURATION ---
vec4 TRAIL_COLOR = vec4(sRGBToLinear(iCurrentCursorColor.rgb), iCurrentCursorColor.a); // for custom color: vec4(0.2, 0.6, 1.0, 0.5); (wrap in sRGBToLinear for correct brightness)
const float DURATION = 0.2; // in seconds
const float TRAIL_LENGTH = 0.5;
const float BLUR = 2.0; // blur size in pixels (for antialiasing)
// --- CONSTANTS for easing functions ---
const float PI = 3.14159265359;
const float C1_BACK = 1.70158;
const float C2_BACK = C1_BACK * 1.525;
const float C3_BACK = C1_BACK + 1.0;
const float C4_ELASTIC = (2.0 * PI) / 3.0;
const float C5_ELASTIC = (2.0 * PI) / 4.5;
const float SPRING_STIFFNESS = 9.0;
const float SPRING_DAMPING = 0.9;
// --- EASING FUNCTIONS ---
// // Linear
// float ease(float x) {
// return x;
// }
// // EaseOutQuad
// float ease(float x) {
// return 1.0 - (1.0 - x) * (1.0 - x);
// }
// EaseOutCubic
float ease(float x) {
return 1.0 - pow(1.0 - x, 3.0);
}
// // EaseOutQuart
// float ease(float x) {
// return 1.0 - pow(1.0 - x, 4.0);
// }
// // EaseOutQuint
// float ease(float x) {
// return 1.0 - pow(1.0 - x, 5.0);
// }
// EaseOutSine
// float ease(float x) {
// return sin((x * PI) / 2.0);
// }
// // EaseOutExpo
// float ease(float x) {
// return x == 1.0 ? 1.0 : 1.0 - pow(2.0, -10.0 * x);
// }
// // EaseOutCirc
// float ease(float x) {
// return sqrt(1.0 - pow(x - 1.0, 2.0));
// }
// // EaseOutBack
// float ease(float x) {
// return 1.0 + C3_BACK * pow(x - 1.0, 3.0) + C1_BACK * pow(x - 1.0, 2.0);
// }
// // EaseOutElastic
// float ease(float x) {
// return x == 0.0 ? 0.0
// : x == 1.0 ? 1.0
// : pow(2.0, -10.0 * x) * sin((x * 10.0 - 0.75) * C4_ELASTIC) + 1.0;
// }
// Parametric Spring
// float ease(float x) {
// x = clamp(x, 0.0, 1.0);
// float decay = exp(-SPRING_DAMPING * SPRING_STIFFNESS * x);
// float freq = sqrt(SPRING_STIFFNESS * (1.0 - SPRING_DAMPING * SPRING_DAMPING));
// float osc = cos(freq * 6.283185 * x) + (SPRING_DAMPING * sqrt(SPRING_STIFFNESS) / freq) * sin(freq * 6.283185 * x);
// return 1.0 - decay * osc;
// }
float getSdfRectangle(in vec2 point, in vec2 center, in vec2 halfSize)
{
vec2 d = abs(point - center) - halfSize;
return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
}
// Based on Inigo Quilez's 2D distance functions article: https://iquilezles.org/articles/distfunctions2d/
// Potencially optimized by eliminating conditionals and loops to enhance performance and reduce branching
float seg(in vec2 p, in vec2 a, in vec2 b, inout float s, float d) {
vec2 e = b - a;
vec2 w = p - a;
vec2 proj = a + e * clamp(dot(w, e) / dot(e, e), 0.0, 1.0);
float segd = dot(p - proj, p - proj);
d = min(d, segd);
float c0 = step(0.0, p.y - a.y);
float c1 = 1.0 - step(0.0, p.y - b.y);
float c2 = 1.0 - step(0.0, e.x * w.y - e.y * w.x);
float allCond = c0 * c1 * c2;
float noneCond = (1.0 - c0) * (1.0 - c1) * (1.0 - c2);
float flip = mix(1.0, -1.0, step(0.5, allCond + noneCond));
s *= flip;
return d;
}
float getSdfParallelogram(in vec2 p, in vec2 v0, in vec2 v1, in vec2 v2, in vec2 v3) {
float s = 1.0;
float d = dot(p - v0, p - v0);
d = seg(p, v0, v3, s, d);
d = seg(p, v1, v0, s, d);
d = seg(p, v2, v1, s, d);
d = seg(p, v3, v2, s, d);
return s * sqrt(d);
}
vec2 normalize(vec2 value, float isPosition) {
return (value * 2.0 - (iResolution.xy * isPosition)) / iResolution.y;
}
float antialising(float distance) {
return 1. - smoothstep(0., normalize(vec2(BLUR, BLUR), 0.).x, distance);
}
float getTopVertexFlag(vec2 a, vec2 b) {
float condition1 = step(b.x, a.x) * step(a.y, b.y); // a.x < b.x && a.y > b.y
float condition2 = step(a.x, b.x) * step(b.y, a.y); // a.x > b.x && a.y < b.y
// if neither condition is met, return 1 (else case)
return 1.0 - max(condition1, condition2);
}
vec2 getRectangleCenter(vec4 rectangle) {
return vec2(rectangle.x + (rectangle.z / 2.), rectangle.y - (rectangle.w / 2.));
}
void mainImage(out vec4 fragColor, in vec2 fragCoord){
#if !defined(WEB)
fragColor = texture(iChannel0, fragCoord.xy / iResolution.xy);
#endif
// normalization & setup(-1, 1 coords)
vec2 vu = normalize(fragCoord, 1.);
vec2 offsetFactor = vec2(-.5, 0.5);
vec4 currentCursor = vec4(normalize(iCurrentCursor.xy, 1.), normalize(iCurrentCursor.zw, 0.));
vec4 previousCursor = vec4(normalize(iPreviousCursor.xy, 1.), normalize(iPreviousCursor.zw, 0.));
vec2 centerCC = currentCursor.xy - (currentCursor.zw * offsetFactor);
vec2 centerCP = previousCursor.xy - (previousCursor.zw * offsetFactor);
float sdfCurrentCursor = getSdfRectangle(vu, centerCC, currentCursor.zw * 0.5);
float lineLength = distance(centerCC, centerCP);
vec4 newColor = vec4(fragColor);
float minDist = currentCursor.w * 1.5;
float progress = clamp((iTime - iTimeCursorChange) / DURATION, 0.0, 1.0);
if (lineLength > minDist) {
// --- Animation Logic ---
float shrinkFactor = ease(progress);
// detect straight moves
vec2 delta = abs(centerCC - centerCP);
float threshold = 0.001;
float isHorizontal = step(delta.y, threshold);
float isVertical = step(delta.x, threshold);
float isStraightMove = max(isHorizontal, isVertical);
// -- Making parallelogram sdf (diagonal moves) ---
float topVertexFlag = getTopVertexFlag(currentCursor.xy, previousCursor.xy);
float bottomVertexFlag = 1.0 - topVertexFlag;
vec2 v0 = vec2(currentCursor.x + currentCursor.z * topVertexFlag, currentCursor.y - currentCursor.w);
vec2 v1 = vec2(currentCursor.x + currentCursor.z * bottomVertexFlag, currentCursor.y);
vec2 v2_full = vec2(previousCursor.x + currentCursor.z * bottomVertexFlag, previousCursor.y);
vec2 v3_full = vec2(previousCursor.x + currentCursor.z * topVertexFlag, previousCursor.y - previousCursor.w);
vec2 v2_start = mix(v1, v2_full, TRAIL_LENGTH);
vec2 v3_start = mix(v0, v3_full, TRAIL_LENGTH);
vec2 v2_anim = mix(v2_start, v1, shrinkFactor);
vec2 v3_anim = mix(v3_start, v0, shrinkFactor);
float sdfTrail_diag = getSdfParallelogram(vu, v0, v1, v2_anim, v3_anim);
// --- Making rectangle sdf (straight moves) ---
vec2 min_center = min(centerCP, centerCC);
vec2 max_center = max(centerCP, centerCC);
vec2 bBoxSize_full = (max_center - min_center) + currentCursor.zw;
vec2 bBoxCenter_full = (min_center + max_center) * 0.5;
vec2 bBoxSize_start = mix(currentCursor.zw, bBoxSize_full, TRAIL_LENGTH);
vec2 bBoxCenter_start = mix(centerCC, bBoxCenter_full, TRAIL_LENGTH);
vec2 animSize = mix(bBoxSize_start, currentCursor.zw, shrinkFactor);
vec2 animCenter = mix(bBoxCenter_start, centerCC, shrinkFactor);
float sdfTrail_rect = getSdfRectangle(vu, animCenter, animSize * 0.5);
// -- Selecting and drawing the trail sdf --
float sdfTrail = mix(sdfTrail_diag, sdfTrail_rect, isStraightMove);
vec4 trail = TRAIL_COLOR;
float trailAlpha = antialising(sdfTrail);
newColor = mix(newColor, trail, trailAlpha);
// Punch hole
newColor = mix(newColor, fragColor, step(sdfCurrentCursor, 0.));
}
fragColor = newColor;
}

View file

@ -0,0 +1,239 @@
// sRGB -> Linear conversion (needed because Ghostty passes sRGB values but the shader pipeline operates in linear color space)
vec3 sRGBToLinear(vec3 c) {
return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(vec3(0.04045), c));
}
// --- CONFIGURATION ---
vec4 TRAIL_COLOR = vec4(sRGBToLinear(iCurrentCursorColor.rgb), iCurrentCursorColor.a); // for custom color: vec4(0.2, 0.6, 1.0, 0.5); (wrap in sRGBToLinear for correct brightness)
const float DURATION = 0.09; // in seconds
const float MAX_TRAIL_LENGTH = 0.2;
const float THRESHOLD_MIN_DISTANCE = 1.5; // min distance to show trail (units of cursor width)
const float BLUR = 2.0; // blur size in pixels (for antialiasing)
// --- CONSTANTS for easing functions ---
const float PI = 3.14159265359;
const float C1_BACK = 1.70158;
const float C2_BACK = C1_BACK * 1.525;
const float C3_BACK = C1_BACK + 1.0;
const float C4_ELASTIC = (2.0 * PI) / 3.0;
const float C5_ELASTIC = (2.0 * PI) / 4.5;
const float SPRING_STIFFNESS = 9.0;
const float SPRING_DAMPING = 0.9;
// --- EASING FUNCTIONS ---
// // Linear
// float ease(float x) {
// return x;
// }
// // EaseOutQuad
// float ease(float x) {
// return 1.0 - (1.0 - x) * (1.0 - x);
// }
// // EaseOutCubic
// float ease(float x) {
// return 1.0 - pow(1.0 - x, 3.0);
// }
// // EaseOutQuart
// float ease(float x) {
// return 1.0 - pow(1.0 - x, 4.0);
// }
// // EaseOutQuint
// float ease(float x) {
// return 1.0 - pow(1.0 - x, 5.0);
// }
// // EaseOutSine
// float ease(float x) {
// return sin((x * PI) / 2.0);
// }
// // EaseOutExpo
// float ease(float x) {
// return x == 1.0 ? 1.0 : 1.0 - pow(2.0, -10.0 * x);
// }
// EaseOutCirc
float ease(float x) {
return sqrt(1.0 - pow(x - 1.0, 2.0));
}
// // EaseOutBack
// float ease(float x) {
// return 1.0 + C3_BACK * pow(x - 1.0, 3.0) + C1_BACK * pow(x - 1.0, 2.0);
// }
// // EaseOutElastic
// float ease(float x) {
// return x == 0.0 ? 0.0
// : x == 1.0 ? 1.0
// : pow(2.0, -10.0 * x) * sin((x * 10.0 - 0.75) * C4_ELASTIC) + 1.0;
// }
// Parametric Spring
// float ease(float x) {
// x = clamp(x, 0.0, 1.0);
// float decay = exp(-SPRING_DAMPING * SPRING_STIFFNESS * x);
// float freq = sqrt(SPRING_STIFFNESS * (1.0 - SPRING_DAMPING * SPRING_DAMPING));
// float osc = cos(freq * 6.283185 * x) + (SPRING_DAMPING * sqrt(SPRING_STIFFNESS) / freq) * sin(freq * 6.283185 * x);
// return 1.0 - decay * osc;
// }
float getSdfRectangle(in vec2 p, in vec2 xy, in vec2 b)
{
vec2 d = abs(p - xy) - b;
return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
}
// Based on Inigo Quilez's 2D distance functions article: https://iquilezles.org/articles/distfunctions2d/
// Potencially optimized by eliminating conditionals and loops to enhance performance and reduce branching
float seg(in vec2 p, in vec2 a, in vec2 b, inout float s, float d) {
vec2 e = b - a;
vec2 w = p - a;
vec2 proj = a + e * clamp(dot(w, e) / dot(e, e), 0.0, 1.0);
float segd = dot(p - proj, p - proj);
d = min(d, segd);
float c0 = step(0.0, p.y - a.y);
float c1 = 1.0 - step(0.0, p.y - b.y);
float c2 = 1.0 - step(0.0, e.x * w.y - e.y * w.x);
float allCond = c0 * c1 * c2;
float noneCond = (1.0 - c0) * (1.0 - c1) * (1.0 - c2);
float flip = mix(1.0, -1.0, step(0.5, allCond + noneCond));
s *= flip;
return d;
}
float getSdfParallelogram(in vec2 p, in vec2 v0, in vec2 v1, in vec2 v2, in vec2 v3) {
float s = 1.0;
float d = dot(p - v0, p - v0);
d = seg(p, v0, v3, s, d);
d = seg(p, v1, v0, s, d);
d = seg(p, v2, v1, s, d);
d = seg(p, v3, v2, s, d);
return s * sqrt(d);
}
vec2 normalize(vec2 value, float isPosition) {
return (value * 2.0 - (iResolution.xy * isPosition)) / iResolution.y;
}
float antialising(float distance) {
return 1. - smoothstep(0., normalize(vec2(BLUR, BLUR), 0.).x, distance);
}
float determineIfTopRightIsLeading(vec2 a, vec2 b) {
float condition1 = step(b.x, a.x) * step(a.y, b.y); // a.x < b.x && a.y > b.y
float condition2 = step(a.x, b.x) * step(b.y, a.y); // a.x > b.x && a.y < b.y
// if neither condition is met, return 1 (else case)
return 1.0 - max(condition1, condition2);
}
vec2 getRectangleCenter(vec4 rectangle) {
return vec2(rectangle.x + (rectangle.z / 2.), rectangle.y - (rectangle.w / 2.));
}
void mainImage(out vec4 fragColor, in vec2 fragCoord){
#if !defined(WEB)
fragColor = texture(iChannel0, fragCoord.xy / iResolution.xy);
#endif
// normalization & setup(-1, 1 coords)
vec2 vu = normalize(fragCoord, 1.);
vec2 offsetFactor = vec2(-.5, 0.5);
vec4 currentCursor = vec4(normalize(iCurrentCursor.xy, 1.), normalize(iCurrentCursor.zw, 0.));
vec4 previousCursor = vec4(normalize(iPreviousCursor.xy, 1.), normalize(iPreviousCursor.zw, 0.));
vec2 centerCC = currentCursor.xy - (currentCursor.zw * offsetFactor);
vec2 centerCP = previousCursor.xy - (previousCursor.zw * offsetFactor);
vec2 delta = centerCP - centerCC;
float lineLength = length(delta);
float sdfCurrentCursor = getSdfRectangle(vu, centerCC, currentCursor.zw * 0.5);
vec4 newColor = vec4(fragColor);
float minDist = currentCursor.w * THRESHOLD_MIN_DISTANCE;
float progress = clamp((iTime - iTimeCursorChange) / DURATION, 0.0, 1.0);
if (lineLength > minDist) {
// ANIMATION logic
float head_eased = 0.0;
float tail_eased = 0.0;
float tail_delay_factor = MAX_TRAIL_LENGTH / lineLength;
float isLongMove = step(MAX_TRAIL_LENGTH, lineLength);
float head_eased_short = ease(progress);
float tail_eased_short = ease(smoothstep(tail_delay_factor, 1.0, progress));
float head_eased_long = 1.0;
float tail_eased_long = ease(progress);
head_eased = mix(head_eased_long, head_eased_short, isLongMove);
tail_eased = mix(tail_eased_long, tail_eased_short, isLongMove);
// detect straight moves
vec2 delta_abs = abs(centerCC - centerCP);
float threshold = 0.001;
float isHorizontal = step(delta_abs.y, threshold);
float isVertical = step(delta_abs.x, threshold);
float isStraightMove = max(isHorizontal, isVertical);
// -- Making the parallelogram sdf (diagonal move) --
// animate the TOP-LEFT corners
vec2 head_pos_tl = mix(previousCursor.xy, currentCursor.xy, head_eased);
vec2 tail_pos_tl = mix(previousCursor.xy, currentCursor.xy, tail_eased);
float isTopRightLeading = determineIfTopRightIsLeading(currentCursor.xy, previousCursor.xy);
float isBottomLeftLeading = 1.0 - isTopRightLeading;
// v0, v1 : "front" of the trail (head)
vec2 v0 = vec2(head_pos_tl.x + currentCursor.z * isTopRightLeading, head_pos_tl.y - currentCursor.w);
vec2 v1 = vec2(head_pos_tl.x + currentCursor.z * isBottomLeftLeading, head_pos_tl.y);
// v2, v3: "back" of the trail (tail)
vec2 v2 = vec2(tail_pos_tl.x + currentCursor.z * isBottomLeftLeading, tail_pos_tl.y);
vec2 v3 = vec2(tail_pos_tl.x + currentCursor.z * isTopRightLeading, tail_pos_tl.y - previousCursor.w);
float sdfTrail_diag = getSdfParallelogram(vu, v0, v1, v2, v3);
// -- Making the rectangle sdf (straight move) --
vec2 head_center = mix(centerCP, centerCC, head_eased);
vec2 tail_center = mix(centerCP, centerCC, tail_eased);
vec2 min_center = min(head_center, tail_center);
vec2 max_center = max(head_center, tail_center);
vec2 box_size = (max_center - min_center) + currentCursor.zw;
vec2 box_center = (min_center + max_center) * 0.5;
float sdfTrail_rect = getSdfRectangle(vu, box_center, box_size * 0.5);
// -- FINAL SELECTING AND DRAWING --
float sdfTrail = mix(sdfTrail_diag, sdfTrail_rect, isStraightMove);
vec4 trail = TRAIL_COLOR;
float trailAlpha = antialising(sdfTrail);
newColor = mix(newColor, trail, trailAlpha);
// punch hole
newColor = mix(newColor, fragColor, step(sdfCurrentCursor, 0.));
}
fragColor = newColor;
}

View file

@ -0,0 +1,308 @@
// sRGB -> Linear conversion (needed because Ghostty passes sRGB values but the shader pipeline operates in linear color space)
vec3 sRGBToLinear(vec3 c) {
return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(vec3(0.04045), c));
}
// --- CONFIGURATION ---
vec4 TRAIL_COLOR = vec4(sRGBToLinear(iCurrentCursorColor.rgb), iCurrentCursorColor.a); // for custom color: vec4(0.2, 0.6, 1.0, 0.5); (wrap in sRGBToLinear for correct brightness)
const float DURATION = 0.2; // total animation time
const float TRAIL_SIZE = 0.8; // 0.0 = all corners move together. 1.0 = max smear (leading corners jump instantly)
const float THRESHOLD_MIN_DISTANCE = 1.5; // min distance to show trail (units of cursor height)
const float BLUR = 1.0; // blur size in pixels (for antialiasing)
const float TRAIL_THICKNESS = 1.0; // 1.0 = full cursor height, 0.0 = zero height, >1.0 = funky aah
const float TRAIL_THICKNESS_X = 0.9;
const float FADE_ENABLED = 0.0; // 1.0 to enable fade gradient along the trail, 0.0 to disable
const float FADE_EXPONENT = 5.0; // exponent for fade gradient along the trail
// --- CONSTANTS for easing functions ---
const float PI = 3.14159265359;
const float C1_BACK = 1.70158;
const float C2_BACK = C1_BACK * 1.525;
const float C3_BACK = C1_BACK + 1.0;
const float C4_ELASTIC = (2.0 * PI) / 3.0;
const float C5_ELASTIC = (2.0 * PI) / 4.5;
const float SPRING_STIFFNESS = 9.0;
const float SPRING_DAMPING = 0.9;
// --- EASING FUNCTIONS ---
// // Linear
// float ease(float x) {
// return x;
// }
// // EaseOutQuad
// float ease(float x) {
// return 1.0 - (1.0 - x) * (1.0 - x);
// }
// // EaseOutCubic
// float ease(float x) {
// return 1.0 - pow(1.0 - x, 3.0);
// }
// // EaseOutQuart
// float ease(float x) {
// return 1.0 - pow(1.0 - x, 4.0);
// }
// // EaseOutQuint
// float ease(float x) {
// return 1.0 - pow(1.0 - x, 5.0);
// }
// // EaseOutSine
// float ease(float x) {
// return sin((x * PI) / 2.0);
// }
// // EaseOutExpo
// float ease(float x) {
// return x == 1.0 ? 1.0 : 1.0 - pow(2.0, -10.0 * x);
// }
// EaseOutCirc
float ease(float x) {
return sqrt(1.0 - pow(x - 1.0, 2.0));
}
// // EaseOutBack
// float ease(float x) {
// return 1.0 + C3_BACK * pow(x - 1.0, 3.0) + C1_BACK * pow(x - 1.0, 2.0);
// }
// // EaseOutElastic
// float ease(float x) {
// return x == 0.0 ? 0.0
// : x == 1.0 ? 1.0
// : pow(2.0, -10.0 * x) * sin((x * 10.0 - 0.75) * C4_ELASTIC) + 1.0;
// }
// // Parametric Spring
// float ease(float x) {
// x = clamp(x, 0.0, 1.0);
// float decay = exp(-SPRING_DAMPING * SPRING_STIFFNESS * x);
// float freq = sqrt(SPRING_STIFFNESS * (1.0 - SPRING_DAMPING * SPRING_DAMPING));
// float osc = cos(freq * 6.283185 * x) + (SPRING_DAMPING * sqrt(SPRING_STIFFNESS) / freq) * sin(freq * 6.283185 * x);
// return 1.0 - decay * osc;
// }
float getSdfRectangle(in vec2 p, in vec2 xy, in vec2 b)
{
vec2 d = abs(p - xy) - b;
return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
}
// Based on Inigo Quilez's 2D distance functions article: https://iquilezles.org/articles/distfunctions2d/
// Potencially optimized by eliminating conditionals and loops to enhance performance and reduce branching
float seg(in vec2 p, in vec2 a, in vec2 b, inout float s, float d) {
vec2 e = b - a;
vec2 w = p - a;
vec2 proj = a + e * clamp(dot(w, e) / dot(e, e), 0.0, 1.0);
float segd = dot(p - proj, p - proj);
d = min(d, segd);
float c0 = step(0.0, p.y - a.y);
float c1 = 1.0 - step(0.0, p.y - b.y);
float c2 = 1.0 - step(0.0, e.x * w.y - e.y * w.x);
float allCond = c0 * c1 * c2;
float noneCond = (1.0 - c0) * (1.0 - c1) * (1.0 - c2);
float flip = mix(1.0, -1.0, step(0.5, allCond + noneCond));
s *= flip;
return d;
}
float getSdfConvexQuad(in vec2 p, in vec2 v1, in vec2 v2, in vec2 v3, in vec2 v4) {
float s = 1.0;
float d = dot(p - v1, p - v1);
d = seg(p, v1, v2, s, d);
d = seg(p, v2, v3, s, d);
d = seg(p, v3, v4, s, d);
d = seg(p, v4, v1, s, d);
return s * sqrt(d);
}
vec2 normalize(vec2 value, float isPosition) {
return (value * 2.0 - (iResolution.xy * isPosition)) / iResolution.y;
}
float antialising(float distance, float blurAmount) {
return 1. - smoothstep(0., normalize(vec2(blurAmount, blurAmount), 0.).x, distance);
}
// Determines animation duration based on a corner's alignment with the move direction(dot product)
// dot_val will be in [-2, 2]
// > 0.5 (1 or 2) = Leading
// > -0.5 (0) = Side
// <= -0.5 (-1 or -2) = Trailing
float getDurationFromDot(float dot_val, float DURATION_LEAD, float DURATION_SIDE, float DURATION_TRAIL) {
float isLead = step(0.5, dot_val);
float isSide = step(-0.5, dot_val) * (1.0 - isLead);
// Start with trailing duration
float duration = mix(DURATION_TRAIL, DURATION_SIDE, isSide);
// Mix in leading duration
duration = mix(duration, DURATION_LEAD, isLead);
return duration;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord){
#if !defined(WEB)
fragColor = texture(iChannel0, fragCoord.xy / iResolution.xy);
#endif
// normalization & setup(-1, 1 coords)
vec2 vu = normalize(fragCoord, 1.);
vec2 offsetFactor = vec2(-.5, 0.5);
vec4 currentCursor = vec4(normalize(iCurrentCursor.xy, 1.), normalize(iCurrentCursor.zw, 0.));
vec4 previousCursor = vec4(normalize(iPreviousCursor.xy, 1.), normalize(iPreviousCursor.zw, 0.));
vec2 centerCC = currentCursor.xy - (currentCursor.zw * offsetFactor);
vec2 halfSizeCC = currentCursor.zw * 0.5;
vec2 centerCP = previousCursor.xy - (previousCursor.zw * offsetFactor);
vec2 halfSizeCP = previousCursor.zw * 0.5;
float sdfCurrentCursor = getSdfRectangle(vu, centerCC, halfSizeCC);
float lineLength = distance(centerCC, centerCP);
float minDist = currentCursor.w * THRESHOLD_MIN_DISTANCE;
vec4 newColor = vec4(fragColor);
float baseProgress = iTime - iTimeCursorChange;
if (lineLength > minDist && baseProgress < DURATION - 0.001) {
// defining corners of cursors
// Y (Height) with TRAIL_THICKNESS
float cc_half_height = currentCursor.w * 0.5;
float cc_center_y = currentCursor.y - cc_half_height;
float cc_new_half_height = cc_half_height * TRAIL_THICKNESS;
float cc_new_top_y = cc_center_y + cc_new_half_height;
float cc_new_bottom_y = cc_center_y - cc_new_half_height;
// X (Width) with TRAIL_THICKNESS
float cc_half_width = currentCursor.z * 0.5;
float cc_center_x = currentCursor.x + cc_half_width;
float cc_new_half_width = cc_half_width * TRAIL_THICKNESS_X;
float cc_new_left_x = cc_center_x - cc_new_half_width;
float cc_new_right_x = cc_center_x + cc_new_half_width;
vec2 cc_tl = vec2(cc_new_left_x, cc_new_top_y);
vec2 cc_tr = vec2(cc_new_right_x, cc_new_top_y);
vec2 cc_bl = vec2(cc_new_left_x, cc_new_bottom_y);
vec2 cc_br = vec2(cc_new_right_x, cc_new_bottom_y);
// same thing for previous cursor
float cp_half_height = previousCursor.w * 0.5;
float cp_center_y = previousCursor.y - cp_half_height;
float cp_new_half_height = cp_half_height * TRAIL_THICKNESS;
float cp_new_top_y = cp_center_y + cp_new_half_height;
float cp_new_bottom_y = cp_center_y - cp_new_half_height;
float cp_half_width = previousCursor.z * 0.5;
float cp_center_x = previousCursor.x + cp_half_width;
float cp_new_half_width = cp_half_width * TRAIL_THICKNESS_X;
float cp_new_left_x = cp_center_x - cp_new_half_width;
float cp_new_right_x = cp_center_x + cp_new_half_width;
vec2 cp_tl = vec2(cp_new_left_x, cp_new_top_y);
vec2 cp_tr = vec2(cp_new_right_x, cp_new_top_y);
vec2 cp_bl = vec2(cp_new_left_x, cp_new_bottom_y);
vec2 cp_br = vec2(cp_new_right_x, cp_new_bottom_y);
// calculating durations for every corner
const float DURATION_TRAIL = DURATION;
const float DURATION_LEAD = DURATION * (1.0 - TRAIL_SIZE);
const float DURATION_SIDE = (DURATION_LEAD + DURATION_TRAIL) / 2.0;
vec2 moveVec = centerCC - centerCP;
vec2 s = sign(moveVec);
// dot products for each corner, determining alignment with movement direction
float dot_tl = dot(vec2(-1., 1.), s);
float dot_tr = dot(vec2( 1., 1.), s);
float dot_bl = dot(vec2(-1.,-1.), s);
float dot_br = dot(vec2( 1.,-1.), s);
// assign durations based on dot products
float dur_tl = getDurationFromDot(dot_tl, DURATION_LEAD, DURATION_SIDE, DURATION_TRAIL);
float dur_tr = getDurationFromDot(dot_tr, DURATION_LEAD, DURATION_SIDE, DURATION_TRAIL);
float dur_bl = getDurationFromDot(dot_bl, DURATION_LEAD, DURATION_SIDE, DURATION_TRAIL);
float dur_br = getDurationFromDot(dot_br, DURATION_LEAD, DURATION_SIDE, DURATION_TRAIL);
// check direction of horizontal movement
float isMovingRight = step(0.5, s.x);
float isMovingLeft = step(0.5, -s.x);
// calculate vertical-rail durations
float dot_right_edge = (dot_tr + dot_br) * 0.5;
float dur_right_rail = getDurationFromDot(dot_right_edge, DURATION_LEAD, DURATION_SIDE, DURATION_TRAIL);
float dot_left_edge = (dot_tl + dot_bl) * 0.5;
float dur_left_rail = getDurationFromDot(dot_left_edge, DURATION_LEAD, DURATION_SIDE, DURATION_TRAIL);
float final_dur_tl = mix(dur_tl, dur_left_rail, isMovingLeft);
float final_dur_bl = mix(dur_bl, dur_left_rail, isMovingLeft);
float final_dur_tr = mix(dur_tr, dur_right_rail, isMovingRight);
float final_dur_br = mix(dur_br, dur_right_rail, isMovingRight);
// calculate progress for each corner based on the duration and time since cursor change
float prog_tl = ease(clamp(baseProgress / final_dur_tl, 0.0, 1.0));
float prog_tr = ease(clamp(baseProgress / final_dur_tr, 0.0, 1.0));
float prog_bl = ease(clamp(baseProgress / final_dur_bl, 0.0, 1.0));
float prog_br = ease(clamp(baseProgress / final_dur_br, 0.0, 1.0));
// get the trial corner positions based on progress
vec2 v_tl = mix(cp_tl, cc_tl, prog_tl);
vec2 v_tr = mix(cp_tr, cc_tr, prog_tr);
vec2 v_br = mix(cp_br, cc_br, prog_br);
vec2 v_bl = mix(cp_bl, cc_bl, prog_bl);
// DRAWING THE TRAIL
float sdfTrail = getSdfConvexQuad(vu, v_tl, v_tr, v_br, v_bl);
// --- FADE GRADIENT CALCULATION ---
vec2 fragVec = vu - centerCP;
// project fragment onto movement vector, normalize to [0, 1]
// 0.0 at tail, 1.0 at head
// tiny epsilon to avoid division by zero if moveVec is (0,0)
float fadeProgress = clamp(dot(fragVec, moveVec) / (dot(moveVec, moveVec) + 1e-6), 0.0, 1.0);
vec4 trail = TRAIL_COLOR;
float effectiveBlur = BLUR;
if (BLUR < 2.5) {
// no antialising on horizontal/vertical movement, fixes 'pulse' like thing on end cursor
float isDiagonal = abs(s.x) * abs(s.y); // 1.0 if diagonal, 0.0 if H/V
float effectiveBlur = mix(0.0, BLUR, isDiagonal);
}
float shapeAlpha = antialising(sdfTrail, effectiveBlur); // shape mask
if (FADE_ENABLED > 0.5) {
// apply fade gradient along the trail
// float fadeStart = 0.2;
// float easedProgress = smoothstep(fadeStart, 1.0, fadeProgress);
// easedProgress = pow(2.0, 10.0 * (fadeProgress - 1.0));
float easedProgress = pow(fadeProgress, FADE_EXPONENT);
trail.a *= easedProgress;
}
float finalAlpha = trail.a * shapeAlpha;
// newColor.a to preserve the background alpha.
newColor = mix(newColor, vec4(trail.rgb, newColor.a), finalAlpha);
// punch hole on the trail, so current cursor is drawn on top
newColor = mix(newColor, fragColor, step(sdfCurrentCursor, 0.));
}
fragColor = newColor;
}

View file

@ -0,0 +1,170 @@
#define PI 3.141592653589793
float spotlightBeam(vec2 uv, vec2 origin, float angle, float width, float lengthFade) {
vec2 p = uv - origin;
float c = cos(angle);
float s = sin(angle);
// Rotate into beam space
vec2 q = vec2(
c * p.x + s * p.y,
-s * p.x + c * p.y
);
// Beam extends along +Y in local space
float across = abs(q.x);
float along = q.y;
float beamCore = exp(-pow(across / width, 2.0));
float beamLength = smoothstep(-0.05, 0.10, along) * (1.0 - smoothstep(lengthFade, 1.35, along));
return beamCore * beamLength;
}
float hash12(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * 0.1031);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
float sparkleGrid(vec2 uv, float scale, float threshold, float speed) {
vec2 g = uv * vec2(scale, scale * 0.56);
vec2 id = floor(g);
vec2 f = fract(g) - 0.5;
float n = hash12(id);
float gate = step(threshold, n);
float twinkle = pow(max(0.0, sin(iTime * speed + n * 6.2831853)), 28.0);
float diamond = 1.0 - smoothstep(0.02, 0.13, abs(f.x) + abs(f.y));
float flareX = (1.0 - smoothstep(0.000, 0.016, abs(f.y))) *
(1.0 - smoothstep(0.000, 0.42, abs(f.x)));
float flareY = (1.0 - smoothstep(0.000, 0.016, abs(f.x))) *
(1.0 - smoothstep(0.000, 0.42, abs(f.y)));
return gate * twinkle * (diamond + 0.35 * flareX + 0.35 * flareY);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord.xy / iResolution.xy;
vec2 px = 1.0 / iResolution.xy;
// Very light late-90s video wobble.
float wobble = sin(uv.y * 90.0 + iTime * 2.0) * 0.0008
+ sin(uv.y * 12.0 - iTime * 0.35) * 0.0010;
vec2 tUv = clamp(uv + vec2(wobble, 0.0), vec2(0.001), vec2(0.999));
// Tiny chromatic shift.
vec3 src;
src.r = texture(iChannel0, clamp(tUv + vec2(px.x * 1.1, 0.0), vec2(0.001), vec2(0.999))).r;
src.g = texture(iChannel0, tUv).g;
src.b = texture(iChannel0, clamp(tUv - vec2(px.x * 1.1, 0.0), vec2(0.001), vec2(0.999))).b;
float lum = dot(src, vec3(0.299, 0.587, 0.114));
float glyph = smoothstep(0.06, 0.30, lum);
// Velvet / leather background.
vec3 velvet = vec3(0.012, 0.007, 0.002);
vec3 brown = vec3(0.075, 0.038, 0.010);
vec3 bg = mix(velvet, brown, uv.y + 0.15 * sin(uv.x * 4.0 + iTime * 0.12));
// Cash Money gold.
vec3 gold = vec3(1.00, 0.62, 0.10);
vec3 paleGold = vec3(1.00, 0.88, 0.45);
vec3 hotGold = mix(gold, paleGold, pow(lum, 0.65));
// Tint glyphs gold while preserving some original terminal color.
vec3 col = mix(bg, src, glyph * 0.28);
col = mix(col, hotGold + src * 0.28, glyph * 0.82);
// Chrome sweep, like jewelry-store lights / spinning rims.
float sweep1 = pow(max(0.0, sin((uv.x * 1.8 + uv.y * 0.65) * PI * 2.0 - iTime * 0.55)), 22.0);
float sweep2 = pow(max(0.0, sin((uv.x * -1.1 + uv.y * 1.20) * PI * 2.0 + iTime * 0.40)), 30.0);
col += (sweep1 * 0.15 + sweep2 * 0.10) * vec3(1.0, 0.77, 0.32) * (0.35 + glyph);
// Independent moving spotlight trails
float ray1 = spotlightBeam(
uv,
vec2(0.10, 1.10),
-0.95 + 0.22 * sin(iTime * 0.63 + 0.3),
0.045 + 0.008 * sin(iTime * 1.10 + 0.7),
1.00
);
float ray2 = spotlightBeam(
uv,
vec2(0.35, 1.12),
-1.20 + 0.18 * sin(iTime * 0.47 + 1.7),
0.055 + 0.006 * sin(iTime * 0.90 + 2.1),
1.05
);
float ray3 = spotlightBeam(
uv,
vec2(0.72, 1.08),
-1.55 + 0.20 * sin(iTime * 0.71 + 2.8),
0.050 + 0.010 * sin(iTime * 1.30 + 0.5),
0.95
);
float ray4 = spotlightBeam(
uv,
vec2(0.92, 1.15),
-1.85 + 0.24 * sin(iTime * 0.58 + 4.0),
0.060 + 0.007 * sin(iTime * 0.80 + 3.4),
1.10
);
// Optional faint upward beam from bottom-left for extra motion
float ray5 = spotlightBeam(
uv,
vec2(-0.05, -0.05),
0.70 + 0.15 * sin(iTime * 0.52 + 5.2),
0.040 + 0.005 * sin(iTime * 1.00 + 1.3),
0.90
);
float rays = 0.0;
rays += ray1 * (0.75 + 0.25 * sin(iTime * 0.90 + 0.2));
rays += ray2 * (0.65 + 0.35 * sin(iTime * 0.70 + 1.9));
rays += ray3 * (0.80 + 0.20 * sin(iTime * 1.10 + 2.6));
rays += ray4 * (0.70 + 0.30 * sin(iTime * 0.85 + 4.3));
rays += ray5 * (0.45 + 0.25 * sin(iTime * 0.95 + 3.1));
// Warm gold spotlight tint
vec3 rayColorA = vec3(1.00, 0.74, 0.22);
vec3 rayColorB = vec3(1.00, 0.86, 0.48);
// Mix two shades so it feels less flat
vec3 spotlightColor = mix(rayColorA, rayColorB, 0.5 + 0.5 * sin(iTime * 0.33));
// Add trails mostly into background, not too hard on text
col += rays * spotlightColor * (0.10 + 0.10 * (1.0 - glyph));
// Diamond glints.
float spark = sparkleGrid(uv, 62.0, 0.978, 4.0);
spark += 0.50 * sparkleGrid(uv + vec2(0.13, -0.09), 38.0, 0.965, 3.0);
col += spark * vec3(1.0, 0.90, 0.55) * (0.8 + 0.5 * glyph);
// Gold frame around the terminal.
float edgeD = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y));
float frame = 1.0 - smoothstep(0.000, 0.045, edgeD);
float frameWave = pow(0.5 + 0.5 * sin((uv.x + uv.y) * 90.0 - iTime * 1.8), 8.0);
col += frame * vec3(1.0, 0.72, 0.20) * (0.10 + 0.22 * frameWave);
// Scanline shimmer, not too heavy.
float scan = 0.965 + 0.035 * sin(fragCoord.y * PI);
col *= scan;
// Music-video vignette.
float vig = 1.0 - smoothstep(0.22, 0.82, distance(uv, vec2(0.5)));
col *= 0.78 + 0.22 * vig;
// Keep text readable.
col = mix(col, src, glyph * 0.16);
fragColor = vec4(clamp(col, 0.0, 1.0), 1.0);
}

View file

@ -0,0 +1,267 @@
#define PI 3.141592653589793
float smin(float a, float b, float k) {
float h = max(k - abs(a - b), 0.0) / k;
return min(a, b) - h * h * h * k * (1.0 / 6.0);
}
float rectSDF(vec2 p, vec2 b) {
vec2 d = abs(p) - b;
return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
}
float fill(float d, float aa) {
return 1.0 - smoothstep(0.0, aa, d);
}
float stroke(float d, float w, float aa) {
return 1.0 - smoothstep(0.0, aa, abs(d) - w);
}
float box01(vec2 uv, vec2 c, vec2 s, float aa) {
return fill(rectSDF(uv - c, s), aa);
}
float frame01(vec2 uv, vec2 c, vec2 s, float w, float aa) {
return stroke(rectSDF(uv - c, s), w, aa);
}
float in01(vec2 uv) {
return step(0.0, uv.x) * step(0.0, uv.y) * step(uv.x, 1.0) * step(uv.y, 1.0);
}
// ------------------------------------------------------------------
// Very simple block-letter glyphs in a 0..1 local cell.
// Not perfect typography, but close enough to read.
// ------------------------------------------------------------------
float glyphC(vec2 uv, float aa) {
float m = 0.0;
m = max(m, box01(uv, vec2(0.22, 0.50), vec2(0.10, 0.42), aa));
m = max(m, box01(uv, vec2(0.52, 0.84), vec2(0.30, 0.08), aa));
m = max(m, box01(uv, vec2(0.52, 0.16), vec2(0.30, 0.08), aa));
return m * in01(uv);
}
float glyphA(vec2 uv, float aa) {
float m = 0.0;
m = max(m, box01(uv, vec2(0.22, 0.50), vec2(0.10, 0.42), aa));
m = max(m, box01(uv, vec2(0.78, 0.50), vec2(0.10, 0.42), aa));
m = max(m, box01(uv, vec2(0.50, 0.84), vec2(0.30, 0.08), aa));
m = max(m, box01(uv, vec2(0.50, 0.50), vec2(0.30, 0.08), aa));
return m * in01(uv);
}
float glyphS(vec2 uv, float aa) {
float m = 0.0;
m = max(m, box01(uv, vec2(0.50, 0.84), vec2(0.30, 0.08), aa));
m = max(m, box01(uv, vec2(0.50, 0.50), vec2(0.30, 0.08), aa));
m = max(m, box01(uv, vec2(0.50, 0.16), vec2(0.30, 0.08), aa));
m = max(m, box01(uv, vec2(0.22, 0.67), vec2(0.10, 0.17), aa));
m = max(m, box01(uv, vec2(0.78, 0.33), vec2(0.10, 0.17), aa));
return m * in01(uv);
}
float glyphH(vec2 uv, float aa) {
float m = 0.0;
m = max(m, box01(uv, vec2(0.22, 0.50), vec2(0.10, 0.42), aa));
m = max(m, box01(uv, vec2(0.78, 0.50), vec2(0.10, 0.42), aa));
m = max(m, box01(uv, vec2(0.50, 0.50), vec2(0.30, 0.08), aa));
return m * in01(uv);
}
float glyphM(vec2 uv, float aa) {
float m = 0.0;
m = max(m, box01(uv, vec2(0.16, 0.50), vec2(0.09, 0.42), aa));
m = max(m, box01(uv, vec2(0.84, 0.50), vec2(0.09, 0.42), aa));
m = max(m, box01(uv, vec2(0.38, 0.65), vec2(0.08, 0.27), aa));
m = max(m, box01(uv, vec2(0.62, 0.65), vec2(0.08, 0.27), aa));
m = max(m, box01(uv, vec2(0.50, 0.84), vec2(0.25, 0.08), aa));
return m * in01(uv);
}
float glyphO(vec2 uv, float aa) {
float outer = box01(uv, vec2(0.50, 0.50), vec2(0.34, 0.42), aa);
float inner = box01(uv, vec2(0.50, 0.50), vec2(0.18, 0.24), aa);
return max(0.0, outer - inner) * in01(uv);
}
float glyphN(vec2 uv, float aa) {
float m = 0.0;
m = max(m, box01(uv, vec2(0.20, 0.50), vec2(0.10, 0.42), aa));
m = max(m, box01(uv, vec2(0.80, 0.50), vec2(0.10, 0.42), aa));
m = max(m, box01(uv, vec2(0.50, 0.50), vec2(0.08, 0.42), aa));
return m * in01(uv);
}
float glyphE(vec2 uv, float aa) {
float m = 0.0;
m = max(m, box01(uv, vec2(0.22, 0.50), vec2(0.10, 0.42), aa));
m = max(m, box01(uv, vec2(0.52, 0.84), vec2(0.30, 0.08), aa));
m = max(m, box01(uv, vec2(0.48, 0.50), vec2(0.26, 0.08), aa));
m = max(m, box01(uv, vec2(0.52, 0.16), vec2(0.30, 0.08), aa));
return m * in01(uv);
}
float glyphY(vec2 uv, float aa) {
float m = 0.0;
m = max(m, box01(uv, vec2(0.22, 0.72), vec2(0.10, 0.20), aa));
m = max(m, box01(uv, vec2(0.78, 0.72), vec2(0.10, 0.20), aa));
m = max(m, box01(uv, vec2(0.50, 0.50), vec2(0.10, 0.12), aa));
m = max(m, box01(uv, vec2(0.50, 0.20), vec2(0.10, 0.20), aa));
return m * in01(uv);
}
float glyphR(vec2 uv, float aa) {
float m = 0.0;
m = max(m, box01(uv, vec2(0.22, 0.50), vec2(0.10, 0.42), aa));
m = max(m, box01(uv, vec2(0.50, 0.84), vec2(0.28, 0.08), aa));
m = max(m, box01(uv, vec2(0.72, 0.68), vec2(0.10, 0.16), aa));
m = max(m, box01(uv, vec2(0.50, 0.50), vec2(0.26, 0.08), aa));
m = max(m, box01(uv, vec2(0.68, 0.24), vec2(0.10, 0.20), aa));
return m * in01(uv);
}
float glyphD(vec2 uv, float aa) {
float m = 0.0;
m = max(m, box01(uv, vec2(0.22, 0.50), vec2(0.10, 0.42), aa));
m = max(m, box01(uv, vec2(0.50, 0.84), vec2(0.26, 0.08), aa));
m = max(m, box01(uv, vec2(0.50, 0.16), vec2(0.26, 0.08), aa));
m = max(m, box01(uv, vec2(0.74, 0.50), vec2(0.10, 0.34), aa));
return m * in01(uv);
}
float drawGlyph(int g, vec2 uv, float aa) {
if (g == 0) return glyphC(uv, aa);
if (g == 1) return glyphA(uv, aa);
if (g == 2) return glyphS(uv, aa);
if (g == 3) return glyphH(uv, aa);
if (g == 4) return glyphM(uv, aa);
if (g == 5) return glyphO(uv, aa);
if (g == 6) return glyphN(uv, aa);
if (g == 7) return glyphE(uv, aa);
if (g == 8) return glyphY(uv, aa);
if (g == 9) return glyphR(uv, aa);
if (g == 10) return glyphD(uv, aa);
return 0.0;
}
float placeGlyph(vec2 q, vec4 box, int glyphId, float aa) {
vec2 uv = (q - box.xy) / (box.zw - box.xy);
return drawGlyph(glyphId, uv, aa);
}
float dollarMark(vec2 uv, float aa) {
float m = 0.0;
// vertical bar
m = max(m, box01(uv, vec2(0.50, 0.50), vec2(0.05, 0.42), aa));
// top/bottom heavy bars to make a chunky $
m = max(m, box01(uv, vec2(0.50, 0.82), vec2(0.22, 0.10), aa));
m = max(m, box01(uv, vec2(0.50, 0.50), vec2(0.22, 0.10), aa));
m = max(m, box01(uv, vec2(0.50, 0.18), vec2(0.22, 0.10), aa));
// left upper / right lower chunks
m = max(m, box01(uv, vec2(0.30, 0.66), vec2(0.10, 0.18), aa));
m = max(m, box01(uv, vec2(0.70, 0.34), vec2(0.10, 0.18), aa));
return m * in01(uv);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord.xy / iResolution.xy;
vec4 base = texture(iChannel0, uv);
vec3 col = base.rgb;
float aa = 1.6 / iResolution.y;
// ----------------------------------------------------------
// Bouncing rectangular badge, DVD-logo style
// ----------------------------------------------------------
vec2 badgeSize = vec2(0.18, 0.44);
vec2 minP = badgeSize * 0.5 + vec2(0.02, 0.03);
vec2 maxP = vec2(1.0) - minP;
vec2 travel = maxP - minP;
vec2 phase = fract(vec2(iTime * 0.060, iTime * 0.047) + vec2(0.21, 0.58));
vec2 tri = abs(phase * 2.0 - 1.0);
vec2 center = minP + travel * tri;
vec2 p = uv - center;
p.x *= iResolution.x / iResolution.y;
vec2 halfSize = vec2(badgeSize.x * 0.5 * iResolution.x / iResolution.y, badgeSize.y * 0.5);
// q = normalized coordinates inside the badge
vec2 q = p / halfSize;
q = q * 0.5 + 0.5;
float outer = fill(rectSDF(p, halfSize), aa);
float border = frame01(p, vec2(0.0), halfSize, 0.006, aa);
// shadow
float shadow = fill(rectSDF(p - vec2(0.010, -0.012), halfSize), aa) * 0.18;
col = mix(col, col * 0.80, shadow);
// white sticker body
vec3 logoCol = vec3(0.0);
logoCol += outer * vec3(0.96);
logoCol = mix(logoCol, vec3(0.04), border);
// top word: CASH MONEY
float top = 0.0;
float y0 = 0.78;
float y1 = 0.96;
float w = 0.075;
float gap = 0.010;
top = max(top, placeGlyph(q, vec4(0.04, y0, 0.04 + w, y1), 0, aa)); // C
top = max(top, placeGlyph(q, vec4(0.04 + 1.0*(w+gap), y0, 0.04 + 1.0*(w+gap) + w, y1), 1, aa)); // A
top = max(top, placeGlyph(q, vec4(0.04 + 2.0*(w+gap), y0, 0.04 + 2.0*(w+gap) + w, y1), 2, aa)); // S
top = max(top, placeGlyph(q, vec4(0.04 + 3.0*(w+gap), y0, 0.04 + 3.0*(w+gap) + w, y1), 3, aa)); // H
float start2 = 0.40;
top = max(top, placeGlyph(q, vec4(start2 + 0.0*(w+gap), y0, start2 + 0.0*(w+gap) + w, y1), 4, aa)); // M
top = max(top, placeGlyph(q, vec4(start2 + 1.0*(w+gap), y0, start2 + 1.0*(w+gap) + w, y1), 5, aa)); // O
top = max(top, placeGlyph(q, vec4(start2 + 2.0*(w+gap), y0, start2 + 2.0*(w+gap) + w, y1), 6, aa)); // N
top = max(top, placeGlyph(q, vec4(start2 + 3.0*(w+gap), y0, start2 + 3.0*(w+gap) + w, y1), 7, aa)); // E
top = max(top, placeGlyph(q, vec4(start2 + 4.0*(w+gap), y0, start2 + 4.0*(w+gap) + w, y1), 8, aa)); // Y
// bottom word: RECORDS
float bottom = 0.0;
float by0 = 0.03;
float by1 = 0.19;
float bw = 0.095;
float bg = 0.012;
float bstart = 0.10;
bottom = max(bottom, placeGlyph(q, vec4(bstart + 0.0*(bw+bg), by0, bstart + 0.0*(bw+bg) + bw, by1), 9, aa)); // R
bottom = max(bottom, placeGlyph(q, vec4(bstart + 1.0*(bw+bg), by0, bstart + 1.0*(bw+bg) + bw, by1), 7, aa)); // E
bottom = max(bottom, placeGlyph(q, vec4(bstart + 2.0*(bw+bg), by0, bstart + 2.0*(bw+bg) + bw, by1), 0, aa)); // C
bottom = max(bottom, placeGlyph(q, vec4(bstart + 3.0*(bw+bg), by0, bstart + 3.0*(bw+bg) + bw, by1), 5, aa)); // O
bottom = max(bottom, placeGlyph(q, vec4(bstart + 4.0*(bw+bg), by0, bstart + 4.0*(bw+bg) + bw, by1), 9, aa)); // R
bottom = max(bottom, placeGlyph(q, vec4(bstart + 5.0*(bw+bg), by0, bstart + 5.0*(bw+bg) + bw, by1), 10, aa)); // D
bottom = max(bottom, placeGlyph(q, vec4(bstart + 6.0*(bw+bg), by0, bstart + 6.0*(bw+bg) + bw, by1), 2, aa)); // S
// central $
vec2 suv = (q - vec2(0.18, 0.23)) / vec2(0.64, 0.50);
float dollar = dollarMark(suv, aa);
// black print
float printMask = max(max(top, bottom), dollar);
logoCol = mix(logoCol, vec3(0.03), printMask);
// small gold glint so it still fits the bling theme
float glint = pow(max(0.0, sin((uv.x * 4.0 + uv.y * 2.0) * PI * 2.0 - iTime * 1.4)), 18.0);
logoCol += outer * glint * vec3(1.0, 0.84, 0.35) * 0.12;
// subtle sticker highlight
float shine = 1.0 - smoothstep(0.0, 0.25, abs(q.x - 0.25) + abs(q.y - 0.78));
logoCol += outer * shine * vec3(1.0) * 0.08;
// composite behind text
col = mix(col, col + logoCol, outer * 0.22);
fragColor = vec4(clamp(col, 0.0, 1.0), base.a);
}

View file

@ -0,0 +1,142 @@
#define PI 3.141592653589793
float sdEllipse(vec2 p, vec2 r) {
vec2 q = p / r;
return (length(q) - 1.0) * min(r.x, r.y);
}
float sdCircle(vec2 p, float r) {
return length(p) - r;
}
float sdBox(vec2 p, vec2 b) {
vec2 d = abs(p) - b;
return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
}
float opStroke(float d, float w) {
return abs(d) - w;
}
float fill(float d) {
return 1.0 - smoothstep(0.0, 0.004, d);
}
float stroke(float d, float w) {
return 1.0 - smoothstep(0.0, 0.004, opStroke(d, w));
}
// Simple dollar sign SDF-ish shape
float dollarSign(vec2 p) {
float s = 0.0;
// main vertical bar
float bar = fill(sdBox(p, vec2(0.018, 0.18)));
// upper curve
vec2 pu = p - vec2(0.02, 0.07);
float upperOuter = stroke(sdCircle(pu, 0.11), 0.020);
float upperCut = fill(sdBox(pu - vec2(-0.07, 0.00), vec2(0.08, 0.14)));
float upper = upperOuter * (1.0 - upperCut);
// lower curve
vec2 pl = p - vec2(-0.02, -0.07);
float lowerOuter = stroke(sdCircle(pl, 0.11), 0.020);
float lowerCut = fill(sdBox(pl - vec2(0.07, 0.00), vec2(0.08, 0.14)));
float lower = lowerOuter * (1.0 - lowerCut);
s = max(bar, max(upper, lower));
return s;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord.xy / iResolution.xy;
vec4 base = texture(iChannel0, uv);
vec3 col = base.rgb;
// ------------------------------------------------------------------
// Bouncing "DVD logo" style motion
// ------------------------------------------------------------------
vec2 logoSize = vec2(0.23, 0.13); // width/height of medallion in UV space
vec2 minP = logoSize * 0.5 + vec2(0.03, 0.04);
vec2 maxP = vec2(1.0) - minP;
// Ping-pong motion
vec2 travel = maxP - minP;
vec2 phase = fract(vec2(iTime * 0.070, iTime * 0.052) + vec2(0.17, 0.61));
vec2 tri = abs(phase * 2.0 - 1.0);
vec2 center = minP + travel * tri;
// Local coordinates around logo center
vec2 p = uv - center;
p.x *= iResolution.x / iResolution.y; // aspect correction
// ------------------------------------------------------------------
// Cash Money inspired medallion
// ------------------------------------------------------------------
float outer = sdEllipse(p, vec2(0.17, 0.095));
float inner = sdEllipse(p, vec2(0.145, 0.078));
float core = sdEllipse(p, vec2(0.122, 0.060));
float outerFill = fill(outer);
float innerRing = stroke(inner, 0.010);
float coreFill = fill(core);
// Rim beading / jewelry vibe
float angle = atan(p.y, p.x);
float beads = pow(0.5 + 0.5 * sin(angle * 26.0 - iTime * 2.2), 8.0);
beads *= stroke(sdEllipse(p, vec2(0.158, 0.086)), 0.008);
// Chrome/gold light sweep
float sweep = pow(max(0.0, sin((uv.x * 4.0 + uv.y * 2.6) * PI * 2.0 - iTime * 1.7)), 18.0);
vec3 darkGold = vec3(0.28, 0.12, 0.02);
vec3 gold = vec3(0.95, 0.67, 0.16);
vec3 paleGold = vec3(1.00, 0.88, 0.52);
vec3 diamond = vec3(1.00, 0.96, 0.85);
vec3 logoCol = vec3(0.0);
// outer medallion
logoCol += outerFill * mix(darkGold, gold, 0.65);
logoCol += innerRing * paleGold * 0.9;
logoCol += beads * paleGold * 0.8;
logoCol += coreFill * mix(gold, paleGold, 0.35);
// embossed center
float emboss = 0.5 + 0.5 * sin(p.x * 28.0 + p.y * 12.0 - iTime * 0.6);
logoCol += coreFill * emboss * vec3(0.10, 0.05, 0.01);
// dollar sign centerpiece
float ds = dollarSign(p * 1.7);
logoCol = mix(logoCol, diamond, ds * 0.9);
logoCol += ds * paleGold * 0.35;
// little sparkle hits
float sparkle1 = exp(-length((p - vec2(-0.07, 0.03)) * vec2(1.0, 1.2)) * 60.0);
float sparkle2 = exp(-length((p - vec2(0.08, -0.01)) * vec2(1.0, 1.2)) * 80.0);
float cross1 = (1.0 - smoothstep(0.0, 0.004, abs(p.x + 0.07))) * (1.0 - smoothstep(0.0, 0.05, abs(p.y - 0.03)));
cross1 += (1.0 - smoothstep(0.0, 0.004, abs(p.y - 0.03))) * (1.0 - smoothstep(0.0, 0.05, abs(p.x + 0.07)));
float cross2 = (1.0 - smoothstep(0.0, 0.003, abs(p.x - 0.08))) * (1.0 - smoothstep(0.0, 0.04, abs(p.y + 0.01)));
cross2 += (1.0 - smoothstep(0.0, 0.003, abs(p.y + 0.01))) * (1.0 - smoothstep(0.0, 0.04, abs(p.x - 0.08)));
float twinkle = 0.65 + 0.35 * sin(iTime * 5.0);
logoCol += (sparkle1 * cross1 + sparkle2 * cross2) * paleGold * twinkle * 0.9;
// moving light sweep across logo
logoCol += outerFill * sweep * vec3(1.0, 0.92, 0.60) * 0.45;
// subtle shadow behind logo
float shadow = fill(sdEllipse(p - vec2(0.010, -0.010), vec2(0.17, 0.095)));
shadow *= 0.18;
// Logo mask
float logoMask = outerFill;
// Keep it in background so text stays readable
// Darken behind medallion slightly, then add logo
col = mix(col, col * 0.82, shadow);
col = mix(col, col + logoCol, logoMask * 0.32);
fragColor = vec4(clamp(col, 0.0, 1.0), base.a);
}

View file

@ -0,0 +1,5 @@
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord.xy / iResolution.xy;
vec4 c = texture(iChannel0, uv);
fragColor = vec4(1.0 - c.rgb, 1.0);
}

View file

@ -0,0 +1,30 @@
{
"permissions": {
"allow": [
"Bash(mount)",
"Bash(getenforce)",
"Bash(podman --version)",
"Read(//etc/**)",
"Bash(grep \"^$\\(whoami\\):\")",
"Bash(mkdir -p \"/run/media/mr0xb/1TB Samsung 860/postgres-data\")",
"Bash(mkdir -p ~/podman)",
"Bash(podman unshare *)",
"Bash(podman secret *)",
"Bash(openssl rand *)",
"Bash(podman kube *)",
"Bash(podman pod *)",
"Bash(podman ps *)",
"Bash(podman logs *)",
"Bash(rm -rf \"/run/media/mr0xb/1TB Samsung 860/postgres-data\")",
"Bash(podman exec *)",
"Bash(podman inspect *)",
"Bash(podman image *)",
"Bash(podman run *)",
"Bash(chcon -Rt container_file_t \"/run/media/mr0xb/1TB Samsung 860/postgres-data\")",
"Bash(PGPASSWORD='ksJZqsVlMZZ2G+f6qSp+GdykGIIKjZq3' psql -h 127.0.0.1 -p 5432 -U postgres -c \"select 1;\")",
"Bash(timeout 2 bash -c \"echo > /dev/tcp/127.0.0.1/5432\")",
"Bash(ip -4 addr show)",
"Bash(timeout 2 bash -c \"echo > /dev/tcp/192.168.1.69/5432\")"
]
}
}

View file

@ -0,0 +1,109 @@
// This Ghostty shader is a lightly modified port of https://www.shadertoy.com/view/4dBGRw
#define BLACK_BLEND_THRESHOLD .4
//Creates a diagonal red-and-white striped pattern.
vec3 barberpole(vec2 pos, vec2 rocketpos) {
float d = (pos.x - rocketpos.x) + (pos.y - rocketpos.y);
vec3 col = vec3(1.0);
d = mod(d * 20., 2.0);
if (d > 1.0) {
col = vec3(1.0, 0.0, 0.0);
}
return col;
}
vec3 rocket(vec2 pos, vec2 rocketpos) {
vec3 col = vec3(0.0);
float f = 0.;
float absx = abs(rocketpos.x - pos.x);
float absy = abs(rocketpos.y - pos.y);
// Wooden stick
if (absx < 0.01 && absy < 0.22) {
col = vec3(1.0, 0.5, 0.5);
}
// Barberpole
if (absx < 0.05 && absy < 0.15) {
col = barberpole(pos, rocketpos);
}
// Rocket Point
float pointw = (rocketpos.y - pos.y - 0.25) * -0.7;
if ((rocketpos.y - pos.y) > 0.1) {
f = smoothstep(pointw - 0.001, pointw + 0.001, absx);
col = mix(vec3(1.0, 0.0, 0.0), col, f);
}
// Shadow
f = -.5 + smoothstep(-0.05, 0.05, (rocketpos.x - pos.x));
col *= 0.7 + f;
return col;
}
float rand(float val, float seed) {
return cos(val * sin(val * seed) * seed);
}
float distance2(in vec2 a, in vec2 b) {
return dot(a - b, a - b);
}
mat2 rr = mat2(cos(1.0), -sin(1.0), sin(1.0), cos(1.0));
vec3 drawParticles(vec2 pos, vec3 particolor, float time, vec2 cpos, float gravity, float seed, float timelength) {
vec3 col = vec3(0.0);
vec2 pp = vec2(1.0, 0.0);
for (float i = 1.0; i <= 128.0; i++) {
float d = rand(i, seed);
float fade = (i / 128.0) * time;
vec2 particpos = cpos + time * pp * d;
pp = rr * pp;
col = mix(particolor / fade, col, smoothstep(0.0, 0.0001, distance2(particpos, pos)));
}
col *= smoothstep(0.0, 1.0, (timelength - time) / timelength);
return col;
}
vec3 drawFireworks(float time, vec2 uv, vec3 particolor, float seed) {
float timeoffset = 2.0;
vec3 col = vec3(0.0);
if (time <= 0.) {
return col;
}
if (mod(time, 6.0) > timeoffset) {
col = drawParticles(uv, particolor, mod(time, 6.0) - timeoffset, vec2(rand(ceil(time / 6.0), seed), -0.5), 0.5, ceil(time / 6.0), seed);
} else {
col = rocket(uv * 3., vec2(3. * rand(ceil(time / 6.0), seed), 3. * (-0.5 + (timeoffset - mod(time, 6.0)))));
}
return col;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = 1.0 - 2.0 * fragCoord.xy / iResolution.xy;
uv.x *= iResolution.x / iResolution.y;
vec3 col = vec3(0.1, 0.1, 0.2);
// Flip the y-axis so that the rocket is drawn from the bottom of the screen
uv.y = -uv.y;
col += 0.1 * uv.y;
col += drawFireworks(iTime, uv, vec3(1.0, 0.1, 0.1), 1.);
col += drawFireworks(iTime - 2.0, uv, vec3(0.0, 1.0, 0.5), 2.);
col += drawFireworks(iTime - 4.0, uv, vec3(1.0, 1.0, 0.1), 3.);
vec2 termUV = fragCoord.xy / iResolution.xy;
vec4 terminalColor = texture(iChannel0, termUV);
float alpha = step(length(terminalColor.rgb), BLACK_BLEND_THRESHOLD);
vec3 blendedColor = mix(terminalColor.rgb * 1.0, col.rgb * 0.3, alpha);
fragColor = vec4(blendedColor, terminalColor.a);
}

View file

@ -0,0 +1,139 @@
float triangle(float x, float period) {
return 2.0 * abs(3.0* ((x / period) - floor((x / period) + 0.5))) - 1.0;
}
float field(in vec3 position) {
float strength = 7.0 + 0.03 * log(1.0e-6 + fract(sin(iTime) * 373.11));
float accumulated = 0.0;
float previousMagnitude = 0.0;
float totalWeight = 0.0;
for (int i = 0; i < 6; ++i) {
float magnitude = dot(position, position);
position = abs(position) / magnitude + vec3(-0.5, -0.8 + 0.1 * sin(-iTime * 0.1 + 2.0), -1.1 + 0.3 * cos(iTime * 0.3));
float weight = exp(-float(i) / 7.0);
accumulated += weight * exp(-strength * pow(abs(magnitude - previousMagnitude), 2.3));
totalWeight += weight;
previousMagnitude = magnitude;
}
return max(0.0, 5.0 * accumulated / totalWeight - 0.7);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
const float baseSpeed = 0.02;
const int maxIterations = 16;
const float formulaParameter = 0.79;
const float volumeSteps = 7.0;
const float stepSize = 0.24;
const float zoomFactor = 0.1;
const float tilingFactor = 0.85;
const float baseBrightness = 0.0008;
const float darkMatter = 0.2;
const float distanceFading = 0.56;
const float colorSaturation = 0.9;
const float transverseMotion = 0.2;
const float cloudOpacity = 0.28;
const float zoomSpeed = 0.00002;
const float effectIntensity = 0.02; // 0.0 = terminal only, 1.0 = original intensity
vec2 normalizedCoordinates = 2.0 * fragCoord.xy / vec2(512) - 1.0;
vec2 scaledCoordinates = normalizedCoordinates * vec2(512) / 512.0;
float timeElapsed = iTime;
float speedAdjustment = -baseSpeed;
float formulaAdjustment = formulaParameter;
speedAdjustment = zoomSpeed * cos(iTime * 0.02 + 3.1415926 / 4.0);
vec2 uvCoordinates = scaledCoordinates;
float rotationXZ = 0.9;
float rotationYZ = -0.6;
float rotationXY = 0.9 + iTime * 0.08;
mat2 rotationMatrixXZ = mat2(vec2(cos(rotationXZ), sin(rotationXZ)), vec2(-sin(rotationXZ), cos(rotationXZ)));
mat2 rotationMatrixYZ = mat2(vec2(cos(rotationYZ), sin(rotationYZ)), vec2(-sin(rotationYZ), cos(rotationYZ)));
mat2 rotationMatrixXY = mat2(vec2(cos(rotationXY), sin(rotationXY)), vec2(-sin(rotationXY), cos(rotationXY)));
vec2 canvasCenter = vec2(0.5, 0.5);
vec3 rayDirection = vec3(uvCoordinates * zoomFactor, 1.0);
vec3 cameraPosition = vec3(0.0, 0.0, 0.0);
cameraPosition.x -= 2.0 * (canvasCenter.x - 0.5);
cameraPosition.y -= 2.0 * (canvasCenter.y - 0.5);
vec3 forwardVector = vec3(0.0, 0.0, 1.0);
cameraPosition.x += transverseMotion * cos(0.01 * iTime) + 0.001 * iTime;
cameraPosition.y += transverseMotion * sin(0.01 * iTime) + 0.001 * iTime;
cameraPosition.z += 0.003 * iTime;
rayDirection.xz *= rotationMatrixXZ;
forwardVector.xz *= rotationMatrixXZ;
rayDirection.yz *= rotationMatrixYZ;
forwardVector.yz *= rotationMatrixYZ;
cameraPosition.xy *= -1.0 * rotationMatrixXY;
cameraPosition.xz *= rotationMatrixXZ;
cameraPosition.yz *= rotationMatrixYZ;
float zoomOffset = (timeElapsed - 3311.0) * speedAdjustment;
cameraPosition += forwardVector * zoomOffset;
float sampleOffset = mod(zoomOffset, stepSize);
float normalizedSampleOffset = sampleOffset / stepSize;
float stepDistance = 0.24;
float secondaryStepDistance = stepDistance + stepSize / 2.0;
vec3 accumulatedColor = vec3(0.0);
float fieldContribution = 0.0;
vec3 backgroundColor = vec3(0.0);
for (float stepIndex = 0.0; stepIndex < volumeSteps; ++stepIndex) {
vec3 primaryPosition = cameraPosition + (stepDistance + sampleOffset) * rayDirection;
vec3 secondaryPosition = cameraPosition + (secondaryStepDistance + sampleOffset) * rayDirection;
primaryPosition = abs(vec3(tilingFactor) - mod(primaryPosition, vec3(tilingFactor * 2.0)));
secondaryPosition = abs(vec3(tilingFactor) - mod(secondaryPosition, vec3(tilingFactor * 2.0)));
fieldContribution = field(secondaryPosition);
float particleAccumulator = 0.0, particleDistance = 0.0;
for (int i = 0; i < maxIterations; ++i) {
primaryPosition = abs(primaryPosition) / dot(primaryPosition, primaryPosition) - formulaAdjustment;
float distanceChange = abs(length(primaryPosition) - particleDistance);
particleAccumulator += i > 2 ? min(12.0, distanceChange) : distanceChange;
particleDistance = length(primaryPosition);
}
particleAccumulator *= particleAccumulator * particleAccumulator;
float fadeFactor = pow(distanceFading, max(0.0, float(stepIndex) - normalizedSampleOffset));
accumulatedColor += vec3(stepDistance, stepDistance * stepDistance, stepDistance * stepDistance * stepDistance * stepDistance)
* particleAccumulator * baseBrightness * fadeFactor;
backgroundColor += mix(0.4, 1.0, cloudOpacity) * vec3(1.8 * fieldContribution * fieldContribution * fieldContribution,
1.4 * fieldContribution * fieldContribution, fieldContribution) * fadeFactor;
stepDistance += stepSize;
secondaryStepDistance += stepSize;
}
accumulatedColor = mix(vec3(length(accumulatedColor)), accumulatedColor, colorSaturation);
vec4 foregroundColor = vec4(accumulatedColor * 0.01, 1.0);
backgroundColor *= cloudOpacity;
backgroundColor.b *= 1.8;
backgroundColor.r *= 0.05;
backgroundColor.b = 0.5 * mix(backgroundColor.g, backgroundColor.b, 0.8);
backgroundColor.g = 0.0;
backgroundColor.bg = mix(backgroundColor.gb, backgroundColor.bg, 0.5 * (cos(iTime * 0.01) + 1.0));
vec2 terminalUV = fragCoord.xy / iResolution.xy;
vec4 terminalColor = texture(iChannel0, terminalUV);
float brightnessThreshold = 0.1;
float terminalBrightness = dot(terminalColor.rgb, vec3(0.2126, 0.7152, 0.0722));
if (terminalBrightness < brightnessThreshold) {
fragColor = mix(terminalColor, vec4(foregroundColor.rgb + backgroundColor, 1.0), 0.24 * effectIntensity);
} else {
fragColor = terminalColor;
}
}

View file

@ -0,0 +1,202 @@
// geocities.glsl — a tribute to the golden age of blinking, tiled,
// under-construction personal homepages. Caution-stripe wallpaper,
// a rainbow sparkle field with hard blink (not a gentle twinkle),
// a scrolling pixel-font "UNDER CONSTRUCTION" marquee, and a bouncing
// rotating star badge, DVD-logo style.
// transparent background
const bool transparent = true;
// terminal contents luminance threshold to be considered background (0.0 to 1.0)
const float threshold = 0.02;
// show/hide individual gimmicks
const bool showStripes = true;
const bool showSparkles = true;
const bool showMarquee = true;
const bool showBadge = true;
// caution-stripe wallpaper opacity (0.0 to 1.0)
const float stripeOpacity = 0.02;
// sparkle field brightness
const float sparkleIntensity = 0.1;
// marquee scroll speed, in pixels per second
const float scrollSpeed = 90.0;
// ---------------------------------------------------------------
// helpers
// ---------------------------------------------------------------
float luminance(vec3 color) {
return dot(color, vec3(0.2126, 0.7152, 0.0722));
}
vec3 hsv2rgb(vec3 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
float hash21(vec2 p) {
p = fract(p * vec2(233.34, 851.73));
p += dot(p, p + 23.45);
return fract(p.x * p.y);
}
// 0 -> 1 -> 0 triangle wave, used to ping-pong the bouncing badge
float triangleWave(float x, float period) {
float t = fract(x / period);
return 1.0 - abs(2.0 * t - 1.0);
}
// ---------------------------------------------------------------
// tiny 5x7 pixel font, just enough glyphs to spell "UNDER CONSTRUCTION"
// each row is packed as a 5-bit value, bit weight 16 = leftmost column
// ---------------------------------------------------------------
const int FONT_ROWS = 7;
const int FONT[77] = int[77](
0, 0, 0, 0, 0, 0, 0, // 0: space
17, 17, 17, 17, 17, 17, 14, // 1: U
17, 25, 21, 21, 19, 17, 17, // 2: N
30, 17, 17, 17, 17, 17, 30, // 3: D
31, 16, 16, 30, 16, 16, 31, // 4: E
30, 17, 17, 30, 20, 18, 17, // 5: R
15, 16, 16, 16, 16, 16, 15, // 6: C
14, 17, 17, 17, 17, 17, 14, // 7: O
15, 16, 16, 14, 1, 1, 30, // 8: S
31, 4, 4, 4, 4, 4, 4, // 9: T
31, 4, 4, 4, 4, 4, 31 // 10: I
);
// "UNDER CONSTRUCTION" as indices into the glyph table above
const int MSG_LEN = 18;
const int MSG[18] = int[18](1, 2, 3, 4, 5, 0, 6, 7, 2, 8, 9, 5, 1, 6, 9, 10, 7, 2);
float fontBit(int code, int row, int col) {
int rowVal = FONT[code * FONT_ROWS + row];
float shifted = floor(float(rowVal) / pow(2.0, float(4 - col)));
return mod(shifted, 2.0);
}
// ---------------------------------------------------------------
// background gimmicks
// ---------------------------------------------------------------
vec3 hazardStripes(vec2 fragCoord) {
float stripeWidth = 18.0;
float diag = (fragCoord.x + fragCoord.y) + iTime * 40.0;
float m = mod(diag, stripeWidth * 2.0);
vec3 yellow = vec3(1.0, 0.82, 0.0);
vec3 blk = vec3(0.05);
return m < stripeWidth ? yellow : blk;
}
vec3 sparkleStars(vec2 uv, vec2 resolution) {
vec2 gridUV = uv;
gridUV.x *= resolution.x / resolution.y;
gridUV *= 26.0;
vec2 ipos = floor(gridUV);
vec2 fpos = fract(gridUV);
// only a fraction of cells host a star
float rnd = hash21(ipos);
if (rnd > 0.12) return vec3(0.0);
vec2 center = vec2(hash21(ipos + 3.1), hash21(ipos + 7.7));
float d = length(fpos - center);
// hard on/off blink rather than a smooth twinkle, tacky on purpose
float phase = hash21(ipos + 11.0) * 10.0;
float blink = step(0.5, fract(iTime * (1.5 + rnd * 4.0) + phase));
float glow = smoothstep(0.12, 0.0, d);
vec3 hue = hsv2rgb(vec3(fract(rnd * 4.0 + iTime * 0.1), 1.0, 1.0));
return hue * glow * blink;
}
vec3 starBadge(vec2 fragCoord, vec2 resolution) {
float radius = 26.0;
float px = mix(radius, resolution.x - radius, triangleWave(iTime * 0.09, 1.0));
float py = mix(radius, resolution.y - radius, triangleWave(iTime * 0.13 + 0.37, 1.0));
vec2 p = fragCoord - vec2(px, py);
float d = length(p);
float a = atan(p.y, p.x) - iTime * 2.0;
float starR = radius * (0.55 + 0.45 * cos(a * 5.0));
float shape = smoothstep(1.5, -1.5, d - starR);
vec3 hue = hsv2rgb(vec3(fract(iTime * 0.3), 1.0, 1.0));
return hue * shape;
}
// returns rgb + coverage alpha for the scrolling marquee ticker
vec4 marquee(vec2 fragCoord, vec2 resolution) {
float scale = clamp(resolution.y / 220.0, 2.0, 6.0);
float charW = 6.0 * scale; // 5 columns + 1 spacing column
float charH = 8.0 * scale; // 7 rows + 1 spacing row
int gap = 6; // blank characters between loops of the message
int totalChars = MSG_LEN + gap;
// band hugs the bottom edge of the terminal
float bandY0 = resolution.y - charH - scale * 2.0;
float localY = fragCoord.y - bandY0;
if (localY < 0.0 || localY >= charH) return vec4(0.0);
int row = int(floor(localY / scale));
if (row >= FONT_ROWS) return vec4(0.0);
float scrollX = fragCoord.x + iTime * scrollSpeed;
float totalWidth = float(totalChars) * charW;
float wrapped = mod(scrollX, totalWidth);
int charIndex = int(floor(wrapped / charW));
if (charIndex >= MSG_LEN) return vec4(0.0); // in the gap, blank
float localX = mod(wrapped, charW);
int col = int(floor(localX / scale));
if (col >= 5) return vec4(0.0);
int code = MSG[charIndex];
if (fontBit(code, row, col) < 0.5) return vec4(0.0);
vec3 color = hsv2rgb(vec3(fract(float(charIndex) / float(MSG_LEN) + iTime * 0.15), 1.0, 1.0));
return vec4(color, 1.0);
}
// ---------------------------------------------------------------
// main
// ---------------------------------------------------------------
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
vec4 terminalColor = texture(iChannel0, uv);
vec3 deco = vec3(0.0);
if (showStripes) {
deco += hazardStripes(fragCoord) * stripeOpacity;
}
if (showSparkles) {
deco += sparkleStars(uv, iResolution.xy) * sparkleIntensity;
}
if (showBadge) {
deco = max(deco, starBadge(fragCoord, iResolution.xy));
}
if (showMarquee) {
vec4 marq = marquee(fragCoord, iResolution.xy);
deco = mix(deco, marq.rgb, marq.a);
}
if (transparent) {
deco += terminalColor.rgb;
}
// only show the effect where the terminal is background (not text)
float mask = 1.0 - step(threshold, luminance(terminalColor.rgb));
vec3 blended = mix(terminalColor.rgb, deco, mask);
fragColor = vec4(blended, terminalColor.a);
}

View file

@ -0,0 +1,91 @@
#define PI 3.141592653589793
vec2 pxToNdc(vec2 p, float isPosition) {
return (p * 2.0 - iResolution.xy * isPosition) / iResolution.y;
}
float segPos(vec2 p, vec2 a, vec2 b) {
vec2 ba = b - a;
return clamp(dot(p - a, ba) / max(dot(ba, ba), 0.000001), 0.0, 1.0);
}
float segDist(vec2 p, vec2 a, vec2 b) {
float h = segPos(p, a, b);
return length(p - (a + (b - a) * h));
}
float diamond(vec2 p, vec2 c, float r) {
float d = abs(p.x - c.x) + abs(p.y - c.y);
return 1.0 - smoothstep(r * 0.25, r, d);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord.xy / iResolution.xy;
vec4 base = texture(iChannel0, uv);
vec3 col = base.rgb;
vec2 p = pxToNdc(fragCoord.xy, 1.0);
// Ghostty cursor rect: xy is the top-left-ish corner, zw is width/height.
vec2 curPx = vec2(
iCurrentCursor.x + iCurrentCursor.z * 0.5,
iCurrentCursor.y - iCurrentCursor.w * 0.5
);
vec2 prevPx = vec2(
iPreviousCursor.x + iPreviousCursor.z * 0.5,
iPreviousCursor.y - iPreviousCursor.w * 0.5
);
vec2 cur = pxToNdc(curPx, 1.0);
vec2 prev = pxToNdc(prevPx, 1.0);
float cell = max(0.008, max(iCurrentCursor.z, iCurrentCursor.w) * 2.0 / iResolution.y);
float age = clamp(iTime - iTimeCursorChange, 0.0, 2.0);
float pulse = exp(-age * 4.5);
float moved = smoothstep(cell * 1.2, cell * 5.0, distance(cur, prev));
// Orange-gold trail after cursor jumps.
float h = segPos(p, prev, cur);
float trailD = segDist(p, prev, cur);
float trail = (1.0 - smoothstep(cell * 0.18, cell * 0.85, trailD));
trail *= sin(h * PI) * moved * pulse;
col += trail * vec3(1.0, 0.36, 0.05) * 0.90;
// Gold glow around current cursor.
float d = length(p - cur);
float shimmer = 0.75 + 0.25 * sin(iTime * 9.0 + d * 90.0);
float glow = exp(-d / (cell * 4.0)) * 0.28 * shimmer;
col += glow * vec3(1.0, 0.62, 0.12);
// Expanding diamond ring when cursor moves.
float ringRadius = cell * (1.30 + 2.20 * pulse);
float ring = 1.0 - smoothstep(cell * 0.10, cell * 0.35, abs(d - ringRadius));
col += ring * vec3(1.0, 0.92, 0.65) * (0.42 + 0.75 * pulse);
// Tiny orbiting diamonds: pinky ring / grill sparkle.
float orbit = 0.0;
for (int k = 0; k < 8; k++) {
float fi = float(k);
float dir = mix(-1.0, 1.0, step(0.5, mod(fi, 2.0)));
float a = fi * PI * 2.0 / 8.0 + iTime * (1.5 + 0.1 * fi) * dir;
float rad = cell * (2.4 + 0.45 * sin(iTime * 2.0 + fi * 1.7));
vec2 q = cur + vec2(cos(a), sin(a)) * rad;
float tw = 0.65 + 0.35 * sin(iTime * 7.0 + fi * 2.0);
orbit += diamond(p, q, cell * 0.45) * tw;
}
col += orbit * vec3(1.0, 0.86, 0.50) * 0.70;
// Cross flare directly on cursor.
float flareX = (1.0 - smoothstep(0.0, cell * 0.035, abs(p.y - cur.y))) *
(1.0 - smoothstep(0.0, cell * 4.5, abs(p.x - cur.x)));
float flareY = (1.0 - smoothstep(0.0, cell * 0.035, abs(p.x - cur.x))) *
(1.0 - smoothstep(0.0, cell * 4.5, abs(p.y - cur.y)));
col += (flareX + flareY) * vec3(1.0, 0.92, 0.60) * 0.11;
fragColor = vec4(clamp(col, 0.0, 1.0), base.a);
}

View file

@ -0,0 +1,154 @@
// CONFIGURATION
const float DURATION = 0.15; // How long the ripple animates (seconds)
const float MAX_SIZE = 0.05; // Max radius in normalized coords (0.5 = 1/4 screen height)
const float ANIMATION_START_OFFSET = 0.0; // Start the ripple slightly progressed (0.0 - 1.0)
vec4 COLOR = vec4(0.35, 0.36, 0.44, 1.0); // change to iCurrentCursorColor for your cursor's color
const float CURSOR_WIDTH_CHANGE_THRESHOLD = 0.5; // Triggers ripple if cursor width changes by this fraction
const float BLUR = 3.0; // Blur level in pixels
// Easing functions
float easeOutQuad(float t) {
return 1.0 - (1.0 - t) * (1.0 - t);
}
float easeInOutQuad(float t) {
return t < 0.5 ? 2.0 * t * t : 1.0 - pow(-2.0 * t + 2.0, 2.0) / 2.0;
}
float easeOutCubic(float t) {
return 1.0 - pow(1.0 - t, 3.0);
}
float easeOutQuart(float t) {
return 1.0 - pow(1.0 - t, 4.0);
}
float easeOutQuint(float t) {
return 1.0 - pow(1.0 - t, 5.0);
}
float easeOutExpo(float t) {
return t == 1.0 ? 1.0 : 1.0 - pow(2.0, -10.0 * t);
}
float easeOutCirc(float t) {
return sqrt(1.0 - pow(t - 1.0, 2.0));
}
float easeOutSine(float t) {
return sin((t * 3.1415916) / 2.0);
}
float easeOutElastic(float t) {
const float c4 = (2.0 * 3.1415916) / 3.0;
return t == 0.0 ? 0.0 : t == 1.0 ? 1.0 : pow(2.0, -10.0 * t) * sin((t * 10.0 - 0.75) * c4) + 1.0;
}
float easeOutBounce(float t) {
const float n1 = 7.5625;
const float d1 = 2.75;
if (t < 1.0 / d1) {
return n1 * t * t;
} else if (t < 2.0 / d1) {
return n1 * (t -= 1.5 / d1) * t + 0.75;
} else if (t < 2.5 / d1) {
return n1 * (t -= 2.25 / d1) * t + 0.9375;
} else {
return n1 * (t -= 2.625 / d1) * t + 0.984375;
}
}
float easeOutBack(float t) {
const float c1 = 1.70158;
const float c3 = c1 + 1.0;
return 1.0 + c3 * pow(t - 1.0, 3.0) + c1 * pow(t - 1.0, 2.0);
}
// Pulse fade functions
float smoothstepPulse(float t) {
return 4.0 * t * (1.0 - t);
}
float easeOutPulse(float t) {
return t * (2.0 - t);
}
float powerCurvePulse(float t) {
float x = t * 2.0 - 1.0;
return 1.0 - x * x;
}
float doubleSmoothstepPulse(float t) {
return smoothstep(0.0, 0.5, t) * (1.0 - smoothstep(0.5, 1.0, t));
}
float exponentialDecayPulse(float t) {
return exp(-3.0 * t) * sin(t * 3.1415916);
}
float sinPulse(float t) {
return sin(t * 3.1415916);
}
vec2 normalize(vec2 value, float isPosition) {
return (value * 2.0 - (iResolution.xy * isPosition)) / iResolution.y;
}
float getSdfRectangle(in vec2 p, in vec2 xy, in vec2 b){
vec2 d = abs(p - xy) - b;
return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord){
#if !defined(WEB)
fragColor = texture(iChannel0, fragCoord.xy / iResolution.xy);
#endif
// Normalization & setup (-1 to 1 coords)
vec2 vu = normalize(fragCoord, 1.);
vec2 offsetFactor = vec2(-.5, 0.5);
vec4 currentCursor = vec4(normalize(iCurrentCursor.xy, 1.), normalize(iCurrentCursor.zw, 0.));
vec4 previousCursor = vec4(normalize(iPreviousCursor.xy, 1.), normalize(iPreviousCursor.zw, 0.));
vec2 centerCC = currentCursor.xy - (currentCursor.zw * offsetFactor);
float cellWidth = max(currentCursor.z, previousCursor.z); // width of the 'block' cursor
// check for significant width change
float widthChange = abs(currentCursor.z - previousCursor.z);
float widthThresholdNorm = cellWidth * CURSOR_WIDTH_CHANGE_THRESHOLD;
float isModeChange = step(widthThresholdNorm, widthChange);
// ANIMATION
float rippleProgress = (iTime - iTimeCursorChange) / DURATION + ANIMATION_START_OFFSET;
// don't clamp yet; we need to know if it's > 1.0 (finished)
float isAnimating = 1.0 - step(1.0, rippleProgress); // progress < 1.0 ? 1.0: 0.0
// WHY NOT BRANCHLESS??? here ya go:
// because we NEVER have divergence, even in this if/else branchfull logic
// why? because its UNIFORM branching (ie, all fragments take the same path) which modern GPUs handles efficiently
// its far more efficient than calculating the ripple EVERY FRAME even when not needed(branchless)
if (isModeChange > 0.0 && isAnimating > 0.0) {
// float easedProgress = rippleProgress;
// float easedProgress = easeOutQuad(rippleProgress);
// float easedProgress = easeInOutQuad(rippleProgress);
// float easedProgress = easeOutCubic(rippleProgress);
// float easedProgress = easeOutQuart(rippleProgress);
// float easedProgress = easeOutQuint(rippleProgress);
// float easedProgress = easeOutExpo(rippleProgress);
float easedProgress = easeOutCirc(rippleProgress);
// float easedProgress = easeOutSine(rippleProgress);
// float easedProgress = easeOutBack(rippleProgress);
// easedProgress = clamp(easedProgress, 0.0, 1.0);
// RIPPLE CALCULATION
float rippleExpansion = easedProgress * MAX_SIZE;
// float fade = 1.0; // no fade
// float fade = 1.0 - easedProgress; // linear fade
// float fade = 1.0 - smoothstepPulse(rippleProgress);
float fade = 1.0 - easeOutPulse(rippleProgress);
// float fade = 1.0 - powerCurvePulse(rippleProgress);
// float fade = doubleSmoothstepPulse(rippleProgress);
// float fade = exponentialDecayPulse(rippleProgress);
// float fade = sinPulse(rippleProgress);
vec2 halfSizeCC = vec2(currentCursor.z, currentCursor.w) * 0.5 + vec2(rippleExpansion);
float sdfRectRing = getSdfRectangle(vu, centerCC, halfSizeCC);
// Antialias (1-pixel width in normalized coords)
float antiAliasSize = normalize(vec2(BLUR, BLUR), 0.0).x;
float ripple = (1.0 - smoothstep(-antiAliasSize, antiAliasSize, sdfRectRing)) * fade;
// Apply ripple effect
fragColor = mix(fragColor, COLOR, ripple * COLOR.a);
}
// else: do nothing, keep original fragColor
}

View file

@ -0,0 +1,132 @@
// CONFIGURATION
const float DURATION = 0.15; // How long the ripple animates (seconds)
const float MAX_RADIUS = 0.05; // Max radius in normalized coords (0.5 = 1/4 screen height)
const float RING_THICKNESS = 0.02; // Ring width in normalized coords
const float CURSOR_WIDTH_CHANGE_THRESHOLD = 0.5; // Triggers ripple if cursor width changes by this fraction
vec4 COLOR = vec4(0.35, 0.36, 0.44, 1.0); // change to iCurrentCursorColor for your cursor's color
const float BLUR = 3.0; // Blur level in pixels
const float ANIMATION_START_OFFSET = 0.0; // Start the ripple slightly progressed (0.0 - 1.0)
// Easing functions
float easeOutQuad(float t) {
return 1.0 - (1.0 - t) * (1.0 - t);
}
float easeInOutQuad(float t) {
return t < 0.5 ? 2.0 * t * t : 1.0 - pow(-2.0 * t + 2.0, 2.0) / 2.0;
}
float easeOutCubic(float t) {
return 1.0 - pow(1.0 - t, 3.0);
}
float easeOutQuart(float t) {
return 1.0 - pow(1.0 - t, 4.0);
}
float easeOutQuint(float t) {
return 1.0 - pow(1.0 - t, 5.0);
}
float easeOutExpo(float t) {
return t == 1.0 ? 1.0 : 1.0 - pow(2.0, -10.0 * t);
}
float easeOutCirc(float t) {
return sqrt(1.0 - pow(t - 1.0, 2.0));
}
float easeOutSine(float t) {
return sin((t * 3.1415916) / 2.0);
}
float easeOutElastic(float t) {
const float c4 = (2.0 * 3.1415916) / 3.0;
return t == 0.0 ? 0.0 : t == 1.0 ? 1.0 : pow(2.0, -10.0 * t) * sin((t * 10.0 - 0.75) * c4) + 1.0;
}
float easeOutBounce(float t) {
const float n1 = 7.5625;
const float d1 = 2.75;
if (t < 1.0 / d1) {
return n1 * t * t;
} else if (t < 2.0 / d1) {
return n1 * (t -= 1.5 / d1) * t + 0.75;
} else if (t < 2.5 / d1) {
return n1 * (t -= 2.25 / d1) * t + 0.9375;
} else {
return n1 * (t -= 2.625 / d1) * t + 0.984375;
}
}
float easeOutBack(float t) {
const float c1 = 1.70158;
const float c3 = c1 + 1.0;
return 1.0 + c3 * pow(t - 1.0, 3.0) + c1 * pow(t - 1.0, 2.0);
}
// Pulse fade functions
float easeOutPulse(float t) {
return t * (2.0 - t);
}
float exponentialDecayPulse(float t) {
return exp(-3.0 * t) * sin(t * 3.1415916);
}
vec2 normalize(vec2 value, float isPosition) {
return (value * 2.0 - (iResolution.xy * isPosition)) / iResolution.y;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord){
#if !defined(WEB)
fragColor = texture(iChannel0, fragCoord.xy / iResolution.xy);
#endif
// Normalization & setup (-1 to 1 coords)
vec2 vu = normalize(fragCoord, 1.);
vec2 offsetFactor = vec2(-.5, 0.5);
vec4 currentCursor = vec4(normalize(iCurrentCursor.xy, 1.), normalize(iCurrentCursor.zw, 0.));
vec4 previousCursor = vec4(normalize(iPreviousCursor.xy, 1.), normalize(iPreviousCursor.zw, 0.));
vec2 centerCC = currentCursor.xy - (currentCursor.zw * offsetFactor);
float cellWidth = max(currentCursor.z, previousCursor.z); // width of the 'block' cursor
// check for significant width change
float widthChange = abs(currentCursor.z - previousCursor.z);
float widthThresholdNorm = cellWidth * CURSOR_WIDTH_CHANGE_THRESHOLD;
float isModeChange = step(widthThresholdNorm, widthChange);
// ANIMATION
float rippleProgress = (iTime - iTimeCursorChange) / DURATION + ANIMATION_START_OFFSET;
// don't clamp yet; we need to know if it's > 1.0 (finished)
float isAnimating = 1.0 - step(1.0, rippleProgress); // progress < 1.0 ? 1.0: 0.0
if (isModeChange > 0.0 && isAnimating > 0.0) {
// Apply easing to progress
// float easedProgress = rippleProgress;
// float easedProgress = easeOutQuad(rippleProgress);
// float easedProgress = easeInOutQuad(rippleProgress);
// float easedProgress = easeOutCubic(rippleProgress);
// float easedProgress = easeOutQuart(rippleProgress);
// float easedProgress = easeOutQuint(rippleProgress);
// float easedProgress = easeOutExpo(rippleProgress);
float easedProgress = easeOutCirc(rippleProgress);
// float easedProgress = easeOutSine(rippleProgress);
// float easedProgress = easeOutBack(rippleProgress);
// RIPPLE CALCULATION
float rippleRadius = easedProgress * MAX_RADIUS;
// float fade = 1.0; // no fade
// float fade = 1.0 - easedProgress; // linear fade
float fade = 1.0 - easeOutPulse(rippleProgress);
// float fade = 1.0 - exponentialDecayPulse(rippleProgress);
// Calculate distance from frag to cursor center
float dist = distance(vu, centerCC);
float sdfRing = abs(dist - rippleRadius) - RING_THICKNESS * 0.5;
// Antialias (1-pixel width in normalized coords)
float antiAliasSize = normalize(vec2(BLUR, BLUR), 0.0).x;
float ripple = (1.0 - smoothstep(-antiAliasSize, antiAliasSize, sdfRing)) * fade;
// Apply ripple effect
fragColor = mix(fragColor, COLOR, ripple * COLOR.a);
}
// else: do nothing, keep original fragColor
}

View file

@ -0,0 +1,138 @@
// CONFIGURATION
const float DURATION = 0.15; // How long the ripple animates (seconds)
const float MAX_SIZE = 0.05; // Max radius in normalized coords (0.5 = 1/4 screen height)
const float RING_THICKNESS = 0.02; // Ring width in normalized coords
const float CURSOR_WIDTH_CHANGE_THRESHOLD = 0.5; // Triggers ripple if cursor width changes by this fraction
vec4 COLOR = vec4(0.35, 0.36, 0.44, 1.0); // change to iCurrentCursorColor for your cursor's color
const float BLUR = 1.0; // Blur level in pixels
const float ANIMATION_START_OFFSET = 0.0; // Start the ripple slightly progressed (0.0 - 1.0)
// Easing functions
float easeOutQuad(float t) {
return 1.0 - (1.0 - t) * (1.0 - t);
}
float easeInOutQuad(float t) {
return t < 0.5 ? 2.0 * t * t : 1.0 - pow(-2.0 * t + 2.0, 2.0) / 2.0;
}
float easeOutCubic(float t) {
return 1.0 - pow(1.0 - t, 3.0);
}
float easeOutQuart(float t) {
return 1.0 - pow(1.0 - t, 4.0);
}
float easeOutQuint(float t) {
return 1.0 - pow(1.0 - t, 5.0);
}
float easeOutExpo(float t) {
return t == 1.0 ? 1.0 : 1.0 - pow(2.0, -10.0 * t);
}
float easeOutCirc(float t) {
return sqrt(1.0 - pow(t - 1.0, 2.0));
}
float easeOutSine(float t) {
return sin((t * 3.1415916) / 2.0);
}
float easeOutElastic(float t) {
const float c4 = (2.0 * 3.1415916) / 3.0;
return t == 0.0 ? 0.0 : t == 1.0 ? 1.0 : pow(2.0, -10.0 * t) * sin((t * 10.0 - 0.75) * c4) + 1.0;
}
float easeOutBounce(float t) {
const float n1 = 7.5625;
const float d1 = 2.75;
if (t < 1.0 / d1) {
return n1 * t * t;
} else if (t < 2.0 / d1) {
return n1 * (t -= 1.5 / d1) * t + 0.75;
} else if (t < 2.5 / d1) {
return n1 * (t -= 2.25 / d1) * t + 0.9375;
} else {
return n1 * (t -= 2.625 / d1) * t + 0.984375;
}
}
float easeOutBack(float t) {
const float c1 = 1.70158;
const float c3 = c1 + 1.0;
return 1.0 + c3 * pow(t - 1.0, 3.0) + c1 * pow(t - 1.0, 2.0);
}
// Pulse fade functions
float easeOutPulse(float t) {
return t * (2.0 - t);
}
float exponentialDecayPulse(float t) {
return exp(-3.0 * t) * sin(t * 3.1415916);
}
vec2 normalize(vec2 value, float isPosition) {
return (value * 2.0 - (iResolution.xy * isPosition)) / iResolution.y;
}
float getSdfRectangle(in vec2 p, in vec2 xy, in vec2 b){
vec2 d = abs(p - xy) - b;
return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord){
#if !defined(WEB)
fragColor = texture(iChannel0, fragCoord.xy / iResolution.xy);
#endif
// Normalization & setup (-1 to 1 coords)
vec2 vu = normalize(fragCoord, 1.);
vec2 offsetFactor = vec2(-.5, 0.5);
vec4 currentCursor = vec4(normalize(iCurrentCursor.xy, 1.), normalize(iCurrentCursor.zw, 0.));
vec4 previousCursor = vec4(normalize(iPreviousCursor.xy, 1.), normalize(iPreviousCursor.zw, 0.));
vec2 centerCC = currentCursor.xy - (currentCursor.zw * offsetFactor);
float cellWidth = max(currentCursor.z, previousCursor.z); // width of the 'block' cursor
// check for significant width change
float widthChange = abs(currentCursor.z - previousCursor.z);
float widthThresholdNorm = cellWidth * CURSOR_WIDTH_CHANGE_THRESHOLD;
float isModeChange = step(widthThresholdNorm, widthChange);
// ANIMATION
float rippleProgress = (iTime - iTimeCursorChange) / DURATION + ANIMATION_START_OFFSET;
// don't clamp yet; we need to know if it's > 1.0 (finished)
float isAnimating = 1.0 - step(1.0, rippleProgress); // progress < 1.0 ? 1.0: 0.0
if (isModeChange > 0.0 && isAnimating > 0.0) {
// Apply easing to progress
// float easedProgress = rippleProgress;
// float easedProgress = easeOutQuad(rippleProgress);
// float easedProgress = easeInOutQuad(rippleProgress);
// float easedProgress = easeOutCubic(rippleProgress);
// float easedProgress = easeOutQuart(rippleProgress);
// float easedProgress = easeOutQuint(rippleProgress);
// float easedProgress = easeOutExpo(rippleProgress);
float easedProgress = easeOutCirc(rippleProgress);
// float easedProgress = easeOutSine(rippleProgress);
// float easedProgress = easeOutBack(rippleProgress);
// RIPPLE CALCULATION
float rippleExpansion = easedProgress * MAX_SIZE;
// float fade = 1.0; // no fade
// float fade = 1.0 - easedProgress; // linear fade
float fade = 1.0 - easeOutPulse(rippleProgress);
// float fade = 1.0 - exponentialDecayPulse(rippleProgress);
// Calculate distance from frag to cursor center
// float dist = distance(vu, centerCC);
// float sdfRing = abs(dist - rippleExpansion) - RING_THICKNESS * 0.5;
vec2 halfSizeCC = vec2(currentCursor.z, currentCursor.w) * 0.5 + vec2(rippleExpansion);
float sdfRectRing = abs(getSdfRectangle(vu, centerCC, halfSizeCC)) - RING_THICKNESS * 0.5;
// Antialias (1-pixel width in normalized coords)
float antiAliasSize = normalize(vec2(BLUR, BLUR), 0.0).x;
float ripple = (1.0 - smoothstep(-antiAliasSize, antiAliasSize, sdfRectRing)) * fade;
// Apply ripple effect
fragColor = mix(fragColor, COLOR, ripple * COLOR.a);
}
// else: do nothing, keep original fragColor
}

View file

@ -0,0 +1,148 @@
// CONFIGURATION
const float DURATION = 0.15; // How long the ripple animates (seconds)
const float MAX_RADIUS = 0.06; // Max radius in normalized coords (0.5 = 1/4 screen height)
const float ANIMATION_START_OFFSET = 0.0; // Start the ripple slightly progressed (0.0 - 1.0)
vec4 COLOR = vec4(0.35, 0.36, 0.44, 1.0); // change to iCurrentCursorColor for your cursor's color
const float CURSOR_WIDTH_CHANGE_THRESHOLD = 0.5; // Triggers ripple if cursor width changes by this fraction
const float BLUR = 3.0; // Blur level in pixels
// Easing functions
float easeOutQuad(float t) {
return 1.0 - (1.0 - t) * (1.0 - t);
}
float easeInOutQuad(float t) {
return t < 0.5 ? 2.0 * t * t : 1.0 - pow(-2.0 * t + 2.0, 2.0) / 2.0;
}
float easeOutCubic(float t) {
return 1.0 - pow(1.0 - t, 3.0);
}
float easeOutQuart(float t) {
return 1.0 - pow(1.0 - t, 4.0);
}
float easeOutQuint(float t) {
return 1.0 - pow(1.0 - t, 5.0);
}
float easeOutExpo(float t) {
return t == 1.0 ? 1.0 : 1.0 - pow(2.0, -10.0 * t);
}
float easeOutCirc(float t) {
return sqrt(1.0 - pow(t - 1.0, 2.0));
}
float easeOutSine(float t) {
return sin((t * 3.1415916) / 2.0);
}
float easeOutElastic(float t) {
const float c4 = (2.0 * 3.1415916) / 3.0;
return t == 0.0 ? 0.0 : t == 1.0 ? 1.0 : pow(2.0, -10.0 * t) * sin((t * 10.0 - 0.75) * c4) + 1.0;
}
float easeOutBounce(float t) {
const float n1 = 7.5625;
const float d1 = 2.75;
if (t < 1.0 / d1) {
return n1 * t * t;
} else if (t < 2.0 / d1) {
return n1 * (t -= 1.5 / d1) * t + 0.75;
} else if (t < 2.5 / d1) {
return n1 * (t -= 2.25 / d1) * t + 0.9375;
} else {
return n1 * (t -= 2.625 / d1) * t + 0.984375;
}
}
float easeOutBack(float t) {
const float c1 = 1.70158;
const float c3 = c1 + 1.0;
return 1.0 + c3 * pow(t - 1.0, 3.0) + c1 * pow(t - 1.0, 2.0);
}
// Pulse fade functions
float smoothstepPulse(float t) {
return 4.0 * t * (1.0 - t);
}
float easeOutPulse(float t) {
return t * (2.0 - t);
}
float powerCurvePulse(float t) {
float x = t * 2.0 - 1.0;
return 1.0 - x * x;
}
float doubleSmoothstepPulse(float t) {
return smoothstep(0.0, 0.5, t) * (1.0 - smoothstep(0.5, 1.0, t));
}
float exponentialDecayPulse(float t) {
return exp(-3.0 * t) * sin(t * 3.1415916);
}
float sinPulse(float t) {
return sin(t * 3.1415916);
}
vec2 normalize(vec2 value, float isPosition) {
return (value * 2.0 - (iResolution.xy * isPosition)) / iResolution.y;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord){
#if !defined(WEB)
fragColor = texture(iChannel0, fragCoord.xy / iResolution.xy);
#endif
// Normalization & setup (-1 to 1 coords)
vec2 vu = normalize(fragCoord, 1.);
vec2 offsetFactor = vec2(-.5, 0.5);
vec4 currentCursor = vec4(normalize(iCurrentCursor.xy, 1.), normalize(iCurrentCursor.zw, 0.));
vec4 previousCursor = vec4(normalize(iPreviousCursor.xy, 1.), normalize(iPreviousCursor.zw, 0.));
vec2 centerCC = currentCursor.xy - (currentCursor.zw * offsetFactor);
float cellWidth = max(currentCursor.z, previousCursor.z); // width of the 'block' cursor
// check for significant width change
float widthChange = abs(currentCursor.z - previousCursor.z);
float widthThresholdNorm = cellWidth * CURSOR_WIDTH_CHANGE_THRESHOLD;
float isModeChange = step(widthThresholdNorm, widthChange);
// ANIMATION
float rippleProgress = (iTime - iTimeCursorChange) / DURATION + ANIMATION_START_OFFSET;
// don't clamp yet; we need to know if it's > 1.0 (finished)
float isAnimating = 1.0 - step(1.0, rippleProgress); // progress < 1.0 ? 1.0: 0.0
if (isModeChange > 0.0 && isAnimating > 0.0) {
// float easedProgress = rippleProgress;
// float easedProgress = easeOutQuad(rippleProgress);
// float easedProgress = easeInOutQuad(rippleProgress);
// float easedProgress = easeOutCubic(rippleProgress);
// float easedProgress = easeOutQuart(rippleProgress);
// float easedProgress = easeOutQuint(rippleProgress);
// float easedProgress = easeOutExpo(rippleProgress);
float easedProgress = easeOutCirc(rippleProgress);
// float easedProgress = easeOutSine(rippleProgress);
// float easedProgress = easeOutBack(rippleProgress);
// easedProgress = clamp(easedProgress, 0.0, 1.0);
// RIPPLE CALCULATION
float rippleRadius = easedProgress * MAX_RADIUS;
// float fade = 1.0; // no fade
// float fade = 1.0 - easedProgress; // linear fade
// float fade = 1.0 - smoothstepPulse(rippleProgress);
float fade = 1.0 - easeOutPulse(rippleProgress);
// float fade = 1.0 - powerCurvePulse(rippleProgress);
// float fade = doubleSmoothstepPulse(rippleProgress);
// float fade = exponentialDecayPulse(rippleProgress);
// float fade = sinPulse(rippleProgress);
// Calculate distance from frag to cursor center
float dist = distance(vu, centerCC);
float sdfCircle = dist - rippleRadius;
// Antialias (1-pixel width in normalized coords)
float antiAliasSize = normalize(vec2(BLUR, BLUR), 0.0).x;
float ripple = (1.0 - smoothstep(-antiAliasSize, antiAliasSize, sdfCircle)) * fade;
// Apply ripple effect
fragColor = mix(fragColor, COLOR, ripple * COLOR.a);
}
}

View file

@ -0,0 +1,162 @@
// transparent background
const bool transparent = true;
// terminal contents luminance threshold to be considered background (0.0 to 1.0)
const float threshold = 0.05;
// overall star brightness (0.0 = invisible, 1.0 = original brightness)
const float intensity = 0.09;
// divisions of grid
const float repeats = 15.;
// number of layers
const float layers = 20.;
// star colours
const vec3 blue = vec3(51., 64., 195.) / 255.;
const vec3 cyan = vec3(117., 250., 254.) / 255.;
const vec3 white = vec3(255., 255., 255.) / 255.;
const vec3 yellow = vec3(251., 245., 44.) / 255.;
const vec3 red = vec3(247, 2., 20.) / 255.;
float luminance(vec3 color) {
return dot(color, vec3(0.2126, 0.7152, 0.0722));
}
// spectrum function
vec3 spectrum(vec2 pos) {
pos.x *= 4.;
vec3 outCol = vec3(0);
if (pos.x > 0.) {
outCol = mix(blue, cyan, fract(pos.x));
}
if (pos.x > 1.) {
outCol = mix(cyan, white, fract(pos.x));
}
if (pos.x > 2.) {
outCol = mix(white, yellow, fract(pos.x));
}
if (pos.x > 3.) {
outCol = mix(yellow, red, fract(pos.x));
}
return 1. - (pos.y * (1. - outCol));
}
float N21(vec2 p) {
p = fract(p * vec2(233.34, 851.73));
p += dot(p, p + 23.45);
return fract(p.x * p.y);
}
vec2 N22(vec2 p) {
float n = N21(p);
return vec2(n, N21(p + n));
}
mat2 scale(vec2 _scale) {
return mat2(_scale.x, 0.0,
0.0, _scale.y);
}
// 2D Noise based on Morgan McGuire
float noise(in vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
// Four corners in 2D of a tile
float a = N21(i);
float b = N21(i + vec2(1.0, 0.0));
float c = N21(i + vec2(0.0, 1.0));
float d = N21(i + vec2(1.0, 1.0));
// Smooth Interpolation
vec2 u = f * f * (3.0 - 2.0 * f); // Cubic Hermite Curve
// Mix 4 corners percentages
return mix(a, b, u.x) +
(c - a) * u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
float perlin2(vec2 uv, int octaves, float pscale) {
float col = 1.;
float initScale = 4.;
for (int l; l < octaves; l++) {
float val = noise(uv * initScale);
if (col <= 0.01) {
col = 0.;
break;
}
val -= 0.01;
val *= 0.5;
col *= val;
initScale *= pscale;
}
return col;
}
vec3 stars(vec2 uv, float offset) {
float timeScale = -(iTime + offset) / layers;
float trans = fract(timeScale);
float newRnd = floor(timeScale);
vec3 col = vec3(0.);
// Translate uv then scale for center
uv -= vec2(0.5);
uv = scale(vec2(trans)) * uv;
uv += vec2(0.5);
// Create square aspect ratio
uv.x *= iResolution.x / iResolution.y;
// Create boxes
uv *= repeats;
// Get position
vec2 ipos = floor(uv);
// Return uv as 0 to 1
uv = fract(uv);
// Calculate random xy and size
vec2 rndXY = N22(newRnd + ipos * (offset + 1.)) * 0.9 + 0.05;
float rndSize = N21(ipos) * 100. + 200.;
vec2 j = (rndXY - uv) * rndSize;
float sparkle = 1. / dot(j, j);
// Set stars to be pure white
col += spectrum(fract(rndXY * newRnd * ipos)) * vec3(sparkle);
col *= smoothstep(1., 0.8, trans);
return col; // Return pure white stars only
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord / iResolution.xy;
vec3 col = vec3(0.);
for (float i = 0.; i < layers; i++) {
col += stars(uv, i);
}
col *= intensity;
// Sample the terminal screen texture including alpha channel
vec4 terminalColor = texture(iChannel0, uv);
if (transparent) {
col += terminalColor.rgb;
}
// Make a mask that is 1.0 where the terminal content is not black
float mask = 1 - step(threshold, luminance(terminalColor.rgb));
vec3 blendedColor = mix(terminalColor.rgb, col, mask);
// Apply terminal's alpha to control overall opacity
fragColor = vec4(blendedColor, terminalColor.a);
}

View file

@ -0,0 +1,135 @@
// transparent background
const bool transparent = false;
// terminal contents luminance threshold to be considered background (0.0 to 1.0)
const float threshold = 0.15;
// divisions of grid
const float repeats = 30.;
// number of layers
const float layers = 21.;
// star colors
const vec3 white = vec3(1.0); // Set star color to pure white
float luminance(vec3 color) {
return dot(color, vec3(0.2126, 0.7152, 0.0722));
}
float N21(vec2 p) {
p = fract(p * vec2(233.34, 851.73));
p += dot(p, p + 23.45);
return fract(p.x * p.y);
}
vec2 N22(vec2 p) {
float n = N21(p);
return vec2(n, N21(p + n));
}
mat2 scale(vec2 _scale) {
return mat2(_scale.x, 0.0,
0.0, _scale.y);
}
// 2D Noise based on Morgan McGuire
float noise(in vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
// Four corners in 2D of a tile
float a = N21(i);
float b = N21(i + vec2(1.0, 0.0));
float c = N21(i + vec2(0.0, 1.0));
float d = N21(i + vec2(1.0, 1.0));
// Smooth Interpolation
vec2 u = f * f * (3.0 - 2.0 * f); // Cubic Hermite Curve
// Mix 4 corners percentages
return mix(a, b, u.x) +
(c - a) * u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
float perlin2(vec2 uv, int octaves, float pscale) {
float col = 1.;
float initScale = 4.;
for (int l; l < octaves; l++) {
float val = noise(uv * initScale);
if (col <= 0.01) {
col = 0.;
break;
}
val -= 0.01;
val *= 0.5;
col *= val;
initScale *= pscale;
}
return col;
}
vec3 stars(vec2 uv, float offset) {
float timeScale = -(iTime + offset) / layers;
float trans = fract(timeScale);
float newRnd = floor(timeScale);
vec3 col = vec3(0.);
// Translate uv then scale for center
uv -= vec2(0.5);
uv = scale(vec2(trans)) * uv;
uv += vec2(0.5);
// Create square aspect ratio
uv.x *= iResolution.x / iResolution.y;
// Create boxes
uv *= repeats;
// Get position
vec2 ipos = floor(uv);
// Return uv as 0 to 1
uv = fract(uv);
// Calculate random xy and size
vec2 rndXY = N22(newRnd + ipos * (offset + 1.)) * 0.9 + 0.05;
float rndSize = N21(ipos) * 100. + 200.;
vec2 j = (rndXY - uv) * rndSize;
float sparkle = 1. / dot(j, j);
// Set stars to be pure white
col += white * sparkle;
col *= smoothstep(1., 0.8, trans);
return col; // Return pure white stars only
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord / iResolution.xy;
vec3 col = vec3(0.);
for (float i = 0.; i < layers; i++) {
col += stars(uv, i);
}
// Sample the terminal screen texture including alpha channel
vec4 terminalColor = texture(iChannel0, uv);
if (transparent) {
col += terminalColor.rgb;
}
// Make a mask that is 1.0 where the terminal content is not black
float mask = 1 - step(threshold, luminance(terminalColor.rgb));
vec3 blendedColor = mix(terminalColor.rgb, col, mask);
// Apply terminal's alpha to control overall opacity
fragColor = vec4(blendedColor, terminalColor.a);
}