├── animated-gradient-shader.glsl ├── bettercrt.glsl ├── bloom.glsl ├── cineShader-Lava.glsl ├── crt.glsl ├── cubes.glsl ├── dither.glsl ├── drunkard.glsl ├── fireworks-rockets.glsl ├── fireworks.glsl ├── galaxy.glsl ├── gears-and-belts.glsl ├── glitchy.glsl ├── glow-rgbsplit-twitchy.glsl ├── gradient-background.glsl ├── in-game-crt.glsl ├── inside-the-matrix.glsl ├── just-snow.glsl ├── matrix-hallway.glsl ├── mnoise.glsl ├── negative.glsl ├── retro-terminal.glsl ├── sin-interference.glsl ├── smoke-and-ghost.glsl ├── sparks-from-fire.glsl ├── spotlight.glsl ├── starfield-colors.glsl ├── starfield.glsl ├── tft.glsl ├── underwater.glsl └── water.glsl /animated-gradient-shader.glsl: -------------------------------------------------------------------------------- 1 | // credits: https://github.com/unkn0wncode 2 | void mainImage(out vec4 fragColor, in vec2 fragCoord) 3 | { 4 | vec2 uv = fragCoord.xy / iResolution.xy; 5 | 6 | // Create seamless gradient animation 7 | float speed = 0.2; 8 | float gradientFactor = (uv.x + uv.y) / 2.0; 9 | 10 | // Use smoothstep and multiple sin waves for smoother transition 11 | float t = sin(iTime * speed) * 0.5 + 0.5; 12 | gradientFactor = smoothstep(0.0, 1.0, gradientFactor); 13 | 14 | // Create smooth circular animation 15 | float angle = iTime * speed; 16 | vec3 color1 = vec3(0.1, 0.1, 0.5); 17 | vec3 color2 = vec3(0.5, 0.1, 0.1); 18 | vec3 color3 = vec3(0.1, 0.5, 0.1); 19 | 20 | // Smooth interpolation between colors using multiple mix operations 21 | vec3 gradientStartColor = mix( 22 | mix(color1, color2, smoothstep(0.0, 1.0, sin(angle) * 0.5 + 0.5)), 23 | color3, 24 | smoothstep(0.0, 1.0, sin(angle + 2.0) * 0.5 + 0.5) 25 | ); 26 | 27 | vec3 gradientEndColor = mix( 28 | mix(color2, color3, smoothstep(0.0, 1.0, sin(angle + 1.0) * 0.5 + 0.5)), 29 | color1, 30 | smoothstep(0.0, 1.0, sin(angle + 3.0) * 0.5 + 0.5) 31 | ); 32 | 33 | vec3 gradientColor = mix(gradientStartColor, gradientEndColor, gradientFactor); 34 | 35 | vec4 terminalColor = texture(iChannel0, uv); 36 | float mask = 1.0 - step(0.5, dot(terminalColor.rgb, vec3(1.0))); 37 | vec3 blendedColor = mix(terminalColor.rgb, gradientColor, mask); 38 | 39 | fragColor = vec4(blendedColor, terminalColor.a); 40 | } 41 | -------------------------------------------------------------------------------- /bettercrt.glsl: -------------------------------------------------------------------------------- 1 | // Original shader collected from: https://www.shadertoy.com/view/WsVSzV 2 | // Licensed under Shadertoy's default since the original creator didn't provide any license. (CC BY NC SA 3.0) 3 | // Slight modifications were made to give a green-ish effect. 4 | 5 | // This shader was modified by April Hall (arithefirst) 6 | // Sourced from https://github.com/m-ahdal/ghostty-shaders/blob/main/retro-terminal.glsl 7 | // Changes made: 8 | // - Removed tint 9 | // - Made the boundaries match ghostty's background color 10 | 11 | float warp = 0.25; // simulate curvature of CRT monitor 12 | float scan = 0.50; // simulate darkness between scanlines 13 | 14 | void mainImage(out vec4 fragColor, in vec2 fragCoord) 15 | { 16 | // squared distance from center 17 | vec2 uv = fragCoord / iResolution.xy; 18 | vec2 dc = abs(0.5 - uv); 19 | dc *= dc; 20 | 21 | // warp the fragment coordinates 22 | uv.x -= 0.5; uv.x *= 1.0 + (dc.y * (0.3 * warp)); uv.x += 0.5; 23 | uv.y -= 0.5; uv.y *= 1.0 + (dc.x * (0.4 * warp)); uv.y += 0.5; 24 | 25 | // determine if we are drawing in a scanline 26 | float apply = abs(sin(fragCoord.y) * 0.25 * scan); 27 | 28 | // sample the texture 29 | vec3 color = texture(iChannel0, uv).rgb; 30 | 31 | // mix the sampled color with the scanline intensity 32 | fragColor = vec4(mix(color, vec3(0.0), apply), 1.0); 33 | } 34 | -------------------------------------------------------------------------------- /bloom.glsl: -------------------------------------------------------------------------------- 1 | // source: https://gist.github.com/qwerasd205/c3da6c610c8ffe17d6d2d3cc7068f17f 2 | // credits: https://github.com/qwerasd205 3 | // Golden spiral samples, [x, y, weight] weight is inverse of distance. 4 | const vec3[24] samples = { 5 | vec3(0.1693761725038636, 0.9855514761735895, 1), 6 | vec3(-1.333070830962943, 0.4721463328627773, 0.7071067811865475), 7 | vec3(-0.8464394909806497, -1.51113870578065, 0.5773502691896258), 8 | vec3(1.554155680728463, -1.2588090085709776, 0.5), 9 | vec3(1.681364377589461, 1.4741145918052656, 0.4472135954999579), 10 | vec3(-1.2795157692199817, 2.088741103228784, 0.4082482904638631), 11 | vec3(-2.4575847530631187, -0.9799373355024756, 0.3779644730092272), 12 | vec3(0.5874641440200847, -2.7667464429345077, 0.35355339059327373), 13 | vec3(2.997715703369726, 0.11704939884745152, 0.3333333333333333), 14 | vec3(0.41360842451688395, 3.1351121305574803, 0.31622776601683794), 15 | vec3(-3.167149933769243, 0.9844599011770256, 0.30151134457776363), 16 | vec3(-1.5736713846521535, -3.0860263079123245, 0.2886751345948129), 17 | vec3(2.888202648340422, -2.1583061557896213, 0.2773500981126146), 18 | vec3(2.7150778983300325, 2.5745586041105715, 0.2672612419124244), 19 | vec3(-2.1504069972377464, 3.2211410627650165, 0.2581988897471611), 20 | vec3(-3.6548858794907493, -1.6253643308191343, 0.25), 21 | vec3(1.0130775986052671, -3.9967078676335834, 0.24253562503633297), 22 | vec3(4.229723673607257, 0.33081361055181563, 0.23570226039551587), 23 | vec3(0.40107790291173834, 4.340407413572593, 0.22941573387056174), 24 | vec3(-4.319124570236028, 1.159811599693438, 0.22360679774997896), 25 | vec3(-1.9209044802827355, -4.160543952132907, 0.2182178902359924), 26 | vec3(3.8639122286635708, -2.6589814382925123, 0.21320071635561041), 27 | vec3(3.3486228404946234, 3.4331800232609, 0.20851441405707477), 28 | vec3(-2.8769733643574344, 3.9652268864187157, 0.20412414523193154) 29 | }; 30 | 31 | float lum(vec4 c) { 32 | return 0.299 * c.r + 0.587 * c.g + 0.114 * c.b; 33 | } 34 | 35 | void mainImage(out vec4 fragColor, in vec2 fragCoord) { 36 | vec2 uv = fragCoord.xy / iResolution.xy; 37 | 38 | vec4 color = texture(iChannel0, uv); 39 | 40 | vec2 step = vec2(1.414) / iResolution.xy; 41 | 42 | for (int i = 0; i < 24; i++) { 43 | vec3 s = samples[i]; 44 | vec4 c = texture(iChannel0, uv + s.xy * step); 45 | float l = lum(c); 46 | if (l > 0.2) { 47 | color += l * s.z * c * 0.2; 48 | } 49 | } 50 | 51 | fragColor = color; 52 | } -------------------------------------------------------------------------------- /cineShader-Lava.glsl: -------------------------------------------------------------------------------- 1 | // INFO: This shader is a port of https://www.shadertoy.com/view/3sySRK 2 | 3 | // INFO: Change these variables to create some variation in the animation 4 | #define BLACK_BLEND_THRESHOLD .4 // This is controls the dim of the screen 5 | #define COLOR_SPEED 0.1 // This controls the speed at which the colors change 6 | #define MOVEMENT_SPEED 0.1 // This controls the speed at which the balls move 7 | 8 | float opSmoothUnion( float d1, float d2, float k ) 9 | { 10 | float h = clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 ); 11 | return mix( d2, d1, h ) - k*h*(1.0-h); 12 | } 13 | 14 | float sdSphere( vec3 p, float s ) 15 | { 16 | return length(p)-s; 17 | } 18 | 19 | float map(vec3 p) 20 | { 21 | float d = 2.0; 22 | for (int i = 0; i < 16; i++) { 23 | float fi = float(i); 24 | float time = iTime * (fract(fi * 412.531 + 0.513) - 0.5) * 2.0; 25 | d = opSmoothUnion( 26 | sdSphere(p + sin(time*MOVEMENT_SPEED + fi * vec3(52.5126, 64.62744, 632.25)) * vec3(2.0, 2.0, 0.8), mix(0.5, 1.0, fract(fi * 412.531 + 0.5124))), 27 | d, 28 | 0.4 29 | ); 30 | } 31 | return d; 32 | } 33 | 34 | vec3 calcNormal( in vec3 p ) 35 | { 36 | const float h = 1e-5; // or some other value 37 | const vec2 k = vec2(1,-1); 38 | return normalize( k.xyy*map( p + k.xyy*h ) + 39 | k.yyx*map( p + k.yyx*h ) + 40 | k.yxy*map( p + k.yxy*h ) + 41 | k.xxx*map( p + k.xxx*h ) ); 42 | } 43 | 44 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 45 | { 46 | vec2 uv = fragCoord/iResolution.xy; 47 | 48 | vec3 rayOri = vec3((uv - 0.5) * vec2(iResolution.x/iResolution.y, 1.0) * 6.0, 3.0); 49 | vec3 rayDir = vec3(0.0, 0.0, -1.0); 50 | 51 | float depth = 0.0; 52 | vec3 p; 53 | 54 | for(int i = 0; i < 64; i++) { 55 | p = rayOri + rayDir * depth; 56 | float dist = map(p); 57 | depth += dist; 58 | if (dist < 1e-6) { 59 | break; 60 | } 61 | } 62 | 63 | depth = min(6.0, depth); 64 | vec3 n = calcNormal(p); 65 | float b = max(0.0, dot(n, vec3(0.577))); 66 | vec3 col = (0.5 + 0.5 * cos((b + iTime*COLOR_SPEED * 3.0) + uv.xyx * 2.0 + vec3(0,2,4))) * (0.85 + b * 0.35); 67 | col *= exp( -depth * 0.15 ); 68 | 69 | 70 | vec2 termUV = fragCoord.xy / iResolution.xy; 71 | vec4 terminalColor = texture(iChannel0, termUV); 72 | 73 | float alpha = step(length(terminalColor.rgb), BLACK_BLEND_THRESHOLD); 74 | vec3 blendedColor = mix(terminalColor.rgb * 1.0, col.rgb * 0.3, alpha); 75 | 76 | fragColor = vec4(blendedColor, terminalColor.a); 77 | 78 | } 79 | 80 | -------------------------------------------------------------------------------- /crt.glsl: -------------------------------------------------------------------------------- 1 | // source: https://gist.github.com/qwerasd205/c3da6c610c8ffe17d6d2d3cc7068f17f 2 | // credits: https://github.com/qwerasd205 3 | //============================================================== 4 | // 5 | // [CRTS] PUBLIC DOMAIN CRT-STYLED SCALAR by Timothy Lottes 6 | // 7 | // [+] Adapted with alterations for use in Ghostty by Qwerasd. 8 | // For more information on changes, see comment below license. 9 | // 10 | //============================================================== 11 | // 12 | // LICENSE = UNLICENSE (aka PUBLIC DOMAIN) 13 | // 14 | //-------------------------------------------------------------- 15 | // This is free and unencumbered software released into the 16 | // public domain. 17 | //-------------------------------------------------------------- 18 | // Anyone is free to copy, modify, publish, use, compile, sell, 19 | // or distribute this software, either in source code form or as 20 | // a compiled binary, for any purpose, commercial or 21 | // non-commercial, and by any means. 22 | //-------------------------------------------------------------- 23 | // In jurisdictions that recognize copyright laws, the author or 24 | // authors of this software dedicate any and all copyright 25 | // interest in the software to the public domain. We make this 26 | // dedication for the benefit of the public at large and to the 27 | // detriment of our heirs and successors. We intend this 28 | // dedication to be an overt act of relinquishment in perpetuity 29 | // of all present and future rights to this software under 30 | // copyright law. 31 | //-------------------------------------------------------------- 32 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 33 | // KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 34 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 35 | // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE 36 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 37 | // AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 38 | // OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 39 | // DEALINGS IN THE SOFTWARE. 40 | //-------------------------------------------------------------- 41 | // For more information, please refer to 42 | // 43 | //============================================================== 44 | 45 | // This shader is a modified version of the excellent 46 | // FixingPixelArtFast by Timothy Lottes on Shadertoy. 47 | // 48 | // The original shader can be found at: 49 | // https://www.shadertoy.com/view/MtSfRK 50 | // 51 | // Modifications have been made to reduce the verbosity, 52 | // and many of the comments have been removed / reworded. 53 | // Additionally, the license has been moved to the top of 54 | // the file, and can be read above. I (Qwerasd) choose to 55 | // release the modified version under the same license. 56 | 57 | // The appearance of this shader can be altered 58 | // by adjusting the parameters defined below. 59 | 60 | // "Scanlines" per real screen pixel. 61 | // e.g. SCALE 0.5 means each scanline is 2 pixels. 62 | // Recommended values: 63 | // o High DPI displays: 0.33333333 64 | // - Low DPI displays: 0.66666666 65 | #define SCALE 0.33333333 66 | 67 | // "Tube" warp 68 | #define CRTS_WARP 1 69 | 70 | // Darkness of vignette in corners after warping 71 | // 0.0 = completely black 72 | // 1.0 = no vignetting 73 | #define MIN_VIN 0.5 74 | 75 | // Try different masks 76 | // #define CRTS_MASK_GRILLE 1 77 | // #define CRTS_MASK_GRILLE_LITE 1 78 | // #define CRTS_MASK_NONE 1 79 | #define CRTS_MASK_SHADOW 1 80 | 81 | // Scanline thinness 82 | // 0.50 = fused scanlines 83 | // 0.70 = recommended default 84 | // 1.00 = thinner scanlines (too thin) 85 | #define INPUT_THIN 0.75 86 | 87 | // Horizonal scan blur 88 | // -3.0 = pixely 89 | // -2.5 = default 90 | // -2.0 = smooth 91 | // -1.0 = too blurry 92 | #define INPUT_BLUR -2.75 93 | 94 | // Shadow mask effect, ranges from, 95 | // 0.25 = large amount of mask (not recommended, too dark) 96 | // 0.50 = recommended default 97 | // 1.00 = no shadow mask 98 | #define INPUT_MASK 0.65 99 | 100 | float FromSrgb1(float c) { 101 | return (c <= 0.04045) ? c * (1.0 / 12.92) : 102 | pow(c * (1.0 / 1.055) + (0.055 / 1.055), 2.4); 103 | } 104 | vec3 FromSrgb(vec3 c) { 105 | return vec3( 106 | FromSrgb1(c.r), FromSrgb1(c.g), FromSrgb1(c.b)); 107 | } 108 | 109 | vec3 CrtsFetch(vec2 uv) { 110 | return FromSrgb(texture(iChannel0, uv.xy).rgb); 111 | } 112 | 113 | #define CrtsRcpF1(x) (1.0/(x)) 114 | #define CrtsSatF1(x) clamp((x),0.0,1.0) 115 | 116 | float CrtsMax3F1(float a, float b, float c) { 117 | return max(a, max(b, c)); 118 | } 119 | 120 | vec2 CrtsTone( 121 | float thin, 122 | float mask) { 123 | #ifdef CRTS_MASK_NONE 124 | mask = 1.0; 125 | #endif 126 | 127 | #ifdef CRTS_MASK_GRILLE_LITE 128 | // Normal R mask is {1.0,mask,mask} 129 | // LITE R mask is {mask,1.0,1.0} 130 | mask = 0.5 + mask * 0.5; 131 | #endif 132 | 133 | vec2 ret; 134 | float midOut = 0.18 / ((1.5 - thin) * (0.5 * mask + 0.5)); 135 | float pMidIn = 0.18; 136 | ret.x = ((-pMidIn) + midOut) / ((1.0 - pMidIn) * midOut); 137 | ret.y = ((-pMidIn) * midOut + pMidIn) / (midOut * (-pMidIn) + midOut); 138 | 139 | return ret; 140 | } 141 | 142 | vec3 CrtsMask(vec2 pos, float dark) { 143 | #ifdef CRTS_MASK_GRILLE 144 | vec3 m = vec3(dark, dark, dark); 145 | float x = fract(pos.x * (1.0 / 3.0)); 146 | if (x < (1.0 / 3.0)) m.r = 1.0; 147 | else if (x < (2.0 / 3.0)) m.g = 1.0; 148 | else m.b = 1.0; 149 | return m; 150 | #endif 151 | 152 | #ifdef CRTS_MASK_GRILLE_LITE 153 | vec3 m = vec3(1.0, 1.0, 1.0); 154 | float x = fract(pos.x * (1.0 / 3.0)); 155 | if (x < (1.0 / 3.0)) m.r = dark; 156 | else if (x < (2.0 / 3.0)) m.g = dark; 157 | else m.b = dark; 158 | return m; 159 | #endif 160 | 161 | #ifdef CRTS_MASK_NONE 162 | return vec3(1.0, 1.0, 1.0); 163 | #endif 164 | 165 | #ifdef CRTS_MASK_SHADOW 166 | pos.x += pos.y * 3.0; 167 | vec3 m = vec3(dark, dark, dark); 168 | float x = fract(pos.x * (1.0 / 6.0)); 169 | if (x < (1.0 / 3.0)) m.r = 1.0; 170 | else if (x < (2.0 / 3.0)) m.g = 1.0; 171 | else m.b = 1.0; 172 | return m; 173 | #endif 174 | } 175 | 176 | vec3 CrtsFilter( 177 | vec2 ipos, 178 | vec2 inputSizeDivOutputSize, 179 | vec2 halfInputSize, 180 | vec2 rcpInputSize, 181 | vec2 rcpOutputSize, 182 | vec2 twoDivOutputSize, 183 | float inputHeight, 184 | vec2 warp, 185 | float thin, 186 | float blur, 187 | float mask, 188 | vec2 tone 189 | ) { 190 | // Optional apply warp 191 | vec2 pos; 192 | #ifdef CRTS_WARP 193 | // Convert to {-1 to 1} range 194 | pos = ipos * twoDivOutputSize - vec2(1.0, 1.0); 195 | 196 | // Distort pushes image outside {-1 to 1} range 197 | pos *= vec2( 198 | 1.0 + (pos.y * pos.y) * warp.x, 199 | 1.0 + (pos.x * pos.x) * warp.y); 200 | 201 | // TODO: Vignette needs optimization 202 | float vin = 1.0 - ( 203 | (1.0 - CrtsSatF1(pos.x * pos.x)) * (1.0 - CrtsSatF1(pos.y * pos.y))); 204 | vin = CrtsSatF1((-vin) * inputHeight + inputHeight); 205 | 206 | // Leave in {0 to inputSize} 207 | pos = pos * halfInputSize + halfInputSize; 208 | #else 209 | pos = ipos * inputSizeDivOutputSize; 210 | #endif 211 | 212 | // Snap to center of first scanline 213 | float y0 = floor(pos.y - 0.5) + 0.5; 214 | // Snap to center of one of four pixels 215 | float x0 = floor(pos.x - 1.5) + 0.5; 216 | 217 | // Inital UV position 218 | vec2 p = vec2(x0 * rcpInputSize.x, y0 * rcpInputSize.y); 219 | // Fetch 4 nearest texels from 2 nearest scanlines 220 | vec3 colA0 = CrtsFetch(p); 221 | p.x += rcpInputSize.x; 222 | vec3 colA1 = CrtsFetch(p); 223 | p.x += rcpInputSize.x; 224 | vec3 colA2 = CrtsFetch(p); 225 | p.x += rcpInputSize.x; 226 | vec3 colA3 = CrtsFetch(p); 227 | p.y += rcpInputSize.y; 228 | vec3 colB3 = CrtsFetch(p); 229 | p.x -= rcpInputSize.x; 230 | vec3 colB2 = CrtsFetch(p); 231 | p.x -= rcpInputSize.x; 232 | vec3 colB1 = CrtsFetch(p); 233 | p.x -= rcpInputSize.x; 234 | vec3 colB0 = CrtsFetch(p); 235 | 236 | // Vertical filter 237 | // Scanline intensity is using sine wave 238 | // Easy filter window and integral used later in exposure 239 | float off = pos.y - y0; 240 | float pi2 = 6.28318530717958; 241 | float hlf = 0.5; 242 | float scanA = cos(min(0.5, off * thin) * pi2) * hlf + hlf; 243 | float scanB = cos(min(0.5, (-off) * thin + thin) * pi2) * hlf + hlf; 244 | 245 | // Horizontal kernel is simple gaussian filter 246 | float off0 = pos.x - x0; 247 | float off1 = off0 - 1.0; 248 | float off2 = off0 - 2.0; 249 | float off3 = off0 - 3.0; 250 | float pix0 = exp2(blur * off0 * off0); 251 | float pix1 = exp2(blur * off1 * off1); 252 | float pix2 = exp2(blur * off2 * off2); 253 | float pix3 = exp2(blur * off3 * off3); 254 | float pixT = CrtsRcpF1(pix0 + pix1 + pix2 + pix3); 255 | 256 | #ifdef CRTS_WARP 257 | // Get rid of wrong pixels on edge 258 | pixT *= max(MIN_VIN, vin); 259 | #endif 260 | 261 | scanA *= pixT; 262 | scanB *= pixT; 263 | 264 | // Apply horizontal and vertical filters 265 | vec3 color = 266 | (colA0 * pix0 + colA1 * pix1 + colA2 * pix2 + colA3 * pix3) * scanA + 267 | (colB0 * pix0 + colB1 * pix1 + colB2 * pix2 + colB3 * pix3) * scanB; 268 | 269 | // Apply phosphor mask 270 | color *= CrtsMask(ipos, mask); 271 | 272 | // Tonal control, start by protecting from /0 273 | float peak = max(1.0 / (256.0 * 65536.0), 274 | CrtsMax3F1(color.r, color.g, color.b)); 275 | // Compute the ratios of {R,G,B} 276 | vec3 ratio = color * CrtsRcpF1(peak); 277 | // Apply tonal curve to peak value 278 | peak = peak * CrtsRcpF1(peak * tone.x + tone.y); 279 | // Reconstruct color 280 | return ratio * peak; 281 | } 282 | 283 | float ToSrgb1(float c) { 284 | return (c < 0.0031308 ? c * 12.92 : 1.055 * pow(c, 0.41666) - 0.055); 285 | } 286 | vec3 ToSrgb(vec3 c) { 287 | return vec3( 288 | ToSrgb1(c.r), ToSrgb1(c.g), ToSrgb1(c.b)); 289 | } 290 | 291 | void mainImage(out vec4 fragColor, in vec2 fragCoord) { 292 | float aspect = iResolution.x / iResolution.y; 293 | fragColor.rgb = CrtsFilter( 294 | fragCoord.xy, 295 | vec2(1.0), 296 | iResolution.xy * SCALE * 0.5, 297 | 1.0 / (iResolution.xy * SCALE), 298 | 1.0 / iResolution.xy, 299 | 2.0 / iResolution.xy, 300 | iResolution.y, 301 | vec2(1.0 / (50.0 * aspect), 1.0 / 50.0), 302 | INPUT_THIN, 303 | INPUT_BLUR, 304 | INPUT_MASK, 305 | CrtsTone(INPUT_THIN, INPUT_MASK) 306 | ); 307 | 308 | // Linear to SRGB for output. 309 | fragColor.rgb = ToSrgb(fragColor.rgb); 310 | } -------------------------------------------------------------------------------- /cubes.glsl: -------------------------------------------------------------------------------- 1 | // credits: https://github.com/rymdlego 2 | 3 | const float speed = 0.2; 4 | const float cube_size = 1.0; 5 | const float cube_brightness = 1.0; 6 | const float cube_rotation_speed = 2.8; 7 | const float camera_rotation_speed = 0.1; 8 | 9 | 10 | 11 | mat3 rotationMatrix(vec3 m,float a) { 12 | m = normalize(m); 13 | float c = cos(a),s=sin(a); 14 | return mat3(c+(1.-c)*m.x*m.x, 15 | (1.-c)*m.x*m.y-s*m.z, 16 | (1.-c)*m.x*m.z+s*m.y, 17 | (1.-c)*m.x*m.y+s*m.z, 18 | c+(1.-c)*m.y*m.y, 19 | (1.-c)*m.y*m.z-s*m.x, 20 | (1.-c)*m.x*m.z-s*m.y, 21 | (1.-c)*m.y*m.z+s*m.x, 22 | c+(1.-c)*m.z*m.z); 23 | } 24 | 25 | float sphere(vec3 pos, float radius) 26 | { 27 | return length(pos) - radius; 28 | } 29 | 30 | float box(vec3 pos, vec3 size) 31 | { 32 | float t = iTime; 33 | 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); 34 | return length(max(abs(pos) - size, 0.0)); 35 | } 36 | 37 | 38 | float distfunc(vec3 pos) 39 | { 40 | float t = iTime; 41 | 42 | float size = 0.45 + 0.25*abs(16.0*sin(t*speed/4.0)); 43 | // float size = 2.3 + 1.8*tan((t-5.4)*6.549); 44 | size = cube_size * 0.16 * clamp(size, 2.0, 4.0); 45 | 46 | //pos = pos * rotationMatrix(vec3(0.,-3.,0.7), 3.3 * mod(t/30.0, 4.0)); 47 | vec3 q = mod(pos, 5.0) - 2.5; 48 | float obj1 = box(q, vec3(size)); 49 | return obj1; 50 | } 51 | 52 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 53 | { 54 | float t = iTime; 55 | vec2 screenPos = -1.0 + 2.0 * fragCoord.xy / iResolution.xy; 56 | screenPos.x *= iResolution.x / iResolution.y; 57 | vec3 cameraOrigin = vec3(t*1.0*speed, 0.0, 0.0); 58 | // vec3 cameraOrigin = vec3(t*1.8*speed, 3.0+t*0.02*speed, 0.0); 59 | vec3 cameraTarget = vec3(t*100., 0.0, 0.0); 60 | cameraTarget = vec3(t*20.0,0.0,0.0) * rotationMatrix(vec3(0.0,0.0,1.0), t*speed*camera_rotation_speed); 61 | 62 | vec3 upDirection = vec3(0.5, 1.0, 0.6); 63 | 64 | vec3 cameraDir = normalize(cameraTarget - cameraOrigin); 65 | vec3 cameraRight = normalize(cross(upDirection, cameraOrigin)); 66 | vec3 cameraUp = cross(cameraDir, cameraRight); 67 | 68 | vec3 rayDir = normalize(cameraRight * screenPos.x + cameraUp * screenPos.y + cameraDir); 69 | 70 | const int MAX_ITER = 64; 71 | const float MAX_DIST = 48.0; 72 | const float EPSILON = 0.001; 73 | 74 | float totalDist = 0.0; 75 | vec3 pos = cameraOrigin; 76 | float dist = EPSILON; 77 | 78 | for (int i = 0; i < MAX_ITER; i++) 79 | { 80 | if (dist < EPSILON || totalDist > MAX_DIST) 81 | break; 82 | dist = distfunc(pos); 83 | totalDist += dist; 84 | pos += dist*rayDir; 85 | } 86 | 87 | vec4 cubes; 88 | 89 | if (dist < EPSILON) 90 | { 91 | // Lighting Code 92 | vec2 eps = vec2(0.0, EPSILON); 93 | vec3 normal = normalize(vec3( 94 | distfunc(pos + eps.yxx) - distfunc(pos - eps.yxx), 95 | distfunc(pos + eps.xyx) - distfunc(pos - eps.xyx), 96 | distfunc(pos + eps.xxy) - distfunc(pos - eps.xxy))); 97 | float diffuse = max(0., dot(-rayDir, normal)); 98 | float specular = pow(diffuse, 32.0); 99 | vec3 color = vec3(diffuse + specular); 100 | vec3 cubeColor = vec3(abs(screenPos),0.5+0.5*sin(t*2.0))*0.8; 101 | cubeColor = mix(cubeColor.rgb, vec3(0.0,0.0,0.0), 1.0); 102 | color += cubeColor; 103 | cubes = vec4(color, 1.0) * vec4(1.0 - (totalDist/MAX_DIST)); 104 | cubes = vec4(cubes.rgb*0.02*cube_brightness, 0.1); 105 | } 106 | else { 107 | cubes = vec4(0.0); 108 | } 109 | 110 | vec2 uv = fragCoord/iResolution.xy; 111 | vec4 terminalColor = texture(iChannel0, uv); 112 | vec3 blendedColor = terminalColor.rgb + cubes.rgb; 113 | fragColor = vec4(blendedColor, terminalColor.a); 114 | } 115 | -------------------------------------------------------------------------------- /dither.glsl: -------------------------------------------------------------------------------- 1 | // Simple "dithering" effect 2 | // (c) moni-dz (https://github.com/moni-dz) 3 | // CC BY-NC-SA 4.0 (https://creativecommons.org/licenses/by-nc-sa/4.0/) 4 | 5 | // Packed bayer pattern using bit manipulation 6 | const float bayerPattern[4] = float[4]( 7 | 0x0514, // Encoding 0,8,2,10 8 | 0xC4E6, // Encoding 12,4,14,6 9 | 0x3B19, // Encoding 3,11,1,9 10 | 0xF7D5 // Encoding 15,7,13,5 11 | ); 12 | 13 | float getBayerFromPacked(int x, int y) { 14 | int idx = (x & 3) + ((y & 3) << 2); 15 | return float((int(bayerPattern[y & 3]) >> ((x & 3) << 2)) & 0xF) * (1.0 / 16.0); 16 | } 17 | 18 | #define LEVELS 2.0 // Available color steps per channel 19 | #define INV_LEVELS (1.0 / LEVELS) 20 | 21 | void mainImage(out vec4 fragColor, in vec2 fragCoord) 22 | { 23 | vec2 uv = fragCoord * (1.0 / iResolution.xy); 24 | vec3 color = texture(iChannel0, uv).rgb; 25 | 26 | float threshold = getBayerFromPacked(int(fragCoord.x), int(fragCoord.y)); 27 | vec3 dithered = floor(color * LEVELS + threshold) * INV_LEVELS; 28 | 29 | fragColor = vec4(dithered, 1.0); 30 | } 31 | -------------------------------------------------------------------------------- /drunkard.glsl: -------------------------------------------------------------------------------- 1 | // Drunken stupor effect using fractal Brownian motion and Perlin noise 2 | // (c) moni-dz (https://github.com/moni-dz) 3 | // CC BY-NC-SA 4.0 (https://creativecommons.org/licenses/by-nc-sa/4.0/) 4 | 5 | vec2 hash2(vec2 p) { 6 | uvec2 q = uvec2(floatBitsToUint(p.x), floatBitsToUint(p.y)); 7 | q = (q * uvec2(1597334673U, 3812015801U)) ^ (q.yx * uvec2(2798796415U, 1979697793U)); 8 | return vec2(q) * (1.0/float(0xffffffffU)) * 2.0 - 1.0; 9 | } 10 | 11 | float perlin2d(vec2 p) { 12 | vec2 i = floor(p); 13 | vec2 f = fract(p); 14 | vec2 u = f*f*(3.0-2.0*f); 15 | 16 | return mix(mix(dot(hash2(i + vec2(0.0,0.0)), f - vec2(0.0,0.0)), 17 | dot(hash2(i + vec2(1.0,0.0)), f - vec2(1.0,0.0)), u.x), 18 | mix(dot(hash2(i + vec2(0.0,1.0)), f - vec2(0.0,1.0)), 19 | dot(hash2(i + vec2(1.0,1.0)), f - vec2(1.0,1.0)), u.x), u.y); 20 | } 21 | 22 | #define OCTAVES 10 // How many passes of fractal Brownian motion to perform 23 | #define GAIN 0.5 // How much should each pixel move 24 | #define LACUNARITY 2.0 // How fast should each ripple be per pass 25 | 26 | float fbm(vec2 p) { 27 | float sum = 0.0; 28 | float amp = 0.5; 29 | float freq = 1.0; 30 | 31 | for(int i = 0; i < OCTAVES; i++) { 32 | sum += amp * perlin2d(p * freq); 33 | freq *= LACUNARITY; 34 | amp *= GAIN; 35 | } 36 | 37 | return sum; 38 | } 39 | 40 | 41 | #define NOISE_SCALE 1.0 // How distorted the image you want to be 42 | #define NOISE_INTENSITY 0.05 // How strong the noise effect is 43 | #define ABERRATION true // Chromatic aberration 44 | #define ABERRATION_DELTA 0.1 // How strong the chromatic aberration effect is 45 | #define ANIMATE true 46 | #define SPEED 0.4 // Animation speed 47 | 48 | void mainImage(out vec4 fragColor, in vec2 fragCoord) 49 | { 50 | vec2 uv = fragCoord/iResolution.xy; 51 | float time = ANIMATE ? iTime * SPEED : 0.0; 52 | 53 | vec2 noisePos = uv * NOISE_SCALE + vec2(time); 54 | float noise = fbm(noisePos) * NOISE_INTENSITY; 55 | 56 | vec3 col; 57 | 58 | if (ABERRATION) { 59 | col.r = texture(iChannel0, uv + vec2(noise * (1.0 + ABERRATION_DELTA))).r; 60 | col.g = texture(iChannel0, uv + vec2(noise)).g; 61 | col.b = texture(iChannel0, uv + vec2(noise * (1.0 - ABERRATION_DELTA))).b; 62 | } else { 63 | vec2 distortedUV = uv + vec2(noise); 64 | col = texture(iChannel0, distortedUV).rgb; 65 | } 66 | 67 | fragColor = vec4(col, 1.0); 68 | } 69 | -------------------------------------------------------------------------------- /fireworks-rockets.glsl: -------------------------------------------------------------------------------- 1 | // This Ghostty shader is a lightly modified port of https://www.shadertoy.com/view/4dBGRw 2 | 3 | #define BLACK_BLEND_THRESHOLD .4 4 | 5 | //Creates a diagonal red-and-white striped pattern. 6 | vec3 barberpole(vec2 pos, vec2 rocketpos) { 7 | float d = (pos.x - rocketpos.x) + (pos.y - rocketpos.y); 8 | vec3 col = vec3(1.0); 9 | 10 | d = mod(d * 20., 2.0); 11 | if (d > 1.0) { 12 | col = vec3(1.0, 0.0, 0.0); 13 | } 14 | 15 | return col; 16 | } 17 | 18 | vec3 rocket(vec2 pos, vec2 rocketpos) { 19 | vec3 col = vec3(0.0); 20 | float f = 0.; 21 | float absx = abs(rocketpos.x - pos.x); 22 | float absy = abs(rocketpos.y - pos.y); 23 | 24 | // Wooden stick 25 | if (absx < 0.01 && absy < 0.22) { 26 | col = vec3(1.0, 0.5, 0.5); 27 | } 28 | 29 | // Barberpole 30 | if (absx < 0.05 && absy < 0.15) { 31 | col = barberpole(pos, rocketpos); 32 | } 33 | 34 | // Rocket Point 35 | float pointw = (rocketpos.y - pos.y - 0.25) * -0.7; 36 | if ((rocketpos.y - pos.y) > 0.1) { 37 | f = smoothstep(pointw - 0.001, pointw + 0.001, absx); 38 | 39 | col = mix(vec3(1.0, 0.0, 0.0), col, f); 40 | } 41 | 42 | // Shadow 43 | f = -.5 + smoothstep(-0.05, 0.05, (rocketpos.x - pos.x)); 44 | col *= 0.7 + f; 45 | 46 | return col; 47 | } 48 | 49 | float rand(float val, float seed) { 50 | return cos(val * sin(val * seed) * seed); 51 | } 52 | 53 | float distance2(in vec2 a, in vec2 b) { 54 | return dot(a - b, a - b); 55 | } 56 | 57 | mat2 rr = mat2(cos(1.0), -sin(1.0), sin(1.0), cos(1.0)); 58 | 59 | vec3 drawParticles(vec2 pos, vec3 particolor, float time, vec2 cpos, float gravity, float seed, float timelength) { 60 | vec3 col = vec3(0.0); 61 | vec2 pp = vec2(1.0, 0.0); 62 | for (float i = 1.0; i <= 128.0; i++) { 63 | float d = rand(i, seed); 64 | float fade = (i / 128.0) * time; 65 | vec2 particpos = cpos + time * pp * d; 66 | pp = rr * pp; 67 | col = mix(particolor / fade, col, smoothstep(0.0, 0.0001, distance2(particpos, pos))); 68 | } 69 | col *= smoothstep(0.0, 1.0, (timelength - time) / timelength); 70 | 71 | return col; 72 | } 73 | vec3 drawFireworks(float time, vec2 uv, vec3 particolor, float seed) { 74 | float timeoffset = 2.0; 75 | vec3 col = vec3(0.0); 76 | if (time <= 0.) { 77 | return col; 78 | } 79 | if (mod(time, 6.0) > timeoffset) { 80 | 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); 81 | } else { 82 | col = rocket(uv * 3., vec2(3. * rand(ceil(time / 6.0), seed), 3. * (-0.5 + (timeoffset - mod(time, 6.0))))); 83 | } 84 | return col; 85 | } 86 | 87 | void mainImage(out vec4 fragColor, in vec2 fragCoord) 88 | { 89 | vec2 uv = 1.0 - 2.0 * fragCoord.xy / iResolution.xy; 90 | uv.x *= iResolution.x / iResolution.y; 91 | vec3 col = vec3(0.1, 0.1, 0.2); 92 | 93 | // Flip the y-axis so that the rocket is drawn from the bottom of the screen 94 | uv.y = -uv.y; 95 | 96 | col += 0.1 * uv.y; 97 | 98 | col += drawFireworks(iTime, uv, vec3(1.0, 0.1, 0.1), 1.); 99 | col += drawFireworks(iTime - 2.0, uv, vec3(0.0, 1.0, 0.5), 2.); 100 | col += drawFireworks(iTime - 4.0, uv, vec3(1.0, 1.0, 0.1), 3.); 101 | 102 | vec2 termUV = fragCoord.xy / iResolution.xy; 103 | vec4 terminalColor = texture(iChannel0, termUV); 104 | 105 | float alpha = step(length(terminalColor.rgb), BLACK_BLEND_THRESHOLD); 106 | vec3 blendedColor = mix(terminalColor.rgb * 1.0, col.rgb * 0.3, alpha); 107 | 108 | fragColor = vec4(blendedColor, terminalColor.a); 109 | } 110 | -------------------------------------------------------------------------------- /fireworks.glsl: -------------------------------------------------------------------------------- 1 | // This Ghostty shader is a port of https://www.shadertoy.com/view/lscGRl 2 | 3 | // "Fireworks" by Martijn Steinrucken aka BigWings - 2015 4 | // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. 5 | // Email:countfrolic@gmail.com Twitter:@The_ArtOfCode 6 | 7 | #define BLACK_BLEND_THRESHOLD .4 8 | #define PI 3.141592653589793238 9 | #define TWOPI 6.283185307179586 10 | #define S(x,y,z) smoothstep(x,y,z) 11 | #define B(x,y,z,w) S(x-z, x+z, w)*S(y+z, y-z, w) 12 | #define saturate(x) clamp(x,0.,1.) 13 | 14 | #define NUM_EXPLOSIONS 3. 15 | #define NUM_PARTICLES 42. 16 | 17 | // Noise functions by Dave Hoskins 18 | #define MOD3 vec3(.1031,.11369,.13787) 19 | vec3 hash31(float p) { 20 | vec3 p3 = fract(vec3(p) * MOD3); 21 | p3 += dot(p3, p3.yzx + 19.19); 22 | return fract(vec3((p3.x + p3.y) * p3.z, (p3.x + p3.z) * p3.y, (p3.y + p3.z) * p3.x)); 23 | } 24 | float hash12(vec2 p) { 25 | vec3 p3 = fract(vec3(p.xyx) * MOD3); 26 | p3 += dot(p3, p3.yzx + 19.19); 27 | return fract((p3.x + p3.y) * p3.z); 28 | } 29 | 30 | float circ(vec2 uv, vec2 pos, float size) { 31 | uv -= pos; 32 | 33 | size *= size; 34 | return S(size * 1.1, size, dot(uv, uv)); 35 | } 36 | 37 | float light(vec2 uv, vec2 pos, float size) { 38 | uv -= pos; 39 | 40 | size *= size; 41 | return size / dot(uv, uv); 42 | } 43 | 44 | vec3 explosion(vec2 uv, vec2 p, float seed, float t) { 45 | vec3 col = vec3(0.); 46 | 47 | vec3 en = hash31(seed); 48 | vec3 baseCol = en; 49 | for (float i = 0.; i < NUM_PARTICLES; i++) { 50 | vec3 n = hash31(i) - .5; 51 | 52 | vec2 startP = p - vec2(0., t * t * .1); 53 | vec2 endP = startP + normalize(n.xy) * n.z - vec2(0., t * .2); 54 | 55 | float pt = 1. - pow(t - 1., 2.); 56 | vec2 pos = mix(p, endP, pt); 57 | float size = mix(.01, .005, S(0., .1, pt)); 58 | size *= S(1., .1, pt); 59 | 60 | float sparkle = (sin((pt + n.z) * 21.) * .5 + .5); 61 | sparkle = pow(sparkle, pow(en.x, 3.) * 50.) * mix(0.01, .01, en.y * n.y); 62 | 63 | //size += sparkle*B(.6, 1., .1, t); 64 | size += sparkle * B(en.x, en.y, en.z, t); 65 | 66 | col += baseCol * light(uv, pos, size); 67 | } 68 | 69 | return col; 70 | } 71 | 72 | vec3 Rainbow(vec3 c) { 73 | float t = iTime; 74 | 75 | float avg = (c.r + c.g + c.b) / 3.; 76 | c = avg + (c - avg) * sin(vec3(0., .333, .666) + t); 77 | 78 | c += sin(vec3(.4, .3, .3) * t + vec3(1.1244, 3.43215, 6.435)) * vec3(.4, .1, .5); 79 | 80 | return c; 81 | } 82 | 83 | void mainImage(out vec4 fragColor, in vec2 fragCoord) 84 | { 85 | vec2 uv = fragCoord.xy / iResolution.xy; 86 | uv.x -= .5; 87 | uv.x *= iResolution.x / iResolution.y; 88 | 89 | // Flip the y-axis so that the gravity is downwards 90 | uv.y = -uv.y + 1.; 91 | 92 | float n = hash12(uv + 10.); 93 | float t = iTime * .5; 94 | 95 | vec3 c = vec3(0.); 96 | 97 | for (float i = 0.; i < NUM_EXPLOSIONS; i++) { 98 | float et = t + i * 1234.45235; 99 | float id = floor(et); 100 | et -= id; 101 | 102 | vec2 p = hash31(id).xy; 103 | p.x -= .5; 104 | p.x *= 1.6; 105 | c += explosion(uv, p, id, et); 106 | } 107 | c = Rainbow(c); 108 | 109 | vec2 termUV = fragCoord.xy / iResolution.xy; 110 | vec4 terminalColor = texture(iChannel0, termUV); 111 | 112 | float alpha = step(length(terminalColor.rgb), BLACK_BLEND_THRESHOLD); 113 | vec3 blendedColor = mix(terminalColor.rgb * 1.0, c.rgb * 0.3, alpha); 114 | 115 | fragColor = vec4(blendedColor, terminalColor.a); 116 | } 117 | -------------------------------------------------------------------------------- /galaxy.glsl: -------------------------------------------------------------------------------- 1 | float triangle(float x, float period) { 2 | return 2.0 * abs(3.0* ((x / period) - floor((x / period) + 0.5))) - 1.0; 3 | } 4 | 5 | float field(in vec3 position) { 6 | float strength = 7.0 + 0.03 * log(1.0e-6 + fract(sin(iTime) * 373.11)); 7 | float accumulated = 0.0; 8 | float previousMagnitude = 0.0; 9 | float totalWeight = 0.0; 10 | 11 | for (int i = 0; i < 6; ++i) { 12 | float magnitude = dot(position, position); 13 | 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)); 14 | float weight = exp(-float(i) / 7.0); 15 | accumulated += weight * exp(-strength * pow(abs(magnitude - previousMagnitude), 2.3)); 16 | totalWeight += weight; 17 | previousMagnitude = magnitude; 18 | } 19 | 20 | return max(0.0, 5.0 * accumulated / totalWeight - 0.7); 21 | } 22 | 23 | void mainImage(out vec4 fragColor, in vec2 fragCoord) { 24 | const float baseSpeed = 0.02; 25 | const int maxIterations = 16; 26 | const float formulaParameter = 0.79; 27 | const float volumeSteps = 7.0; 28 | const float stepSize = 0.24; 29 | const float zoomFactor = 0.1; 30 | const float tilingFactor = 0.85; 31 | const float baseBrightness = 0.0008; 32 | const float darkMatter = 0.2; 33 | const float distanceFading = 0.56; 34 | const float colorSaturation = 0.9; 35 | const float transverseMotion = 0.2; 36 | const float cloudOpacity = 0.48; 37 | const float zoomSpeed = 0.0002; 38 | 39 | vec2 normalizedCoordinates = 2.0 * fragCoord.xy / vec2(512) - 1.0; 40 | vec2 scaledCoordinates = normalizedCoordinates * vec2(512) / 512.0; 41 | 42 | float timeElapsed = iTime; 43 | float speedAdjustment = -baseSpeed; 44 | float formulaAdjustment = formulaParameter; 45 | 46 | speedAdjustment = zoomSpeed * cos(iTime * 0.02 + 3.1415926 / 4.0); 47 | 48 | vec2 uvCoordinates = scaledCoordinates; 49 | 50 | float rotationXZ = 0.9; 51 | float rotationYZ = -0.6; 52 | float rotationXY = 0.9 + iTime * 0.08; 53 | 54 | mat2 rotationMatrixXZ = mat2(vec2(cos(rotationXZ), sin(rotationXZ)), vec2(-sin(rotationXZ), cos(rotationXZ))); 55 | mat2 rotationMatrixYZ = mat2(vec2(cos(rotationYZ), sin(rotationYZ)), vec2(-sin(rotationYZ), cos(rotationYZ))); 56 | mat2 rotationMatrixXY = mat2(vec2(cos(rotationXY), sin(rotationXY)), vec2(-sin(rotationXY), cos(rotationXY))); 57 | 58 | vec2 canvasCenter = vec2(0.5, 0.5); 59 | vec3 rayDirection = vec3(uvCoordinates * zoomFactor, 1.0); 60 | vec3 cameraPosition = vec3(0.0, 0.0, 0.0); 61 | cameraPosition.x -= 2.0 * (canvasCenter.x - 0.5); 62 | cameraPosition.y -= 2.0 * (canvasCenter.y - 0.5); 63 | 64 | vec3 forwardVector = vec3(0.0, 0.0, 1.0); 65 | cameraPosition.x += transverseMotion * cos(0.01 * iTime) + 0.001 * iTime; 66 | cameraPosition.y += transverseMotion * sin(0.01 * iTime) + 0.001 * iTime; 67 | cameraPosition.z += 0.003 * iTime; 68 | 69 | rayDirection.xz *= rotationMatrixXZ; 70 | forwardVector.xz *= rotationMatrixXZ; 71 | rayDirection.yz *= rotationMatrixYZ; 72 | forwardVector.yz *= rotationMatrixYZ; 73 | 74 | cameraPosition.xy *= -1.0 * rotationMatrixXY; 75 | cameraPosition.xz *= rotationMatrixXZ; 76 | cameraPosition.yz *= rotationMatrixYZ; 77 | 78 | float zoomOffset = (timeElapsed - 3311.0) * speedAdjustment; 79 | cameraPosition += forwardVector * zoomOffset; 80 | float sampleOffset = mod(zoomOffset, stepSize); 81 | float normalizedSampleOffset = sampleOffset / stepSize; 82 | 83 | float stepDistance = 0.24; 84 | float secondaryStepDistance = stepDistance + stepSize / 2.0; 85 | vec3 accumulatedColor = vec3(0.0); 86 | float fieldContribution = 0.0; 87 | vec3 backgroundColor = vec3(0.0); 88 | 89 | for (float stepIndex = 0.0; stepIndex < volumeSteps; ++stepIndex) { 90 | vec3 primaryPosition = cameraPosition + (stepDistance + sampleOffset) * rayDirection; 91 | vec3 secondaryPosition = cameraPosition + (secondaryStepDistance + sampleOffset) * rayDirection; 92 | 93 | primaryPosition = abs(vec3(tilingFactor) - mod(primaryPosition, vec3(tilingFactor * 2.0))); 94 | secondaryPosition = abs(vec3(tilingFactor) - mod(secondaryPosition, vec3(tilingFactor * 2.0))); 95 | 96 | fieldContribution = field(secondaryPosition); 97 | 98 | float particleAccumulator = 0.0, particleDistance = 0.0; 99 | for (int i = 0; i < maxIterations; ++i) { 100 | primaryPosition = abs(primaryPosition) / dot(primaryPosition, primaryPosition) - formulaAdjustment; 101 | float distanceChange = abs(length(primaryPosition) - particleDistance); 102 | particleAccumulator += i > 2 ? min(12.0, distanceChange) : distanceChange; 103 | particleDistance = length(primaryPosition); 104 | } 105 | particleAccumulator *= particleAccumulator * particleAccumulator; 106 | 107 | float fadeFactor = pow(distanceFading, max(0.0, float(stepIndex) - normalizedSampleOffset)); 108 | accumulatedColor += vec3(stepDistance, stepDistance * stepDistance, stepDistance * stepDistance * stepDistance * stepDistance) 109 | * particleAccumulator * baseBrightness * fadeFactor; 110 | backgroundColor += mix(0.4, 1.0, cloudOpacity) * vec3(1.8 * fieldContribution * fieldContribution * fieldContribution, 111 | 1.4 * fieldContribution * fieldContribution, fieldContribution) * fadeFactor; 112 | stepDistance += stepSize; 113 | secondaryStepDistance += stepSize; 114 | } 115 | 116 | accumulatedColor = mix(vec3(length(accumulatedColor)), accumulatedColor, colorSaturation); 117 | 118 | vec4 foregroundColor = vec4(accumulatedColor * 0.01, 1.0); 119 | backgroundColor *= cloudOpacity; 120 | backgroundColor.b *= 1.8; 121 | backgroundColor.r *= 0.05; 122 | 123 | backgroundColor.b = 0.5 * mix(backgroundColor.g, backgroundColor.b, 0.8); 124 | backgroundColor.g = 0.0; 125 | backgroundColor.bg = mix(backgroundColor.gb, backgroundColor.bg, 0.5 * (cos(iTime * 0.01) + 1.0)); 126 | 127 | vec2 terminalUV = fragCoord.xy / iResolution.xy; 128 | vec4 terminalColor = texture(iChannel0, terminalUV); 129 | 130 | float brightnessThreshold = 0.1; 131 | float terminalBrightness = dot(terminalColor.rgb, vec3(0.2126, 0.7152, 0.0722)); 132 | 133 | if (terminalBrightness < brightnessThreshold) { 134 | fragColor = mix(terminalColor, vec4(foregroundColor.rgb + backgroundColor, 1.0), 0.24); 135 | } else { 136 | fragColor = terminalColor; 137 | } 138 | } 139 | 140 | -------------------------------------------------------------------------------- /gears-and-belts.glsl: -------------------------------------------------------------------------------- 1 | // sligltly modified version of https://www.shadertoy.com/view/DsVSDV 2 | // The only changes are done in the mainImage function 3 | // Ive added comments on what to modify 4 | // works really well with most colorschemes 5 | 6 | #define Rot(a) mat2(cos(a),-sin(a),sin(a),cos(a)) 7 | #define antialiasing(n) n/min(iResolution.y,iResolution.x) 8 | #define S(d,b) smoothstep(antialiasing(3.0),b,d) 9 | #define B(p,s) max(abs(p).x-s.x,abs(p).y-s.y) 10 | #define deg45 .707 11 | #define R45(p) (( p + vec2(p.y,-p.x) ) *deg45) 12 | #define Tri(p,s) max(R45(p).x,max(R45(p).y,B(p,s))) 13 | #define DF(a,b) length(a) * cos( mod( atan(a.y,a.x)+6.28/(b*8.0), 6.28/((b*8.0)*0.5))+(b-1.)*6.28/(b*8.0) + vec2(0,11) ) 14 | 15 | float random (vec2 p) { 16 | return fract(sin(dot(p.xy, vec2(12.9898,78.233)))* 43758.5453123); 17 | } 18 | 19 | float innerGear(vec2 p, float dir){ 20 | p*=Rot(radians(-iTime*45.+45.)*dir); 21 | vec2 prevP = p; 22 | 23 | //p*=Rot(radians(iTime*45.+20.)); 24 | p = DF(p,7.); 25 | p-=vec2(0.24); 26 | p*=Rot(deg45); 27 | float d = B(p,vec2(0.01,0.06)); 28 | p = prevP; 29 | float d2 = abs(length(p)-0.42)-0.02; 30 | d = min(d,d2); 31 | d2 = abs(length(p)-0.578)-0.02; 32 | d = min(d,d2); 33 | d2 = abs(length(p)-0.499)-0.005; 34 | d = min(d,d2); 35 | 36 | p = DF(p,7.); 37 | p-=vec2(0.43); 38 | p*=Rot(deg45); 39 | d2 = B(p,vec2(0.01,0.04)); 40 | d = min(d,d2); 41 | 42 | return d; 43 | } 44 | 45 | vec3 pattern1(vec2 p, vec3 col, float dir){ 46 | vec2 prevP = p; 47 | float size = 0.499; 48 | float thick = 0.15; 49 | 50 | p+=vec2(size); 51 | float d = abs(length(p)-size)-thick; 52 | d = max(d,innerGear(p,dir)); 53 | col = mix(col,vec3(1.),S(d,0.0)); 54 | 55 | p = prevP; 56 | p-=vec2(size); 57 | d = abs(length(p)-size)-thick; 58 | d = max(d,innerGear(p,dir)); 59 | col = mix(col,vec3(1.),S(d,0.0)); 60 | 61 | return col; 62 | } 63 | 64 | vec3 pattern2(vec2 p, vec3 col, float dir){ 65 | 66 | vec2 prevP = p; 67 | float size = 0.33; 68 | float thick = 0.15; 69 | float thift = 0.0; 70 | float speed = 0.3; 71 | 72 | p-=vec2(size,0.); 73 | float d = B(p,vec2(size,thick)); 74 | 75 | p.x+=thift; 76 | p.x-=iTime*speed*dir; 77 | p.x=mod(p.x,0.08)-0.04; 78 | d = max(d,B(p,vec2(0.011,thick))); 79 | p = prevP; 80 | d = max(-(abs(p.y)-0.1),d); 81 | //d = min(B(p,vec2(1.,0.1)),d); 82 | p.y=abs(p.y)-0.079; 83 | d = min(B(p,vec2(1.,0.02)),d); 84 | 85 | p = prevP; 86 | p-=vec2(0.0,size); 87 | float d2 = B(p,vec2(thick,size)); 88 | 89 | p.y+=thift; 90 | p.y+=iTime*speed*dir; 91 | p.y=mod(p.y,0.08)-0.04; 92 | d2 = max(d2,B(p,vec2(thick,0.011))); 93 | 94 | p = prevP; 95 | d2 = max(-(abs(p.x)-0.1),d2); 96 | d2 = min(B(p,vec2(0.005,1.)),d2); 97 | p.x=abs(p.x)-0.079; 98 | d2 = min(B(p,vec2(0.02,1.)),d2); 99 | 100 | d = min(d,d2); 101 | 102 | p = prevP; 103 | p+=vec2(0.0,size); 104 | d2 = B(p,vec2(thick,size)); 105 | 106 | p.y+=thift; 107 | p.y-=iTime*speed*dir; 108 | p.y=mod(p.y,0.08)-0.04; 109 | d2 = max(d2,B(p,vec2(thick,0.011))); 110 | 111 | p = prevP; 112 | d2 = max(-(abs(p.x)-0.1),d2); 113 | d2 = min(B(p,vec2(0.005,1.)),d2); 114 | p.x=abs(p.x)-0.079; 115 | d2 = min(B(p,vec2(0.02,1.)),d2); 116 | 117 | d = min(d,d2); 118 | 119 | p = prevP; 120 | p+=vec2(size,0.0); 121 | d2 = B(p,vec2(size,thick)); 122 | 123 | p.x+=thift; 124 | p.x+=iTime*speed*dir; 125 | p.x=mod(p.x,0.08)-0.04; 126 | d2 = max(d2,B(p,vec2(0.011,thick))); 127 | d = min(d,d2); 128 | p = prevP; 129 | d = max(-(abs(p.y)-0.1),d); 130 | d = min(B(p,vec2(1.,0.005)),d); 131 | p.y=abs(p.y)-0.079; 132 | d = min(B(p,vec2(1.,0.02)),d); 133 | 134 | p = prevP; 135 | d2 = abs(B(p,vec2(size*0.3)))-0.05; 136 | d = min(d,d2); 137 | 138 | col = mix(col,vec3(1.),S(d,0.0)); 139 | 140 | d = B(p,vec2(0.08)); 141 | col = mix(col,vec3(0.),S(d,0.0)); 142 | 143 | p*=Rot(radians(60.*iTime*dir)); 144 | d = B(p,vec2(0.03)); 145 | col = mix(col,vec3(1.),S(d,0.0)); 146 | 147 | return col; 148 | } 149 | 150 | vec3 drawBelt(vec2 p, vec3 col, float size){ 151 | vec2 prevP = p; 152 | 153 | p*=size; 154 | vec2 id = floor(p); 155 | vec2 gr = fract(p)-0.5; 156 | float dir = mod(id.x+id.y,2.)*2.-1.; 157 | float n = random(id); 158 | 159 | if(n<0.5){ 160 | if(n<0.25){ 161 | gr.x*=-1.; 162 | } 163 | col = pattern1(gr,col,dir); 164 | } else { 165 | if(n>0.75){ 166 | gr.x*=-1.; 167 | } 168 | col = pattern2(gr,col,dir); 169 | } 170 | 171 | return col; 172 | } 173 | 174 | vec3 gear(vec2 p, vec3 col, float dir){ 175 | vec2 prevP = p; 176 | 177 | p*=Rot(radians(iTime*45.+13.)*-dir); 178 | p = DF(p,7.); 179 | p-=vec2(0.23); 180 | p*=Rot(deg45); 181 | float d = B(p,vec2(0.01,0.04)); 182 | p = prevP; 183 | float d2 = abs(length(p)-0.29)-0.02; 184 | d = min(d,d2); 185 | col = mix(col,vec3(1.),S(d,0.0)); 186 | 187 | p*=Rot(radians(iTime*30.-30.)*dir); 188 | p = DF(p,6.); 189 | p-=vec2(0.14); 190 | p*=Rot(radians(45.)); 191 | d = B(p,vec2(0.01,0.03)); 192 | p = prevP; 193 | d2 =abs( length(p)-0.1)-0.02; 194 | p*=Rot(radians(iTime*25.+30.)*-dir); 195 | d2 = max(-(abs(p.x)-0.05),d2); 196 | d = min(d,d2); 197 | col = mix(col,vec3(1.),S(d,0.0)); 198 | 199 | return col; 200 | } 201 | 202 | vec3 item0(vec2 p, vec3 col, float dir){ 203 | vec2 prevP = p; 204 | p.x*=dir; 205 | p*=Rot(radians(iTime*30.+30.)); 206 | float d = abs(length(p)-0.2)-0.05; 207 | col = mix(col,vec3(0.3),S(d,0.0)); 208 | 209 | d = abs(length(p)-0.2)-0.05; 210 | d = max(-p.x,d); 211 | float a = clamp(atan(p.x,p.y)*0.5,0.3,1.); 212 | 213 | col = mix(col,vec3(a),S(d,0.0)); 214 | 215 | return col; 216 | } 217 | 218 | 219 | vec3 item1(vec2 p, vec3 col, float dir){ 220 | p.x*=dir; 221 | vec2 prevP = p; 222 | p*=Rot(radians(iTime*30.+30.)); 223 | float d = abs(length(p)-0.25)-0.04; 224 | d = abs(max((abs(p.y)-0.15),d))-0.005; 225 | float d2 = abs(length(p)-0.25)-0.01; 226 | d2 = max((abs(p.y)-0.12),d2); 227 | d = min(d,d2); 228 | 229 | d2 = abs(length(p)-0.27)-0.01; 230 | d2 = max(-(abs(p.y)-0.22),d2); 231 | d = min(d,d2); 232 | d2 = B(p,vec2(0.01,0.32)); 233 | d2 = max(-(abs(p.y)-0.22),d2); 234 | d = min(d,d2); 235 | 236 | p = prevP; 237 | p*=Rot(radians(iTime*-20.+30.)); 238 | p = DF(p,2.); 239 | p-=vec2(0.105); 240 | p*=Rot(radians(45.)); 241 | d2 = B(p,vec2(0.03,0.01)); 242 | d = min(d,d2); 243 | 244 | p = prevP; 245 | d2 = abs(length(p)-0.09)-0.005; 246 | d2 = max(-(abs(p.x)-0.03),d2); 247 | d2 = max(-(abs(p.y)-0.03),d2); 248 | d = min(d,d2); 249 | 250 | col = mix(col,vec3(0.6),S(d,0.0)); 251 | 252 | return col; 253 | } 254 | 255 | vec3 item2(vec2 p, vec3 col, float dir){ 256 | p.x*=dir; 257 | p*=Rot(radians(iTime*50.-10.)); 258 | vec2 prevP = p; 259 | float d = abs(length(p)-0.15)-0.005; 260 | float d2 = abs(length(p)-0.2)-0.01; 261 | d2 = max((abs(p.y)-0.15),d2); 262 | d = min(d,d2); 263 | 264 | p = DF(p,1.); 265 | p-=vec2(0.13); 266 | p*=Rot(radians(45.)); 267 | d2 = B(p,vec2(0.008,0.1)); 268 | d = min(d,d2); 269 | 270 | p = prevP; 271 | p = DF(p,4.); 272 | p-=vec2(0.18); 273 | p*=Rot(radians(45.)); 274 | d2 = B(p,vec2(0.005,0.02)); 275 | d = min(d,d2); 276 | 277 | col = mix(col,vec3(0.6),S(d,0.0)); 278 | 279 | return col; 280 | } 281 | 282 | float needle(vec2 p){ 283 | p.y-=0.05; 284 | p*=1.5; 285 | vec2 prevP = p; 286 | p.y-=0.3; 287 | p.x*=6.; 288 | float d = Tri(p,vec2(0.3)); 289 | p = prevP; 290 | p.y+=0.1; 291 | p.x*=2.; 292 | p.y*=-1.; 293 | float d2 = Tri(p,vec2(0.1)); 294 | d = min(d,d2); 295 | return d; 296 | } 297 | 298 | vec3 item3(vec2 p, vec3 col, float dir){ 299 | 300 | p*=Rot(radians(sin(iTime*dir)*120.)); 301 | vec2 prevP = p; 302 | 303 | p.y= abs(p.y)-0.05; 304 | float d = needle(p); 305 | p = prevP; 306 | float d2 = abs(length(p)-0.1)-0.003; 307 | d2 = max(-(abs(p.x)-0.05),d2); 308 | d = min(d,d2); 309 | d2 = abs(length(p)-0.2)-0.005; 310 | d2 = max(-(abs(p.x)-0.08),d2); 311 | d = min(d,d2); 312 | 313 | p = DF(p,4.); 314 | p-=vec2(0.18); 315 | d2 = length(p)-0.01; 316 | p = prevP; 317 | d2 = max(-(abs(p.x)-0.03),d2); 318 | d = min(d,d2); 319 | 320 | col = mix(col,vec3(0.6),S(d,0.0)); 321 | 322 | return col; 323 | } 324 | 325 | vec3 drawGearsAndItems(vec2 p, vec3 col, float size){ 326 | vec2 prevP = p; 327 | p*=size; 328 | p+=vec2(0.5); 329 | 330 | vec2 id = floor(p); 331 | vec2 gr = fract(p)-0.5; 332 | 333 | float n = random(id); 334 | float dir = mod(id.x+id.y,2.)*2.-1.; 335 | if(n<0.3){ 336 | col = gear(gr,col,dir); 337 | } else if(n>=0.3 && n<0.5){ 338 | col = item0(gr,col,dir); 339 | } else if(n>=0.5 && n<0.7){ 340 | col = item1(gr,col,dir); 341 | } else if(n>=0.7 && n<0.8) { 342 | col = item2(gr,col,dir); 343 | } else if(n>=0.8){ 344 | col = item3(gr,col,dir); 345 | } 346 | 347 | return col; 348 | } 349 | 350 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 351 | { 352 | vec2 p = (fragCoord-0.5*iResolution.xy)/iResolution.y; 353 | // set speed of downwards motion 354 | p.y+=iTime*0.02; 355 | 356 | float size = 4.; 357 | vec3 col = vec3(0.); 358 | 359 | // Modify the colors to be darker by multiplying with a small factor 360 | vec3 darkFactor = vec3(.5); // This makes everything 50% as bright 361 | 362 | // Get the original colors but make them darker 363 | col = drawBelt(p, col, size) * darkFactor; 364 | col = drawGearsAndItems(p, col, size) * darkFactor; 365 | 366 | // Additional option: you can add a color tint to make it less stark white 367 | vec3 tint = vec3(0.1, 0.12, 0.15); // Slight blue-ish dark tint 368 | col = col * tint; 369 | 370 | vec2 uv = fragCoord/iResolution.xy; 371 | vec4 terminalColor = texture(iChannel0, uv); 372 | 373 | // Blend with reduced opacity for the shader elements 374 | vec3 blendedColor = terminalColor.rgb + col.rgb * 0.7; // Reduced blend factor 375 | 376 | fragColor = vec4(blendedColor, terminalColor.a); 377 | } 378 | -------------------------------------------------------------------------------- /glitchy.glsl: -------------------------------------------------------------------------------- 1 | // modified version of https://www.shadertoy.com/view/wld3WN 2 | // amount of seconds for which the glitch loop occurs 3 | #define DURATION 10. 4 | // percentage of the duration for which the glitch is triggered 5 | #define AMT .1 6 | 7 | #define SS(a, b, x) (smoothstep(a, b, x) * smoothstep(b, a, x)) 8 | 9 | #define UI0 1597334673U 10 | #define UI1 3812015801U 11 | #define UI2 uvec2(UI0, UI1) 12 | #define UI3 uvec3(UI0, UI1, 2798796415U) 13 | #define UIF (1. / float(0xffffffffU)) 14 | 15 | // Hash by David_Hoskins 16 | vec3 hash33(vec3 p) 17 | { 18 | uvec3 q = uvec3(ivec3(p)) * UI3; 19 | q = (q.x ^ q.y ^ q.z)*UI3; 20 | return -1. + 2. * vec3(q) * UIF; 21 | } 22 | 23 | // Gradient noise by iq 24 | float gnoise(vec3 x) 25 | { 26 | // grid 27 | vec3 p = floor(x); 28 | vec3 w = fract(x); 29 | 30 | // quintic interpolant 31 | vec3 u = w * w * w * (w * (w * 6. - 15.) + 10.); 32 | 33 | // gradients 34 | vec3 ga = hash33(p + vec3(0., 0., 0.)); 35 | vec3 gb = hash33(p + vec3(1., 0., 0.)); 36 | vec3 gc = hash33(p + vec3(0., 1., 0.)); 37 | vec3 gd = hash33(p + vec3(1., 1., 0.)); 38 | vec3 ge = hash33(p + vec3(0., 0., 1.)); 39 | vec3 gf = hash33(p + vec3(1., 0., 1.)); 40 | vec3 gg = hash33(p + vec3(0., 1., 1.)); 41 | vec3 gh = hash33(p + vec3(1., 1., 1.)); 42 | 43 | // projections 44 | float va = dot(ga, w - vec3(0., 0., 0.)); 45 | float vb = dot(gb, w - vec3(1., 0., 0.)); 46 | float vc = dot(gc, w - vec3(0., 1., 0.)); 47 | float vd = dot(gd, w - vec3(1., 1., 0.)); 48 | float ve = dot(ge, w - vec3(0., 0., 1.)); 49 | float vf = dot(gf, w - vec3(1., 0., 1.)); 50 | float vg = dot(gg, w - vec3(0., 1., 1.)); 51 | float vh = dot(gh, w - vec3(1., 1., 1.)); 52 | 53 | // interpolation 54 | float gNoise = va + u.x * (vb - va) + 55 | u.y * (vc - va) + 56 | u.z * (ve - va) + 57 | u.x * u.y * (va - vb - vc + vd) + 58 | u.y * u.z * (va - vc - ve + vg) + 59 | u.z * u.x * (va - vb - ve + vf) + 60 | u.x * u.y * u.z * (-va + vb + vc - vd + ve - vf - vg + vh); 61 | 62 | return 2. * gNoise; 63 | } 64 | 65 | // gradient noise in range [0, 1] 66 | float gnoise01(vec3 x) 67 | { 68 | return .5 + .5 * gnoise(x); 69 | } 70 | 71 | // warp uvs for the crt effect 72 | vec2 crt(vec2 uv) 73 | { 74 | float tht = atan(uv.y, uv.x); 75 | float r = length(uv); 76 | // curve without distorting the center 77 | r /= (1. - .1 * r * r); 78 | uv.x = r * cos(tht); 79 | uv.y = r * sin(tht); 80 | return .5 * (uv + 1.); 81 | } 82 | 83 | 84 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 85 | { 86 | vec2 uv = fragCoord / iResolution.xy; 87 | float t = iTime; 88 | 89 | // smoothed interval for which the glitch gets triggered 90 | float glitchAmount = SS(DURATION * .001, DURATION * AMT, mod(t, DURATION)); 91 | float displayNoise = 0.; 92 | vec3 col = vec3(0.); 93 | vec2 eps = vec2(5. / iResolution.x, 0.); 94 | vec2 st = vec2(0.); 95 | 96 | // analog distortion 97 | float y = uv.y * iResolution.y; 98 | float distortion = gnoise(vec3(0., y * .01, t * 500.)) * (glitchAmount * 4. + .1); 99 | distortion *= gnoise(vec3(0., y * .02, t * 250.)) * (glitchAmount * 2. + .025); 100 | 101 | ++displayNoise; 102 | distortion += smoothstep(.999, 1., sin((uv.y + t * 1.6) * 2.)) * .02; 103 | distortion -= smoothstep(.999, 1., sin((uv.y + t) * 2.)) * .02; 104 | st = uv + vec2(distortion, 0.); 105 | // chromatic aberration 106 | col.r += textureLod(iChannel0, st + eps + distortion, 0.).r; 107 | col.g += textureLod(iChannel0, st, 0.).g; 108 | col.b += textureLod(iChannel0, st - eps - distortion, 0.).b; 109 | 110 | // white noise + scanlines 111 | displayNoise = 0.2 * clamp(displayNoise, 0., 1.); 112 | col += (.15 + .65 * glitchAmount) * (hash33(vec3(fragCoord, mod(float(iFrame), 113 | 1000.))).r) * displayNoise; 114 | col -= (.25 + .75 * glitchAmount) * (sin(4. * t + uv.y * iResolution.y * 1.75)) 115 | * displayNoise; 116 | fragColor = vec4(col, 1.0); 117 | } 118 | -------------------------------------------------------------------------------- /glow-rgbsplit-twitchy.glsl: -------------------------------------------------------------------------------- 1 | // First it does a "chromatic aberration" by splitting the rgb signals by a product of sin functions 2 | // over time, then it does a glow effect in a perceptual color space 3 | // Based on kalgynirae's Ghostty passable glow shader and NickWest's Chromatic Aberration shader demo 4 | // Passable glow: https://github.com/kalgynirae/dotfiles/blob/main/ghostty/glow.glsl 5 | // "Chromatic Aberration": https://www.shadertoy.com/view/Mds3zn 6 | 7 | // sRGB linear -> nonlinear transform from https://bottosson.github.io/posts/colorwrong/ 8 | float f(float x) { 9 | if (x >= 0.0031308) { 10 | return 1.055 * pow(x, 1.0 / 2.4) - 0.055; 11 | } else { 12 | return 12.92 * x; 13 | } 14 | } 15 | 16 | float f_inv(float x) { 17 | if (x >= 0.04045) { 18 | return pow((x + 0.055) / 1.055, 2.4); 19 | } else { 20 | return x / 12.92; 21 | } 22 | } 23 | 24 | // Oklab <-> linear sRGB conversions from https://bottosson.github.io/posts/oklab/ 25 | vec4 toOklab(vec4 rgb) { 26 | vec3 c = vec3(f_inv(rgb.r), f_inv(rgb.g), f_inv(rgb.b)); 27 | float l = 0.4122214708 * c.r + 0.5363325363 * c.g + 0.0514459929 * c.b; 28 | float m = 0.2119034982 * c.r + 0.6806995451 * c.g + 0.1073969566 * c.b; 29 | float s = 0.0883024619 * c.r + 0.2817188376 * c.g + 0.6299787005 * c.b; 30 | float l_ = pow(l, 1.0 / 3.0); 31 | float m_ = pow(m, 1.0 / 3.0); 32 | float s_ = pow(s, 1.0 / 3.0); 33 | return vec4( 34 | 0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_, 35 | 1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_, 36 | 0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_, 37 | rgb.a 38 | ); 39 | } 40 | 41 | vec4 toRgb(vec4 oklab) { 42 | vec3 c = oklab.rgb; 43 | float l_ = c.r + 0.3963377774 * c.g + 0.2158037573 * c.b; 44 | float m_ = c.r - 0.1055613458 * c.g - 0.0638541728 * c.b; 45 | float s_ = c.r - 0.0894841775 * c.g - 1.2914855480 * c.b; 46 | float l = l_ * l_ * l_; 47 | float m = m_ * m_ * m_; 48 | float s = s_ * s_ * s_; 49 | vec3 linear_srgb = vec3( 50 | 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s, 51 | -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s, 52 | -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s 53 | ); 54 | return vec4( 55 | clamp(f(linear_srgb.r), 0.0, 1.0), 56 | clamp(f(linear_srgb.g), 0.0, 1.0), 57 | clamp(f(linear_srgb.b), 0.0, 1.0), 58 | oklab.a 59 | ); 60 | } 61 | 62 | // Bloom samples from https://gist.github.com/qwerasd205/c3da6c610c8ffe17d6d2d3cc7068f17f 63 | const vec3[24] samples = { 64 | vec3(0.1693761725038636, 0.9855514761735895, 1), 65 | vec3(-1.333070830962943, 0.4721463328627773, 0.7071067811865475), 66 | vec3(-0.8464394909806497, -1.51113870578065, 0.5773502691896258), 67 | vec3(1.554155680728463, -1.2588090085709776, 0.5), 68 | vec3(1.681364377589461, 1.4741145918052656, 0.4472135954999579), 69 | vec3(-1.2795157692199817, 2.088741103228784, 0.4082482904638631), 70 | vec3(-2.4575847530631187, -0.9799373355024756, 0.3779644730092272), 71 | vec3(0.5874641440200847, -2.7667464429345077, 0.35355339059327373), 72 | vec3(2.997715703369726, 0.11704939884745152, 0.3333333333333333), 73 | vec3(0.41360842451688395, 3.1351121305574803, 0.31622776601683794), 74 | vec3(-3.167149933769243, 0.9844599011770256, 0.30151134457776363), 75 | vec3(-1.5736713846521535, -3.0860263079123245, 0.2886751345948129), 76 | vec3(2.888202648340422, -2.1583061557896213, 0.2773500981126146), 77 | vec3(2.7150778983300325, 2.5745586041105715, 0.2672612419124244), 78 | vec3(-2.1504069972377464, 3.2211410627650165, 0.2581988897471611), 79 | vec3(-3.6548858794907493, -1.6253643308191343, 0.25), 80 | vec3(1.0130775986052671, -3.9967078676335834, 0.24253562503633297), 81 | vec3(4.229723673607257, 0.33081361055181563, 0.23570226039551587), 82 | vec3(0.40107790291173834, 4.340407413572593, 0.22941573387056174), 83 | vec3(-4.319124570236028, 1.159811599693438, 0.22360679774997896), 84 | vec3(-1.9209044802827355, -4.160543952132907, 0.2182178902359924), 85 | vec3(3.8639122286635708, -2.6589814382925123, 0.21320071635561041), 86 | vec3(3.3486228404946234, 3.4331800232609, 0.20851441405707477), 87 | vec3(-2.8769733643574344, 3.9652268864187157, 0.20412414523193154) 88 | }; 89 | 90 | float offsetFunction(float iTime) { 91 | float amount = 1.0; 92 | const float periods[4] = {6.0, 16.0, 19.0, 27.0}; 93 | for (int i = 0; i < 4; i++) { 94 | amount *= 1.0 + 0.5 * sin(iTime*periods[i]); 95 | } 96 | //return amount; 97 | return amount * periods[3]; 98 | } 99 | 100 | const float DIM_CUTOFF = 0.35; 101 | const float BRIGHT_CUTOFF = 0.65; 102 | const float ABBERATION_FACTOR = 0.05; 103 | 104 | void mainImage(out vec4 fragColor, in vec2 fragCoord) { 105 | vec2 uv = fragCoord.xy / iResolution.xy; 106 | 107 | float amount = offsetFunction(iTime); 108 | 109 | vec3 col; 110 | col.r = texture( iChannel0, vec2(uv.x-ABBERATION_FACTOR*amount / iResolution.x, uv.y) ).r; 111 | col.g = texture( iChannel0, uv ).g; 112 | col.b = texture( iChannel0, vec2(uv.x+ABBERATION_FACTOR*amount / iResolution.x, uv.y) ).b; 113 | 114 | vec4 splittedColor = vec4(col, 1.0); 115 | vec4 source = toOklab(splittedColor); 116 | vec4 dest = source; 117 | 118 | if (source.x > DIM_CUTOFF) { 119 | dest.x *= 1.2; 120 | // dest.x = 1.2; 121 | } else { 122 | vec2 step = vec2(1.414) / iResolution.xy; 123 | vec3 glow = vec3(0.0); 124 | for (int i = 0; i < 24; i++) { 125 | vec3 s = samples[i]; 126 | float weight = s.z; 127 | vec4 c = toOklab(texture(iChannel0, uv + s.xy * step)); 128 | if (c.x > DIM_CUTOFF) { 129 | glow.yz += c.yz * weight * 0.3; 130 | if (c.x <= BRIGHT_CUTOFF) { 131 | glow.x += c.x * weight * 0.05; 132 | } else { 133 | glow.x += c.x * weight * 0.10; 134 | } 135 | } 136 | } 137 | // float lightness_diff = clamp(glow.x - dest.x, 0.0, 1.0); 138 | // dest.x = lightness_diff; 139 | // dest.yz = dest.yz * (1.0 - lightness_diff) + glow.yz * lightness_diff; 140 | dest.xyz += glow.xyz; 141 | } 142 | 143 | fragColor = toRgb(dest); 144 | } 145 | -------------------------------------------------------------------------------- /gradient-background.glsl: -------------------------------------------------------------------------------- 1 | // credits: https://github.com/unkn0wncode 2 | void mainImage(out vec4 fragColor, in vec2 fragCoord) 3 | { 4 | // Normalize pixel coordinates (range from 0 to 1) 5 | vec2 uv = fragCoord.xy / iResolution.xy; 6 | 7 | // Create a gradient from bottom right to top left as a function (x + y)/2 8 | float gradientFactor = (uv.x + uv.y) / 2.0; 9 | 10 | // Define gradient colors (adjust to your preference) 11 | vec3 gradientStartColor = vec3(0.1, 0.1, 0.5); // Start color (e.g., dark blue) 12 | vec3 gradientEndColor = vec3(0.5, 0.1, 0.1); // End color (e.g., dark red) 13 | 14 | vec3 gradientColor = mix(gradientStartColor, gradientEndColor, gradientFactor); 15 | 16 | // Sample the terminal screen texture including alpha channel 17 | vec4 terminalColor = texture(iChannel0, uv); 18 | 19 | // Make a mask that is 1.0 where the terminal content is not black 20 | float mask = 1 - step(0.5, dot(terminalColor.rgb, vec3(1.0))); 21 | vec3 blendedColor = mix(terminalColor.rgb, gradientColor, mask); 22 | 23 | // Apply terminal's alpha to control overall opacity 24 | fragColor = vec4(blendedColor, terminalColor.a); 25 | } -------------------------------------------------------------------------------- /in-game-crt.glsl: -------------------------------------------------------------------------------- 1 | // In-game CRT shader 2 | // Author: sarphiv 3 | // License: CC BY-NC-SA 4.0 4 | // Description: 5 | // Shader for ghostty that is focussed on being usable while looking like a stylized CRT terminal in a modern video game. 6 | // I know a tiny bit about shaders, and nothing about GLSL, 7 | // so this is a Frakenstein's monster combination of other shaders together with a lot of surgery. 8 | // On the bright side, i've cleaned up the body parts and surgery a lot. 9 | 10 | // Based on: 11 | // 1. https://gist.github.com/mitchellh/39d62186910dcc27cad097fed16eb882 (forces the choice of license) 12 | // 2. https://gist.github.com/qwerasd205/c3da6c610c8ffe17d6d2d3cc7068f17f 13 | // 3. https://gist.github.com/seanwcom/0fbe6b270aaa5f28823e053d3dbb14ca 14 | 15 | 16 | // Settings: 17 | // How straight the terminal is in each axis 18 | // (x, y) \in R^2 : x, y > 0 19 | #define CURVE 13.0, 11.0 20 | 21 | // How far apart the different colors are from each other 22 | // x \in R 23 | #define COLOR_FRINGING_SPREAD 1.0 24 | 25 | // How much the ghost images are spread out 26 | // x \in R : x >= 0 27 | #define GHOSTING_SPREAD 0.75 28 | // How visible ghost images are 29 | // x \in R : x >= 0 30 | #define GHOSTING_STRENGTH 1.0 31 | 32 | // How much of the non-linearly darkened colors are mixed in 33 | // [0, 1] 34 | #define DARKEN_MIX 0.4 35 | 36 | // How far in the vignette spreads 37 | // x \in R : x >= 0 38 | #define VIGNETTE_SPREAD 0.3 39 | // How bright the vignette is 40 | // x \in R : x >= 0 41 | #define VIGNETTE_BRIGHTNESS 6.4 42 | 43 | // Tint all colors 44 | // [0, 1]^3 45 | #define TINT 0.93, 1.00, 0.96 46 | 47 | // How visible the scan line effect is 48 | // NOTE: Technically these are not scan lines, but rather the lack of them 49 | // [0, 1] 50 | #define SCAN_LINES_STRENGTH 0.15 51 | // How bright the spaces between the lines are 52 | // [0, 1] 53 | #define SCAN_LINES_VARIANCE 0.35 54 | // Pixels per scan line effect 55 | // x \in R : x > 0 56 | #define SCAN_LINES_PERIOD 4.0 57 | 58 | // How visible the aperture grille is 59 | // x \in R : x >= 0 60 | #define APERTURE_GRILLE_STRENGTH 0.2 61 | // Pixels per aperture grille 62 | // x \in R : x > 0 63 | #define APERTURE_GRILLE_PERIOD 2.0 64 | 65 | // How much the screen flickers 66 | // x \in R : x >= 0 67 | #define FLICKER_STRENGTH 0.05 68 | // How fast the screen flickers 69 | // x \in R : x > 0 70 | #define FLICKER_FREQUENCY 15.0 71 | 72 | // How much noise is added to filled areas 73 | // [0, 1] 74 | #define NOISE_CONTENT_STRENGTH 0.15 75 | // How much noise is added everywhere 76 | // [0, 1] 77 | #define NOISE_UNIFORM_STRENGTH 0.03 78 | 79 | // How big the bloom is 80 | // x \in R : x >= 0 81 | #define BLOOM_SPREAD 8.0 82 | // How visible the bloom is 83 | // [0, 1] 84 | #define BLOOM_STRENGTH 0.04 85 | 86 | // How fast colors fade in and out 87 | // [0, 1] 88 | #define FADE_FACTOR 0.55 89 | 90 | 91 | 92 | // Disabled values for when the settings are not defined 93 | #ifndef COLOR_FRINGING_SPREAD 94 | #define COLOR_FRINGING_SPREAD 0.0 95 | #endif 96 | 97 | #if !defined(GHOSTING_SPREAD) || !defined(GHOSTING_STRENGTH) 98 | #undef GHOSTING_SPREAD 99 | #undef GHOSTING_STRENGTH 100 | #define GHOSTING_SPREAD 0.0 101 | #define GHOSTING_STRENGTH 0.0 102 | #endif 103 | 104 | #ifndef DARKEN_MIX 105 | #define DARKEN_MIX 0.0 106 | #endif 107 | 108 | #if !defined(VIGNETTE_SPREAD) || !defined(VIGNETTE_BRIGHTNESS) 109 | #undef VIGNETTE_SPREAD 110 | #undef VIGNETTE_BRIGHTNESS 111 | #define VIGNETTE_SPREAD 0.0 112 | #define VIGNETTE_BRIGHTNESS 1.0 113 | #endif 114 | 115 | #ifndef TINT 116 | #define TINT 1.00, 1.00, 1.00 117 | #endif 118 | 119 | #if !defined(SCAN_LINES_STRENGTH) || !defined(SCAN_LINES_VARIANCE) || !defined(SCAN_LINES_PERIOD) 120 | #undef SCAN_LINES_STRENGTH 121 | #undef SCAN_LINES_VARIANCE 122 | #undef SCAN_LINES_PERIOD 123 | #define SCAN_LINES_STRENGTH 0.0 124 | #define SCAN_LINES_VARIANCE 1.0 125 | #define SCAN_LINES_PERIOD 1.0 126 | #endif 127 | 128 | #if !defined(APERTURE_GRILLE_STRENGTH) || !defined(APERTURE_GRILLE_PERIOD) 129 | #undef APERTURE_GRILLE_STRENGTH 130 | #undef APERTURE_GRILLE_PERIOD 131 | #define APERTURE_GRILLE_STRENGTH 0.0 132 | #define APERTURE_GRILLE_PERIOD 1.0 133 | #endif 134 | 135 | #if !defined(FLICKER_STRENGTH) || !defined(FLICKER_FREQUENCY) 136 | #undef FLICKER_STRENGTH 137 | #undef FLICKER_FREQUENCY 138 | #define FLICKER_STRENGTH 0.0 139 | #define FLICKER_FREQUENCY 1.0 140 | #endif 141 | 142 | #if !defined(NOISE_CONTENT_STRENGTH) || !defined(NOISE_UNIFORM_STRENGTH) 143 | #undef NOISE_CONTENT_STRENGTH 144 | #undef NOISE_UNIFORM_STRENGTH 145 | #define NOISE_CONTENT_STRENGTH 0.0 146 | #define NOISE_UNIFORM_STRENGTH 0.0 147 | #endif 148 | 149 | #if !defined(BLOOM_SPREAD) || !defined(BLOOM_STRENGTH) 150 | #undef BLOOM_SPREAD 151 | #undef BLOOM_STRENGTH 152 | #define BLOOM_SPREAD 0.0 153 | #define BLOOM_STRENGTH 0.0 154 | #endif 155 | 156 | #ifndef FADE_FACTOR 157 | #define FADE_FACTOR 1.00 158 | #endif 159 | 160 | 161 | 162 | // Constants 163 | #define PI 3.1415926535897932384626433832795 164 | 165 | #ifdef BLOOM_SPREAD 166 | // Golden spiral samples used for bloom. 167 | // [x, y, weight] weight is inverse of distance. 168 | const vec3[24] bloom_samples = { 169 | vec3( 0.1693761725038636, 0.9855514761735895, 1), 170 | vec3(-1.333070830962943, 0.4721463328627773, 0.7071067811865475), 171 | vec3(-0.8464394909806497, -1.51113870578065, 0.5773502691896258), 172 | vec3( 1.554155680728463, -1.2588090085709776, 0.5), 173 | vec3( 1.681364377589461, 1.4741145918052656, 0.4472135954999579), 174 | vec3(-1.2795157692199817, 2.088741103228784, 0.4082482904638631), 175 | vec3(-2.4575847530631187, -0.9799373355024756, 0.3779644730092272), 176 | vec3( 0.5874641440200847, -2.7667464429345077, 0.35355339059327373), 177 | vec3( 2.997715703369726, 0.11704939884745152, 0.3333333333333333), 178 | vec3( 0.41360842451688395, 3.1351121305574803, 0.31622776601683794), 179 | vec3(-3.167149933769243, 0.9844599011770256, 0.30151134457776363), 180 | vec3(-1.5736713846521535, -3.0860263079123245, 0.2886751345948129), 181 | vec3( 2.888202648340422, -2.1583061557896213, 0.2773500981126146), 182 | vec3( 2.7150778983300325, 2.5745586041105715, 0.2672612419124244), 183 | vec3(-2.1504069972377464, 3.2211410627650165, 0.2581988897471611), 184 | vec3(-3.6548858794907493, -1.6253643308191343, 0.25), 185 | vec3( 1.0130775986052671, -3.9967078676335834, 0.24253562503633297), 186 | vec3( 4.229723673607257, 0.33081361055181563, 0.23570226039551587), 187 | vec3( 0.40107790291173834, 4.340407413572593, 0.22941573387056174), 188 | vec3(-4.319124570236028, 1.159811599693438, 0.22360679774997896), 189 | vec3(-1.9209044802827355, -4.160543952132907, 0.2182178902359924), 190 | vec3( 3.8639122286635708, -2.6589814382925123, 0.21320071635561041), 191 | vec3( 3.3486228404946234, 3.4331800232609, 0.20851441405707477), 192 | vec3(-2.8769733643574344, 3.9652268864187157, 0.20412414523193154) 193 | }; 194 | #endif 195 | 196 | 197 | 198 | 199 | void mainImage(out vec4 fragColor, in vec2 fragCoord) { 200 | // Get texture coordinates 201 | vec2 uv = fragCoord.xy / iResolution.xy; 202 | 203 | #ifdef CURVE 204 | // Curve texture coordinates to mimic non-flat CRT monior 205 | uv = (uv - 0.5) * 2.0; 206 | uv.xy *= 1.0 + pow((abs(vec2(uv.y, uv.x)) / vec2(CURVE)), vec2(2.0)); 207 | uv = (uv / 2.0) + 0.5; 208 | #endif 209 | 210 | 211 | // Retrieve colors from appropriate locations 212 | fragColor.r = texture(iChannel0, vec2(uv.x + 0.0003 * COLOR_FRINGING_SPREAD, uv.y + 0.0003 * COLOR_FRINGING_SPREAD)).x; 213 | fragColor.g = texture(iChannel0, vec2(uv.x + 0.0000 * COLOR_FRINGING_SPREAD, uv.y - 0.0006 * COLOR_FRINGING_SPREAD)).y; 214 | fragColor.b = texture(iChannel0, vec2(uv.x - 0.0006 * COLOR_FRINGING_SPREAD, uv.y + 0.0000 * COLOR_FRINGING_SPREAD)).z; 215 | fragColor.a = texture(iChannel0, uv).a; 216 | 217 | 218 | // Add faint ghost images 219 | fragColor.r += 0.04 * GHOSTING_STRENGTH * texture(iChannel0, GHOSTING_SPREAD * vec2(+0.025, -0.027) + uv.xy).x; 220 | fragColor.g += 0.02 * GHOSTING_STRENGTH * texture(iChannel0, GHOSTING_SPREAD * vec2(-0.022, -0.020) + uv.xy).y; 221 | fragColor.b += 0.04 * GHOSTING_STRENGTH * texture(iChannel0, GHOSTING_SPREAD * vec2(-0.020, -0.018) + uv.xy).z; 222 | 223 | 224 | // Quadratically darken everything 225 | fragColor.rgb = mix(fragColor.rgb, fragColor.rgb*fragColor.rgb, DARKEN_MIX); 226 | 227 | 228 | // Vignette effect 229 | fragColor.rgb *= VIGNETTE_BRIGHTNESS * pow(uv.x * uv.y * (1.0-uv.x) * (1.0-uv.y), VIGNETTE_SPREAD); 230 | 231 | 232 | // Tint all colors 233 | fragColor.rgb *= vec3(TINT); 234 | 235 | 236 | // NOTE: At this point, RGB values may be above 1 237 | 238 | 239 | // Add scan lines effect 240 | fragColor.rgb *= mix( 241 | 1.0, 242 | SCAN_LINES_VARIANCE/2.0*(1.0 + sin(2*PI* uv.y * iResolution.y/SCAN_LINES_PERIOD)), 243 | SCAN_LINES_STRENGTH 244 | ); 245 | 246 | 247 | // Add aperture grille 248 | int aperture_grille_step = int(8 * mod(fragCoord.x, APERTURE_GRILLE_PERIOD) / APERTURE_GRILLE_PERIOD); 249 | float aperture_grille_mask; 250 | 251 | if (aperture_grille_step < 3) 252 | aperture_grille_mask = 0.0; 253 | else if (aperture_grille_step < 4) 254 | aperture_grille_mask = mod(8*fragCoord.x, APERTURE_GRILLE_PERIOD) / APERTURE_GRILLE_PERIOD; 255 | else if (aperture_grille_step < 7) 256 | aperture_grille_mask = 1.0; 257 | else if (aperture_grille_step < 8) 258 | aperture_grille_mask = mod(-8*fragCoord.x, APERTURE_GRILLE_PERIOD) / APERTURE_GRILLE_PERIOD; 259 | 260 | fragColor.rgb *= 1.0 - APERTURE_GRILLE_STRENGTH*aperture_grille_mask; 261 | 262 | 263 | // Add flicker 264 | fragColor *= 1.0 - FLICKER_STRENGTH/2.0*(1.0 + sin(2*PI*FLICKER_FREQUENCY*iTime)); 265 | 266 | 267 | // Add noise 268 | // NOTE: Hard-coded noise distributions 269 | float noiseContent = smoothstep(0.4, 0.6, fract(sin(uv.x * uv.y * (1.0-uv.x) * (1.0-uv.y) * iTime * 4096.0) * 65536.0)); 270 | float noiseUniform = smoothstep(0.4, 0.6, fract(sin(uv.x * uv.y * (1.0-uv.x) * (1.0-uv.y) * iTime * 8192.0) * 65536.0)); 271 | fragColor.rgb *= clamp(noiseContent + 1.0 - NOISE_CONTENT_STRENGTH, 0.0, 1.0); 272 | fragColor.rgb = clamp(fragColor.rgb + noiseUniform * NOISE_UNIFORM_STRENGTH, 0.0, 1.0); 273 | 274 | 275 | // NOTE: At this point, RGB values are again within [0, 1] 276 | 277 | 278 | // Remove output outside of screen bounds 279 | if (uv.x < 0.0 || uv.x > 1.0) 280 | fragColor.rgb *= 0.0; 281 | if (uv.y < 0.0 || uv.y > 1.0) 282 | fragColor.rgb *= 0.0; 283 | 284 | 285 | #ifdef BLOOM_SPREAD 286 | // Add bloom 287 | vec2 step = BLOOM_SPREAD * vec2(1.414) / iResolution.xy; 288 | 289 | for (int i = 0; i < 24; i++) { 290 | vec3 bloom_sample = bloom_samples[i]; 291 | vec4 neighbor = texture(iChannel0, uv + bloom_sample.xy * step); 292 | float luminance = 0.299 * neighbor.r + 0.587 * neighbor.g + 0.114 * neighbor.b; 293 | 294 | fragColor += luminance * bloom_sample.z * neighbor * BLOOM_STRENGTH; 295 | } 296 | 297 | fragColor = clamp(fragColor, 0.0, 1.0); 298 | #endif 299 | 300 | 301 | // Add fade effect to smoothen out color transitions 302 | // NOTE: May need to be iTime/iTimeDelta dependent 303 | fragColor = vec4(FADE_FACTOR*fragColor.rgb, FADE_FACTOR); 304 | } 305 | -------------------------------------------------------------------------------- /inside-the-matrix.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | Feel free to do anything you want with this code. 3 | This shader uses "runes" code by FabriceNeyret2 (https://www.shadertoy.com/view/4ltyDM) 4 | which is based on "runes" by otaviogood (https://shadertoy.com/view/MsXSRn). 5 | These random runes look good as matrix symbols and have acceptable performance. 6 | 7 | @pkazmier modified this shader to work in Ghostty. 8 | */ 9 | 10 | const int ITERATIONS = 40; //use less value if you need more performance 11 | const float SPEED = .5; 12 | 13 | const float STRIP_CHARS_MIN = 7.; 14 | const float STRIP_CHARS_MAX = 40.; 15 | const float STRIP_CHAR_HEIGHT = 0.15; 16 | const float STRIP_CHAR_WIDTH = 0.10; 17 | const float ZCELL_SIZE = 1. * (STRIP_CHAR_HEIGHT * STRIP_CHARS_MAX); //the multiplier can't be less than 1. 18 | const float XYCELL_SIZE = 12. * STRIP_CHAR_WIDTH; //the multiplier can't be less than 1. 19 | 20 | const int BLOCK_SIZE = 10; //in cells 21 | const int BLOCK_GAP = 2; //in cells 22 | 23 | const float WALK_SPEED = 0.5 * XYCELL_SIZE; 24 | const float BLOCKS_BEFORE_TURN = 3.; 25 | 26 | 27 | const float PI = 3.14159265359; 28 | 29 | 30 | // ---- random ---- 31 | 32 | float hash(float v) { 33 | return fract(sin(v)*43758.5453123); 34 | } 35 | 36 | float hash(vec2 v) { 37 | return hash(dot(v, vec2(5.3983, 5.4427))); 38 | } 39 | 40 | vec2 hash2(vec2 v) 41 | { 42 | v = vec2(v * mat2(127.1, 311.7, 269.5, 183.3)); 43 | return fract(sin(v)*43758.5453123); 44 | } 45 | 46 | vec4 hash4(vec2 v) 47 | { 48 | vec4 p = vec4(v * mat4x2( 127.1, 311.7, 49 | 269.5, 183.3, 50 | 113.5, 271.9, 51 | 246.1, 124.6 )); 52 | return fract(sin(p)*43758.5453123); 53 | } 54 | 55 | vec4 hash4(vec3 v) 56 | { 57 | vec4 p = vec4(v * mat4x3( 127.1, 311.7, 74.7, 58 | 269.5, 183.3, 246.1, 59 | 113.5, 271.9, 124.6, 60 | 271.9, 269.5, 311.7 ) ); 61 | return fract(sin(p)*43758.5453123); 62 | } 63 | 64 | 65 | // ---- symbols ---- 66 | // Slightly modified version of "runes" by FabriceNeyret2 - https://www.shadertoy.com/view/4ltyDM 67 | // Which is based on "runes" by otaviogood - https://shadertoy.com/view/MsXSRn 68 | 69 | float rune_line(vec2 p, vec2 a, vec2 b) { // from https://www.shadertoy.com/view/4dcfW8 70 | p -= a, b -= a; 71 | float h = clamp(dot(p, b) / dot(b, b), 0., 1.); // proj coord on line 72 | return length(p - b * h); // dist to segment 73 | } 74 | 75 | float rune(vec2 U, vec2 seed, float highlight) 76 | { 77 | float d = 1e5; 78 | for (int i = 0; i < 4; i++) // number of strokes 79 | { 80 | vec4 pos = hash4(seed); 81 | seed += 1.; 82 | 83 | // each rune touches the edge of its box on all 4 sides 84 | if (i == 0) pos.y = .0; 85 | if (i == 1) pos.x = .999; 86 | if (i == 2) pos.x = .0; 87 | if (i == 3) pos.y = .999; 88 | // snap the random line endpoints to a grid 2x3 89 | vec4 snaps = vec4(2, 3, 2, 3); 90 | pos = ( floor(pos * snaps) + .5) / snaps; 91 | 92 | if (pos.xy != pos.zw) //filter out single points (when start and end are the same) 93 | d = min(d, rune_line(U, pos.xy, pos.zw + .001) ); // closest line 94 | } 95 | return smoothstep(0.1, 0., d) + highlight*smoothstep(0.4, 0., d); 96 | } 97 | 98 | float random_char(vec2 outer, vec2 inner, float highlight) { 99 | vec2 seed = vec2(dot(outer, vec2(269.5, 183.3)), dot(outer, vec2(113.5, 271.9))); 100 | return rune(inner, seed, highlight); 101 | } 102 | 103 | 104 | // ---- digital rain ---- 105 | 106 | // xy - horizontal, z - vertical 107 | vec3 rain(vec3 ro3, vec3 rd3, float time) { 108 | vec4 result = vec4(0.); 109 | 110 | // normalized 2d projection 111 | vec2 ro2 = vec2(ro3); 112 | vec2 rd2 = normalize(vec2(rd3)); 113 | 114 | // we use formulas `ro3 + rd3 * t3` and `ro2 + rd2 * t2`, `t3_to_t2` is a multiplier to convert t3 to t2 115 | bool prefer_dx = abs(rd2.x) > abs(rd2.y); 116 | float t3_to_t2 = prefer_dx ? rd3.x / rd2.x : rd3.y / rd2.y; 117 | 118 | // at first, horizontal space (xy) is divided into cells (which are columns in 3D) 119 | // then each xy-cell is divided into vertical cells (along z) - each of these cells contains one raindrop 120 | 121 | ivec3 cell_side = ivec3(step(0., rd3)); //for positive rd.x use cell side with higher x (1) as the next side, for negative - with lower x (0), the same for y and z 122 | ivec3 cell_shift = ivec3(sign(rd3)); //shift to move to the next cell 123 | 124 | // move through xy-cells in the ray direction 125 | float t2 = 0.; // the ray formula is: ro2 + rd2 * t2, where t2 is positive as the ray has a direction. 126 | ivec2 next_cell = ivec2(floor(ro2/XYCELL_SIZE)); //first cell index where ray origin is located 127 | for (int i=0; i= t2s && tmin <= t2) { 175 | float u = s.x * rd2.y - s.y * rd2.x; //horizontal coord in the matrix strip 176 | if (abs(u) < target_rad) { 177 | u = (u/target_rad + 1.) / 2.; 178 | float z = ro3.z + rd3.z * tmin/t3_to_t2; 179 | float v = (z - target_z) / target_length; //vertical coord in the matrix strip 180 | if (v >= 0.0 && v < 1.0) { 181 | float c = floor(v * chars_count); //symbol index relative to the start of the strip, with addition of char_z_shift it becomes an index relative to the whole cell 182 | float q = fract(v * chars_count); 183 | vec2 char_hash = hash2(vec2(c+char_z_shift, cell_hash2.x)); 184 | if (char_hash.x >= 0.1 || c == 0.) { //10% of missed symbols 185 | float time_factor = floor(c == 0. ? time*5.0 : //first symbol is changed fast 186 | time*(1.0*cell_hash2.z + //strips are changed sometime with different speed 187 | cell_hash2.w*cell_hash2.w*4.*pow(char_hash.y, 4.))); //some symbols in some strips are changed relatively often 188 | float a = random_char(vec2(char_hash.x, time_factor), vec2(u,q), max(1., 3. - c/2.)*0.2); //alpha 189 | a *= clamp((chars_count - 0.5 - c) / 2., 0., 1.); //tail fade 190 | if (a > 0.) { 191 | float attenuation = 1. + pow(0.06*tmin/t3_to_t2, 2.); 192 | vec3 col = (c == 0. ? vec3(0.67, 1.0, 0.82) : vec3(0.25, 0.80, 0.40)) / attenuation; 193 | float a1 = result.a; 194 | result.a = a1 + (1. - a1) * a; 195 | result.xyz = (result.xyz * a1 + col * (1. - a1) * a) / result.a; 196 | if (result.a > 0.98) return result.xyz; 197 | } 198 | } 199 | } 200 | } 201 | } 202 | // not found in this cell - go to next vertical cell 203 | zcell += cell_shift.z; 204 | } 205 | // go to next horizontal cell 206 | } 207 | 208 | return result.xyz * result.a; 209 | } 210 | 211 | 212 | // ---- main, camera ---- 213 | 214 | vec2 rotate(vec2 v, float a) { 215 | float s = sin(a); 216 | float c = cos(a); 217 | mat2 m = mat2(c, -s, s, c); 218 | return m * v; 219 | } 220 | 221 | vec3 rotateX(vec3 v, float a) { 222 | float s = sin(a); 223 | float c = cos(a); 224 | return mat3(1.,0.,0.,0.,c,-s,0.,s,c) * v; 225 | } 226 | 227 | vec3 rotateY(vec3 v, float a) { 228 | float s = sin(a); 229 | float c = cos(a); 230 | return mat3(c,0.,-s,0.,1.,0.,s,0.,c) * v; 231 | } 232 | 233 | vec3 rotateZ(vec3 v, float a) { 234 | float s = sin(a); 235 | float c = cos(a); 236 | return mat3(c,-s,0.,s,c,0.,0.,0.,1.) * v; 237 | } 238 | 239 | float smoothstep1(float x) { 240 | return smoothstep(0., 1., x); 241 | } 242 | 243 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 244 | { 245 | if (STRIP_CHAR_WIDTH > XYCELL_SIZE || STRIP_CHAR_HEIGHT * STRIP_CHARS_MAX > ZCELL_SIZE) { 246 | // error 247 | fragColor = vec4(1., 0., 0., 1.); 248 | return; 249 | } 250 | 251 | vec2 uv = fragCoord.xy / iResolution.xy; 252 | 253 | float time = iTime * SPEED; 254 | 255 | const float turn_rad = 0.25 / BLOCKS_BEFORE_TURN; //0 .. 0.5 256 | const float turn_abs_time = (PI/2.*turn_rad) * 1.5; //multiplier different than 1 means a slow down on turns 257 | const float turn_time = turn_abs_time / (1. - 2.*turn_rad + turn_abs_time); //0..1, but should be <= 0.5 258 | 259 | float level1_size = float(BLOCK_SIZE) * BLOCKS_BEFORE_TURN * XYCELL_SIZE; 260 | float level2_size = 4. * level1_size; 261 | float gap_size = float(BLOCK_GAP) * XYCELL_SIZE; 262 | 263 | vec3 ro = vec3(gap_size/2., gap_size/2., 0.); 264 | vec3 rd = vec3(uv.x, 2.0, uv.y); 265 | 266 | float tq = fract(time / (level2_size*4.) * WALK_SPEED); //the whole cycle time counter 267 | float t8 = fract(tq*4.); //time counter while walking on one of the four big sides 268 | float t1 = fract(t8*8.); //time counter while walking on one of the eight sides of the big side 269 | 270 | vec2 prev; 271 | vec2 dir; 272 | if (tq < 0.25) { 273 | prev = vec2(0.,0.); 274 | dir = vec2(0.,1.); 275 | } else if (tq < 0.5) { 276 | prev = vec2(0.,1.); 277 | dir = vec2(1.,0.); 278 | } else if (tq < 0.75) { 279 | prev = vec2(1.,1.); 280 | dir = vec2(0.,-1.); 281 | } else { 282 | prev = vec2(1.,0.); 283 | dir = vec2(-1.,0.); 284 | } 285 | float angle = floor(tq * 4.); //0..4 wich means 0..2*PI 286 | 287 | prev *= 4.; 288 | 289 | const float first_turn_look_angle = 0.4; 290 | const float second_turn_drift_angle = 0.5; 291 | const float fifth_turn_drift_angle = 0.25; 292 | 293 | vec2 turn; 294 | float turn_sign = 0.; 295 | vec2 dirL = rotate(dir, -PI/2.); 296 | vec2 dirR = -dirL; 297 | float up_down = 0.; 298 | float rotate_on_turns = 1.; 299 | float roll_on_turns = 1.; 300 | float add_angel = 0.; 301 | if (t8 < 0.125) { 302 | turn = dirL; 303 | //dir = dir; 304 | turn_sign = -1.; 305 | angle -= first_turn_look_angle * (max(0., t1 - (1. - turn_time*2.)) / turn_time - max(0., t1 - (1. - turn_time)) / turn_time * 2.5); 306 | roll_on_turns = 0.; 307 | } else if (t8 < 0.250) { 308 | prev += dir; 309 | turn = dir; 310 | dir = dirL; 311 | angle -= 1.; 312 | turn_sign = 1.; 313 | add_angel += first_turn_look_angle*0.5 + (-first_turn_look_angle*0.5+1.0+second_turn_drift_angle)*t1; 314 | rotate_on_turns = 0.; 315 | roll_on_turns = 0.; 316 | } else if (t8 < 0.375) { 317 | prev += dir + dirL; 318 | turn = dirR; 319 | //dir = dir; 320 | turn_sign = 1.; 321 | add_angel += second_turn_drift_angle*sqrt(1.-t1); 322 | //roll_on_turns = 0.; 323 | } else if (t8 < 0.5) { 324 | prev += dir + dir + dirL; 325 | turn = dirR; 326 | dir = dirR; 327 | angle += 1.; 328 | turn_sign = 0.; 329 | up_down = sin(t1*PI) * 0.37; 330 | } else if (t8 < 0.625) { 331 | prev += dir + dir; 332 | turn = dir; 333 | dir = dirR; 334 | angle += 1.; 335 | turn_sign = -1.; 336 | up_down = sin(-min(1., t1/(1.-turn_time))*PI) * 0.37; 337 | } else if (t8 < 0.750) { 338 | prev += dir + dir + dirR; 339 | turn = dirL; 340 | //dir = dir; 341 | turn_sign = -1.; 342 | add_angel -= (fifth_turn_drift_angle + 1.) * smoothstep1(t1); 343 | rotate_on_turns = 0.; 344 | roll_on_turns = 0.; 345 | } else if (t8 < 0.875) { 346 | prev += dir + dir + dir + dirR; 347 | turn = dir; 348 | dir = dirL; 349 | angle -= 1.; 350 | turn_sign = 1.; 351 | add_angel -= fifth_turn_drift_angle - smoothstep1(t1) * (fifth_turn_drift_angle * 2. + 1.); 352 | rotate_on_turns = 0.; 353 | roll_on_turns = 0.; 354 | } else { 355 | prev += dir + dir + dir; 356 | turn = dirR; 357 | //dir = dir; 358 | turn_sign = 1.; 359 | angle += fifth_turn_drift_angle * (1.5*min(1., (1.-t1)/turn_time) - 0.5*smoothstep1(1. - min(1.,t1/(1.-turn_time)))); 360 | } 361 | 362 | if (iMouse.x > 10. || iMouse.y > 10.) { 363 | vec2 mouse = iMouse.xy / iResolution.xy * 2. - 1.; 364 | up_down = -0.7 * mouse.y; 365 | angle += mouse.x; 366 | rotate_on_turns = 1.; 367 | roll_on_turns = 0.; 368 | } else { 369 | angle += add_angel; 370 | } 371 | 372 | rd = rotateX(rd, up_down); 373 | 374 | vec2 p; 375 | if (turn_sign == 0.) { 376 | // move forward 377 | p = prev + dir * (turn_rad + 1. * t1); 378 | } 379 | else if (t1 > (1. - turn_time)) { 380 | // turn 381 | float tr = (t1 - (1. - turn_time)) / turn_time; 382 | vec2 c = prev + dir * (1. - turn_rad) + turn * turn_rad; 383 | p = c + turn_rad * rotate(dir, (tr - 1.) * turn_sign * PI/2.); 384 | angle += tr * turn_sign * rotate_on_turns; 385 | rd = rotateY(rd, sin(tr*turn_sign*PI) * 0.2 * roll_on_turns); //roll 386 | } else { 387 | // move forward 388 | t1 /= (1. - turn_time); 389 | p = prev + dir * (turn_rad + (1. - turn_rad*2.) * t1); 390 | } 391 | 392 | rd = rotateZ(rd, angle * PI/2.); 393 | 394 | ro.xy += level1_size * p; 395 | 396 | ro += rd * 0.2; 397 | rd = normalize(rd); 398 | 399 | // vec3 col = rain(ro, rd, time); 400 | vec3 col = rain(ro, rd, time) * 0.25; 401 | 402 | // Sample the terminal screen texture including alpha channel 403 | vec4 terminalColor = texture(iChannel0, uv); 404 | 405 | // Combine the matrix effect with the terminal color 406 | // vec3 blendedColor = terminalColor.rgb + col; 407 | 408 | // Make a mask that is 1.0 where the terminal content is not black 409 | float mask = 1.2 - step(0.5, dot(terminalColor.rgb, vec3(1.0))); 410 | vec3 blendedColor = mix(terminalColor.rgb * 1.2, col, mask); 411 | 412 | fragColor = vec4(blendedColor, terminalColor.a); 413 | } 414 | -------------------------------------------------------------------------------- /just-snow.glsl: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Andrew Baldwin (twitter: baldand, www: http://thndl.com) 2 | // License = Attribution-NonCommercial-ShareAlike (http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US) 3 | 4 | // "Just snow" 5 | // Simple (but not cheap) snow made from multiple parallax layers with randomly positioned 6 | // flakes and directions. Also includes a DoF effect. Pan around with mouse. 7 | 8 | #define LIGHT_SNOW // Comment this out for a blizzard 9 | 10 | #ifdef LIGHT_SNOW 11 | #define LAYERS 50 12 | #define DEPTH .5 13 | #define WIDTH .3 14 | #define SPEED .6 15 | #else // BLIZZARD 16 | #define LAYERS 200 17 | #define DEPTH .1 18 | #define WIDTH .8 19 | #define SPEED 1.5 20 | #endif 21 | 22 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 23 | { 24 | const mat3 p = mat3(13.323122,23.5112,21.71123,21.1212,28.7312,11.9312,21.8112,14.7212,61.3934); 25 | vec2 uv = fragCoord.xy / iResolution.xy; 26 | 27 | vec3 acc = vec3(0.0); 28 | float dof = 5.0 * sin(iTime * 0.1); 29 | for (int i = 0; i < LAYERS; i++) { 30 | float fi = float(i); 31 | vec2 q =-uv*(1.0 + fi * DEPTH); 32 | q += vec2(q.y * (WIDTH * mod(fi * 7.238917, 1.0) - WIDTH * 0.5), -SPEED * iTime / (1.0 + fi * DEPTH * 0.03)); 33 | vec3 n = vec3(floor(q), 31.189 + fi); 34 | vec3 m = floor(n) * 0.00001 + fract(n); 35 | vec3 mp = (31415.9 + m) / fract(p * m); 36 | vec3 r = fract(mp); 37 | vec2 s = abs(mod(q, 1.0) - 0.5 + 0.9 * r.xy - 0.45); 38 | s += 0.01 * abs(2.0 * fract(10.0 * q.yx) - 1.0); 39 | float d = 0.6 * max(s.x - s.y, s.x + s.y) + max(s.x, s.y) - 0.01; 40 | float edge = 0.005 + 0.05 * min(0.5 * abs(fi - 5.0 - dof), 1.0); 41 | acc += vec3(smoothstep(edge, -edge, d) * (r.x / (1.0 + 0.02 * fi * DEPTH))); 42 | } 43 | 44 | // Sample the terminal screen texture including alpha channel 45 | vec4 terminalColor = texture(iChannel0, uv); 46 | 47 | // Combine the snow effect with the terminal color 48 | vec3 blendedColor = terminalColor.rgb + acc; 49 | 50 | // Use the terminal's original alpha 51 | fragColor = vec4(blendedColor, terminalColor.a); 52 | } 53 | -------------------------------------------------------------------------------- /matrix-hallway.glsl: -------------------------------------------------------------------------------- 1 | // based on the following Shader Toy entry 2 | // 3 | // [SH17A] Matrix rain. Created by Reinder Nijhoff 2017 4 | // Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 5 | // @reindernijhoff 6 | // 7 | // https://www.shadertoy.com/view/ldjBW1 8 | // 9 | 10 | #define SPEED_MULTIPLIER 1. 11 | #define GREEN_ALPHA .33 12 | 13 | #define BLACK_BLEND_THRESHOLD .4 14 | 15 | #define R fract(1e2 * sin(p.x * 8. + p.y)) 16 | 17 | void mainImage(out vec4 fragColor, vec2 fragCoord) { 18 | vec3 v = vec3(fragCoord, 1) / iResolution - .5; 19 | // vec3 s = .5 / abs(v); 20 | // scale? 21 | vec3 s = .9 / abs(v); 22 | s.z = min(s.y, s.x); 23 | vec3 i = ceil( 8e2 * s.z * ( s.y < s.x ? v.xzz : v.zyz ) ) * .1; 24 | vec3 j = fract(i); 25 | i -= j; 26 | vec3 p = vec3(9, int(iTime * SPEED_MULTIPLIER * (9. + 8. * sin(i).x)), 0) + i; 27 | vec3 col = fragColor.rgb; 28 | col.g = R / s.z; 29 | p *= j; 30 | col *= (R >.5 && j.x < .6 && j.y < .8) ? GREEN_ALPHA : 0.; 31 | 32 | // Sample the terminal screen texture including alpha channel 33 | vec2 uv = fragCoord.xy / iResolution.xy; 34 | vec4 terminalColor = texture(iChannel0, uv); 35 | 36 | float alpha = step(length(terminalColor.rgb), BLACK_BLEND_THRESHOLD); 37 | vec3 blendedColor = mix(terminalColor.rgb * 1.2, col, alpha); 38 | 39 | fragColor = vec4(blendedColor, terminalColor.a); 40 | } 41 | -------------------------------------------------------------------------------- /mnoise.glsl: -------------------------------------------------------------------------------- 1 | vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } 2 | vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } 3 | vec4 permute(vec4 x) { return mod289(((x * 34.0) + 10.0) * x); } 4 | vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; } 5 | float snoise(vec3 v) { 6 | const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0); 7 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); 8 | 9 | // First corner 10 | vec3 i = floor(v + dot(v, C.yyy)); 11 | vec3 x0 = v - i + dot(i, C.xxx); 12 | 13 | // Other corners 14 | vec3 g = step(x0.yzx, x0.xyz); 15 | vec3 l = 1.0 - g; 16 | vec3 i1 = min(g.xyz, l.zxy); 17 | vec3 i2 = max(g.xyz, l.zxy); 18 | 19 | // x0 = x0 - 0.0 + 0.0 * C.xxx; 20 | // x1 = x0 - i1 + 1.0 * C.xxx; 21 | // x2 = x0 - i2 + 2.0 * C.xxx; 22 | // x3 = x0 - 1.0 + 3.0 * C.xxx; 23 | vec3 x1 = x0 - i1 + C.xxx; 24 | vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y 25 | vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y 26 | 27 | // Permutations 28 | i = mod289(i); 29 | vec4 p = permute(permute(permute(i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y + 30 | vec4(0.0, i1.y, i2.y, 1.0)) + 31 | i.x + vec4(0.0, i1.x, i2.x, 1.0)); 32 | 33 | // Gradients: 7x7 points over a square, mapped onto an octahedron. 34 | // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) 35 | float n_ = 0.142857142857; // 1.0/7.0 36 | vec3 ns = n_ * D.wyz - D.xzx; 37 | 38 | vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) 39 | 40 | vec4 x_ = floor(j * ns.z); 41 | vec4 y_ = floor(j - 7.0 * x_); // mod(j,N) 42 | 43 | vec4 x = x_ * ns.x + ns.yyyy; 44 | vec4 y = y_ * ns.x + ns.yyyy; 45 | vec4 h = 1.0 - abs(x) - abs(y); 46 | 47 | vec4 b0 = vec4(x.xy, y.xy); 48 | vec4 b1 = vec4(x.zw, y.zw); 49 | 50 | // vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; 51 | // vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; 52 | vec4 s0 = floor(b0) * 2.0 + 1.0; 53 | vec4 s1 = floor(b1) * 2.0 + 1.0; 54 | vec4 sh = -step(h, vec4(0.0)); 55 | 56 | vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; 57 | vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; 58 | 59 | vec3 p0 = vec3(a0.xy, h.x); 60 | vec3 p1 = vec3(a0.zw, h.y); 61 | vec3 p2 = vec3(a1.xy, h.z); 62 | vec3 p3 = vec3(a1.zw, h.w); 63 | 64 | // Normalise gradients 65 | vec4 norm = 66 | taylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3))); 67 | p0 *= norm.x; 68 | p1 *= norm.y; 69 | p2 *= norm.z; 70 | p3 *= norm.w; 71 | 72 | // Mix final noise value 73 | vec4 m = 74 | max(0.5 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0); 75 | m = m * m; 76 | return 105.0 * 77 | dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); 78 | } 79 | 80 | float noise2D(vec2 uv) { 81 | uvec2 pos = uvec2(floor(uv * 1000.)); 82 | return float((pos.x * 68657387u ^ pos.y * 361524851u + pos.x) % 890129u) * 83 | (1.0 / 890128.0); 84 | } 85 | 86 | float roundRectSDF(vec2 center, vec2 size, float radius) { 87 | return length(max(abs(center) - size + radius, 0.)) - radius; 88 | } 89 | 90 | void mainImage(out vec4 fragColor, in vec2 fragCoord) { 91 | vec2 uv = fragCoord / iResolution.xy, sd = vec2(2.), sdh = vec2(1.); 92 | vec4 ghosttyCol = texture(iChannel0, uv); 93 | float ratio = iResolution.y / iResolution.x, 94 | fw = max(fwidth(uv.x), fwidth(uv.y)); 95 | 96 | vec2 puv = floor(uv * vec2(60., 60. * ratio)) / 60.; 97 | puv += 98 | (smoothstep(0., 0.7, noise2D(puv)) - 0.5) * 0.05 - vec2(0., iTime * 0.08); 99 | 100 | uv = fract(vec2(uv.x, uv.y * ratio) * 10.); 101 | float d = roundRectSDF((sd + 0.01) * (uv - .5), sdh, 0.075), 102 | d2 = roundRectSDF((sd + 0.065) * (fract(uv * 6.) - .5), sdh, 0.2), 103 | noiseTime = iTime * 0.03, noise = snoise(vec3(puv, noiseTime)); 104 | 105 | noise += snoise(vec3(puv * 1.1, noiseTime + 0.5)) + .1; 106 | noise += snoise(vec3(puv * 2., noiseTime + 0.8)); 107 | noise = pow(noise, 2.); 108 | 109 | vec3 col1 = vec3(0.), col2 = vec3(0.), col3 = vec3(0.07898), 110 | col4 = vec3(0.089184), 111 | fcol = mix(mix(mix(col1, col3, smoothstep(0.0, 0.3, noise)), col2, 112 | smoothstep(0.0, 0.5, noise)), 113 | col4, smoothstep(0.0, 1.0, noise)); 114 | 115 | fragColor = vec4( 116 | ghosttyCol.rgb + 117 | mix(col4, fcol, smoothstep(fw, -fw, d) * smoothstep(fw, -fw, d2)), 118 | ghosttyCol.a); 119 | } 120 | -------------------------------------------------------------------------------- /negative.glsl: -------------------------------------------------------------------------------- 1 | 2 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 3 | { 4 | vec2 uv = fragCoord/iResolution.xy; 5 | vec4 color = texture(iChannel0, uv); 6 | fragColor = vec4(1.0 - color.x, 1.0 - color.y, 1.0 - color.z, color.w); 7 | } 8 | 9 | -------------------------------------------------------------------------------- /retro-terminal.glsl: -------------------------------------------------------------------------------- 1 | // Original shader collected from: https://www.shadertoy.com/view/WsVSzV 2 | // Licensed under Shadertoy's default since the original creator didn't provide any license. (CC BY NC SA 3.0) 3 | // Slight modifications were made to give a green-ish effect. 4 | 5 | float warp = 0.25; // simulate curvature of CRT monitor 6 | float scan = 0.50; // simulate darkness between scanlines 7 | 8 | void mainImage(out vec4 fragColor, in vec2 fragCoord) 9 | { 10 | // squared distance from center 11 | vec2 uv = fragCoord / iResolution.xy; 12 | vec2 dc = abs(0.5 - uv); 13 | dc *= dc; 14 | 15 | // warp the fragment coordinates 16 | uv.x -= 0.5; uv.x *= 1.0 + (dc.y * (0.3 * warp)); uv.x += 0.5; 17 | uv.y -= 0.5; uv.y *= 1.0 + (dc.x * (0.4 * warp)); uv.y += 0.5; 18 | 19 | // sample inside boundaries, otherwise set to black 20 | if (uv.y > 1.0 || uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0) 21 | fragColor = vec4(0.0, 0.0, 0.0, 1.0); 22 | else 23 | { 24 | // determine if we are drawing in a scanline 25 | float apply = abs(sin(fragCoord.y) * 0.5 * scan); 26 | 27 | // sample the texture and apply a teal tint 28 | vec3 color = texture(iChannel0, uv).rgb; 29 | vec3 tealTint = vec3(0.0, 0.8, 0.6); // teal color (slightly more green than blue) 30 | 31 | // mix the sampled color with the teal tint based on scanline intensity 32 | fragColor = vec4(mix(color * tealTint, vec3(0.0), apply), 1.0); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /sin-interference.glsl: -------------------------------------------------------------------------------- 1 | // Based on https://www.shadertoy.com/view/ms3cWn 2 | float map(float value, float min1, float max1, float min2, float max2) { 3 | return min2 + (value - min1) * (max2 - min2) / (max1 - min1); 4 | } 5 | 6 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 7 | { 8 | vec2 uv = fragCoord / iResolution.xy; 9 | float d = length(uv - 0.5) * 2.0; 10 | float t = d * d * 25.0 - iTime * 2.0; 11 | vec3 col = 0.5 + 0.5 * cos(t / 20.0 + uv.xyx + vec3(0.0,2.0,4.0)); 12 | 13 | vec2 center = iResolution.xy * 0.5; 14 | float distCentre = distance(fragCoord.xy, center); 15 | float dCSin = sin(distCentre * 0.05); 16 | 17 | vec2 anim = vec2(map(sin(iTime),-1.0,1.0,0.0,iResolution.x),map(sin(iTime*1.25),-1.0,1.0,0.0,iResolution.y)); 18 | float distMouse = distance(fragCoord.xy, anim); 19 | float dMSin = sin(distMouse * 0.05); 20 | 21 | float greycol = (((dMSin * dCSin) + 1.0) * 0.5); 22 | greycol = greycol * map(d, 0.0, 1.4142135623730951, 0.5, 0.0); 23 | 24 | vec4 terminalColor = texture(iChannel0, uv); 25 | vec3 blendedColor = mix(terminalColor.rgb, vec3(greycol * col.x, greycol * col.y, greycol * col.z), 0.25); 26 | 27 | fragColor = vec4(blendedColor, terminalColor.a); 28 | } 29 | -------------------------------------------------------------------------------- /smoke-and-ghost.glsl: -------------------------------------------------------------------------------- 1 | // Settings for detection 2 | #define TARGET_COLOR vec3(0.0, 0.0, 0.0) // RGB target pixels to transform 3 | #define REPLACE_COLOR vec3(0.0, 0.0, 0.0) // Color to replace target pixels 4 | #define COLOR_TOLERANCE 0.001 // Color matching tolerance 5 | 6 | // Smoke effect settings 7 | #define SMOKE_COLOR vec3(1., 1., 1.0) // Base color of smoke 8 | #define SMOKE_RADIUS 0.011 // How far the smoke spreads 9 | #define SMOKE_SPEED 0.5 // Speed of smoke movement 10 | #define SMOKE_SCALE 25.0 // Scale of smoke detail 11 | #define SMOKE_INTENSITY 0.2 // Intensity of the smoke effect 12 | #define SMOKE_RISE_HEIGHT 0.14 // How high the smoke rises 13 | #define ALPHA_MAX 0.5 // Maximum opacity for smoke 14 | #define VERTICAL_BIAS 1.0 15 | 16 | // Ghost face settings 17 | #define FACE_COUNT 1 // Number of ghost faces 18 | #define FACE_SCALE vec2(0.03, 0.05) // Size of faces, can be wider/elongated 19 | #define FACE_DURATION 1.2 // How long faces last, can be wider/elongated 20 | #define FACE_TRANSITION 1.5 // Face fade in/out duration 21 | #define FACE_COLOR vec3(0.0, 0.0, 0.0) 22 | #define GHOST_BG_COLOR vec3(1.0, 1.0, 1.0) 23 | #define GHOST_BG_SCALE vec2(0.03, 0.06) 24 | 25 | float random(vec2 st) { 26 | return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123); 27 | } 28 | 29 | float random1(float n) { 30 | return fract(sin(n) * 43758.5453123); 31 | } 32 | 33 | vec2 random2(float n) { 34 | return vec2( 35 | random1(n), 36 | random1(n + 1234.5678) 37 | ); 38 | } 39 | 40 | float noise(vec2 st) { 41 | vec2 i = floor(st); 42 | vec2 f = fract(st); 43 | 44 | float a = random(i); 45 | float b = random(i + vec2(1.0, 0.0)); 46 | float c = random(i + vec2(0.0, 1.0)); 47 | float d = random(i + vec2(1.0, 1.0)); 48 | 49 | vec2 u = f * f * (3.0 - 2.0 * f); 50 | return mix(a, b, u.x) + (c - a)* u.y * (1.0 - u.x) + (d - b) * u.x * u.y; 51 | } 52 | 53 | // Modified elongated ellipse for more cartoon-like shapes 54 | float cartoonEllipse(vec2 uv, vec2 center, vec2 scale) { 55 | vec2 d = (uv - center) / scale; 56 | float len = length(d); 57 | // Add cartoon-like falloff 58 | return smoothstep(1.0, 0.8, len); 59 | } 60 | 61 | // Function to create ghost background shape 62 | float ghostBackground(vec2 uv, vec2 center) { 63 | vec2 d = (uv - center) / GHOST_BG_SCALE; 64 | float baseShape = length(d * vec2(1.0, 0.8)); // Slightly oval 65 | 66 | // Add wavy bottom 67 | float wave = sin(d.x * 6.28 + iTime) * 0.2; 68 | float bottomWave = smoothstep(0.0, -0.5, d.y + wave); 69 | 70 | return smoothstep(1.0, 0.8, baseShape) + bottomWave; 71 | } 72 | 73 | float ghostFace(vec2 uv, vec2 center, float time, float seed) { 74 | vec2 faceUV = (uv - center) / FACE_SCALE; 75 | 76 | float eyeSize = 0.25 + random1(seed) * 0.05; 77 | float eyeSpacing = 0.35; 78 | vec2 leftEyePos = vec2(-eyeSpacing, 0.2); 79 | vec2 rightEyePos = vec2(eyeSpacing, 0.2); 80 | 81 | float leftEye = cartoonEllipse(faceUV, leftEyePos, vec2(eyeSize)); 82 | float rightEye = cartoonEllipse(faceUV, rightEyePos, vec2(eyeSize)); 83 | 84 | // Add simple eye highlights 85 | float leftHighlight = cartoonEllipse(faceUV, leftEyePos + vec2(0.1, 0.1), vec2(eyeSize * 0.3)); 86 | float rightHighlight = cartoonEllipse(faceUV, rightEyePos + vec2(0.1, 0.1), vec2(eyeSize * 0.3)); 87 | 88 | vec2 mouthUV = faceUV - vec2(0.0, -0.9); 89 | float mouthWidth = 0.5 + random1(seed + 3.0) * 0.1; 90 | float mouthHeight = 0.8 + random1(seed + 7.0) * 0.1; 91 | 92 | float mouth = cartoonEllipse(mouthUV, vec2(0.0), vec2(mouthWidth, mouthHeight)); 93 | 94 | // Combine features 95 | float face = max(max(leftEye, rightEye), mouth); 96 | face = max(face, max(leftHighlight, rightHighlight)); 97 | 98 | // Add border falloff 99 | face *= smoothstep(1.2, 0.8, length(faceUV)); 100 | 101 | return face; 102 | } 103 | 104 | float calculateSmoke(vec2 uv, vec2 sourcePos) { 105 | float verticalDisp = (uv.y - sourcePos.y) * VERTICAL_BIAS; 106 | vec2 smokeUV = uv * SMOKE_SCALE; 107 | smokeUV.y -= iTime * SMOKE_SPEED * (1.0 + verticalDisp); 108 | smokeUV.x += sin(iTime * 0.5 + uv.y * 4.0) * 0.1; 109 | 110 | float n = noise(smokeUV) * 0.5 + 0.5; 111 | n += noise(smokeUV * 2.0 + iTime * 0.1) * 0.25; 112 | 113 | float verticalFalloff = 1.0 - smoothstep(0.0, SMOKE_RISE_HEIGHT, verticalDisp); 114 | return n * verticalFalloff; 115 | } 116 | 117 | float isTargetPixel(vec2 uv) { 118 | vec4 color = texture(iChannel0, uv); 119 | return float(all(lessThan(abs(color.rgb - TARGET_COLOR), vec3(COLOR_TOLERANCE)))); 120 | } 121 | 122 | void mainImage(out vec4 fragColor, in vec2 fragCoord) { 123 | vec2 uv = fragCoord/iResolution.xy; 124 | vec4 originalColor = texture(iChannel0, uv); 125 | 126 | // Calculate smoke effect 127 | float smokeAccum = 0.0; 128 | float targetInfluence = 0.0; 129 | 130 | float stepSize = SMOKE_RADIUS / 4.0; 131 | for (float x = -SMOKE_RADIUS; x <= SMOKE_RADIUS; x += stepSize) { 132 | for (float y = -SMOKE_RADIUS; y <= 0.0; y += stepSize) { 133 | vec2 offset = vec2(x, y); 134 | vec2 sampleUV = uv + offset; 135 | 136 | if (sampleUV.x >= 0.0 && sampleUV.x <= 1.0 && 137 | sampleUV.y >= 0.0 && sampleUV.y <= 1.0) { 138 | float isTarget = isTargetPixel(sampleUV); 139 | if (isTarget > 0.0) { 140 | float dist = length(offset); 141 | float falloff = 1.0 - smoothstep(0.0, SMOKE_RADIUS, dist); 142 | float smoke = calculateSmoke(uv, sampleUV); 143 | smokeAccum += smoke * falloff; 144 | targetInfluence += falloff; 145 | } 146 | } 147 | } 148 | } 149 | 150 | smokeAccum /= max(targetInfluence, 1.0); 151 | targetInfluence = smoothstep(0.0, 1.0, targetInfluence); 152 | float smokePresence = smokeAccum * targetInfluence; 153 | 154 | // Calculate ghost faces with backgrounds 155 | float faceAccum = 0.0; 156 | float bgAccum = 0.0; 157 | float timeBlock = floor(iTime / FACE_DURATION); 158 | 159 | if (smokePresence > 0.2) { 160 | for (int i = 0; i < FACE_COUNT; i++) { 161 | vec2 facePos = random2(timeBlock + float(i) * 1234.5); 162 | facePos = facePos * 0.8 + 0.1; 163 | 164 | float faceTime = mod(iTime, FACE_DURATION); 165 | float fadeFactor = smoothstep(0.0, FACE_TRANSITION, faceTime) * 166 | (1.0 - smoothstep(FACE_DURATION - FACE_TRANSITION, FACE_DURATION, faceTime)); 167 | 168 | // Add ghost background 169 | float ghostBg = ghostBackground(uv, facePos) * fadeFactor; 170 | bgAccum = max(bgAccum, ghostBg); 171 | 172 | // Add face features 173 | float face = ghostFace(uv, facePos, iTime, timeBlock + float(i) * 100.0) * fadeFactor; 174 | faceAccum = max(faceAccum, face); 175 | } 176 | 177 | bgAccum *= smoothstep(0.2, 0.4, smokePresence); 178 | faceAccum *= smoothstep(0.2, 0.4, smokePresence); 179 | } 180 | 181 | // Combine all elements 182 | bool isTarget = all(lessThan(abs(originalColor.rgb - TARGET_COLOR), vec3(COLOR_TOLERANCE))); 183 | vec3 baseColor = isTarget ? REPLACE_COLOR : originalColor.rgb; 184 | 185 | // Layer the effects: base -> smoke -> ghost background -> face features 186 | vec3 smokeEffect = mix(baseColor, SMOKE_COLOR, smokeAccum * SMOKE_INTENSITY * targetInfluence * (1.0 - faceAccum)); 187 | vec3 withBackground = mix(smokeEffect, GHOST_BG_COLOR, bgAccum * 0.7); 188 | vec3 finalColor = mix(withBackground, FACE_COLOR, faceAccum); 189 | 190 | float alpha = mix(originalColor.a, ALPHA_MAX, max(smokePresence, max(bgAccum, faceAccum) * smokePresence)); 191 | 192 | fragColor = vec4(finalColor, alpha); 193 | } 194 | -------------------------------------------------------------------------------- /sparks-from-fire.glsl: -------------------------------------------------------------------------------- 1 | // adapted by Alex Sherwin for Ghstty from https://www.shadertoy.com/view/wl2Gzc 2 | 3 | //Shader License: CC BY 3.0 4 | //Author: Jan Mróz (jaszunio15) 5 | 6 | #define SMOKE_INTENSITY_MULTIPLIER 0.9 7 | #define PARTICLES_ALPHA_MOD 0.9 8 | #define SMOKE_ALPHA_MOD 0.5 9 | #define LAYERS_COUNT 8 10 | 11 | #define BLACK_BLEND_THRESHOLD .4 12 | 13 | #define VEC3_1 (vec3(1.0)) 14 | 15 | #define PI 3.1415927 16 | #define TWO_PI 6.283185 17 | 18 | #define ANIMATION_SPEED 1.0 19 | #define MOVEMENT_SPEED .33 20 | #define MOVEMENT_DIRECTION vec2(0.7, 1.0) 21 | 22 | #define PARTICLE_SIZE 0.0025 23 | 24 | #define PARTICLE_SCALE (vec2(0.5, 1.6)) 25 | #define PARTICLE_SCALE_VAR (vec2(0.25, 0.2)) 26 | 27 | #define PARTICLE_BLOOM_SCALE (vec2(0.5, 0.8)) 28 | #define PARTICLE_BLOOM_SCALE_VAR (vec2(0.3, 0.1)) 29 | 30 | #define SPARK_COLOR vec3(1.0, 0.4, 0.05) * 1.5 31 | #define BLOOM_COLOR vec3(1.0, 0.4, 0.05) * 0.8 32 | #define SMOKE_COLOR vec3(1.0, 0.43, 0.1) * 0.8 33 | 34 | #define SIZE_MOD 1.05 35 | 36 | 37 | float hash1_2(in vec2 x) 38 | { 39 | return fract(sin(dot(x, vec2(52.127, 61.2871))) * 521.582); 40 | } 41 | 42 | vec2 hash2_2(in vec2 x) 43 | { 44 | return fract(sin(x * mat2x2(20.52, 24.1994, 70.291, 80.171)) * 492.194); 45 | } 46 | 47 | //Simple interpolated noise 48 | vec2 noise2_2(vec2 uv) 49 | { 50 | //vec2 f = fract(uv); 51 | vec2 f = smoothstep(0.0, 1.0, fract(uv)); 52 | 53 | vec2 uv00 = floor(uv); 54 | vec2 uv01 = uv00 + vec2(0,1); 55 | vec2 uv10 = uv00 + vec2(1,0); 56 | vec2 uv11 = uv00 + 1.0; 57 | vec2 v00 = hash2_2(uv00); 58 | vec2 v01 = hash2_2(uv01); 59 | vec2 v10 = hash2_2(uv10); 60 | vec2 v11 = hash2_2(uv11); 61 | 62 | vec2 v0 = mix(v00, v01, f.y); 63 | vec2 v1 = mix(v10, v11, f.y); 64 | vec2 v = mix(v0, v1, f.x); 65 | 66 | return v; 67 | } 68 | 69 | //Simple interpolated noise 70 | float noise1_2(in vec2 uv) 71 | { 72 | // vec2 f = fract(uv); 73 | vec2 f = smoothstep(0.0, 1.0, fract(uv)); 74 | 75 | vec2 uv00 = floor(uv); 76 | vec2 uv01 = uv00 + vec2(0,1); 77 | vec2 uv10 = uv00 + vec2(1,0); 78 | vec2 uv11 = uv00 + 1.0; 79 | 80 | float v00 = hash1_2(uv00); 81 | float v01 = hash1_2(uv01); 82 | float v10 = hash1_2(uv10); 83 | float v11 = hash1_2(uv11); 84 | 85 | float v0 = mix(v00, v01, f.y); 86 | float v1 = mix(v10, v11, f.y); 87 | float v = mix(v0, v1, f.x); 88 | 89 | return v; 90 | } 91 | 92 | 93 | float layeredNoise1_2(in vec2 uv, in float sizeMod, in float alphaMod, in int layers, in float animation) 94 | { 95 | float noise = 0.0; 96 | float alpha = 1.0; 97 | float size = 1.0; 98 | vec2 offset; 99 | for (int i = 0; i < layers; i++) 100 | { 101 | offset += hash2_2(vec2(alpha, size)) * 10.0; 102 | 103 | //Adding noise with movement 104 | noise += noise1_2(uv * size + iTime * animation * 8.0 * MOVEMENT_DIRECTION * MOVEMENT_SPEED + offset) * alpha; 105 | alpha *= alphaMod; 106 | size *= sizeMod; 107 | } 108 | 109 | noise *= (1.0 - alphaMod)/(1.0 - pow(alphaMod, float(layers))); 110 | return noise; 111 | } 112 | 113 | //Rotates point around 0,0 114 | vec2 rotate(in vec2 point, in float deg) 115 | { 116 | float s = sin(deg); 117 | float c = cos(deg); 118 | return mat2x2(s, c, -c, s) * point; 119 | } 120 | 121 | //Cell center from point on the grid 122 | vec2 voronoiPointFromRoot(in vec2 root, in float deg) 123 | { 124 | vec2 point = hash2_2(root) - 0.5; 125 | float s = sin(deg); 126 | float c = cos(deg); 127 | point = mat2x2(s, c, -c, s) * point * 0.66; 128 | point += root + 0.5; 129 | return point; 130 | } 131 | 132 | //Voronoi cell point rotation degrees 133 | float degFromRootUV(in vec2 uv) 134 | { 135 | return iTime * ANIMATION_SPEED * (hash1_2(uv) - 0.5) * 2.0; 136 | } 137 | 138 | vec2 randomAround2_2(in vec2 point, in vec2 range, in vec2 uv) 139 | { 140 | return point + (hash2_2(uv) - 0.5) * range; 141 | } 142 | 143 | 144 | vec3 fireParticles(in vec2 uv, in vec2 originalUV) 145 | { 146 | vec3 particles = vec3(0.0); 147 | vec2 rootUV = floor(uv); 148 | float deg = degFromRootUV(rootUV); 149 | vec2 pointUV = voronoiPointFromRoot(rootUV, deg); 150 | float dist = 2.0; 151 | float distBloom = 0.0; 152 | 153 | //UV manipulation for the faster particle movement 154 | vec2 tempUV = uv + (noise2_2(uv * 2.0) - 0.5) * 0.1; 155 | tempUV += -(noise2_2(uv * 3.0 + iTime) - 0.5) * 0.07; 156 | 157 | //Sparks sdf 158 | dist = length(rotate(tempUV - pointUV, 0.7) * randomAround2_2(PARTICLE_SCALE, PARTICLE_SCALE_VAR, rootUV)); 159 | 160 | //Bloom sdf 161 | distBloom = length(rotate(tempUV - pointUV, 0.7) * randomAround2_2(PARTICLE_BLOOM_SCALE, PARTICLE_BLOOM_SCALE_VAR, rootUV)); 162 | 163 | //Add sparks 164 | particles += (1.0 - smoothstep(PARTICLE_SIZE * 0.6, PARTICLE_SIZE * 3.0, dist)) * SPARK_COLOR; 165 | 166 | //Add bloom 167 | particles += pow((1.0 - smoothstep(0.0, PARTICLE_SIZE * 6.0, distBloom)) * 1.0, 3.0) * BLOOM_COLOR; 168 | 169 | //Upper disappear curve randomization 170 | float border = (hash1_2(rootUV) - 0.5) * 2.0; 171 | float disappear = 1.0 - smoothstep(border, border + 0.5, originalUV.y); 172 | 173 | //Lower appear curve randomization 174 | border = (hash1_2(rootUV + 0.214) - 1.8) * 0.7; 175 | float appear = smoothstep(border, border + 0.4, originalUV.y); 176 | 177 | return particles * disappear * appear; 178 | } 179 | 180 | 181 | //Layering particles to imitate 3D view 182 | vec3 layeredParticles(in vec2 uv, in float sizeMod, in float alphaMod, in int layers, in float smoke) 183 | { 184 | vec3 particles = vec3(0); 185 | float size = 1.0; 186 | // float alpha = 1.0; 187 | float alpha = 1.0; 188 | vec2 offset = vec2(0.0); 189 | vec2 noiseOffset; 190 | vec2 bokehUV; 191 | 192 | for (int i = 0; i < layers; i++) 193 | { 194 | //Particle noise movement 195 | noiseOffset = (noise2_2(uv * size * 2.0 + 0.5) - 0.5) * 0.15; 196 | 197 | //UV with applied movement 198 | bokehUV = (uv * size + iTime * MOVEMENT_DIRECTION * MOVEMENT_SPEED) + offset + noiseOffset; 199 | 200 | //Adding particles if there is more smoke, remove smaller particles 201 | particles += fireParticles(bokehUV, uv) * alpha * (1.0 - smoothstep(0.0, 1.0, smoke) * (float(i) / float(layers))); 202 | 203 | //Moving uv origin to avoid generating the same particles 204 | offset += hash2_2(vec2(alpha, alpha)) * 10.0; 205 | 206 | alpha *= alphaMod; 207 | size *= sizeMod; 208 | } 209 | 210 | return particles; 211 | } 212 | 213 | void mainImage(out vec4 fragColor, in vec2 fragCoord) { 214 | vec2 uv = (2.0 * fragCoord - iResolution.xy) / iResolution.x; 215 | 216 | // float vignette = 1.1 - smoothstep(0.4, 1.4, length(uv + vec2(0.0, 0.3))); 217 | float vignette = 1.3 - smoothstep(0.4, 1.4, length(uv + vec2(0.0, 0.3))); 218 | 219 | uv *= 2.5; 220 | 221 | float smokeIntensity = layeredNoise1_2(uv * 10.0 + iTime * 4.0 * MOVEMENT_DIRECTION * MOVEMENT_SPEED, 1.7, 0.7, 6, 0.2); 222 | smokeIntensity *= pow(smoothstep(-1.0, 1.6, uv.y), 2.0); 223 | vec3 smoke = smokeIntensity * SMOKE_COLOR * vignette * SMOKE_INTENSITY_MULTIPLIER * SMOKE_ALPHA_MOD; 224 | 225 | //Cutting holes in smoke 226 | smoke *= pow(layeredNoise1_2(uv * 4.0 + iTime * 0.5 * MOVEMENT_DIRECTION * MOVEMENT_SPEED, 1.8, 0.5, 3, 0.2), 2.0) * 1.5; 227 | 228 | vec3 particles = layeredParticles(uv, SIZE_MOD, PARTICLES_ALPHA_MOD, LAYERS_COUNT, smokeIntensity); 229 | 230 | vec3 col = particles + smoke + SMOKE_COLOR * 0.02; 231 | col *= vignette; 232 | 233 | col = smoothstep(-0.08, 1.0, col); 234 | 235 | vec2 termUV = fragCoord.xy / iResolution.xy; 236 | vec4 terminalColor = texture(iChannel0, termUV); 237 | 238 | float alpha = step(length(terminalColor.rgb), BLACK_BLEND_THRESHOLD); 239 | vec3 blendedColor = mix(terminalColor.rgb, col, alpha); 240 | 241 | fragColor = vec4(blendedColor, terminalColor.a); 242 | } 243 | -------------------------------------------------------------------------------- /spotlight.glsl: -------------------------------------------------------------------------------- 1 | // Created by Paul Robello 2 | 3 | 4 | // Smooth oscillating function that varies over time 5 | float smoothOscillation(float t, float frequency, float phase) { 6 | return sin(t * frequency + phase); 7 | } 8 | 9 | void mainImage(out vec4 fragColor, in vec2 fragCoord) { 10 | // Resolution and UV coordinates 11 | vec2 uv = fragCoord.xy / iResolution.xy; 12 | 13 | // Used to fix distortion when calculating distance to circle center 14 | vec2 ratio = vec2(iResolution.x / iResolution.y, 1.0); 15 | 16 | // Get the texture from iChannel0 17 | vec4 texColor = texture(iChannel0, uv); 18 | 19 | // Spotlight center moving based on a smooth random pattern 20 | float time = iTime * 1.0; // Control speed of motion 21 | vec2 spotlightCenter = vec2( 22 | 0.5 + 0.4 * smoothOscillation(time, 1.0, 0.0), // Smooth X motion 23 | 0.5 + 0.4 * smoothOscillation(time, 1.3, 3.14159) // Smooth Y motion with different frequency and phase 24 | ); 25 | 26 | // Distance from the spotlight center 27 | float distanceToCenter = distance(uv * ratio, spotlightCenter); 28 | 29 | // Spotlight intensity based on distance 30 | float spotlightRadius = 0.25; // Spotlight radius 31 | float softness = 20.0; // Spotlight edge softness. Higher values have sharper edge 32 | float spotlightIntensity = smoothstep(spotlightRadius, spotlightRadius - (1.0 / softness), distanceToCenter); 33 | 34 | // Ambient light level 35 | float ambientLight = 0.5; // Controls the minimum brightness across the texture 36 | 37 | // Combine the spotlight effect with the texture 38 | vec3 spotlightEffect = texColor.rgb * mix(vec3(ambientLight), vec3(1.0), spotlightIntensity); 39 | 40 | // Final color output 41 | fragColor = vec4(spotlightEffect, texColor.a); 42 | } -------------------------------------------------------------------------------- /starfield-colors.glsl: -------------------------------------------------------------------------------- 1 | // transparent background 2 | const bool transparent = false; 3 | 4 | // terminal contents luminance threshold to be considered background (0.0 to 1.0) 5 | const float threshold = 0.15; 6 | 7 | // divisions of grid 8 | const float repeats = 30.; 9 | 10 | // number of layers 11 | const float layers = 21.; 12 | 13 | // star colours 14 | const vec3 blue = vec3(51., 64., 195.) / 255.; 15 | const vec3 cyan = vec3(117., 250., 254.) / 255.; 16 | const vec3 white = vec3(255., 255., 255.) / 255.; 17 | const vec3 yellow = vec3(251., 245., 44.) / 255.; 18 | const vec3 red = vec3(247, 2., 20.) / 255.; 19 | 20 | float luminance(vec3 color) { 21 | return dot(color, vec3(0.2126, 0.7152, 0.0722)); 22 | } 23 | 24 | // spectrum function 25 | vec3 spectrum(vec2 pos) { 26 | pos.x *= 4.; 27 | vec3 outCol = vec3(0); 28 | if (pos.x > 0.) { 29 | outCol = mix(blue, cyan, fract(pos.x)); 30 | } 31 | if (pos.x > 1.) { 32 | outCol = mix(cyan, white, fract(pos.x)); 33 | } 34 | if (pos.x > 2.) { 35 | outCol = mix(white, yellow, fract(pos.x)); 36 | } 37 | if (pos.x > 3.) { 38 | outCol = mix(yellow, red, fract(pos.x)); 39 | } 40 | 41 | return 1. - (pos.y * (1. - outCol)); 42 | } 43 | 44 | float N21(vec2 p) { 45 | p = fract(p * vec2(233.34, 851.73)); 46 | p += dot(p, p + 23.45); 47 | return fract(p.x * p.y); 48 | } 49 | 50 | vec2 N22(vec2 p) { 51 | float n = N21(p); 52 | return vec2(n, N21(p + n)); 53 | } 54 | 55 | mat2 scale(vec2 _scale) { 56 | return mat2(_scale.x, 0.0, 57 | 0.0, _scale.y); 58 | } 59 | 60 | // 2D Noise based on Morgan McGuire 61 | float noise(in vec2 st) { 62 | vec2 i = floor(st); 63 | vec2 f = fract(st); 64 | 65 | // Four corners in 2D of a tile 66 | float a = N21(i); 67 | float b = N21(i + vec2(1.0, 0.0)); 68 | float c = N21(i + vec2(0.0, 1.0)); 69 | float d = N21(i + vec2(1.0, 1.0)); 70 | 71 | // Smooth Interpolation 72 | vec2 u = f * f * (3.0 - 2.0 * f); // Cubic Hermite Curve 73 | 74 | // Mix 4 corners percentages 75 | return mix(a, b, u.x) + 76 | (c - a) * u.y * (1.0 - u.x) + 77 | (d - b) * u.x * u.y; 78 | } 79 | 80 | float perlin2(vec2 uv, int octaves, float pscale) { 81 | float col = 1.; 82 | float initScale = 4.; 83 | for (int l; l < octaves; l++) { 84 | float val = noise(uv * initScale); 85 | if (col <= 0.01) { 86 | col = 0.; 87 | break; 88 | } 89 | val -= 0.01; 90 | val *= 0.5; 91 | col *= val; 92 | initScale *= pscale; 93 | } 94 | return col; 95 | } 96 | 97 | vec3 stars(vec2 uv, float offset) { 98 | float timeScale = -(iTime + offset) / layers; 99 | float trans = fract(timeScale); 100 | float newRnd = floor(timeScale); 101 | vec3 col = vec3(0.); 102 | 103 | // Translate uv then scale for center 104 | uv -= vec2(0.5); 105 | uv = scale(vec2(trans)) * uv; 106 | uv += vec2(0.5); 107 | 108 | // Create square aspect ratio 109 | uv.x *= iResolution.x / iResolution.y; 110 | 111 | // Create boxes 112 | uv *= repeats; 113 | 114 | // Get position 115 | vec2 ipos = floor(uv); 116 | 117 | // Return uv as 0 to 1 118 | uv = fract(uv); 119 | 120 | // Calculate random xy and size 121 | vec2 rndXY = N22(newRnd + ipos * (offset + 1.)) * 0.9 + 0.05; 122 | float rndSize = N21(ipos) * 100. + 200.; 123 | 124 | vec2 j = (rndXY - uv) * rndSize; 125 | float sparkle = 1. / dot(j, j); 126 | 127 | // Set stars to be pure white 128 | col += spectrum(fract(rndXY * newRnd * ipos)) * vec3(sparkle); 129 | 130 | col *= smoothstep(1., 0.8, trans); 131 | return col; // Return pure white stars only 132 | } 133 | 134 | void mainImage(out vec4 fragColor, in vec2 fragCoord) 135 | { 136 | // Normalized pixel coordinates (from 0 to 1) 137 | vec2 uv = fragCoord / iResolution.xy; 138 | 139 | vec3 col = vec3(0.); 140 | 141 | for (float i = 0.; i < layers; i++) { 142 | col += stars(uv, i); 143 | } 144 | 145 | // Sample the terminal screen texture including alpha channel 146 | vec4 terminalColor = texture(iChannel0, uv); 147 | 148 | if (transparent) { 149 | col += terminalColor.rgb; 150 | } 151 | 152 | // Make a mask that is 1.0 where the terminal content is not black 153 | float mask = 1 - step(threshold, luminance(terminalColor.rgb)); 154 | vec3 blendedColor = mix(terminalColor.rgb, col, mask); 155 | 156 | // Apply terminal's alpha to control overall opacity 157 | fragColor = vec4(blendedColor, terminalColor.a); 158 | } 159 | -------------------------------------------------------------------------------- /starfield.glsl: -------------------------------------------------------------------------------- 1 | // transparent background 2 | const bool transparent = false; 3 | 4 | // terminal contents luminance threshold to be considered background (0.0 to 1.0) 5 | const float threshold = 0.15; 6 | 7 | // divisions of grid 8 | const float repeats = 30.; 9 | 10 | // number of layers 11 | const float layers = 21.; 12 | 13 | // star colors 14 | const vec3 white = vec3(1.0); // Set star color to pure white 15 | 16 | float luminance(vec3 color) { 17 | return dot(color, vec3(0.2126, 0.7152, 0.0722)); 18 | } 19 | 20 | float N21(vec2 p) { 21 | p = fract(p * vec2(233.34, 851.73)); 22 | p += dot(p, p + 23.45); 23 | return fract(p.x * p.y); 24 | } 25 | 26 | vec2 N22(vec2 p) { 27 | float n = N21(p); 28 | return vec2(n, N21(p + n)); 29 | } 30 | 31 | mat2 scale(vec2 _scale) { 32 | return mat2(_scale.x, 0.0, 33 | 0.0, _scale.y); 34 | } 35 | 36 | // 2D Noise based on Morgan McGuire 37 | float noise(in vec2 st) { 38 | vec2 i = floor(st); 39 | vec2 f = fract(st); 40 | 41 | // Four corners in 2D of a tile 42 | float a = N21(i); 43 | float b = N21(i + vec2(1.0, 0.0)); 44 | float c = N21(i + vec2(0.0, 1.0)); 45 | float d = N21(i + vec2(1.0, 1.0)); 46 | 47 | // Smooth Interpolation 48 | vec2 u = f * f * (3.0 - 2.0 * f); // Cubic Hermite Curve 49 | 50 | // Mix 4 corners percentages 51 | return mix(a, b, u.x) + 52 | (c - a) * u.y * (1.0 - u.x) + 53 | (d - b) * u.x * u.y; 54 | } 55 | 56 | float perlin2(vec2 uv, int octaves, float pscale) { 57 | float col = 1.; 58 | float initScale = 4.; 59 | for (int l; l < octaves; l++) { 60 | float val = noise(uv * initScale); 61 | if (col <= 0.01) { 62 | col = 0.; 63 | break; 64 | } 65 | val -= 0.01; 66 | val *= 0.5; 67 | col *= val; 68 | initScale *= pscale; 69 | } 70 | return col; 71 | } 72 | 73 | vec3 stars(vec2 uv, float offset) { 74 | float timeScale = -(iTime + offset) / layers; 75 | float trans = fract(timeScale); 76 | float newRnd = floor(timeScale); 77 | vec3 col = vec3(0.); 78 | 79 | // Translate uv then scale for center 80 | uv -= vec2(0.5); 81 | uv = scale(vec2(trans)) * uv; 82 | uv += vec2(0.5); 83 | 84 | // Create square aspect ratio 85 | uv.x *= iResolution.x / iResolution.y; 86 | 87 | // Create boxes 88 | uv *= repeats; 89 | 90 | // Get position 91 | vec2 ipos = floor(uv); 92 | 93 | // Return uv as 0 to 1 94 | uv = fract(uv); 95 | 96 | // Calculate random xy and size 97 | vec2 rndXY = N22(newRnd + ipos * (offset + 1.)) * 0.9 + 0.05; 98 | float rndSize = N21(ipos) * 100. + 200.; 99 | 100 | vec2 j = (rndXY - uv) * rndSize; 101 | float sparkle = 1. / dot(j, j); 102 | 103 | // Set stars to be pure white 104 | col += white * sparkle; 105 | 106 | col *= smoothstep(1., 0.8, trans); 107 | return col; // Return pure white stars only 108 | } 109 | 110 | void mainImage(out vec4 fragColor, in vec2 fragCoord) 111 | { 112 | // Normalized pixel coordinates (from 0 to 1) 113 | vec2 uv = fragCoord / iResolution.xy; 114 | 115 | vec3 col = vec3(0.); 116 | 117 | for (float i = 0.; i < layers; i++) { 118 | col += stars(uv, i); 119 | } 120 | 121 | // Sample the terminal screen texture including alpha channel 122 | vec4 terminalColor = texture(iChannel0, uv); 123 | 124 | if (transparent) { 125 | col += terminalColor.rgb; 126 | } 127 | 128 | // Make a mask that is 1.0 where the terminal content is not black 129 | float mask = 1 - step(threshold, luminance(terminalColor.rgb)); 130 | 131 | vec3 blendedColor = mix(terminalColor.rgb, col, mask); 132 | 133 | // Apply terminal's alpha to control overall opacity 134 | fragColor = vec4(blendedColor, terminalColor.a); 135 | } 136 | -------------------------------------------------------------------------------- /tft.glsl: -------------------------------------------------------------------------------- 1 | /** Size of TFT "pixels" */ 2 | float resolution = 4.0; 3 | 4 | /** Strength of effect */ 5 | float strength = 0.5; 6 | 7 | void _scanline(inout vec3 color, vec2 uv) 8 | { 9 | float scanline = step(1.2, mod(uv.y * iResolution.y, resolution)); 10 | float grille = step(1.2, mod(uv.x * iResolution.x, resolution)); 11 | color *= max(1.0 - strength, scanline * grille); 12 | } 13 | 14 | void mainImage(out vec4 fragColor, in vec2 fragCoord) 15 | { 16 | vec2 uv = fragCoord.xy / iResolution.xy; 17 | vec3 color = texture(iChannel0, uv).rgb; 18 | 19 | _scanline(color, uv); 20 | 21 | fragColor.xyz = color; 22 | fragColor.w = 1.0; 23 | } 24 | -------------------------------------------------------------------------------- /underwater.glsl: -------------------------------------------------------------------------------- 1 | // adapted by Alex Sherwin for Ghostty from https://www.shadertoy.com/view/lljGDt 2 | 3 | #define BLACK_BLEND_THRESHOLD .4 4 | 5 | float hash21(vec2 p) { 6 | p = fract(p * vec2(233.34, 851.73)); 7 | p += dot(p, p + 23.45); 8 | return fract(p.x * p.y); 9 | } 10 | 11 | float rayStrength(vec2 raySource, vec2 rayRefDirection, vec2 coord, float seedA, float seedB, float speed) 12 | { 13 | vec2 sourceToCoord = coord - raySource; 14 | float cosAngle = dot(normalize(sourceToCoord), rayRefDirection); 15 | 16 | // Add subtle dithering based on screen coordinates 17 | float dither = hash21(coord) * 0.015 - 0.0075; 18 | 19 | float ray = clamp( 20 | (0.45 + 0.15 * sin(cosAngle * seedA + iTime * speed)) + 21 | (0.3 + 0.2 * cos(-cosAngle * seedB + iTime * speed)) + dither, 22 | 0.0, 1.0); 23 | 24 | // Smoothstep the distance falloff 25 | float distFade = smoothstep(0.0, iResolution.x, iResolution.x - length(sourceToCoord)); 26 | return ray * mix(0.5, 1.0, distFade); 27 | } 28 | 29 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 30 | { 31 | vec2 uv = fragCoord.xy / iResolution.xy; 32 | 33 | uv.y = 1.0 - uv.y; 34 | vec2 coord = vec2(fragCoord.x, iResolution.y - fragCoord.y); 35 | 36 | // Set the parameters of the sun rays 37 | vec2 rayPos1 = vec2(iResolution.x * 0.7, iResolution.y * 1.1); 38 | vec2 rayRefDir1 = normalize(vec2(1.0, 0.116)); 39 | float raySeedA1 = 36.2214; 40 | float raySeedB1 = 21.11349; 41 | float raySpeed1 = 1.1; 42 | 43 | vec2 rayPos2 = vec2(iResolution.x * 0.8, iResolution.y * 1.2); 44 | vec2 rayRefDir2 = normalize(vec2(1.0, -0.241)); 45 | const float raySeedA2 = 22.39910; 46 | const float raySeedB2 = 18.0234; 47 | const float raySpeed2 = 0.9; 48 | 49 | // Calculate the colour of the sun rays on the current fragment 50 | vec4 rays1 = 51 | vec4(1.0, 1.0, 1.0, 0.0) * 52 | rayStrength(rayPos1, rayRefDir1, coord, raySeedA1, raySeedB1, raySpeed1); 53 | 54 | vec4 rays2 = 55 | vec4(1.0, 1.0, 1.0, 0.0) * 56 | rayStrength(rayPos2, rayRefDir2, coord, raySeedA2, raySeedB2, raySpeed2); 57 | 58 | vec4 col = rays1 * 0.5 + rays2 * 0.4; 59 | 60 | // Attenuate brightness towards the bottom, simulating light-loss due to depth. 61 | // Give the whole thing a blue-green tinge as well. 62 | float brightness = 1.0 - (coord.y / iResolution.y); 63 | col.r *= 0.05 + (brightness * 0.8); 64 | col.g *= 0.15 + (brightness * 0.6); 65 | col.b *= 0.3 + (brightness * 0.5); 66 | 67 | vec2 termUV = fragCoord.xy / iResolution.xy; 68 | vec4 terminalColor = texture(iChannel0, termUV); 69 | 70 | float alpha = step(length(terminalColor.rgb), BLACK_BLEND_THRESHOLD); 71 | vec3 blendedColor = mix(terminalColor.rgb * 1.0, col.rgb * 0.3, alpha); 72 | 73 | fragColor = vec4(blendedColor, terminalColor.a); 74 | } 75 | -------------------------------------------------------------------------------- /water.glsl: -------------------------------------------------------------------------------- 1 | 2 | #define TAU 6.28318530718 3 | #define MAX_ITER 6 4 | 5 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 6 | { 7 | vec3 water_color = vec3(1.0, 1.0, 1.0) * 0.5; 8 | float time = iTime * 0.5+23.0; 9 | vec2 uv = fragCoord.xy / iResolution.xy; 10 | 11 | vec2 p = mod(uv*TAU, TAU)-250.0; 12 | vec2 i = vec2(p); 13 | float c = 1.0; 14 | float inten = 0.005; 15 | 16 | for (int n = 0; n < MAX_ITER; n++) 17 | { 18 | float t = time * (1.0 - (3.5 / float(n+1))); 19 | i = p + vec2(cos(t - i.x) + sin(t + i.y), sin(t - i.y) + cos(t + i.x)); 20 | c += 1.0/length(vec2(p.x / (sin(i.x+t)/inten),p.y / (cos(i.y+t)/inten))); 21 | } 22 | c /= float(MAX_ITER); 23 | c = 1.17-pow(c, 1.4); 24 | vec3 color = vec3(pow(abs(c), 15.0)); 25 | color = clamp((color + water_color)*1.2, 0.0, 1.0); 26 | 27 | // perterb uv based on value of c from caustic calc above 28 | vec2 tc = vec2(cos(c)-0.75,sin(c)-0.75)*0.04; 29 | uv = clamp(uv + tc,0.0,1.0); 30 | 31 | fragColor = texture(iChannel0, uv); 32 | // give transparent pixels a color 33 | if ( fragColor.a == 0.0 ) fragColor=vec4(1.0,1.0,1.0,1.0); 34 | fragColor *= vec4(color, 1.0); 35 | } --------------------------------------------------------------------------------