1;!function(){try { var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof global?global:"undefined"!=typeof window?window:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&((e._debugIds|| (e._debugIds={}))[n]="668d30c2-9dab-6390-5c59-9408ec5649a2")}catch(e){}}(); 2(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,587114,e=>{"use strict";let t=`#version 300 es 3in vec2 p; 4void main(){ gl_Position = vec4(p, 0.0, 1.0); } 5`,a=`#version 300 es 6precision highp float; 7out vec4 fragColor; 8uniform vec2 u_res; 9uniform float u_time; 10uniform float u_warp; 11uniform float u_flow; 12uniform float u_scale; 13uniform int u_octaves; 14uniform float u_contrast; 15uniform float u_grain; 16uniform float u_grainsize; 17uniform int u_grainBlendMode; 18uniform float u_seed; 19uniform int u_shape; 20uniform float u_angle; 21uniform vec2 u_center; 22uniform float u_softness; 23uniform float u_blur; 24uniform float u_aberration; 25uniform int u_distortion; 26uniform float u_distAmount; 27uniform float u_distCount; 28uniform float u_distShadows; 29uniform float u_distHighlights; 30uniform float u_distBump; 31uniform float u_pixelate; 32uniform int u_ditherMode; 33uniform int u_ditherMatrix; 34uniform float u_ditherPattern; 35uniform float u_ditherLevels; 36uniform float u_ditherStrength; 37uniform int u_ditherColor; 38uniform float u_asciiSize; 39uniform float u_asciiCellRatio; 40uniform int u_asciiStyle; 41uniform int u_asciiColor; 42uniform float u_asciiBackground; 43uniform float u_asciiExposure; 44uniform float u_asciiFill; 45uniform float u_asciiDensity; 46uniform float u_asciiScatter; 47uniform float u_asciiOpacity; 48uniform int u_asciiBlend; 49uniform float u_asciiCharCount; 50uniform float u_halftoneSize; 51uniform int u_halftoneStyle; 52uniform int u_halftoneStagger; 53uniform float u_halftoneDotScale; 54uniform int u_halftoneColor; 55uniform float u_halftoneBackground; 56uniform vec3 u_halftonePaperColor; 57uniform float u_halftoneEmboss; 58uniform float u_plaidColumnSize; 59uniform float u_plaidRowSize; 60uniform float u_plaidColumnStrength; 61uniform float u_plaidRowStrength; 62uniform float u_plaidBandWidth; 63uniform float u_plaidBackground; 64uniform float u_plaidAngle; 65uniform int u_plaidColorMode; 66uniform vec3 u_plaidInkColor; 67uniform int u_plaidBlendMode; 68uniform int u_light; 69uniform float u_lightAmount; 70uniform float u_lightSize; 71uniform vec2 u_lightCenter; 72uniform int u_waves; 73uniform float u_wavesFrequency; 74uniform float u_wavesAmplitude; 75uniform float u_wavesPhase; 76uniform float u_wavesFalloff; 77uniform float u_wavesDisplace; 78uniform vec2 u_wavesCenter; 79uniform float u_gradientOverlay; 80uniform int u_blendMode; 81uniform float u_grainSeed; 82uniform vec2 u_imageRes; 83uniform float u_imageScale; 84uniform vec2 u_imageOffset; 85uniform float u_imgBrightness; 86uniform float u_imgContrast; 87uniform float u_imgSaturation; 88uniform float u_imgExposure; 89uniform int u_imgFilter; 90uniform int u_blobCount; 91uniform float u_blobSize; 92uniform float u_blobSpread; 93uniform float u_blobSoftness; 94uniform float u_blobMerge; 95uniform float u_blobSeed; 96uniform float u_blobDrift; 97uniform float u_blobAmount; 98uniform int u_blobLayout; 99uniform float u_blobDensity; 100uniform int u_blobShape; 101uniform float u_blobShapeAmount; 102uniform int u_stopCount; 103uniform float u_stopPos[8]; 104uniform vec3 u_stopCol[8]; 105 106// Pixel-size params (halftone, ASCII, plaid) are defined at a 1080p-equivalent 107// reference: scaling by the canvas's shorter side keeps the composition 108// identical across preview sizes, thumbnails, and 2K/4K exports. Grain and 109// dither intentionally stay per-physical-pixel. 110float refPxScale(){ 111 return max(min(u_res.x, u_res.y) / 1080.0, 0.05); 112} 113`,o=`vec3 sampleGradientStops(float t){ 114 t = clamp(t, 0.0, 1.0); 115 if(u_stopCount <= 0) return vec3(0.0); 116 if(u_stopCount == 1) return u_stopCol[0]; 117 118 float soften = u_softness * 0.25; 119 120 if(u_stopCount >= 2){ 121 float p0 = u_stopPos[0]; 122 float p1 = u_stopPos[1]; 123 if(t <= p1 || u_stopCount == 2){ 124 float denom = max(p1 - p0, 0.0001); 125 float u = clamp((t - p0) / denom, 0.0, 1.0); 126 u = smoothstep(0.0 - soften, 1.0 + soften, u); 127 return mix(u_stopCol[0], u_stopCol[1], u); 128 } 129 } 130 if(u_stopCount >= 3){ 131 float p0 = u_stopPos[1]; 132 float p1 = u_stopPos[2]; 133 if(t <= p1 || u_stopCount == 3){ 134 float denom = max(p1 - p0, 0.0001); 135 float u = clamp((t - p0) / denom, 0.0, 1.0); 136 u = smoothstep(0.0 - soften, 1.0 + soften, u); 137 return mix(u_stopCol[1], u_stopCol[2], u); 138 } 139 } 140 if(u_stopCount >= 4){ 141 float p0 = u_stopPos[2]; 142 float p1 = u_stopPos[3]; 143 if(t <= p1 || u_stopCount == 4){ 144 float denom = max(p1 - p0, 0.0001); 145 float u = clamp((t - p0) / denom, 0.0, 1.0); 146 u = smoothstep(0.0 - soften, 1.0 + soften, u); 147 return mix(u_stopCol[2], u_stopCol[3], u); 148 } 149 } 150 if(u_stopCount >= 5){ 151 float p0 = u_stopPos[3]; 152 float p1 = u_stopPos[4]; 153 if(t <= p1 || u_stopCount == 5){ 154 float denom = max(p1 - p0, 0.0001); 155 float u = clamp((t - p0) / denom, 0.0, 1.0); 156 u = smoothstep(0.0 - soften, 1.0 + soften, u); 157 return mix(u_stopCol[3], u_stopCol[4], u); 158 } 159 } 160 if(u_stopCount >= 6){ 161 float p0 = u_stopPos[4]; 162 float p1 = u_stopPos[5]; 163 if(t <= p1 || u_stopCount == 6){ 164 float denom = max(p1 - p0, 0.0001); 165 float u = clamp((t - p0) / denom, 0.0, 1.0); 166 u = smoothstep(0.0 - soften, 1.0 + soften, u); 167 return mix(u_stopCol[4], u_stopCol[5], u); 168 } 169 } 170 if(u_stopCount >= 7){ 171 float p0 = u_stopPos[5]; 172 float p1 = u_stopPos[6]; 173 if(t <= p1 || u_stopCount == 7){ 174 float denom = max(p1 - p0, 0.0001); 175 float u = clamp((t - p0) / denom, 0.0, 1.0); 176 u = smoothstep(0.0 - soften, 1.0 + soften, u); 177 return mix(u_stopCol[5], u_stopCol[6], u); 178 } 179 } 180 { 181 float p0 = u_stopPos[6]; 182 float p1 = u_stopPos[7]; 183 float denom = max(p1 - p0, 0.0001); 184 float u = clamp((t - p0) / denom, 0.0, 1.0); 185 u = smoothstep(0.0 - soften, 1.0 + soften, u); 186 return mix(u_stopCol[6], u_stopCol[7], u); 187 } 188}`,r=` 189vec3 mod289(vec3 x){ return x - floor(x * (1.0/289.0)) * 289.0; }
190vec2 mod289(vec2 x){ return x - floor(x * (1.0/289.0)) * 289.0; } 191vec3 permute(vec3 x){ return mod289(((x*34.0)+1.0)*x); } 192float snoise(vec2 v){ 193 const vec4 C = vec4(0.211324865405187, 0.366025403784439, 194 -0.577350269189626, 0.024390243902439); 195 vec2 i = floor(v + dot(v, C.yy)); 196 vec2 x0 = v - i + dot(i, C.xx); 197 vec2 i1 = (x0.x > x0.y) ? vec2(1.0,0.0) : vec2(0.0,1.0); 198 vec4 x12 = x0.xyxy + C.xxzz; 199 x12.xy -= i1; 200 i = mod289(i); 201 vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) 202 + i.x + vec3(0.0, i1.x, 1.0)); 203 vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); 204 m = m*m; m = m*m; 205 vec3 x = 2.0 * fract(p * C.www) - 1.0; 206 vec3 h = abs(x) - 0.5; 207 vec3 ox = floor(x + 0.5); 208 vec3 a0 = x - ox; 209 m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h); 210 vec3 g; 211 g.x = a0.x * x0.x + h.x * x0.y; 212 g.yz = a0.yz * x12.xz + h.yz * x12.yw; 213 return 130.0 * dot(m, g); 214} 215float noise(vec2 p){ return snoise(p) * 0.5 + 0.5; } 216 217float fbm(vec2 p){ 218 float v = 0.0, amp = 0.5; 219 for(int i=0;i<6;i++){ 220 if(i >= u_octaves) break; 221 v += amp * noise(p); 222 p *= 2.0; amp *= 0.5; 223 } 224 return v; 225} 226float hash12(vec2 p){ 227 vec3 p3 = fract(vec3(p.xyx) * 0.1031); 228 p3 += dot(p3, p3.yzx + 33.33); 229 return fract((p3.x + p3.y) * p3.z); 230} 231 232const float PI = 3.14159265359; 233const float TWO_PI = 6.28318530718; 234 235// Seamless looping: u_time runs 0 â TWO_PI per loop. Time-based *translations* 236// orbit a circle (position AND velocity match at the seam, so no jump); *phase* 237// oscillators advance a whole number of cycles per loop. 238vec2 loopOffset(float amp){ 239 return amp * vec2(sin(u_time), 1.0 - cos(u_time)); 240} 241float loopPhaseAdd(float rate){ 242 return u_time * max(1.0, floor(rate + 0.5)); 243} 244 245${o} 246 247`,l=` 248const int MAX_BLOBS = 8; 249 250/** 251 * Distance from a blob's centre â and therefore its outline, since the falloff 252 * below is a plain Gaussian on this value. Every mode returns 1.0 on the edge of 253 * a unit blob, so shape never changes a blob's size. 254 * 255 * u_blobShapeAmount is "how far from a circle": 0 is a circle in *every* mode, 256 * which keeps the control predictable when switching between them. 257 */ 258float blobDistance(vec2 q, float phase){ 259 // Circle â the Euclidean norm, whose level sets are circles. 260 if(u_blobShape == 0) return length(q); 261 262 if(u_blobShape == 3){ 263 // Organic: a wobbled radius. Deliberately polar harmonics rather than fbm â 264 // fbm is up to 6 octaves of simplex, and at 8 blobs per pixel that's ~48 265 // noise evals per fragment. Three incommensurate harmonics cost a handful of 266 // trig ops and read as an amoeba rather than a flower. 267 float a = atan(q.y, q.x); 268 float wob = sin(a * 3.0 + phase) 269 + 0.5 * sin(a * 5.0 - phase * 1.7) 270 + 0.3 * sin(a * 7.0 + phase * 0.6); 271 return length(q) / max(1.0 + u_blobShapeAmount * 0.22 * wob, 0.15); 272 } 273 274 // Superellipse: |x|^e + |y|^e = 1. e = 2 is a circle, e â â squares off, and 275 // e < 1 pinches the sides into a star. Squircle walks up from 2, Diamond down. 276 float e = u_blobShape == 1 277 ? mix(2.0, 8.0, u_blobShapeAmount) 278 : mix(2.0, 0.7, u_blobShapeAmount); 279 vec2 a2 = abs(q); 280 // The 1e-4 floor keeps pow() away from 0^negative when e < 1. 281 return pow(pow(max(a2.x, 1e-4), e) + pow(max(a2.y, 1e-4), e), 1.0 / e); 282} 283 284/** 285 * The particle field: one blob per grid cell, hashed from the cell's coordinate. 286 * 287 * The trick is that a pixel only has to look at its own cell and the eight 288 * neighbours â a particle further away can't reach it. So cost is a fixed nine 289 * evaluations no matter how many particles are on screen, where the scatter 290 * layout costs one per blob. 291 * 292 * Stateless like everything else: a particle's position is a closed-form 293 * function of (cell, seed, u_time), never integrated from the previous frame. 294 * That's what keeps it exportable â video renders frame 47 without ever having 295 * rendered frames 0-46 (see video-export.ts). 296 */ 297float blobFieldGrid(vec2 p){ 298 float cells = mix(3.0, 40.0, u_blobDensity); 299 vec2 gp = p * cells; 300 vec2 base = floor(gp); 301 // Radius in cell units; 0.5 keeps a full-size particle inside its own cell. 302 float rad = max(u_blobSize * 0.5, 1e-3); 303 304 float sum = 0.0; 305 float peak = 0.0; 306 for(int oy = -1; oy <= 1; oy++){
307 for(int ox = -1; ox <= 1; ox++){ 308 vec2 cell = base + vec2(float(ox), float(oy)); 309 vec2 h = vec2(hash12(cell + u_blobSeed), 310 hash12(cell + u_blobSeed + 31.7)); 311 // Inset from the cell edge so neighbours don't line up into rows. 312 vec2 centre = cell + 0.15 + 0.7 * h; 313 float ph = hash12(cell + u_blobSeed + 7.3) * TWO_PI; 314 // Same circular orbit as the scatter layout, so the loop stays seamless. 315 centre += u_blobDrift * 0.3 * 316 vec2(sin(u_time + ph), 1.0 - cos(u_time + ph)); 317 float r = rad * mix(0.35, 1.0, h.x); 318 float d = blobDistance((gp - centre) / max(r, 1e-3), ph); 319 float g = exp(-d * d * mix(6.0, 1.0, u_blobSoftness)); 320 sum += g; 321 peak = max(peak, g); 322 } 323 } 324 return mix(peak, sum, u_blobMerge); 325} 326 327float blobField(vec2 p, float aspect){ 328 if(u_blobLayout == 1) return blobFieldGrid(p); 329 float sum = 0.0; 330 float peak = 0.0; 331 for(int i = 0; i < MAX_BLOBS; i++){ 332 if(i >= u_blobCount) break; 333 float fi = float(i); 334 vec2 h = vec2(hash12(vec2(fi, u_blobSeed)), 335 hash12(vec2(fi + 37.0, u_blobSeed))); 336 vec2 c = vec2(0.5 * aspect, 0.5) 337 + (h - 0.5) * u_blobSpread * vec2(aspect, 1.0); 338 // A constant per-blob phase still traces the same circle, so position and 339 // velocity match at the seam (see loopOffset) and the loop stays seamless â 340 // each blob just starts somewhere else on its orbit. 341 float ph = hash12(vec2(fi + 11.0, u_blobSeed)) * TWO_PI; 342 c += u_blobDrift * 0.12 * vec2(sin(u_time + ph), 1.0 - cos(u_time + ph)); 343 float r = max(u_blobSize * mix(0.6, 1.0, h.y), 1e-3); 344 vec2 q = (p - c) / r; 345 float d = blobDistance(q, ph); 346 // Gaussian falloff: it's what makes overlaps *add* rather than clip. 347 float g = exp(-d * d * mix(6.0, 1.0, u_blobSoftness)); 348 sum += g; 349 peak = max(peak, g); 350 } 351 // max keeps discs distinct; sum fuses them and pushes the overlap further 352 // along the palette ramp â that's the extra colour where two blobs meet. 353 return mix(peak, sum, u_blobMerge); 354} 355`,i=` 356float blobField(vec2 p, float aspect){ return 0.0; } 357`,s=` 358vec3 fieldColorGenerate(vec2 uv){ 359 float aspect = u_res.x / u_res.y; 360 float freqScale = mix(1.0, 0.45, u_softness); 361 float effectiveContrast = mix(u_contrast, 1.0, u_softness); 362 363 vec2 p = uv; 364 p.x *= aspect; 365 vec2 c = u_center; 366 c.x *= aspect; 367 368 vec2 nst = uv; 369 nst.x *= aspect; 370 nst *= u_scale; 371 nst += u_seed; 372 vec2 nstF = nst * freqScale; 373 // Flow: gentle always-on drift, independent of Warp. Nudges the noise 374 // sampling domain (texture flows) and the position reference (center/field 375 // gently orbits) so shapes still feel alive with Warp at 0. 376 nstF += loopOffset(u_flow * 0.55); 377 vec2 flowOff = loopOffset(u_flow * 0.18); 378 379 vec2 q = vec2(fbm(nstF + vec2(0.0,0.0) + loopOffset(0.1)), 380 fbm(nstF + vec2(5.2,1.3) - loopOffset(0.1))); 381 // Warp must displace around zero, or it stops being noise and becomes a plain 382 // translation. fbm sums octaves of amplitude 0.5, 0.25, 0.125..., so it 383 // averages 0.5 * (1 - 2^-octaves), NOT 0.5: that is 0 at Detail 0, 0.25 at 384 // Detail 1, and only ~0.49 by Detail 6. Subtracting a flat 0.5 left a constant 385 // negative bias that shoved the whole field right and up in proportion to 386 // Warp, worst at low Detail (Detail 0 + Warp 0.8 moved it ~23% of the width). 387 // Radial shapes could hide it by moving Center X. Twin Radial cannot, since
388 // Orb X mirrors and so only sets the spread, which is how this surfaced. 389 float fbmMean = 0.5 * (1.0 - pow(0.5, float(u_octaves))); 390 vec2 warpOff = (q - fbmMean) * u_warp; 391 392 float f; 393 float g; 394 bool meshMode = false; 395 vec3 meshCol; 396 397 if(u_waves == 1){ 398 vec2 wc = u_wavesCenter; 399 wc.x *= aspect; 400 vec2 pp = p + warpOff; 401 float dist = length(pp - wc); 402 float freq = mix(12.0, 88.0, u_wavesFrequency); 403 float falloffK = mix(0.0, 5.0, u_wavesFalloff); 404 float phase = dist * freq / (1.0 + falloffK * dist) + u_wavesPhase * 6.28318 + loopPhaseAdd(1.5); 405 float wave01 = sin(phase) * 0.5 + 0.5; 406 float sharpness = mix(1.0, 6.0, u_wavesAmplitude); 407 f = pow(wave01, sharpness); 408 g = clamp(dist * mix(0.65, 1.8, u_wavesFalloff * 0.5 + u_wavesFrequency * 0.5), 0.0, 1.0); 409 } else if(u_shape == 0){ 410 vec2 dir = vec2(cos(u_angle), sin(u_angle)); 411 vec2 pp = p + warpOff + flowOff; 412 f = dot(pp - vec2(0.5*aspect, 0.5), dir) + 0.5; 413 g = clamp(f + (fbm(nstF*0.7) - 0.5)*0.3, 0.0, 1.0); 414 } else if(u_shape == 1){ 415 vec2 pp = p + warpOff + flowOff; 416 f = length(pp - c) * 1.4; 417 g = clamp(f + (fbm(nstF*0.7) - 0.5)*0.3, 0.0, 1.0); 418 } else if(u_shape == 5){ 419 vec2 pp = p + warpOff + flowOff; 420 vec2 c1 = vec2(u_center.x * aspect, u_center.y); 421 vec2 c2 = vec2((1.0 - u_center.x) * aspect, u_center.y); 422 f = min(length(pp - c1), length(pp - c2)) * 1.4; 423 g = clamp(f + (fbm(nstF*0.7) - 0.5)*0.3, 0.0, 1.0); 424 } else if(u_shape == 2){ 425 vec2 pp = p + warpOff + flowOff - c; 426 f = (atan(pp.y, pp.x) + PI) / (2.0*PI); 427 g = clamp(fbm(nstF*0.7 + 3.0), 0.0, 1.0); 428 } else if(u_shape == 3){ 429 vec2 m = clamp(uv + warpOff*0.5 + flowOff*0.5, 0.0, 1.0); 430 vec3 top = mix(u_stopCol[0], u_stopCol[1], m.x); 431 vec3 bot = mix(u_stopCol[2], u_stopCol[3], m.x); 432 meshCol = mix(top, bot, m.y); 433 float centerMix = 1.0 - length((m - 0.5) * 2.0); 434 meshCol = mix(meshCol, u_stopCol[4], smoothstep(0.0, 1.0, centerMix) * 0.65); 435 meshMode = true; 436 f = 0.0; g = 0.0; 437 } else { 438 vec2 r = vec2(fbm(nstF + u_warp*q + vec2(1.7,9.2) + loopOffset(0.15)), 439 fbm(nstF + u_warp*q + vec2(8.3,2.8) - loopOffset(0.12))); 440 f = fbm(nstF + u_warp*r); 441 g = clamp(fbm(nstF*0.7 + r*u_warp + 3.0), 0.0, 1.0); 442 } 443 444 // Blobs blend over whatever field the shape produced, so they compose with 445 // warped-field / conic instead of replacing them. Mesh is the exception: it 446 // short-circuits with its own colour and has no scalar field to mix into, so 447 // blobs take it over outright rather than silently doing nothing. 448 if(u_blobAmount > 0.0){ 449 float bf = clamp(blobField(p + warpOff + flowOff, aspect), 0.0, 1.0); 450 // Centre of a blob maps to the ramp's first stop, matching Radial (shape 1). 451 if(meshMode){ 452 meshMode = false; 453 f = 1.0 - bf; 454 g = bf; 455 } else { 456 f = mix(f, 1.0 - bf, u_blobAmount); 457 } 458 } 459 460 if(meshMode){ 461 return meshCol; 462 } 463 464 f = clamp((f - 0.5) * effectiveContrast + 0.5, 0.0, 1.0); 465 vec3 col = sampleGradientStops(f); 466 return col; 467} 468`,u=` 469vec2 applyPixelate(vec2 uv){ 470 if(u_pixelate < 2.0) return uv; 471 float aspect = u_res.x / u_res.y; 472 vec2 cells = vec2(u_pixelate * aspect, u_pixelate); 473 return (floor(uv * cells) + 0.5) / cells; 474} 475`,n=` 476/** 477 * Pushes the sampled coordinate along the wave field. 478 * 479 * Same concentric sine as the Waves shape, used as a displacement instead of as 480 * a colour ramp input. Because this runs before the photo and the gradient are 481 * sampled (see fieldColorAt), it warps both, which is the entire point: the 482 * shape mode cannot touch the photo, since it only decides how the gradient is 483 * generated. 484 * 485 * Deliberately reuses u_wavesFrequency and the origin/falloff/phase controls so 486 * one set of sliders drives both the visible rings and the warp; a second, 487 * independent frequency would let them disagree and look wrong. The phase 488 * carries loopPhaseAdd so the ripple travels when the look is animated, and 489 * still closes its loop. 490 * 491 * (No backticks in this comment: the whole chunk is a JS template literal.) 492 */
493vec2 wavesDisplaceUv(vec2 uv){ 494 float aspect = u_res.x / u_res.y; 495 vec2 wc = u_wavesCenter; 496 wc.x *= aspect; 497 vec2 p = uv; 498 p.x *= aspect; 499 vec2 delta = p - wc; 500 float dist = length(delta); 501 // At the exact origin the direction is undefined; any fixed vector does, and 502 // the amplitude there is ~0 anyway. 503 vec2 dir = dist > 1e-4 ? delta / dist : vec2(0.0, 1.0); 504 float freq = mix(12.0, 88.0, u_wavesFrequency); 505 float falloffK = mix(0.0, 5.0, u_wavesFalloff); 506 float phase = dist * freq / (1.0 + falloffK * dist) 507 + u_wavesPhase * 6.28318 + loopPhaseAdd(1.5); 508 // 0.06 keeps the strongest setting a firm ripple rather than a smear that 509 // tears the photo apart; Ripple Glass uses 0.034 over a wider Amount range. 510 vec2 offset = dir * sin(phase) * u_wavesDisplace * 0.06; 511 offset.x /= aspect; 512 return uv + offset; 513} 514`,f=` 515float flutePosition(float gx){ 516 float x = fract(gx); 517 float w = 0.02; 518 float edge = abs(x - 0.5) - 0.5; 519 float band = smoothstep(-w, w, edge); 520 return mix(x, 1.0 - x, band); 521} 522 523float prismOffset(float gx, float fractCoord){ 524 float xSmooth = flutePosition(gx); 525 float xNS = fractCoord + 0.0001; 526 float distort = -pow(1.5 * xSmooth, 3.0) + 0.5; 527 float aa = 0.08; 528 float fade = smoothstep(0.0, aa, xNS) * smoothstep(1.0, 1.0 - aa, xNS); 529 distort = mix(0.5, distort, fade); 530 return distort * 3.0 * u_distAmount; 531} 532 533vec2 flutedGlassUv(vec2 uv){ 534 float strips = max(u_distCount, 1.0); 535 vec2 gridUV = uv * vec2(strips, 1.0); 536 vec2 gridFloor = floor(gridUV); 537 vec2 gridFract = fract(gridUV); 538 gridFract.x += prismOffset(gridUV.x, gridFract.x); 539 return (gridFloor + gridFract) / vec2(strips, 1.0); 540} 541 542vec2 mosaicGlassUv(vec2 uv){ 543 float cells = max(u_distCount, 1.0); 544 float aspect = u_res.x / u_res.y; 545 vec2 g = vec2(cells * aspect, cells); 546 vec2 gridUV = uv * g; 547 vec2 gridFloor = floor(gridUV); 548 vec2 gridFract = fract(gridUV); 549 gridFract.x += prismOffset(gridUV.x, gridFract.x); 550 gridFract.y += prismOffset(gridUV.y, gridFract.y); 551 vec2 refracted = (gridFloor + gridFract) / g; 552 vec2 quant = (floor(uv * g) + 0.5) / g; 553 return mix(refracted, quant, u_distAmount * 0.25); 554} 555 556float rippleGlassFreq(){ 557 return mix(12.0, 88.0, clamp((u_distCount - 4.0) / 56.0, 0.0, 1.0)); 558} 559 560float rippleGlassPhase(vec2 uv){ 561 float aspect = u_res.x / u_res.y; 562 vec2 wc = u_wavesCenter; 563 wc.x *= aspect; 564 vec2 p = uv; 565 p.x *= aspect; 566 float dist = length(p - wc); 567 float freq = rippleGlassFreq(); 568 float falloffK = mix(0.0, 5.0, u_wavesFalloff); 569 return dist * freq / (1.0 + falloffK * dist) + u_wavesPhase * 6.28318 + loopPhaseAdd(1.5); 570} 571 572vec2 rippleGlassUv(vec2 uv){ 573 float aspect = u_res.x / u_res.y; 574 vec2 wc = u_wavesCenter; 575 wc.x *= aspect; 576 vec2 p = uv; 577 p.x *= aspect; 578 vec2 delta = p - wc; 579 float dist = length(delta); 580 vec2 dir = dist > 1e-4 ? delta / dist : vec2(0.0, 1.0); 581 float wave = sin(rippleGlassPhase(uv)); 582 float amp = u_distAmount * 0.034; 583 vec2 offset = dir * wave * amp; 584 offset.x /= aspect; 585 return uv + offset; 586} 587 588float glassEdgeHighlight(float coordNS){ 589 float hw = 0.04; 590 return 1.0 - smoothstep(0.0, hw, coordNS) * smoothstep(1.0, 1.0 - hw, coordNS); 591} 592 593vec3 applyFlutedGlassShading(vec3 col, vec2 uv){ 594 float strips = max(u_distCount, 1.0); 595 float gx = uv.x * strips; 596 float x = flutePosition(gx); 597 float xNS = fract(gx) + 0.0001; 598 float shadows = pow(x, 1.3) * pow(u_distShadows, 2.0); 599 float highlights = glassEdgeHighlight(xNS) * u_distHighlights; 600 col = mix(col, vec3(1.0), highlights); 601 col *= (1.0 - shadows); 602 return col; 603} 604 605vec3 applyMosaicGlassShading(vec3 col, vec2 uv){ 606 float cells = max(u_distCount, 1.0); 607 float aspect = u_res.x / u_res.y; 608 vec2 g = vec2(cells * aspect, cells); 609 vec2 gx = uv * g; 610 float x = flutePosition(gx.x); 611 float y = flutePosition(gx.y); 612 float xNS = fract(gx.x) + 0.0001; 613 float yNS = fract(gx.y) + 0.0001; 614 float shadows = max(pow(x, 1.3), pow(y, 1.3)) * pow(u_distShadows, 2.0); 615 float highlights = max(glassEdgeHighlight(xNS), glassEdgeHighlight(yNS)) * u_distHighlights; 616 col = mix(col, vec3(1.0), highlights); 617 col *= (1.0 - shadows); 618 return col; 619} 620 621vec3 applyRippleGlassShading(vec3 col, vec2 uv){ 622 float wave = sin(rippleGlassPhase(uv)); 623 float crest = smoothstep(0.55, 1.0, wave); 624 float trough = smoothstep(-1.0, -0.55, -wave); 625 col = mix(col, vec3(1.0), crest * u_distHighlights * 0.9); 626 col *= (1.0 - trough * pow(u_distShadows, 1.5) * 0.38); 627 return col; 628} 629 630// Frosted / organic glass: a noise vector field refracts the sampled UV, so the 631// scene reads as if seen through hand-poured, bumpy glass (no repeating grid). 632// distCount sets the bump scale, distAmount the overall push, distBump layers a 633// finer, choppier octave on top for a rougher frost. 634vec2 frostedGlassUv(vec2 uv){ 635 float aspect = u_res.x / u_res.y; 636 float scale = max(u_distCount, 1.0) * 0.5; 637 vec2 p = uv * scale; 638 p.x *= aspect; 639 // Base organic warp, centred at 0 so it pushes both ways. 640 vec2 warp = vec2(fbm(p + loopOffset(0.08)), 641 fbm(p + vec2(5.2, 1.3) - loopOffset(0.08))) - 0.5; 642 // Bumpiness: a higher-frequency octave adds fine chatter to the refraction. 643 vec2 fine = vec2(fbm(p * 3.1 + vec2(1.7, 9.2)), 644 fbm(p * 3.1 + vec2(8.3, 2.8))) - 0.5; 645 warp += fine * u_distBump; 646 vec2 offset = warp * u_distAmount * 0.14; 647 offset.x /= aspect; 648 return uv + offset; 649} 650 651vec2 applyDistortion(vec2 uv){ 652 if(u_distortion == 1){ 653 return flutedGlassUv(uv); 654 } else if(u_distortion == 2){ 655 return mosaicGlassUv(uv); 656 } else if(u_distortion == 3){ 657 return rippleGlassUv(uv); 658 } else if(u_distortion == 4){ 659 return frostedGlassUv(uv); 660 } 661 return uv; 662} 663`,c=` 664float bayer2(vec2 p){ 665 float x = mod(floor(p.x), 2.0); 666 float y = mod(floor(p.y), 2.0); 667 if(y < 0.5){ 668 if(x < 0.5) return 0.0; 669 return 0.5; 670 } 671 if(x < 0.5) return 0.75; 672 return 0.25; 673} 674 675float bayer4(vec2 p){ 676 float x = mod(floor(p.x), 4.0); 677 float y = mod(floor(p.y), 4.0); 678 if(y < 1.0){ 679 if(x < 1.0) return 0.0 / 16.0; 680 if(x < 2.0) return 8.0 / 16.0; 681 if(x < 3.0) return 2.0 / 16.0; 682 return 10.0 / 16.0; 683 } 684 if(y < 2.0){ 685 if(x < 1.0) return 12.0 / 16.0; 686 if(x < 2.0) return 4.0 / 16.0; 687 if(x < 3.0) return 14.0 / 16.0; 688 return 6.0 / 16.0; 689 } 690 if(y < 3.0){ 691 if(x < 1.0) return 3.0 / 16.0; 692 if(x < 2.0) return 11.0 / 16.0; 693 if(x < 3.0) return 1.0 / 16.0; 694 return 9.0 / 16.0; 695 } 696 if(x < 1.0) return 15.0 / 16.0; 697 if(x < 2.0) return 7.0 / 16.0; 698 if(x < 3.0) return 13.0 / 16.0; 699 return 5.0 / 16.0; 700} 701 702float bayer8(vec2 p){ 703 float x = mod(floor(p.x), 8.0); 704 float y = mod(floor(p.y), 8.0); 705 if(y < 1.0){ 706 if(x < 1.0) return 0.0; if(x < 2.0) return 32.0; if(x < 3.0) return 8.0; if(x < 4.0) return 40.0; 707 if(x < 5.0) return 2.0; if(x < 6.0) return 34.0; if(x < 7.0) return 10.0; return 42.0; 708 } 709 if(y < 2.0){ 710 if(x < 1.0) return 48.0; if(x < 2.0) return 16.0; if(x < 3.0) return 56.0; if(x < 4.0) return 24.0; 711 if(x < 5.0) return 50.0; if(x < 6.0) return 18.0; if(x < 7.0) return 58.0; return 26.0; 712 } 713 if(y < 3.0){ 714 if(x < 1.0) return 12.0; if(x < 2.0) return 44.0; if(x < 3.0) return 4.0; if(x < 4.0) return 36.0; 715 if(x < 5.0) return 14.0; if(x < 6.0) return 46.0; if(x < 7.0) return 6.0; return 38.0; 716 } 717 if(y < 4.0){ 718 if(x < 1.0) return 60.0; if(x < 2.0) return 28.0; if(x < 3.0) return 52.0; if(x < 4.0) return 20.0; 719 if(x < 5.0) return 62.0; if(x < 6.0) return 30.0; if(x < 7.0) return 54.0; return 22.0; 720 } 721 if(y < 5.0){ 722 if(x < 1.0) return 3.0;
722 if(x < 2.0) return 35.0; if(x < 3.0) return 11.0; if(x < 4.0) return 43.0; 723 if(x < 5.0) return 1.0; if(x < 6.0) return 33.0; if(x < 7.0) return 9.0; return 41.0; 724 } 725 if(y < 6.0){ 726 if(x < 1.0) return 51.0; if(x < 2.0) return 19.0; if(x < 3.0) return 59.0; if(x < 4.0) return 27.0; 727 if(x < 5.0) return 49.0; if(x < 6.0) return 17.0; if(x < 7.0) return 57.0; return 25.0; 728 } 729 if(y < 7.0){ 730 if(x < 1.0) return 15.0; if(x < 2.0) return 47.0; if(x < 3.0) return 7.0; if(x < 4.0) return 39.0; 731 if(x < 5.0) return 13.0; if(x < 6.0) return 45.0; if(x < 7.0) return 5.0; return 37.0; 732 } 733 if(x < 1.0) return 63.0; if(x < 2.0) return 31.0; if(x < 3.0) return 55.0; if(x < 4.0) return 23.0; 734 if(x < 5.0) return 61.0; if(x < 6.0) return 29.0; if(x < 7.0) return 53.0; return 21.0; 735} 736 737float bayerThreshold(vec2 cell){ 738 if(u_ditherMatrix == 2) return bayer2(cell); 739 if(u_ditherMatrix == 8) return bayer8(cell) / 64.0; 740 return bayer4(cell); 741} 742 743float hashRandom(vec2 p){ 744 return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); 745} 746 747float interleavedGradientNoise(vec2 p){ 748 return fract(52.9829189 * fract(0.06711056 * p.x + 0.00583715 * p.y)); 749} 750 751float ditherThreshold(vec2 cell){ 752 if(u_ditherMode == 1){ 753 return hashRandom(cell) - 0.5; 754 } 755 if(u_ditherMode == 2){ 756 return interleavedGradientNoise(cell) - 0.5; 757 } 758 return bayerThreshold(cell) - 0.5; 759} 760 761vec3 applyDitherAt(vec3 col, vec2 fragCoord){ 762 vec2 cell = floor(fragCoord / u_ditherPattern); 763 float threshold = ditherThreshold(cell) * u_ditherStrength; 764 float levels = max(u_ditherLevels, 2.0); 765 if(u_ditherColor == 1){ 766 col += threshold / levels; 767 col = floor(col * (levels - 1.0) + 0.5) / (levels - 1.0); 768 } else { 769 float l = dot(col, vec3(0.2126, 0.7152, 0.0722)); 770 float ld = l + threshold / levels; 771 float lq = floor(ld * (levels - 1.0) + 0.5) / (levels - 1.0); 772 col *= (l > 0.001 ? lq / l : 1.0); 773 } 774 return clamp(col, 0.0, 1.0); 775} 776`,p=` 777vec3 rgb2hsl(vec3 c){ 778 float maxC = max(c.r, max(c.g, c.b)); 779 float minC = min(c.r, min(c.g, c.b)); 780 float l = (maxC + minC) * 0.5; 781 if(maxC == minC) return vec3(0.0, 0.0, l); 782 float d = maxC - minC; 783 float s = l > 0.5 ? d / (2.0 - maxC - minC) : d / (maxC + minC); 784 float h = 0.0; 785 if(maxC == c.r) h = (c.g - c.b) / d + (c.g < c.b ? 6.0 : 0.0); 786 else if(maxC == c.g) h = (c.b - c.r) / d + 2.0; 787 else h = (c.r - c.g) / d + 4.0; 788 h /= 6.0; 789 return vec3(h, s, l); 790} 791 792float hue2rgb(float p, float q, float t){ 793 if(t < 0.0) t += 1.0; 794 if(t > 1.0) t -= 1.0; 795 if(t < 1.0/6.0) return p + (q - p) * 6.0 * t; 796 if(t < 1.0/2.0) return q; 797 if(t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0; 798 return p; 799} 800 801vec3 hsl2rgb(vec3 hsl){ 802 if(hsl.y <= 0.0) return vec3(hsl.z); 803 float q = hsl.z < 0.5 ? hsl.z * (1.0 + hsl.y) : hsl.z + hsl.y - hsl.z * hsl.y; 804 float p = 2.0 * hsl.z - q; 805 return vec3( 806 hue2rgb(p, q, hsl.x + 1.0/3.0), 807 hue2rgb(p, q, hsl.x), 808 hue2rgb(p, q, hsl.x - 1.0/3.0) 809 ); 810} 811 812vec3 blendMultiply(vec3 base, vec3 blend){ return base * blend; } 813vec3 blendScreen(vec3 base, vec3 blend){ return 1.0 - (1.0 - base) * (1.0 - blend); } 814float blendOverlayChannel(float base, float blend){ 815 return base < 0.5 ? 2.0 * base * blend : 1.0 - 2.0 * (1.0 - base) * (1.0 - blend); 816} 817vec3 blendOverlay(vec3 base, vec3 blend){ 818 return vec3( 819 blendOverlayChannel(base.r, blend.r), 820 blendOverlayChannel(base.g, blend.g), 821 blendOverlayChannel(base.b, blend.b) 822 ); 823} 824vec3 blendSoftLight(vec3 base, vec3 blend){ 825 return mix( 826 2.0 * base * blend + base * base * (1.0 - 2.0 * blend), 827 sqrt(max(base, 0.0)) * (2.0 * blend - 1.0) + 2.0 * base * (1.0 - blend), 828 step(vec3(0.5), blend) 829 ); 830} 831vec3 blendHardLight(vec3 base, vec3 blend){ return blendOverlay(blend, base); } 832vec3 blendColor(vec3 base, vec3 blend){ 833 vec3 baseHsl = rgb2hsl(base); 834 vec3 blendHsl = rgb2hsl(blend); 835 return hsl2rgb(vec3(blendHsl.x, blendHsl.y, baseHsl.z)); 836} 837vec3 blendDifference(vec3 base, vec3 blend){ return abs(base - blend); } 838vec3 blendExclusion(vec3 base, vec3 blend){ return base + blend - 2.0 * base * blend; } 839 840vec3 applyGradientBlend(vec3 base, vec3 blend, int mode){ 841 if(mode == 1) return blendMultiply(base, blend); 842 if(mode == 2) return blendScreen(base, blend); 843 if(mode == 3) return blendOverlay(base, blend); 844 if(mode == 4) return blendSoftLight(base, blend); 845 if(mode == 5) return blendHardLight(base, blend);
846 if(mode == 6) return blendColor(base, blend); 847 if(mode == 7) return blendDifference(base, blend); 848 if(mode == 8) return blendExclusion(base, blend); 849 return blend; 850} 851`,d=` 852uniform sampler2D u_image; 853 854vec2 imageUv(vec2 uv){ 855 float viewAspect = u_res.x / u_res.y; 856 float imgAspect = u_imageRes.x / u_imageRes.y; 857 858 // object-fit: cover â the image fills the frame at the selected aspect ratio, 859 // cropping the overflowing dimension (centered). 860 if(viewAspect > imgAspect){ 861 float factor = imgAspect / viewAspect; 862 return vec2(uv.x, (uv.y - 0.5) * factor + 0.5); 863 } 864 float factor = viewAspect / imgAspect; 865 return vec2((uv.x - 0.5) * factor + 0.5, uv.y); 866} 867 868// User zoom (around the frame centre) + pan, applied in frame space before the 869// cover fit. Offset is in frame units, so a 0.5 pan moves the photo half a 870// frame at any zoom level. 871vec2 imageFrameUv(vec2 uv){ 872 return ((uv - u_imageOffset) - 0.5) / u_imageScale + 0.5; 873} 874 875// 1 inside the photo, 0 outside (zoomed out / panned away), with a hair of 876// smoothing so the photo edge doesn't alias. 877float imageMask(vec2 iuv){ 878 vec2 e = smoothstep(vec2(-0.002), vec2(0.0), iuv) 879 * (vec2(1.0) - smoothstep(vec2(1.0), vec2(1.002), iuv)); 880 return e.x * e.y; 881} 882 883// One-click photo filters, layered after the adjustment sliders. 884vec3 applyImageFilter(vec3 col, int f){ 885 float l = dot(col, vec3(0.299, 0.587, 0.114)); 886 if(f == 1) return vec3(l); // grayscale 887 if(f == 2) return vec3( // sepia 888 dot(col, vec3(0.393, 0.769, 0.189)), 889 dot(col, vec3(0.349, 0.686, 0.168)), 890 dot(col, vec3(0.272, 0.534, 0.131))); 891 if(f == 3) return col * vec3(1.10, 1.02, 0.90); // warm 892 if(f == 4) return col * vec3(0.90, 1.00, 1.12); // cool 893 if(f == 5) return vec3(1.0) - col; // invert 894 return col; 895} 896 897// Photo adjustments applied to the sampled image before the gradient blend: 898// exposure (multiply, stops) then brightness (offset), contrast about mid-grey, 899// saturation about luma, then the filter preset. All default to a no-op. 900vec3 adjustImage(vec3 col){ 901 col *= exp2(u_imgExposure); 902 col += u_imgBrightness; 903 col = (col - 0.5) * u_imgContrast + 0.5; 904 float l = dot(clamp(col, 0.0, 1.0), vec3(0.299, 0.587, 0.114)); 905 col = mix(vec3(l), col, u_imgSaturation); 906 col = applyImageFilter(col, u_imgFilter); 907 return clamp(col, 0.0, 1.0); 908} 909 910vec3 compositeUpload(vec2 uv){ 911 vec2 iuv = imageUv(imageFrameUv(uv)); 912 // The mask reveals the gradient through a *framing* gap â where the user 913 // zoomed the photo out or panned it partly off. It must NOT fire for effect 914 // displacement: glass/pixelate/aberration hand us a uv nudged past the frame 915 // edge, and there the photo should clamp to its edge (as it did before image 916 // positioning existed), not tear open to raw gradient. So when the photo is at 917 // full cover (default framing, every look saved before this control), force 918 // the mask solid; the gap logic only applies once it's actually moved. 919 bool fullCover = u_imageScale >= 1.0 920 && abs(u_imageOffset.x) < 0.0001 && abs(u_imageOffset.y) < 0.0001; 921 vec4 tex = texture(u_image, clamp(iuv, 0.0, 1.0)); 922 // A cut-out PNG is see-through *inside* its frame, which the framing mask 923 // above knows nothing about (it only feathers the frame's edges). Sampling 924 // .rgb alone discarded the alpha, so transparent pixels composited as whatever 925 // colour the file happened to store there, usually black, and the cut-out read 926 // as a solid rectangle blended into the gradient. Folding alpha into the mask 927 // lets the gradient through per pixel, exactly as it does outside the frame. 928 // The texture is uploaded straight (never UNPACK_PREMULTIPLY_ALPHA_WEBGL), so 929 // .rgb is un-premultiplied and needs no correction before use. 930 float mask = (fullCover ? 1.0 : imageMask(iuv)) * tex.a; 931 vec3 base = adjustImage(tex.rgb); 932 if(u_gradientOverlay <= 0.001 && mask >= 0.999){ 933 return base; 934 } 935 // Outside the photo the gradient shows through â no edge smearing. 936 vec3 grad = fieldColorGenerate(uv); 937 vec3 col = u_gradientOverlay <= 0.001 938 ? base 939 : mix(base, applyGradientBlend(base, grad, u_blendMode), u_gradientOverlay); 940 return mix(grad, col, mask); 941} 942`,m=` 943// Light effects run on the already-composited colour only â they never re-sample 944// the procedural field (which would inline fbm's loop many times), so they stay 945// cheap. Single-select; the active mode is chosen at runtime. 946 947vec3 applyGlow(vec2 uv, vec3 col){ 948 float aspect = u_res.x / u_res.y; 949 vec2 d = (uv - u_lightCenter) * vec2(aspect, 1.0); 950 float radius = mix(0.12, 0.95, u_lightSize); 951 float glow = pow(1.0 - smoothstep(0.0, radius, length(d)), 1.5); 952 return col + col * glow * u_lightAmount * 2.0 + glow * u_lightAmount * 0.25; 953} 954 955vec3 applyRays(vec2 uv, vec3 col){ 956 vec2 d = uv - u_lightCenter; 957 float ang = atan(d.y, d.x); 958 float freq = mix(6.0, 36.0, u_lightSize); 959 float rays = pow(noise(vec2(ang * freq * 0.5 + 4.0, u_seed + loopOffset(0.4).y)), 2.0); 960 float falloff = 1.0 - smoothstep(0.0, 1.1, length(d)); 961 return col + vec3(rays * falloff) * u_lightAmount * 1.8; 962} 963 964vec3 applyIridescence(vec2 uv, vec3 col){ 965 float aspect = u_res.x / u_res.y; 966 vec2 p = uv; 967 p.x *= aspect; 968 p = p * u_scale + u_seed + loopOffset(0.15); 969 float e = 0.04; 970 float nc = noise(p); 971 float hx = noise(p + vec2(e, 0.0)) - nc; 972 float hy = noise(p + vec2(0.0, e)) - nc; 973 vec3 n = normalize(vec3(-hx, -hy, mix(0.06, 0.012, u_lightSize))); 974 float fres = pow(1.0 - clamp(n.z, 0.0, 1.0), 2.0); 975 float bands = mix(2.0, 8.0, u_lightSize); 976 float phase = (n.x * 0.5 + n.y * 0.5 + fres) * bands; 977 vec3 irid = 0.5 + 0.5 * cos(6.2831853 * (phase + vec3(0.0, 0.33, 0.67))); 978 return mix(col, col + irid * fres, u_lightAmount); 979} 980 981vec3 applyCaustics(vec2 uv, vec3 col){ 982 vec2 p = uv; 983 p.x *= u_res.x / u_res.y; 984 p *= mix(3.0, 11.0, u_lightSize); 985 p += u_seed + loopOffset(0.3); 986 float a = noise(p); 987 float b = noise(p + vec2(a) + vec2(1.7, 9.2)); 988 float veins = pow(1.0 - abs(a - b), 6.0); 989 return col + vec3(veins) * u_lightAmount * 1.6; 990} 991 992vec3 applyLight(vec2 uv, vec3 col){ 993 if(u_light == 1) return applyGlow(uv, col); 994 if(u_light == 2) return applyRays(uv, col); 995 if(u_light == 3) return applyIridescence(uv, col); 996 if(u_light == 4) return applyCaustics(uv, col); 997 return col; 998} 999`,h=` 1000uniform sampler2D u_asciiAtlas; 1001 1002float asciiGlyph(vec2 cellUv, float lum){ 1003 if(u_asciiStyle == 0){ 1004 float idx = floor(lum * (u_asciiCharCount - 1.0)); 1005 idx = clamp(idx, 0.0, u_asciiCharCount - 1.0); 1006 vec2 atlasUv = vec2((idx + cellUv.x) / u_asciiCharCount, 1.0 - cellUv.y); 1007 return texture(u_asciiAtlas, atlasUv).r; 1008 } 1009 if(u_asciiStyle == 1){ 1010 return step(0.5, lum); 1011 } 1012 if(u_asciiStyle == 2){ 1013 float r = lum * 0.48; 1014 float d = length(cellUv - 0.5); 1015 return 1.0 - smoothstep(r - 0.02, r, d); 1016 } 1017 if(u_asciiStyle == 3){ 1018 float bands = 4.0; 1019 float py = fract(cellUv.y * bands); 1020 return step(py, lum); 1021 } 1022 if(u_asciiStyle == 4){ 1023 float t = (1.0 - lum) * 0.45; 1024 float h = step(abs(cellUv.y - 0.5), t); 1025 float v = step(abs(cellUv.x - 0.5), t * 0.75); 1026 return max(h, v); 1027 } 1028 float stripes = fract((cellUv.x + cellUv.y) * 3.0); 1029 return step(stripes, lum); 1030} 1031 1032vec3 applyAscii(vec2 uv, vec3 baseCol){ 1033 float cellW = max(u_asciiSize * refPxScale(), 4.0); 1034 float cellH = max(cellW * u_asciiCellRatio, 4.0); 1035 float cols = max(floor(u_res.x / cellW), 4.0); 1036 float rows = max(floor(u_res.y / cellH), 4.0); 1037 1038 vec2 cellSize = vec2(1.0 / cols, 1.0 / rows); 1039 vec2 cellId = floor(uv / cellSize); 1040 vec2 cellUv = fract(uv / cellSize); 1041 1042 // Scatter â jitter each cell's colour-sample point for a looser, organic grid. 1043 vec2 jitter = (vec2(hash12(cellId + 11.3), hash12(cellId + 47.7)) - 0.5) 1044 * u_asciiScatter * cellSize; 1045 vec2 cellCenter = (cellId + 0.5) * cellSize + jitter; 1046 vec3 cellCol = colorPipelineAt(cellCenter); 1047 1048 float lum = clamp(dot(cellCol, vec3(0.2126, 0.7152, 0.0722)), 0.0, 1.0); 1049 1050 // Density â quantise tone into a limited number of glyph levels (selection 1051 // uses the raw luminance so Exposure never flattens the character variation). 1052 float levels = max(u_asciiDensity, 2.0); 1053 float sel = clamp(floor(lum * levels) / (levels - 1.0), 0.0, 1.0); 1054 1055 float glyph = asciiGlyph(cellUv, sel); 1056 // Fill â glyph weight / coverage. 1057 glyph = clamp(glyph * u_asciiFill, 0.0, 1.0); 1058 1059 // Ink follows the source tone â Mono = luminance grey, Source = colour â just
1060 // like Dither's Luma / RGB quantise. This natural falloff (dark cells â dark 1061 // glyphs) is what makes the image glow. Exposure multiplies the ink, so it's a 1062 // real, visible brightness control. 1063 vec3 base = u_asciiColor == 1 ? cellCol : vec3(lum); 1064 vec3 fg = clamp(base * u_asciiExposure, 0.0, 1.0); 1065 vec3 bg = mix(baseCol, vec3(0.0), u_asciiBackground); 1066 1067 // Screen/Add composite ONLY the glyph ink (ink on black), so the gaps leave the 1068 // scene untouched and just the glyphs add light. Compositing the whole layer 1069 // (background included) would screen the bright scene over itself â white-out. 1070 vec3 ink = fg * glyph; 1071 1072 vec3 blended; 1073 if(u_asciiBlend == 1){ 1074 blended = 1.0 - (1.0 - baseCol) * (1.0 - ink); // screen â glyphs glow 1075 } else if(u_asciiBlend == 2){ 1076 blended = min(baseCol + ink, vec3(1.0)); // add 1077 } else { 1078 blended = mix(bg, fg, glyph); // normal â uses Background 1079 } 1080 1081 return mix(baseCol, blended, u_asciiOpacity); 1082} 1083`,v=` 1084vec2 halftoneSampleUv(vec2 uv, vec2 cellSize){ 1085 vec2 sampleUv = uv; 1086 float rowIndex = floor(uv.y / cellSize.y); 1087 if(u_halftoneStagger == 1 && mod(rowIndex, 2.0) == 1.0){ 1088 sampleUv.x += cellSize.x * 0.5; 1089 } 1090 return sampleUv; 1091} 1092 1093float halftoneAa(){ 1094 return 1.0 / max(u_halftoneSize * refPxScale(), 4.0); 1095} 1096 1097// Signed distance to an upward equilateral triangle of size r, centred at the 1098// origin (IQ). Negative inside â thresholded like the other shapes so its area 1099// still scales with r-squared (and thus with coverage, for correct tone). 1100float sdHalftoneTriangle(vec2 p, float r){ 1101 const float k = 1.7320508; // sqrt(3) 1102 p.x = abs(p.x) - r; 1103 p.y = p.y + r / k; 1104 if(p.x + k * p.y > 0.0) p = vec2(p.x - k * p.y, -k * p.x - p.y) * 0.5; 1105 p.x -= clamp(p.x, -2.0 * r, 0.0); 1106 return -length(p) * sign(p.y); 1107} 1108 1109float halftoneInk(vec2 cellUv, float lum){ 1110 float dist = length(cellUv); 1111 float aa = halftoneAa(); 1112 float maxR = u_halftoneDotScale * 0.5; 1113 float coverage = clamp(lum, 0.0, 1.0); 1114 float radius = maxR * sqrt(0.08 + 0.92 * coverage); 1115 1116 if(u_halftoneStyle == 1){ 1117 float ringThick = maxR * 0.22; 1118 float innerR = max(radius - ringThick, 0.0); 1119 float outer = smoothstep(radius - aa, radius + aa, dist); 1120 float inner = smoothstep(innerR - aa, innerR + aa, dist); 1121 return inner * (1.0 - outer); 1122 } 1123 1124 if(u_halftoneStyle == 2){ 1125 if(coverage >= 0.5){ 1126 return 1.0 - smoothstep(radius - aa, radius + aa, dist); 1127 } 1128 float squareEdge = maxR * 0.92; 1129 float square = 1.0 - smoothstep(squareEdge - aa, squareEdge + aa, max(abs(cellUv.x), abs(cellUv.y))); 1130 float invLum = clamp((0.5 - coverage) * 2.0, 0.0, 1.0); 1131 float whiteR = maxR * sqrt(invLum); 1132 float whiteDot = 1.0 - smoothstep(whiteR - aa, whiteR + aa, dist); 1133 return square * (1.0 - whiteDot); 1134 } 1135 1136 if(u_halftoneStyle == 3){ 1137 float uniformR = maxR * 0.92; 1138 return 1.0 - smoothstep(uniformR - aa, uniformR + aa, dist); 1139 } 1140 1141 if(u_halftoneStyle == 4){ 1142 // Triangle: same coverage-driven size, thresholded from the SDF. 1143 float sd = sdHalftoneTriangle(cellUv, radius); 1144 return 1.0 - smoothstep(-aa, aa, sd); 1145 } 1146 1147 if(u_halftoneStyle == 5){ 1148 // Rhombus (diamond): the L1 norm gives a square rotated 45\xb0. 1149 float dRhombus = abs(cellUv.x) + abs(cellUv.y); 1150 return 1.0 - smoothstep(radius - aa, radius + aa, dRhombus); 1151 } 1152 1153 return 1.0 - smoothstep(radius - aa, radius + aa, dist); 1154} 1155 1156vec3 halftoneEmbossShade(vec2 cellUv, vec3 col, float ink){ 1157 if(u_halftoneEmboss <= 0.0 || ink <= 0.001) return col; 1158 float bevel = (-cellUv.x + cellUv.y) * 2.2; 1159 float shade = 1.0 + u_halftoneEmboss * bevel * 0.24; 1160 return col * clamp(shade, 0.58, 1.32); 1161} 1162 1163vec3 applyHalftone(vec2 uv, vec3 baseCol){ 1164 float cellPx = max(u_halftoneSize * refPxScale(), 4.0); 1165 vec2 cellSize = vec2(cellPx) / u_res; 1166 vec2 sampleUv = halftoneSampleUv(uv, cellSize); 1167 vec2 cellCenter = cellSize * (floor(sampleUv / cellSize) + 0.5); 1168 vec3 cellCol = colorPipelineAt(cellCenter); 1169 1170 float lum = dot(cellCol, vec3(0.2126, 0.7152, 0.0722)); 1171 lum = clamp(lum, 0.0, 1.0); 1172 1173 vec2 cellUv = fract(sampleUv / cellSize) - 0.5; 1174 float ink = halftoneInk(cellUv, lum); 1175 1176 vec3 inkCol = u_halftoneColor == 1 ? cellCol : vec3(lum); 1177 inkCol = halftoneEmbossShade(cellUv, inkCol, ink); 1178 vec3 paper = mix(baseCol, u_halftonePaperColor, u_halftoneBackgroun
1178d); 1179 return mix(paper, inkCol, clamp(ink, 0.0, 1.0)); 1180} 1181`,_=` 1182vec3 applyPlaid(vec2 uv, vec3 baseCol){ 1183 float aspect = u_res.x / u_res.y; 1184 float ca = cos(u_plaidAngle); 1185 float sa = sin(u_plaidAngle); 1186 1187 // Rotate the sampling domain around the frame center (aspect-corrected) so 1188 // the grid can sit at any angle without skewing on non-square canvases. 1189 vec2 pu = uv - 0.5; 1190 pu.x *= aspect; 1191 vec2 ru = vec2(pu.x * ca + pu.y * sa, -pu.x * sa + pu.y * ca); 1192 1193 vec2 cellSize = vec2(max(u_plaidColumnSize * refPxScale(), 4.0), max(u_plaidRowSize * refPxScale(), 4.0)) / u_res; 1194 float colCenterRu = cellSize.x * (floor(ru.x / cellSize.x) + 0.5); 1195 float rowCenterRu = cellSize.y * (floor(ru.y / cellSize.y) + 0.5); 1196 1197 // Band coverage: 1 at the cell center, falling to 0 past plaidBandWidth so a 1198 // paper gap can show between bands (with a hairline of AA at the edge). 1199 float bw = clamp(u_plaidBandWidth, 0.02, 1.0); 1200 float aaX = 1.0 / max(u_plaidColumnSize * refPxScale(), 4.0); 1201 float aaY = 1.0 / max(u_plaidRowSize * refPxScale(), 4.0); 1202 float colFrac = abs(fract(ru.x / cellSize.x) - 0.5) * 2.0; 1203 float rowFrac = abs(fract(ru.y / cellSize.y) - 0.5) * 2.0; 1204 float colCoverage = 1.0 - smoothstep(bw - aaX, bw + aaX, colFrac); 1205 float rowCoverage = 1.0 - smoothstep(bw - aaY, bw + aaY, rowFrac); 1206 1207 // Map each band's rotated cell-center back into real scene UV so its tint 1208 // can be sampled from the (possibly rotated) gradient itself. 1209 vec2 vSampleRu = vec2(colCenterRu, 0.0); 1210 vec2 vSampleUv = vec2(vSampleRu.x * ca - vSampleRu.y * sa, vSampleRu.x * sa + vSampleRu.y * ca); 1211 vSampleUv = clamp(vec2(vSampleUv.x / aspect, vSampleUv.y) + 0.5, 0.0, 1.0); 1212 1213 vec2 hSampleRu = vec2(0.0, rowCenterRu); 1214 vec2 hSampleUv = vec2(hSampleRu.x * ca - hSampleRu.y * sa, hSampleRu.x * sa + hSampleRu.y * ca); 1215 hSampleUv = clamp(vec2(hSampleUv.x / aspect, hSampleUv.y) + 0.5, 0.0, 1.0); 1216 1217 vec3 vTint = u_plaidColorMode == 1 ? colorPipelineAt(vSampleUv) : u_plaidInkColor; 1218 vec3 hTint = u_plaidColorMode == 1 ? colorPipelineAt(hSampleUv) : u_plaidInkColor; 1219 1220 vec3 paper = mix(baseCol, vec3(1.0), u_plaidBackground); 1221 1222 vec3 col = paper; 1223 vec3 vBlended = applyGradientBlend(col, vTint, u_plaidBlendMode); 1224 col = mix(col, vBlended, u_plaidColumnStrength * colCoverage); 1225 vec3 hBlended = applyGradientBlend(col, hTint, u_plaidBlendMode); 1226 col = mix(col, hBlended, u_plaidRowStrength * rowCoverage); 1227 1228 return col; 1229} 1230`;e.s(["ASCII",0,h,"BLEND_MODES",0,p,"BLOBS",0,l,"DITHER",0,c,"FIELD_COLOR",0,s,"GLASS",0,f,"GRADIENT_STOPS",0,o,"HALFTONE",0,v,"LIGHT",0,m,"NOISE_FIELD",0,r,"PIXELATE",0,u,"PLAID",0,_,"VERTEX_SHADER",0,t,"WAVES_DISPLACE",0,n,"buildFragmentShader",0,function(e){let t,o=a+r+(e.blobs?l:i)+s+u;return e.wavesDisplace&&(o+=n),e.glass&&(o+=f),e.dither&&(o+=c),(e.image||e.plaid||e.grainBlend)&&(o+=p),e.image&&(o+=d),o+=(t=` 1231vec3 fieldColorAt(vec2 uv){ 1232 uv = applyPixelate(uv); 1233${e.wavesDisplace?" uv = wavesDisplaceUv(uv);\n":""}${e.glass?" uv = applyDistortion(uv);\n":""}${e.image?" return compositeUpload(uv);\n":" return fieldColorGenerate(uv);\n"}} 1234`,t+` 1235vec3 sceneWithAberration(vec2 uv){ 1236${e.aberration?` vec2 dir = normalize(uv - 0.5 + 1e-5); 1237 float amt = u_aberration * 0.02; 1238 vec3 cR = fieldColorAt(uv + dir * amt); 1239 vec3 cG = fieldColorAt(uv); 1240 vec3 cB = fieldColorAt(uv - dir * amt); 1241 return vec3(cR.r, cG.g, cB.b); 1242`:" return fieldColorAt(uv);\n"}} 1243`+` 1244vec3 colorPipelineAt(vec2 uv){ 1245 vec3 col = sceneWithAberration(uv); 1246${e.glass?` if(u_distortion == 1){ col = applyFlutedGlassShading(col, uv); } 1247 else if(u_distortion == 2){ col = applyMosaicGlassShading(col, uv); }
1248 else if(u_distortion == 3){ col = applyRippleGlassShading(col, uv); } 1249`:""}${e.dither?" col = applyDitherAt(col, uv * u_res);\n":""} return col; 1250} 1251`),e.light&&(o+=m),e.ascii&&(o+=h),e.halftone&&(o+=v),e.plaid&&(o+=_),o+=` 1252void main(){ 1253 vec2 uv = gl_FragCoord.xy / u_res; 1254 vec3 col = colorPipelineAt(uv); 1255${e.light?" col = applyLight(uv, col);\n":""}${e.plaid?" col = applyPlaid(uv, col);\n":""}${e.halftone?" col = applyHalftone(uv, col);\n":""}${e.ascii?" col = applyAscii(uv, col);\n":""} float gr = hash12(floor(gl_FragCoord.xy / max(u_grainsize, 0.5)) + u_grainSeed + mod(floor(u_time * 24.0), floor(TWO_PI * 24.0)) * 137.0) - 0.5; 1256${e.grainBlend?" vec3 grainLayer = clamp(vec3(0.5 + gr), 0.0, 1.0);\n col = mix(col, applyGradientBlend(col, grainLayer, u_grainBlendMode), u_grain);\n":" col += gr * u_grain;\n"} 1257 // Sub-LSB triangular dither breaks 8-bit banding on smooth gradients. It's 1258 // imperceptible on screen but keeps exported PNG/video gradients buttery 1259 // (the encoder no longer has hard bands to blockify). 1260 float dth = (hash12(gl_FragCoord.xy) + hash12(gl_FragCoord.xy + 41.7) - 1.0) / 255.0; 1261 col += dth; 1262 fragColor = vec4(clamp(col, 0.0, 1.0), 1.0); 1263} 1264`},"shaderFeatureKey",0,function(e){return(e.image?"1":"0")+(e.aberration?"1":"0")+(e.glass?"1":"0")+(e.wavesDisplace?"1":"0")+(e.dither?"1":"0")+(e.ascii?"1":"0")+(e.halftone?"1":"0")+(e.plaid?"1":"0")+(e.light?"1":"0")+(e.blobs?"1":"0")+(e.grainBlend?"1":"0")}])},873667,e=>{"use strict";let t=" .'`^\",:;Il!i><~+_-?][}{1)(|\\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$",a=t.length;var o=e.i(723225);function r(e){let t=("string"==typeof e?e:"").replace("#","");return[parseInt(t.slice(0,2),16)/255||0,parseInt(t.slice(2,4),16)/255||0,parseInt(t.slice(4,6),16)/255||0]}var l=e.i(587114);let i=`#version 300 es 1265precision highp float; 1266out vec4 fragColor; 1267uniform sampler2D u_image; 1268uniform vec2 u_res; 1269uniform vec2 u_direction; 1270uniform float u_spread; 1271 1272void main(){ 1273 vec2 uv = gl_FragCoord.xy / u_res; 1274 vec2 step = u_direction * u_spread / u_res; 1275 1276 vec3 acc = texture(u_image, uv).rgb * 0.2270270270; 1277 acc += texture(u_image, uv + step * 1.3846153846).rgb * 0.3162162162; 1278 acc += texture(u_image, uv - step * 1.3846153846).rgb * 0.3162162162; 1279 acc += texture(u_image, uv + step * 3.2307692308).rgb * 0.0702702703; 1280 acc += texture(u_image, uv - step * 3.2307692308).rgb * 0.0702702703; 1281 1282 fragColor = vec4(acc, 1.0); 1283} 1284`,s=`#version 300 es 1285precision highp float; 1286out vec4 fragColor; 1287uniform sampler2D u_image; 1288uniform vec2 u_res; 1289 1290void main(){ 1291 fragColor = vec4(texture(u_image, gl_FragCoord.xy / u_res).rgb, 1.0); 1292} 1293`;function u(e,t,a){let o=e.createShader(t);if(!o)throw Error("Failed to create blur shader");if(e.shaderSource(o,a),e.compileShader(o),!e.getShaderParameter(o,e.COMPILE_STATUS)){let t=e.getShaderInfoLog(o);throw e.deleteShader(o),Error(`Blur shader compile error: ${t}`)}return o}function n(e,t){let a=u(e,e.VERTEX_SHADER,l.VERTEX_SHADER),o=u(e,e.FRAGMENT_SHADER,t),r=e.createProgram();return r?(e.attachShader(r,a),e.attachShader(r,o),e.linkProgram(r),e.deleteShader(a),e.deleteShader(o),e.getProgramParameter(r,e.LINK_STATUS))?r:(e.deleteProgram(r),null):null}function f(e,t,a){let o=e.createFramebuffer(),r=e.createTexture();return o&&r?(e.bindTexture(e.TEXTURE_2D,r),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,t,a,0,e.RGBA,e.UNSIGNED_BYTE,null),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.bindFramebuffer(e.FRAMEBUFFER,o),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,r,0),e.bindFramebuffer(e.FRAMEBUFFER,null),e.bindTexture(e.TEXTURE_2D,null),{framebuffer:o,texture:r,width:t,height:a}):null}function c(e,t){e.deleteFramebuffer(t.framebuffer),e.deleteTexture(t.texture)}function p(e,t){if(t)for(let a of t.levels)c(e,a.a),c(e,a.b)}function d(e,t,a){e.bindBuffer(e.ARRAY_BUFFER,t),e.enableVertexAttribArray(a),e.vertexAttribPointer(a,2,e.FLOAT,!1,0,0),e.drawArrays(e.TRIANGLES,0,3)}function m(e,t,a,o,r,l,i){e.bindFramebuffer(e.FRAMEBUFFER,r.framebuffer),e.viewport(0,0,r.width,r.height),e.useProgram(t.program),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,o),e.uniform1i(t.uImage,0),e.uniform2f(t.uRes,r.width,r.height),e.uniform2f(t.uDirection,+!!i,+!i),e.uniform1f(t.uSpread,l),d(e,a,t.blurAttrib),e.bindFramebuffer(e.FRAMEBUFFER,null)}class h{entries=new Map;lastReadyProgram=null;get(e){return this.entries.get(e)}setPending(e,t){this.entries.has(e)||this.entries.set(e,{status:"pending",dispatched:t})}setReady(e,t){this.entries.set(e,{status:"ready",program:t}),this.lastReadyProgram=t}setFailed(e,t){this.entries.set(e,{status:"failed",message:t})}markUsed(e){let t=this.entries.get(e);t?.status==="ready"&&(this.lastReadyProgram=t.program)}lastReady(){return this.lastReadyProgram}pendingEntries(){let e=[];for(let[t,a]of this.entries)"pending"===a.status&&e.push({key:t,dispatched:a.dispatched});return e}hasPending(){for(let e of this.entries.values())if("pending"===e.status)return!0;return!1}get size(){return this.entries.size}readyPrograms(){let e=[];for(let t of this.entries.values())"ready"===t.status&&e.push(t.program);return e}clear(){this.entries.clear(),this.lastReadyProgram=null}}let v=["u_res","u_time","u_warp","u_flow","u_scale","u_octaves","u_contrast","u_grain","u_grainsize","u_grainBlendMode","u_seed","u_shape","u_angle","u_center","u_softness","u_blur","u_aberration","u_distortion","u_distAmount","u_distCount","u_distShadows","u_distHighlights","u_distBump","u_pixelate","u_ditherMode","u_ditherMatrix","u_ditherPattern","u_ditherLevels","u_ditherStrength","u_ditherColor","u_asciiSize","u_asciiCellRatio","u_asciiStyle","u_asciiColor","u_asciiBackgroun
1293d","u_asciiExposure","u_asciiFill","u_asciiDensity","u_asciiScatter","u_asciiOpacity","u_asciiBlend","u_asciiCharCount","u_asciiAtlas","u_halftoneSize","u_halftoneStyle","u_halftoneStagger","u_halftoneDotScale","u_halftoneColor","u_halftoneBackground","u_halftonePaperColor","u_halftoneEmboss","u_plaidColumnSize","u_plaidRowSize","u_plaidColumnStrength","u_plaidRowStrength","u_plaidBandWidth","u_plaidBackground","u_plaidAngle","u_plaidColorMode","u_plaidInkColor","u_plaidBlendMode","u_light","u_lightAmount","u_lightSize","u_lightCenter","u_waves","u_wavesFrequency","u_wavesAmplitude","u_wavesPhase","u_wavesFalloff","u_wavesDisplace","u_wavesCenter","u_blobCount","u_blobSize","u_blobSpread","u_blobSoftness","u_blobMerge","u_blobSeed","u_blobDrift","u_blobAmount","u_blobLayout","u_blobDensity","u_blobShape","u_blobShapeAmount","u_gradientOverlay","u_blendMode","u_grainSeed","u_imageRes","u_imageScale","u_imageOffset","u_imgBrightness","u_imgContrast","u_imgSaturation","u_imgExposure","u_imgFilter","u_image","u_stopCount","u_stopPos[0]","u_stopPos[1]","u_stopPos[2]","u_stopPos[3]","u_stopPos[4]","u_stopPos[5]","u_stopPos[6]","u_stopPos[7]","u_stopCol[0]","u_stopCol[1]","u_stopCol[2]","u_stopCol[3]","u_stopCol[4]","u_stopCol[5]","u_stopCol[6]","u_stopCol[7]"];function _(e,t){t&&(e.deleteFramebuffer(t.framebuffer),e.deleteTexture(t.texture))}function g(e,t,a){let o=new Uint8Array(t*a*4);e.readPixels(0,0,t,a,e.RGBA,e.UNSIGNED_BYTE,o);let r=4*t,l=new Uint8Array(r);for(let e=0;e<Math.floor(a/2);e++){let t=e*r,i=(a-e-1)*r;l.set(o.subarray(t,t+r)),o.copyWithin(t,i,i+r),o.set(l,i)}let i=document.createElement("canvas");i.width=t,i.height=a;let s=i.getContext("2d");if(!s)return null;let u=s.createImageData(t,a);return u.data.set(o),s.putImageData(u,0,0),i}class b{gl;onError;buffer;sourceTexture;asciiTexture;displayTexture;programCache=new h;parallelExt;blurProgram=null;blurResources=null;exportFbo=null;lastUploadedImage=null;constructor(e,o={}){const r=e.createBuffer(),l=e.createTexture(),i=e.createTexture();if(!r||!l||!i)throw Error("Failed to create WebGL buffers");this.parallelExt=e.getExtension("KHR_parallel_shader_compile"),e.bindBuffer(e.ARRAY_BUFFER,r),e.bufferData(e.ARRAY_BUFFER,new Float32Array([-1,-1,3,-1,-1,3]),e.STATIC_DRAW),e.enable(e.BLEND),e.blendFunc(e.SRC_ALPHA,e.ONE_MINUS_SRC_ALPHA),function(e,t,a){e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,t),e.pixelStorei(e.UNPACK_FLIP_Y_WEBGL,!0),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,e.RGBA,e.UNSIGNED_BYTE,a),e.pixelStorei(e.UNPACK_FLIP_Y_WEBGL,!1),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST)}(e,i,function(){let e=document.createElement("canvas");e.width=32*a,e.height=32;let o=e.getContext("2d");if(!o)return e;o.fillStyle="#000000",o.fillRect(0,0,e.width,e.height),o.fillStyle="#ffffff",o.font=`500 ${Math.round(26.24)}px "Courier New", Courier, monospace`,o.textAlign="center",o.textBaseline="middle";for(let e=0;e<a;e++)o.fillText(t[e],(e+.5)*32,16.64);return e}()),this.gl=e,this.onError=o.onError,this.buffer=r,this.sourceTexture=l,this.asciiTexture=i,this.displayTexture=l}syncImage(e,t,a){if(e){if(e!==this.lastUploadedImage){var o,r;o=this.gl,r=this.sourceTexture,o.activeTexture(o.TEXTURE0),o.bindTexture(o.TEXTURE_2D,r),o.pixelStorei(o.UNPACK_FLIP_Y_WEBGL,!0),o.texImage2D(o.TEXTURE_2D,0,o.RGBA,o.RGBA,o.UNSIGNED_BYTE,e.canvas),o.pixelStorei(o.UNPACK_FLIP_Y_WEBGL,!1),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_S,o.CLAMP_TO_EDGE),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_T,o.CLAMP_TO_EDGE),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MIN_FILTER,o.LINEAR),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MAG_FILTER,o.LINEAR),this.lastUploadedImage=e}this.updateBlur(e,t?a:0)}else this.displayTexture=this.sourceTexture,this.lastUploadedImage=null}updateBlur(e,t){let a=this.gl;if(t<=.001){this.displayTexture=this.sourceTexture;return}this.blurProgram||(this.blurProgram=function(e){try{let t=n(e,i),a=n(e,s);if(!t||!a)return t&&e.deleteProgram(t),a&&e.deleteProgram(a),null;return{program:t,copyProgram:a,blurAttrib:e.getAttribLocation(t,"p"),copyAttrib:e.getAttribLocation(a,"p"),uImage:e.getUniformLocation(t,"u_image"),uRes:e.getUniformLocation(t,"u_res"),uDirection:e.getUniformLocation(t,"u_direction"),uSpread:e.getUniformLocation(t,"u_spread"),cImage:e.getUniformLocation(a,"u_image"),cRes:e.getUniformLocation(a,"u_res")}}catch{return null}}(a));let o=this.blurProgram;if(!o){this.displayTexture=this.sourceTexture;return}this.blurResources=function(e,t,a,o,r){if(!a)return null;if(t&&t.baseWidth===o&&t.baseHeight===r&&t.program===a)return t;t&&p(e,t);let l=[];for(let t=0;t<=5;t++){let a=Math.max(4,Math.ceil(o/2**t)),i=Math.max(4,Math.ceil(r/2**t)),s=f(e,a,i),u=f(e,a,i);
1293if(!s||!u){s&&c(e,s),u&&c(e,u);break}if(l.push({a:s,b:u}),a<=4&&i<=4)break}return 0===l.length?null:{program:a,baseWidth:o,baseHeight:r,levels:l}}(a,this.blurResources,o,e.width,e.height);let r=this.blurResources;if(!r){this.displayTexture=this.sourceTexture;return}this.displayTexture=function(e,t,a,o,r,l,i){let s=Math.max(r,l)*(i*i*.09+.015*i);if(s<.5)return o;let u=t.levels.length-1,n=Math.floor(Math.log2(Math.max(s,1)/4));n=Math.min(Math.max(n,0),u);let f=Math.max(.6,s/2**n/1.69),c=o;for(let o=1;o<=n;o++){var p,h,v;p=t.program,h=c,v=t.levels[o].a,e.bindFramebuffer(e.FRAMEBUFFER,v.framebuffer),e.viewport(0,0,v.width,v.height),e.useProgram(p.copyProgram),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,h),e.uniform1i(p.cImage,0),e.uniform2f(p.cRes,v.width,v.height),d(e,a,p.copyAttrib),e.bindFramebuffer(e.FRAMEBUFFER,null),c=t.levels[o].a.texture}let _=t.levels[n];m(e,t.program,a,c,_.b,f,!0),m(e,t.program,a,_.b.texture,_.a,f,!1);let g=_.a.texture;for(let o=n;o>0;o--){let r=t.levels[o-1];m(e,t.program,a,g,r.b,1.3,!0),m(e,t.program,a,r.b.texture,r.a,1.3,!1),g=r.a.texture}return g}(a,r,this.buffer,this.sourceTexture,e.width,e.height,t)}resolve(e,t){try{var a;let o,r=(a=this.gl,o=e=>{throw a.deleteShader(t.vs),a.deleteShader(t.fs),a.deleteProgram(t.program),Error(e)},a.getShaderParameter(t.vs,a.COMPILE_STATUS)||o(`Shader compile error: ${a.getShaderInfoLog(t.vs)}`),a.getShaderParameter(t.fs,a.COMPILE_STATUS)||o(`Shader compile error: ${a.getShaderInfoLog(t.fs)}`),a.getProgramParameter(t.program,a.LINK_STATUS)||o(`Program link error: ${a.getProgramInfoLog(t.program)}`),a.deleteShader(t.vs),a.deleteShader(t.fs),t.program),l={program:r,uniforms:function(e,t){let a={};for(let o of v)a[o]=e.getUniformLocation(t,o);return a}(this.gl,r),attrib:this.gl.getAttribLocation(r,"p")};return this.programCache.setReady(e,l),l}catch(a){let t=a instanceof Error?a.message:"Shader compilation failed";return this.programCache.setFailed(e,t),console.error(t),this.onError?.(t),null}}isCompileDone(e){return!this.parallelExt||this.gl.getProgramParameter(e,this.parallelExt.COMPLETION_STATUS_KHR)}getProgram(e,t="block"){let a,o=(0,l.shaderFeatureKey)(e),r=this.programCache.get(o);if(r?.status==="failed")return null;if(r?.status==="ready")return this.programCache.markUsed(o),r.program;let i=r?.status==="pending"?r.dispatched:(a=function(e,t){let a=e.createShader(e.VERTEX_SHADER),o=e.createShader(e.FRAGMENT_SHADER),r=e.createProgram();if(!a||!o||!r)throw Error("Failed to create program");return e.shaderSource(a,l.VERTEX_SHADER),e.compileShader(a),e.shaderSource(o,(0,l.buildFragmentShader)(t)),e.compileShader(o),e.attachShader(r,a),e.attachShader(r,o),e.linkProgram(r),{program:r,vs:a,fs:o}}(this.gl,e),this.programCache.setPending(o,a),a);if("block"===t||this.isCompileDone(i.program)){let e=this.resolve(o,i);return e&&this.programCache.markUsed(o),e}return"fallback"===t?this.programCache.lastReady():null}pollPending(){let e=!1;for(let{key:t,dispatched:a}of this.programCache.pendingEntries())this.isCompileDone(a.program)&&(this.resolve(t,a),e=!0);return e}hasPending(){return this.programCache.hasPending()}setUniforms(e,t,l,i,s){let u=this.gl,{gradientParams:n,image:f,imageLayer:c}=t,p=null!==f&&c.enabled;u.uniform2f(e.u_res,i,s),u.uniform1f(e.u_time,l),u.uniform1f(e.u_warp,n.warp),u.uniform1f(e.u_flow,n.flow),u.uniform1f(e.u_scale,n.scale),u.uniform1i(e.u_octaves,n.detail),u.uniform1f(e.u_contrast,n.contrast),u.uniform1f(e.u_grain,n.grainEnabled?n.grain:0),u.uniform1f(e.u_grainsize,n.grainSize),u.uniform1i(e.u_grainBlendMode,n.grainBlendMode),u.uniform1f(e.u_seed,n.seed),u.uniform1f(e.u_grainSeed,n.seed),u.uniform1i(e.u_shape,n.shape),u.uniform1f(e.u_angle,n.angle),u.uniform2f(e.u_center,n.centerX,n.centerY),u.uniform1f(e.u_softness,n.softness),u.uniform1f(e.u_blur,0),u.uniform1f(e.u_gradientOverlay,p?c.opacity:0),u.uniform1i(e.u_blendMode,p?c.blendMode:0),u.uniform1f(e.u_imgBrightness,p?c.brightness:0),u.uniform1f(e.u_imgContrast,p?c.contrast:1),u.uniform1f(e.u_imgSaturation,p?c.saturation:1),u.uniform1f(e.u_imgExposure,p?c.exposure:0),u.uniform1i(e.u_imgFilter,p?c.filter:0),u.uniform1f(e.u_aberration,n.aberration),u.uniform1i(e.u_distortion,n.distortion),u.uniform1f(e.u_distAmount,n.distAmount),u.uniform1f(e.u_distCount,n.distCount),u.uniform1f(e.u_distShadows,n.distShadows),u.uniform1f(e.u_distHighlights,n.distHighlights),u.uniform1f(e.u_distBump,n.distBump),u.uniform1f(e.u_pixelate,n.pixelate),u.uniform1i(e.u_ditherMode,n.ditherMode),u.uniform1i(e.u_ditherMatrix,n.ditherMatrix),u.uniform1f(e.u_ditherPattern,n.ditherPattern),u.uniform1f(e.u_ditherLevels,n.ditherLevels),u.uniform1f(e.u_ditherStrength,n.ditherStrength),u.uniform1i(e.u_ditherColor,+!!n.ditherColor),u.uniform1f(e.u_asciiSize,n.asciiSize),u.uniform1f(e.u_asciiCellRatio,n.asciiCellRatio),u.uniform1i(e.u_asciiStyle,n.asciiStyle),u.uniform1i(e.u_asciiColor,+!!n.asciiColor),u.uniform1f(e.u_asciiBackgroun
1293d,n.asciiBackground),u.uniform1f(e.u_asciiExposure,n.asciiExposure),u.uniform1f(e.u_asciiFill,n.asciiFill),u.uniform1f(e.u_asciiDensity,n.asciiDensity),u.uniform1f(e.u_asciiScatter,n.asciiScatter),u.uniform1f(e.u_asciiOpacity,n.asciiOpacity),u.uniform1i(e.u_asciiBlend,n.asciiBlend),u.uniform1f(e.u_asciiCharCount,a),u.uniform1f(e.u_halftoneSize,n.halftoneSize),u.uniform1i(e.u_halftoneStyle,n.halftoneStyle),u.uniform1i(e.u_halftoneStagger,+!!n.halftoneStagger),u.uniform1f(e.u_halftoneDotScale,n.halftoneDotScale),u.uniform1i(e.u_halftoneColor,+!!n.halftoneColor),u.uniform1f(e.u_halftoneBackground,n.halftoneBackground);{let[t,a,o]=r(n.halftonePaperColor);u.uniform3f(e.u_halftonePaperColor,t,a,o)}u.uniform1f(e.u_halftoneEmboss,n.halftoneEmboss),u.uniform1f(e.u_plaidColumnSize,n.plaidColumnSize),u.uniform1f(e.u_plaidRowSize,n.plaidRowSize),u.uniform1f(e.u_plaidColumnStrength,n.plaidColumnStrength),u.uniform1f(e.u_plaidRowStrength,n.plaidRowStrength),u.uniform1f(e.u_plaidBandWidth,n.plaidBandWidth),u.uniform1f(e.u_plaidBackground,n.plaidBackground),u.uniform1f(e.u_plaidAngle,n.plaidAngle),u.uniform1i(e.u_plaidColorMode,+!!n.plaidColorMode);{let[t,a,o]=r(n.plaidInkColor);u.uniform3f(e.u_plaidInkColor,t,a,o)}u.uniform1i(e.u_plaidBlendMode,n.plaidBlendMode),u.uniform1i(e.u_light,n.light),u.uniform1f(e.u_lightAmount,n.lightAmount),u.uniform1f(e.u_lightSize,n.lightSize),u.uniform2f(e.u_lightCenter,n.lightCenterX,n.lightCenterY),u.uniform1i(e.u_waves,+!!n.waves),u.uniform1f(e.u_wavesFrequency,n.wavesFrequency),u.uniform1f(e.u_wavesAmplitude,n.wavesAmplitude),u.uniform1f(e.u_wavesPhase,n.wavesPhase),u.uniform1f(e.u_wavesFalloff,n.wavesFalloff),u.uniform1f(e.u_wavesDisplace,n.wavesDisplace),u.uniform2f(e.u_wavesCenter,n.wavesCenterX,n.wavesCenterY),u.uniform1i(e.u_blobCount,Math.round(n.blobCount)),u.uniform1f(e.u_blobSize,n.blobSize),u.uniform1f(e.u_blobSpread,n.blobSpread),u.uniform1f(e.u_blobSoftness,n.blobSoftness),u.uniform1f(e.u_blobMerge,n.blobMerge),u.uniform1f(e.u_blobSeed,n.blobSeed),u.uniform1f(e.u_blobDrift,n.blobDrift),u.uniform1f(e.u_blobAmount,n.blobs?n.blobAmount:0),u.uniform1i(e.u_blobLayout,n.blobLayout),u.uniform1f(e.u_blobDensity,n.blobDensity),u.uniform1i(e.u_blobShape,n.blobShape),u.uniform1f(e.u_blobShapeAmount,n.blobShapeAmount);let d=(0,o.normalizeColorStops)(n.colorStops);u.uniform1i(e.u_stopCount,d.length);for(let t=0;t<o.MAX_COLOR_STOPS;t++){let a=e[`u_stopPos[${t}]`],o=e[`u_stopCol[${t}]`];if(t<d.length){let[e,l,i]=r(d[t].color);a&&u.uniform1f(a,d[t].position),o&&u.uniform3f(o,e,l,i)}else if(a&&o){let e=d[d.length-1],[t,l,i]=r(e.color);u.uniform1f(a,e.position),u.uniform3f(o,t,l,i)}}this.asciiTexture&&(u.activeTexture(u.TEXTURE1),u.bindTexture(u.TEXTURE_2D,this.asciiTexture),u.uniform1i(e.u_asciiAtlas,1)),p&&this.displayTexture&&f&&(u.activeTexture(u.TEXTURE0),u.bindTexture(u.TEXTURE_2D,this.displayTexture),u.uniform1i(e.u_image,0),u.uniform2f(e.u_imageRes,f.width,f.height),u.uniform1f(e.u_imageScale,Math.max(c.scale,.01)),u.uniform2f(e.u_imageOffset,c.offsetX,-c.offsetY))}paint(e,t,a,o,r=null,l="block"){let i=this.gl;if(a<=0||o<=0||i.isContextLost())return!1;let{gradientParams:s,image:u,imageLayer:n}=e,f={image:null!==u&&n.enabled,aberration:s.aberration>.001,glass:0!==s.distortion,wavesDisplace:s.wavesDisplace>0,dither:s.dither,ascii:s.ascii,halftone:s.halftone,plaid:s.plaid,light:0!==s.light,blobs:s.blobs,grainBlend:s.grainEnabled&&0!==s.grainBlendMode},c=this.getProgram(f,l);return!!c&&!(c.attrib<0)&&(i.bindFramebuffer(i.FRAMEBUFFER,r),i.viewport(0,0,a,o),i.clearColor(0,0,0,0),i.clear(i.COLOR_BUFFER_BIT),i.useProgram(c.program),this.setUniforms(c.uniforms,e,t,a,o),i.bindBuffer(i.ARRAY_BUFFER,this.buffer),i.enableVertexAttribArray(c.attrib),i.vertexAttribPointer(c.attrib,2,i.FLOAT,!1,0,0),i.drawArrays(i.TRIANGLES,0,3),i.bindFramebuffer(i.FRAMEBUFFER,null),!0)}renderExport(e,t,a,o){let r=this.gl;if(t<=0||a<=0)return null;let l=function(e,t,a,o){if(t?.width===a&&t.height===o)return t;_(e,t);let r=e.createFramebuffer(),l=e.createTexture();if(!r||!l)return null;e.bindTexture(e.TEXTURE_2D,l),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,a,o,0,e.RGBA,e.UNSIGNED_BYTE,null),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.bindFramebuffer(e.FRAMEBUFFER,r),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,l,0);let i=e.checkFramebufferStatus(e.FRAMEBUFFER)===e.FRAMEBUFFER_COMPLETE;return(e.bindFramebuffer(e.FRAMEBUFFER,null),e.bindTexture(e.TEXTURE_2D,null),i)?{framebuffer:r,texture:l,width:a,height:o}:(e.deleteFramebuffer(r),e.deleteTexture(l),null)}(r,this.exportFbo,t,a);l&&(this.exportFbo=l);let i=l&&this.paint(e,o,t,a,l.framebuffer)?l:null,s=null;return i&&(r.bindFramebuffer(r.FRAMEBUFFER,i.framebuffer),s=g(r,t,a),r.bindFramebuffer(r.FRAMEBUFFER,null)),s}dispose(){let e=this.gl;for(let t of this.programCache.readyPrograms())e.deleteProgram(t.program);if(this.programCache.clear(),_(e,this.exportFbo),this.exportFbo=null,p(e,this.blurResources),this.blurResources=null,this.blurProgram){var t;(t=this.blurProgram)&&(e.deleteProgram(t.program),e.deleteProgram(t.copyProgram)),this.blurProgram=null}e.deleteBuffer(this.buffer),e.deleteTexture(this.sourceTexture),e.deleteTexture(this.asciiTexture)}}e.s(["GradientEngine",0,b,"readFramebufferToCanvas",0,g],873667)}]); 1294 1295//# debugId=668d30c2-9dab-6390-5c59-9408ec5649a2
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.