PageSourceSearch

https://pryzm.design/_next/static/chunks/0~l.lasv1r9qw.js?dpl=dpl_HerVwAD5zHqofdKZKHJNSUQsZoUR

js pryzm.design collected 2026-10-08 00:35:52 UTC 67,471 bytes, 1,295 lines download raw bytes

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.