@pixerate/flair 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -316,6 +316,7 @@ var LOGO = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width=
316
316
  var HEART = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
317
317
  <path fill="#f43f5e" d="M256 452C120 352 40 272 40 172C40 102 95 58 160 58C205 58 238 84 256 118C274 84 307 58 352 58C417 58 472 102 472 172C472 272 392 352 256 452Z"/>
318
318
  </svg>`;
319
+ var ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20C5 15.5 3 12 3 8.5a4.5 4.5 0 0 1 9 0a4.5 4.5 0 0 1 9 0c0 3.5-2 7-9 11.5Z"/></svg>`;
319
320
  var SAMPLE_IMAGES = {
320
321
  landscape: svgDataUrl(LANDSCAPE),
321
322
  city: svgDataUrl(CITY),
@@ -325,7 +326,8 @@ var SAMPLE_IMAGES = {
325
326
  /** A ring-and-dot mark, indigo on transparent. */
326
327
  logo: svgDataUrl(LOGO),
327
328
  /** A heart, rose on transparent. */
328
- heart: svgDataUrl(HEART)
329
+ heart: svgDataUrl(HEART),
330
+ icon: svgDataUrl(ICON)
329
331
  };
330
332
  function charWidth(c) {
331
333
  if (/[iIjl.,:;'!|`]/.test(c)) return 0.3;
@@ -353,7 +355,19 @@ function textImage(text, options = {}) {
353
355
  }
354
356
 
355
357
  // src/catalog/shaders/image.ts
358
+ var BACKDROP_CONTROLS = `
359
+ uniform vec3 uBackdrop; // @control color default=#FFFFFF label="Backdrop"
360
+ uniform float uBackdropAlpha; // @control min=0 max=1 default=1 label="Backdrop opacity"`.trim();
361
+ var BACKDROP = `
362
+ // The sample is read as premultiplied: filtering a straight-alpha image whose clear pixels are
363
+ // black (as browsers decode them) premultiplies it, and the clamp clears colour a clear pixel kept.
364
+ vec4 overBackdrop(vec4 s) {
365
+ vec3 colour = min(s.rgb, vec3(s.a)) + uBackdrop * uBackdropAlpha * (1.0 - s.a);
366
+ float alpha = s.a + uBackdropAlpha * (1.0 - s.a);
367
+ return vec4(colour / max(alpha, 1e-4), alpha);
368
+ }`.trim();
356
369
  var PICTURE = `
370
+ ${BACKDROP}
357
371
  vec3 testCard(vec2 uv) {
358
372
  vec3 sky = mix(vec3(0.98, 0.62, 0.42), vec3(0.24, 0.32, 0.72), uv.y);
359
373
  float sun = smoothstep(0.16, 0.15, length((uv - vec2(0.65, 0.58)) * vec2(1.6, 1.0)));
@@ -364,8 +378,11 @@ vec3 testCard(vec2 uv) {
364
378
  col += 0.04 * step(0.48, max(grid.x, grid.y));
365
379
  return col;
366
380
  }
381
+ vec4 pictureRGBA(sampler2D tex, vec2 uv) {
382
+ return textureSize(tex, 0).x > 1 ? overBackdrop(texture(tex, uv)) : vec4(testCard(fract(uv)), 1.0);
383
+ }
367
384
  vec3 picture(sampler2D tex, vec2 uv) {
368
- return textureSize(tex, 0).x > 1 ? texture(tex, uv).rgb : testCard(fract(uv));
385
+ return pictureRGBA(tex, uv).rgb;
369
386
  }`.trim();
370
387
  var imageRipple = {
371
388
  kind: "shader",
@@ -379,7 +396,8 @@ var imageRipple = {
379
396
  uniform sampler2D uImage; // @control filter=linear wrap=mirror default=${SAMPLE_IMAGES.landscape} label="Image"
380
397
  uniform float uAmplitude; // @control min=0 max=0.05 default=0.012 label="Amplitude"
381
398
  uniform float uFrequency; // @control min=5 max=80 default=30 label="Frequency"
382
- uniform float uSpeed; // @control min=0 max=6 default=2 label="Speed"`,
399
+ uniform float uSpeed; // @control min=0 max=6 default=2 label="Speed"
400
+ ${BACKDROP_CONTROLS}`,
383
401
  PICTURE,
384
402
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
385
403
  vec2 uv = fragCoord / iResolution.xy;
@@ -389,9 +407,9 @@ uniform float uSpeed; // @control min=0 max=6 default=2 label="Speed"`,
389
407
  float r = length(d);
390
408
  float wave = sin(r * uFrequency - iTime * uSpeed) * smoothstep(0.9, 0.0, r);
391
409
  vec2 offset = normalize(d + 1e-5) * wave * uAmplitude;
392
- vec3 col = picture(uImage, uv + offset);
393
- col += 0.6 * max(wave, 0.0) * uAmplitude * 8.0;
394
- fragColor = vec4(col, 1.0);
410
+ vec4 pic = pictureRGBA(uImage, uv + offset);
411
+ vec3 col = pic.rgb + 0.6 * max(wave, 0.0) * uAmplitude * 8.0;
412
+ fragColor = vec4(col, pic.a);
395
413
  }`
396
414
  )
397
415
  };
@@ -407,7 +425,8 @@ var imageGlitch = {
407
425
  uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label="Image"
408
426
  uniform float uAmount; // @control min=0 max=1 default=0.5 label="Amount"
409
427
  uniform float uSplit; // @control min=0 max=0.05 default=0.012 label="RGB split"
410
- uniform float uRate; // @control min=0.5 max=12 default=4 label="Bursts per second"`,
428
+ uniform float uRate; // @control min=0.5 max=12 default=4 label="Bursts per second"
429
+ ${BACKDROP_CONTROLS}`,
411
430
  HASH,
412
431
  PICTURE,
413
432
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
@@ -421,14 +440,13 @@ uniform float uRate; // @control min=0.5 max=12 default=4 label="Bursts pe
421
440
  vec2 st = uv + vec2(shift, 0.0);
422
441
  st = mix(st, floor(st * 24.0) / 24.0, corrupt);
423
442
  float split = uSplit * (0.3 + burst);
424
- vec3 col = vec3(
425
- picture(uImage, st + vec2(split, 0.0)).r,
426
- picture(uImage, st).g,
427
- picture(uImage, st - vec2(split, 0.0)).b
428
- );
443
+ vec4 r = pictureRGBA(uImage, st + vec2(split, 0.0));
444
+ vec4 g = pictureRGBA(uImage, st);
445
+ vec4 b = pictureRGBA(uImage, st - vec2(split, 0.0));
446
+ vec3 col = vec3(r.r, g.g, b.b);
429
447
  col = mix(col, col.bgr, corrupt);
430
448
  col *= 0.92 + 0.08 * sin(fragCoord.y * 2.0);
431
- fragColor = vec4(col, 1.0);
449
+ fragColor = vec4(col, max(max(r.a, g.a), b.a));
432
450
  }`
433
451
  )
434
452
  };
@@ -445,16 +463,17 @@ uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE
445
463
  uniform vec3 uShadow; // @control color default=#1E1B4B label="Shadows"
446
464
  uniform vec3 uHighlight; // @control color default=#FB7185 label="Highlights"
447
465
  uniform float uContrast; // @control min=0.5 max=3 default=1.2 label="Contrast"
448
- uniform float uSweep; // @control min=0 max=1 default=0.3 label="Light sweep"`,
466
+ uniform float uSweep; // @control min=0 max=1 default=0.3 label="Light sweep"
467
+ ${BACKDROP_CONTROLS}`,
449
468
  PICTURE,
450
469
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
451
470
  vec2 uv = fragCoord / iResolution.xy;
452
- vec3 src = picture(uImage, uv);
453
- float l = dot(src, vec3(0.2126, 0.7152, 0.0722));
471
+ vec4 src = pictureRGBA(uImage, uv);
472
+ float l = dot(src.rgb, vec3(0.2126, 0.7152, 0.0722));
454
473
  l = clamp((l - 0.5) * uContrast + 0.5, 0.0, 1.0);
455
474
  float sweep = exp(-pow((uv.x + uv.y * 0.4 - fract(iTime * 0.15) * 2.0 + 0.4) * 6.0, 2.0));
456
475
  vec3 col = mix(uShadow, uHighlight, l) + sweep * uSweep;
457
- fragColor = vec4(col, 1.0);
476
+ fragColor = vec4(col, src.a);
458
477
  }`
459
478
  ),
460
479
  presets: [
@@ -475,14 +494,14 @@ var imagePixelate = {
475
494
  uniform sampler2D uImage; // @control filter=nearest wrap=clamp default=${SAMPLE_IMAGES.landscape} label="Image"
476
495
  uniform float uMaxBlock; // @control min=4 max=128 default=48 label="Largest block (px)"
477
496
  uniform bool uAnimate; // @control default=true label="Animate"
478
- uniform float uProgress; // @control min=0 max=1 default=1 label="Detail (when still)"`,
497
+ uniform float uProgress; // @control min=0 max=1 default=1 label="Detail (when still)"
498
+ ${BACKDROP_CONTROLS}`,
479
499
  PICTURE,
480
500
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
481
501
  float k = uAnimate ? 0.5 - 0.5 * cos(iTime * 6.28318 / 4.0) : uProgress;
482
502
  float block = max(1.0, floor(mix(uMaxBlock, 1.0, k)));
483
503
  vec2 cell = (floor(fragCoord / block) + 0.5) * block;
484
- vec3 col = picture(uImage, cell / iResolution.xy);
485
- fragColor = vec4(col, 1.0);
504
+ fragColor = pictureRGBA(uImage, cell / iResolution.xy);
486
505
  }`
487
506
  )
488
507
  };
@@ -499,7 +518,8 @@ uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE
499
518
  uniform vec3 uInk; // @control color default=#111827 label="Ink"
500
519
  uniform vec3 uPaper; // @control color default=#FEF3C7 label="Paper"
501
520
  uniform float uSpacing; // @control min=3 max=30 default=8 label="Dot spacing (px)"
502
- uniform float uAngle; // @control min=0 max=90 default=45 label="Screen angle (\xB0)"`,
521
+ uniform float uAngle; // @control min=0 max=90 default=45 label="Screen angle (\xB0)"
522
+ ${BACKDROP_CONTROLS}`,
503
523
  PICTURE,
504
524
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
505
525
  float a = radians(uAngle);
@@ -507,11 +527,12 @@ uniform float uAngle; // @control min=0 max=90 default=45 label="Screen ang
507
527
  vec2 q = r * fragCoord / uSpacing;
508
528
  vec2 cell = floor(q) + 0.5;
509
529
  vec2 centre = transpose(r) * cell * uSpacing;
510
- float l = dot(picture(uImage, centre / iResolution.xy), vec3(0.2126, 0.7152, 0.0722));
530
+ vec4 pic = pictureRGBA(uImage, centre / iResolution.xy);
531
+ float l = dot(pic.rgb, vec3(0.2126, 0.7152, 0.0722));
511
532
  float radius = sqrt(1.0 - l) * 0.62 * (0.95 + 0.05 * sin(iTime * 2.0));
512
533
  float d = length(q - cell);
513
534
  float ink = smoothstep(radius + 0.06, radius - 0.06, d);
514
- fragColor = vec4(mix(uPaper, uInk, ink), 1.0);
535
+ fragColor = vec4(mix(uPaper, uInk, ink), pic.a);
515
536
  }`
516
537
  )
517
538
  };
@@ -528,7 +549,8 @@ uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPL
528
549
  uniform float uCurve; // @control min=0 max=0.5 default=0.15 label="Curvature"
529
550
  uniform float uScanlines; // @control min=0 max=1 default=0.5 label="Scanlines"
530
551
  uniform float uMask; // @control min=0 max=1 default=0.35 label="Shadow mask"
531
- uniform float uFlicker; // @control min=0 max=0.2 default=0.03 label="Flicker"`,
552
+ uniform float uFlicker; // @control min=0 max=0.2 default=0.03 label="Flicker"
553
+ ${BACKDROP_CONTROLS}`,
532
554
  HASH,
533
555
  PICTURE,
534
556
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
@@ -536,10 +558,11 @@ uniform float uFlicker; // @control min=0 max=0.2 default=0.03 label="Flicke
536
558
  uv *= 1.0 + uCurve * dot(uv.yx, uv.yx) * 0.5;
537
559
  vec2 st = uv * 0.5 + 0.5;
538
560
  if (st.x < 0.0 || st.x > 1.0 || st.y < 0.0 || st.y > 1.0) {
539
- fragColor = vec4(0.0, 0.0, 0.0, 1.0);
561
+ fragColor = vec4(0.0, 0.0, 0.0, uBackdropAlpha);
540
562
  return;
541
563
  }
542
- vec3 col = picture(uImage, st);
564
+ vec4 pic = pictureRGBA(uImage, st);
565
+ vec3 col = pic.rgb;
543
566
  col += 0.25 * picture(uImage, st + vec2(0.003, 0.0)) * col;
544
567
  float scan = 1.0 - uScanlines * 0.5 * (1.0 - cos(st.y * iResolution.y * 3.14159));
545
568
  float m = mod(fragCoord.x, 3.0);
@@ -547,7 +570,7 @@ uniform float uFlicker; // @control min=0 max=0.2 default=0.03 label="Flicke
547
570
  col *= scan * mask;
548
571
  col *= 1.0 - uFlicker * hash12(vec2(floor(iTime * 24.0), 0.0));
549
572
  col *= smoothstep(1.0, 0.7, max(abs(uv.x), abs(uv.y)) * 0.98 + 0.02 * length(uv));
550
- fragColor = vec4(col, 1.0);
573
+ fragColor = vec4(col, pic.a);
551
574
  }`
552
575
  )
553
576
  };
@@ -565,7 +588,8 @@ uniform float uGrain; // @control min=0 max=0.3 default=0.08 label="Grain"
565
588
  uniform float uWeave; // @control min=0 max=0.01 default=0.0015 label="Gate weave"
566
589
  uniform float uHalation; // @control min=0 max=1 default=0.3 label="Halation"
567
590
  uniform float uVignette; // @control min=0 max=1 default=0.45 label="Vignette"
568
- uniform vec3 uTint; // @control color default=#FFF1DC label="Print tint"`,
591
+ uniform vec3 uTint; // @control color default=#FFF1DC label="Print tint"
592
+ ${BACKDROP_CONTROLS}`,
569
593
  HASH,
570
594
  NOISE,
571
595
  PICTURE,
@@ -573,7 +597,8 @@ uniform vec3 uTint; // @control color default=#FFF1DC label="Print tint"
573
597
  vec2 uv = fragCoord / iResolution.xy;
574
598
  float frame = floor(iTime * 24.0);
575
599
  uv += uWeave * (vec2(vnoise(vec2(frame * 0.3, 0.0)), vnoise(vec2(0.0, frame * 0.3))) - 0.5);
576
- vec3 col = picture(uImage, uv);
600
+ vec4 pic = pictureRGBA(uImage, uv);
601
+ vec3 col = pic.rgb;
577
602
  vec3 blur = (picture(uImage, uv + vec2(0.006, 0.0)) + picture(uImage, uv - vec2(0.006, 0.0)) +
578
603
  picture(uImage, uv + vec2(0.0, 0.006)) + picture(uImage, uv - vec2(0.0, 0.006))) * 0.25;
579
604
  float bright = smoothstep(0.6, 1.0, dot(blur, vec3(0.333)));
@@ -581,7 +606,7 @@ uniform vec3 uTint; // @control color default=#FFF1DC label="Print tint"
581
606
  col *= uTint;
582
607
  col += (hash12(fragCoord + frame * 31.7) - 0.5) * uGrain;
583
608
  col *= 1.0 - uVignette * smoothstep(0.3, 0.9, length(uv - 0.5) * 1.3);
584
- fragColor = vec4(col, 1.0);
609
+ fragColor = vec4(col, pic.a);
585
610
  }`
586
611
  )
587
612
  };
@@ -608,12 +633,14 @@ var transitions = TRANSITIONS.map(({ type, id, name, description }) => ({
608
633
  uniform sampler2D uFrom; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label="From image"
609
634
  uniform sampler2D uTo; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.city} label="To image"
610
635
  uniform bool uAnimate; // @control default=true label="Animate"
611
- uniform float uProgress; // @control min=0 max=1 default=0.5 label="Progress (when still)"`,
636
+ uniform float uProgress; // @control min=0 max=1 default=0.5 label="Progress (when still)"
637
+ ${BACKDROP_CONTROLS}`,
638
+ BACKDROP,
612
639
  getTransitionGLSL(type),
613
640
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
614
641
  vec2 uv = fragCoord / iResolution.xy;
615
642
  float p = uAnimate ? smoothstep(0.1, 0.9, 0.5 - 0.5 * cos(iTime * 6.28318 / 4.0)) : uProgress;
616
- fragColor = vec4(flair_transition(uFrom, uTo, uv, p).rgb, 1.0);
643
+ fragColor = overBackdrop(flair_transition(uFrom, uTo, uv, p));
617
644
  }`
618
645
  )
619
646
  }));
@@ -2433,7 +2460,8 @@ uniform int uPalette; // @control min=0 max=5 default=0 label="Palette (0 G
2433
2460
  uniform float uPixel; // @control min=1 max=16 default=4 step=1 label="Pixel size"
2434
2461
  uniform float uDither; // @control min=0 max=1 default=0.7 label="Dither"
2435
2462
  uniform bool uBayer8; // @control default=true label="8 \xD7 8 Bayer matrix"
2436
- uniform float uContrast; // @control min=0.5 max=2 default=1.1 label="Contrast"`,
2463
+ uniform float uContrast; // @control min=0.5 max=2 default=1.1 label="Contrast"
2464
+ ${BACKDROP_CONTROLS}`,
2437
2465
  PICTURE,
2438
2466
  BAYER,
2439
2467
  paletteArray("GAMEBOY", GAMEBOY),
@@ -2466,8 +2494,8 @@ vec3 nearest(vec3 c, int pal) {
2466
2494
 
2467
2495
  void mainImage(out vec4 fragColor, in vec2 fragCoord) {
2468
2496
  vec2 cell = floor(fragCoord / uPixel);
2469
- vec3 src = picture(uImage, (cell + 0.5) * uPixel / iResolution.xy);
2470
- src = clamp((src - 0.5) * uContrast + 0.5, 0.0, 1.0);
2497
+ vec4 pic = pictureRGBA(uImage, (cell + 0.5) * uPixel / iResolution.xy);
2498
+ vec3 src = clamp((pic.rgb - 0.5) * uContrast + 0.5, 0.0, 1.0);
2471
2499
  float threshold = (uBayer8 ? bayer8(cell) + 0.5 / 64.0 : bayer4(cell) + 0.5 / 16.0) - 0.5;
2472
2500
  vec3 col;
2473
2501
  if (uPalette == 0 || uPalette == 5) {
@@ -2480,7 +2508,8 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
2480
2508
  float spread = uPalette == 3 ? 0.55 : 0.22;
2481
2509
  col = nearest(src + threshold * uDither * spread, uPalette);
2482
2510
  }
2483
- fragColor = vec4(col, 1.0);
2511
+ // Pixels are whole, so alpha is cut at a half rather than blended.
2512
+ fragColor = vec4(col, step(0.5, pic.a));
2484
2513
  }`
2485
2514
  ),
2486
2515
  presets: [
@@ -2510,7 +2539,8 @@ uniform bool uDither; // @control default=true label="Dither"
2510
2539
  uniform vec3 uDark; // @control color default=#0F380F label="Darkest shade"
2511
2540
  uniform vec3 uLight; // @control color default=#9BBC0F label="Lightest shade"
2512
2541
  uniform vec3 uBezel; // @control color default=#4E5236 label="Bezel"
2513
- uniform vec3 uCase; // @control color default=#C9C6BC label="Case"`,
2542
+ uniform vec3 uCase; // @control color default=#C9C6BC label="Case"
2543
+ ${BACKDROP_CONTROLS}`,
2514
2544
  PICTURE,
2515
2545
  BAYER,
2516
2546
  `// The LCD shade (0 darkest \u2013 3 lightest) of the picture at uv, for screen pixel cell.
@@ -3132,7 +3162,8 @@ uniform sampler2D uFrom; // @control filter=nearest wrap=clamp default=${SAMPL
3132
3162
  uniform sampler2D uTo; // @control filter=nearest wrap=clamp default=${SAMPLE_IMAGES.city} label="To image"
3133
3163
  uniform bool uAnimate; // @control default=true label="Animate"
3134
3164
  uniform float uProgress; // @control min=0 max=1 default=0.5 label="Progress (when still)"
3135
- uniform float uMaxBlock; // @control min=4 max=96 default=40 step=1 label="Largest block (px)"`,
3165
+ uniform float uMaxBlock; // @control min=4 max=96 default=40 step=1 label="Largest block (px)"
3166
+ ${BACKDROP_CONTROLS}`,
3136
3167
  HASH,
3137
3168
  PICTURE,
3138
3169
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
@@ -3148,8 +3179,7 @@ uniform float uMaxBlock; // @control min=4 max=96 default=40 step=1 label="Lar
3148
3179
  vec2 big = floor(fragCoord / uMaxBlock);
3149
3180
  float swap = clamp((p - 0.35) / 0.3, 0.0, 1.0);
3150
3181
  bool showTo = p >= 0.65 || (p > 0.35 && hash12(big + 0.5) < swap);
3151
- vec3 col = showTo ? picture(uTo, uv) : picture(uFrom, uv);
3152
- fragColor = vec4(col, 1.0);
3182
+ fragColor = showTo ? pictureRGBA(uTo, uv) : pictureRGBA(uFrom, uv);
3153
3183
  }`
3154
3184
  )
3155
3185
  };
@@ -4192,7 +4222,8 @@ uniform sampler2D uImage; // @control filter=linear wrap=mirror default=${SAMPL
4192
4222
  uniform float uStrength; // @control min=0 max=1 default=0.6 label="Strength"
4193
4223
  uniform float uRadius; // @control min=0.05 max=0.8 default=0.3 label="Radius"
4194
4224
  uniform float uWobble; // @control min=0 max=1 default=0.5 label="Wobble"
4195
- uniform bool uDemo; // @control default=true label="Demo when idle"`,
4225
+ uniform bool uDemo; // @control default=true label="Demo when idle"
4226
+ ${BACKDROP_CONTROLS}`,
4196
4227
  HASH,
4197
4228
  NOISE,
4198
4229
  HOVER,
@@ -4212,11 +4243,11 @@ uniform bool uDemo; // @control default=true label="Demo when idle"`,
4212
4243
  off += dir * wave * uWobble * s * 0.02;
4213
4244
  vec2 churn = vec2(vnoise(d * 6.0 + iTime * 0.7), vnoise(d * 6.0 - iTime * 0.6 + 5.0)) - 0.5;
4214
4245
  off += churn * fall * s * uWobble * 0.05;
4215
- vec3 col = picture(uImage, uv + off * vec2(iResolution.y / iResolution.x, 1.0));
4246
+ vec4 pic = pictureRGBA(uImage, uv + off * vec2(iResolution.y / iResolution.x, 1.0));
4216
4247
  // Light catching the swell's upper-left slope.
4217
4248
  float slope = dot(dir, normalize(vec2(-0.6, 0.8))) * r / uRadius;
4218
- col += 0.12 * s * fall * slope + 0.06 * s * wave * uWobble;
4219
- fragColor = vec4(col, 1.0);
4249
+ vec3 col = pic.rgb + 0.12 * s * fall * slope + 0.06 * s * wave * uWobble;
4250
+ fragColor = vec4(col, pic.a);
4220
4251
  }`
4221
4252
  ),
4222
4253
  presets: [
@@ -4238,7 +4269,8 @@ uniform sampler2D uReveal; // @control filter=linear wrap=clamp default=${SAMPLE
4238
4269
  uniform float uRadius; // @control min=0.05 max=0.6 default=0.22 label="Lens radius"
4239
4270
  uniform float uSoftness; // @control min=0 max=1 default=0.35 label="Softness"
4240
4271
  uniform vec3 uRim; // @control color default=#A5F3FC label="Rim"
4241
- uniform bool uDemo; // @control default=true label="Demo when idle"`,
4272
+ uniform bool uDemo; // @control default=true label="Demo when idle"
4273
+ ${BACKDROP_CONTROLS}`,
4242
4274
  HASH,
4243
4275
  NOISE,
4244
4276
  CLICKS,
@@ -4262,10 +4294,11 @@ uniform bool uDemo; // @control default=true label="Demo when idle"`,
4262
4294
  d += (vnoise(fragCoord * px * 9.0 + iTime * 0.6) - 0.5) * 0.25 * R;
4263
4295
  float soft = max(uSoftness * R * 0.6, 1.5 * px);
4264
4296
  float m = 1.0 - smoothstep(R - soft, R, d);
4265
- vec3 col = mix(picture(uImage, uv), picture(uReveal, uv), m);
4297
+ vec4 pic = mix(pictureRGBA(uImage, uv), pictureRGBA(uReveal, uv), m);
4266
4298
  float rim = exp(-pow((d - R + soft * 0.5) / max(soft * 0.5, 2.0 * px), 2.0)) * step(0.001, R);
4267
- col += uRim * rim * 0.6;
4268
- fragColor = vec4(col, 1.0);
4299
+ // The rim is light added over the picture, so it also shows where the picture is clear.
4300
+ float alpha = max(pic.a, clamp(rim * 0.6, 0.0, 1.0));
4301
+ fragColor = vec4((pic.rgb * pic.a + uRim * rim * 0.6) / max(alpha, 1e-4), alpha);
4269
4302
  }`
4270
4303
  ),
4271
4304
  presets: [
@@ -4287,39 +4320,39 @@ uniform float uZoom; // @control min=1 max=5 default=2 label="Zoom"
4287
4320
  uniform float uRadius; // @control min=0.05 max=0.5 default=0.18 label="Radius"
4288
4321
  uniform float uAberration; // @control min=0 max=1 default=0.35 label="Colour fringe"
4289
4322
  uniform vec3 uRim; // @control color default=#FFFFFF label="Rim"
4290
- uniform bool uDemo; // @control default=true label="Demo when idle"`,
4323
+ uniform bool uDemo; // @control default=true label="Demo when idle"
4324
+ ${BACKDROP_CONTROLS}`,
4291
4325
  HOVER,
4292
4326
  PICTURE,
4293
4327
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
4294
4328
  vec2 uv = fragCoord / iResolution.xy;
4295
4329
  vec3 hp = hoverPoint(uDemo, 1.0);
4296
4330
  float s = hp.z;
4297
- vec3 col = picture(uImage, uv);
4331
+ vec4 col = pictureRGBA(uImage, uv);
4298
4332
  float R = uRadius * (0.75 + 0.25 * s) * iResolution.y;
4299
4333
  vec2 d = fragCoord - hp.xy;
4300
4334
  float r = length(d);
4301
4335
  float k = r / R;
4302
4336
  // A soft shadow under the glass.
4303
4337
  float shadow = 1.0 - smoothstep(R * 0.9, R * 1.3, length(d - vec2(R * 0.08, -R * 0.12)));
4304
- col *= 1.0 - 0.35 * shadow * s;
4338
+ col.rgb *= 1.0 - 0.35 * shadow * s;
4305
4339
  if (k < 1.05) {
4306
4340
  // More magnified at the centre, squeezed towards the rim like real glass.
4307
4341
  vec2 q = d / uZoom * (1.0 + 0.4 * k * k * k * k);
4308
4342
  float fr = uAberration * 0.08 * k * k * k;
4309
- vec3 lens = vec3(
4310
- picture(uImage, (hp.xy + q * (1.0 + fr)) / iResolution.xy).r,
4311
- picture(uImage, (hp.xy + q) / iResolution.xy).g,
4312
- picture(uImage, (hp.xy + q * (1.0 - fr)) / iResolution.xy).b
4313
- );
4314
- lens += 0.16 * exp(-pow(length(d / R - vec2(-0.35, 0.4)) * 2.6, 2.0));
4315
- lens *= 1.0 - 0.25 * pow(k, 6.0);
4343
+ vec4 lr = pictureRGBA(uImage, (hp.xy + q * (1.0 + fr)) / iResolution.xy);
4344
+ vec4 lg = pictureRGBA(uImage, (hp.xy + q) / iResolution.xy);
4345
+ vec4 lb = pictureRGBA(uImage, (hp.xy + q * (1.0 - fr)) / iResolution.xy);
4346
+ vec4 lens = vec4(lr.r, lg.g, lb.b, max(max(lr.a, lg.a), lb.a));
4347
+ lens.rgb += 0.16 * exp(-pow(length(d / R - vec2(-0.35, 0.4)) * 2.6, 2.0));
4348
+ lens.rgb *= 1.0 - 0.25 * pow(k, 6.0);
4316
4349
  col = mix(col, lens, (1.0 - smoothstep(R - 1.5, R, r)) * s);
4317
4350
  }
4318
4351
  float rimW = max(R * 0.035, 2.0);
4319
4352
  float lit = 0.6 + 0.4 * dot(d / max(r, 1e-4), normalize(vec2(-1.0, 1.0)));
4320
4353
  float rim = exp(-pow((r - R) / rimW, 2.0));
4321
- col = mix(col, uRim * lit, rim * s * 0.9);
4322
- fragColor = vec4(col, 1.0);
4354
+ col = mix(col, vec4(uRim * lit, 1.0), rim * s * 0.9);
4355
+ fragColor = col;
4323
4356
  }`
4324
4357
  ),
4325
4358
  presets: [
@@ -4343,7 +4376,8 @@ uniform float uGlare; // @control min=0 max=1 default=0.6 label="Glare"
4343
4376
  uniform float uTilt; // @control min=0 max=1 default=0.6 label="Tilt"
4344
4377
  uniform float uCorner; // @control min=0 max=0.25 default=0.06 label="Corner radius"
4345
4378
  uniform vec3 uBorder; // @control color default=#E2E8F0 label="Border"
4346
- uniform bool uDemo; // @control default=true label="Demo when idle"`,
4379
+ uniform bool uDemo; // @control default=true label="Demo when idle"
4380
+ ${BACKDROP_CONTROLS}`,
4347
4381
  HASH,
4348
4382
  NOISE,
4349
4383
  HOVER,
@@ -4494,7 +4528,8 @@ uniform sampler2D uTo; // @control filter=linear wrap=clamp default=${SAMPLE
4494
4528
  uniform vec3 uEdge; // @control color default=#FFB86B label="Edge glow"
4495
4529
  uniform float uEdgeWidth; // @control min=0 max=0.3 default=0.08 label="Edge width"
4496
4530
  uniform float uGrain; // @control min=1 max=20 default=6 label="Noise scale"
4497
- uniform bool uDemo; // @control default=true label="Demo when idle"`,
4531
+ uniform bool uDemo; // @control default=true label="Demo when idle"
4532
+ ${BACKDROP_CONTROLS}`,
4498
4533
  HASH,
4499
4534
  NOISE,
4500
4535
  FBM,
@@ -4516,10 +4551,9 @@ uniform bool uDemo; // @control default=true label="Demo when idle"`,
4516
4551
  float w = max(uEdgeWidth, 0.005);
4517
4552
  float t = progress * (1.0 + w);
4518
4553
  float m = 1.0 - smoothstep(t - w, t, f);
4519
- vec3 col = mix(picture(uFrom, uv), picture(uTo, uv), m);
4554
+ vec4 col = mix(pictureRGBA(uFrom, uv), pictureRGBA(uTo, uv), m);
4520
4555
  float edge = m * (1.0 - m) * 4.0 * step(0.001, uEdgeWidth);
4521
- col = mix(col, uEdge, edge * 0.85);
4522
- fragColor = vec4(col, 1.0);
4556
+ fragColor = mix(col, vec4(uEdge, 1.0), edge * 0.85);
4523
4557
  }`
4524
4558
  ),
4525
4559
  presets: [
@@ -5279,7 +5313,8 @@ uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE
5279
5313
  uniform vec3 uColor; // @control color default=#8FD8FF label="Arc colour"
5280
5314
  uniform float uPeriod; // @control min=1.5 max=8 default=3.5 label="Seconds between surges"
5281
5315
  uniform float uStrength; // @control min=0 max=1.5 default=1 label="Strength"
5282
- uniform float uGlitch; // @control min=0 max=1 default=0.6 label="Glitch"`,
5316
+ uniform float uGlitch; // @control min=0 max=1 default=0.6 label="Glitch"
5317
+ ${BACKDROP_CONTROLS}`,
5283
5318
  HASH,
5284
5319
  ZIGZAG,
5285
5320
  ARC,
@@ -5298,7 +5333,10 @@ uniform float uGlitch; // @control min=0 max=1 default=0.6 label="Glitch"`,
5298
5333
  float tear = (hash12(vec2(rows, tick)) - 0.5) * step(0.55, hash12(vec2(rows, tick + 5.0))) * 0.12 * surge * uGlitch;
5299
5334
  vec2 st = uv + vec2(tear, 0.0);
5300
5335
  float split = 0.012 * surge * uGlitch;
5301
- vec3 col = vec3(picture(uImage, st + vec2(split, 0.0)).r, picture(uImage, st).g, picture(uImage, st - vec2(split, 0.0)).b);
5336
+ vec4 r = pictureRGBA(uImage, st + vec2(split, 0.0));
5337
+ vec4 g = pictureRGBA(uImage, st);
5338
+ vec4 b2 = pictureRGBA(uImage, st - vec2(split, 0.0));
5339
+ vec3 col = vec3(r.r, g.g, b2.b);
5302
5340
  // Brightness spikes, dips in a brownout, then comes back.
5303
5341
  float brownout = 1.0 - 0.3 * min(uStrength, 1.0) * smoothstep(0.25, 0.5, since) * (1.0 - smoothstep(0.7, 1.5, since));
5304
5342
  col = col * brownout * (1.0 + surge * 1.4) + surge * 0.2 * mix(uColor, vec3(1.0), 0.5);
@@ -5317,7 +5355,9 @@ uniform float uGlitch; // @control min=0 max=1 default=0.6 label="Glitch"`,
5317
5355
  light *= on;
5318
5356
  }
5319
5357
  col = 1.0 - (1.0 - clamp(col, 0.0, 1.0)) * exp(-light);
5320
- fragColor = vec4(col, 1.0);
5358
+ // The arc shows where the picture is clear too.
5359
+ float arc = 1.0 - exp(-max(light.r, max(light.g, light.b)));
5360
+ fragColor = vec4(col, max(max(max(r.a, g.a), b2.a), arc));
5321
5361
  }`
5322
5362
  ),
5323
5363
  presets: [
@@ -5458,7 +5498,8 @@ uniform float uViscosity; // @control min=0.004 max=0.04 default=0.02 label="V
5458
5498
  uniform float uGloss; // @control min=0 max=1.5 default=0.8 label="Gloss"
5459
5499
  uniform vec3 uBackground; // @control color default=#17121F label="Behind"
5460
5500
  uniform bool uAnimate; // @control default=true label="Animate"
5461
- uniform float uProgress; // @control min=0 max=1 default=0.7 label="Melt (when still)"`,
5501
+ uniform float uProgress; // @control min=0 max=1 default=0.7 label="Melt (when still)"
5502
+ ${BACKDROP_CONTROLS}`,
5462
5503
  HASH,
5463
5504
  NOISE,
5464
5505
  FBM,
@@ -5514,13 +5555,13 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
5514
5555
  float h = clamp(uv.y / e, 0.0, 1.0);
5515
5556
  float ys = 1.0 - pow(1.0 - h, 1.0 + m * 1.3);
5516
5557
  float xs = uv.x + m * 0.006 * sin(uv.y * 40.0 + uv.x * 13.0);
5517
- vec3 col = picture(uImage, vec2(xs, ys));
5518
- vec3 behind = uBackground * (0.75 + 0.5 * uv.y);
5558
+ vec4 col = pictureRGBA(uImage, vec2(xs, ys));
5559
+ vec4 behind = vec4(uBackground * (0.75 + 0.5 * uv.y), uBackdropAlpha);
5519
5560
  col = mix(col, behind, smoothstep(e - px, e + px, uv.y));
5520
5561
 
5521
5562
  // The liquid's shadow, down and to the right.
5522
5563
  float shadow = liquid(uv + vec2(-0.004, 0.009), m);
5523
- col *= 1.0 - 0.4 * smoothstep(0.012, -0.004, shadow) * smoothstep(0.0, 0.1, m);
5564
+ col.rgb *= 1.0 - 0.4 * smoothstep(0.012, -0.004, shadow) * smoothstep(0.0, 0.1, m);
5524
5565
 
5525
5566
  float d = liquid(uv, m);
5526
5567
  if (d < 4.0 * px) {
@@ -5530,15 +5571,16 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
5530
5571
  float thick = sqrt(clamp(-d / 0.018, 0.0, 1.0));
5531
5572
  vec3 nrm = normalize(vec3(g * (1.0 - thick) * 1.6, 0.4 + thick));
5532
5573
  // Drips carry the colour of the top edge down; the puddle is everything mixed.
5533
- vec3 top = picture(uImage, vec2(uv.x + 0.01 * sin(uv.y * 25.0), 0.965));
5574
+ vec4 top = pictureRGBA(uImage, vec2(uv.x + 0.01 * sin(uv.y * 25.0), 0.965));
5534
5575
  float sx = uv.x + 0.04 * sin(uv.x * 11.0 + uv.y * 30.0);
5535
- vec3 pool = (picture(uImage, vec2(sx - 0.06, 0.25)) + picture(uImage, vec2(sx + 0.06, 0.4)) + picture(uImage, vec2(sx, 0.9))) / 3.0;
5576
+ vec4 pool = (pictureRGBA(uImage, vec2(sx - 0.06, 0.25)) + pictureRGBA(uImage, vec2(sx + 0.06, 0.4)) + pictureRGBA(uImage, vec2(sx, 0.9))) / 3.0;
5536
5577
  float inPool = smoothstep(poolY(uv.x, m) + 0.04, poolY(uv.x, m) - 0.01, uv.y);
5537
- vec3 base = mix(top, pool, inPool) * (0.9 + 0.12 * vnoise(vec2(uv.x * 90.0, uv.y * 3.0)));
5538
- vec3 wet = liquidShade(base * 1.05, nrm, uGloss);
5539
- col = mix(col, wet, smoothstep(px, -px, d));
5578
+ vec4 base = mix(top, pool, inPool);
5579
+ base.rgb *= 0.9 + 0.12 * vnoise(vec2(uv.x * 90.0, uv.y * 3.0));
5580
+ vec3 wet = liquidShade(base.rgb * 1.05, nrm, uGloss);
5581
+ col = mix(col, vec4(wet, base.a), smoothstep(px, -px, d));
5540
5582
  }
5541
- fragColor = vec4(col, 1.0);
5583
+ fragColor = col;
5542
5584
  }`
5543
5585
  ),
5544
5586
  presets: [
@@ -5562,7 +5604,8 @@ uniform float uScale; // @control min=1 max=20 default=7 label="Scale"
5562
5604
  uniform float uSpeed; // @control min=0 max=3 default=1 label="Speed"
5563
5605
  uniform float uHeight; // @control min=0.1 max=1 default=0.8 label="Height"
5564
5606
  uniform vec3 uTint; // @control color default=#FFB070 label="Warm tint"
5565
- uniform float uWarmth; // @control min=0 max=1 default=0.3 label="Tint amount"`,
5607
+ uniform float uWarmth; // @control min=0 max=1 default=0.3 label="Tint amount"
5608
+ ${BACKDROP_CONTROLS}`,
5566
5609
  HASH,
5567
5610
  NOISE,
5568
5611
  FBM,
@@ -5582,16 +5625,21 @@ uniform float uWarmth; // @control min=0 max=1 default=0.3 label="Tint amoun
5582
5625
  vec2 st = uv + off;
5583
5626
  // A touch of colour fringing where the air bends light the most.
5584
5627
  float fringe = 0.25 * length(off);
5585
- vec3 col = vec3(picture(uImage, st + vec2(fringe, 0.0)).r, picture(uImage, st).g, picture(uImage, st - vec2(fringe, 0.0)).b);
5628
+ vec4 r = pictureRGBA(uImage, st + vec2(fringe, 0.0));
5629
+ vec4 g = pictureRGBA(uImage, st);
5630
+ vec4 b2 = pictureRGBA(uImage, st - vec2(fringe, 0.0));
5631
+ vec3 col = vec3(r.r, g.g, b2.b);
5632
+ float alpha = max(max(r.a, g.a), b2.a);
5586
5633
  // An inferior mirage: just above the ground, what sits above it shows again, flipped and shimmering.
5587
5634
  float y0 = 0.05 + 0.15 * uHeight;
5588
5635
  float below = smoothstep(y0, 0.0, uv.y) * (0.6 + 0.4 * b);
5589
- vec3 mirage = picture(uImage, vec2(st.x, y0 + (y0 - uv.y) * 1.4 + off.y * 2.0));
5590
- col = mix(col, mirage, below * uStrength * 0.55);
5636
+ vec4 mirage = pictureRGBA(uImage, vec2(st.x, y0 + (y0 - uv.y) * 1.4 + off.y * 2.0));
5637
+ col = mix(col, mirage.rgb, below * uStrength * 0.55);
5638
+ alpha = mix(alpha, mirage.a, below * uStrength * 0.55);
5591
5639
  // Warm, slightly hazy air.
5592
5640
  col = mix(col, col * mix(vec3(1.0), uTint * 1.35, uWarmth), heat);
5593
5641
  col = mix(col, uTint, heat * uWarmth * 0.12 * (0.7 + 0.6 * a));
5594
- fragColor = vec4(col, 1.0);
5642
+ fragColor = vec4(col, alpha);
5595
5643
  }`
5596
5644
  ),
5597
5645
  presets: [
@@ -5830,7 +5878,8 @@ uniform sampler2D uImage; // @control filter=linear wrap=mirror default=${SAMPL
5830
5878
  uniform float uBrush; // @control min=0.05 max=0.5 default=0.17 label="Brush size"
5831
5879
  uniform float uStrength; // @control min=0 max=2 default=0.8 label="Strength"
5832
5880
  uniform float uRelax; // @control min=0.2 max=5 default=1.2 label="Relax"
5833
- uniform float uSpeed; // @control min=0 max=2 default=1 label="Brush speed"`,
5881
+ uniform float uSpeed; // @control min=0 max=2 default=1 label="Brush speed"
5882
+ ${BACKDROP_CONTROLS}`,
5834
5883
  PICTURE,
5835
5884
  `// The brush at time t (frame units, height 1): a Lissajous path, or stirring round the pointer while it hovers.
5836
5885
  vec2 brush(float t) {
@@ -5860,11 +5909,11 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
5860
5909
  wet += f * fade;
5861
5910
  }
5862
5911
  vec2 uv = (p - disp) * vec2(iResolution.y / iResolution.x, 1.0) + 0.5;
5863
- vec3 col = picture(uImage, uv);
5912
+ vec4 pic = pictureRGBA(uImage, uv);
5864
5913
  // Wet paint catches a little light along the smears.
5865
5914
  float sheen = clamp(dot(disp, normalize(vec2(-0.6, 0.8))) * 12.0, -1.0, 1.0);
5866
- col *= 1.0 + 0.05 * sheen * min(wet, 1.0) * min(uStrength, 1.0);
5867
- fragColor = vec4(col, 1.0);
5915
+ vec3 col = pic.rgb * (1.0 + 0.05 * sheen * min(wet, 1.0) * min(uStrength, 1.0));
5916
+ fragColor = vec4(col, pic.a);
5868
5917
  }`
5869
5918
  ),
5870
5919
  presets: [
@@ -6907,26 +6956,29 @@ uniform float uSpacing; // @control min=0.02 max=0.6 default=0.18 label="Spa
6907
6956
  uniform float uDecay; // @control min=0.1 max=0.95 default=0.65 label="Decay"
6908
6957
  uniform float uMotion; // @control min=0 max=2 default=1.2 label="Motion"
6909
6958
  uniform vec3 uTint; // @control color default=#38BDF8 label="Echo tint"
6910
- uniform float uTintAmount; // @control min=0 max=1 default=0.5 label="Tint strength"`,
6959
+ uniform float uTintAmount; // @control min=0 max=1 default=0.5 label="Tint strength"
6960
+ ${BACKDROP_CONTROLS}`,
6911
6961
  PICTURE,
6912
6962
  COVER,
6913
6963
  SWAY,
6914
6964
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
6915
6965
  vec2 uv = fragCoord / iResolution.xy;
6916
- vec3 sum = picture(uImage, swayUv(uImage, uv, iTime, uMotion));
6966
+ // Summed premultiplied, so clear parts of the picture do not darken the echoes.
6967
+ vec4 now = pictureRGBA(uImage, swayUv(uImage, uv, iTime, uMotion));
6968
+ vec4 sum = vec4(now.rgb * now.a, now.a);
6917
6969
  float total = 1.0;
6918
6970
  float weight = 1.0;
6919
6971
  for (int k = 1; k <= 8; k++) {
6920
6972
  if (k > uEchoes) break;
6921
6973
  weight *= uDecay;
6922
6974
  float age = float(k) / float(uEchoes);
6923
- vec3 echo = picture(uImage, swayUv(uImage, uv, iTime - float(k) * uSpacing, uMotion));
6975
+ vec4 echo = pictureRGBA(uImage, swayUv(uImage, uv, iTime - float(k) * uSpacing, uMotion));
6924
6976
  // Older echoes take on more of the tint.
6925
- echo = mix(echo, uTint * dot(echo, ${LUMA}) * 1.8, uTintAmount * age);
6926
- sum += echo * weight;
6977
+ echo.rgb = mix(echo.rgb, uTint * dot(echo.rgb, ${LUMA}) * 1.8, uTintAmount * age);
6978
+ sum += vec4(echo.rgb * echo.a, echo.a) * weight;
6927
6979
  total += weight;
6928
6980
  }
6929
- fragColor = vec4(sum / total, 1.0);
6981
+ fragColor = vec4(sum.rgb / max(sum.a, 1e-4), sum.a / total);
6930
6982
  }`
6931
6983
  ),
6932
6984
  presets: [
@@ -6948,7 +7000,8 @@ var chronoSplit = {
6948
7000
  uniform sampler2D uImage; // @control filter=linear wrap=mirror default=${SAMPLE_IMAGES.city} label="Image"
6949
7001
  uniform float uLag; // @control min=0 max=0.6 default=0.15 label="Lag per channel (s)"
6950
7002
  uniform float uMotion; // @control min=0 max=2 default=1.4 label="Motion"
6951
- uniform int uOrder; // @control min=0 max=1 default=0 label="Leading channel (0 red, 1 blue)"`,
7003
+ uniform int uOrder; // @control min=0 max=1 default=0 label="Leading channel (0 red, 1 blue)"
7004
+ ${BACKDROP_CONTROLS}`,
6952
7005
  PICTURE,
6953
7006
  COVER,
6954
7007
  SWAY,
@@ -6956,12 +7009,10 @@ uniform int uOrder; // @control min=0 max=1 default=0 label="Leading chan
6956
7009
  vec2 uv = fragCoord / iResolution.xy;
6957
7010
  float lagR = uOrder == 0 ? 0.0 : 2.0 * uLag;
6958
7011
  float lagB = uOrder == 0 ? 2.0 * uLag : 0.0;
6959
- vec3 col = vec3(
6960
- picture(uImage, swayUv(uImage, uv, iTime - lagR, uMotion)).r,
6961
- picture(uImage, swayUv(uImage, uv, iTime - uLag, uMotion)).g,
6962
- picture(uImage, swayUv(uImage, uv, iTime - lagB, uMotion)).b
6963
- );
6964
- fragColor = vec4(col, 1.0);
7012
+ vec4 r = pictureRGBA(uImage, swayUv(uImage, uv, iTime - lagR, uMotion));
7013
+ vec4 g = pictureRGBA(uImage, swayUv(uImage, uv, iTime - uLag, uMotion));
7014
+ vec4 b = pictureRGBA(uImage, swayUv(uImage, uv, iTime - lagB, uMotion));
7015
+ fragColor = vec4(r.r, g.g, b.b, max(max(r.a, g.a), b.a));
6965
7016
  }`
6966
7017
  ),
6967
7018
  presets: [
@@ -6985,7 +7036,8 @@ uniform float uDelay; // @control min=0 max=0.6 default=0.12 label="Delay p
6985
7036
  uniform int uDirection; // @control min=0 max=1 default=0 label="Bands (0 horizontal, 1 vertical)"
6986
7037
  uniform int uPattern; // @control min=0 max=2 default=0 label="Order (0 stepped, 1 from centre, 2 random)"
6987
7038
  uniform float uMotion; // @control min=0 max=2 default=1.5 label="Motion"
6988
- uniform float uSeams; // @control min=0 max=1 default=0.4 label="Seams"`,
7039
+ uniform float uSeams; // @control min=0 max=1 default=0.4 label="Seams"
7040
+ ${BACKDROP_CONTROLS}`,
6989
7041
  HASH,
6990
7042
  PICTURE,
6991
7043
  COVER,
@@ -6998,13 +7050,14 @@ uniform float uSeams; // @control min=0 max=1 default=0.4 label="Seams"`,
6998
7050
  float order = band;
6999
7051
  if (uPattern == 1) order = abs(band - (bands - 1.0) * 0.5);
7000
7052
  if (uPattern == 2) order = floor(hash12(vec2(band, 4.7)) * bands);
7001
- vec3 col = picture(uImage, swayUv(uImage, uv, iTime - order * uDelay, uMotion));
7053
+ vec4 pic = pictureRGBA(uImage, swayUv(uImage, uv, iTime - order * uDelay, uMotion));
7054
+ vec3 col = pic.rgb;
7002
7055
  // A thin dark seam between bands, a pixel or two wide.
7003
7056
  float px = (uDirection == 0 ? iResolution.y : iResolution.x) / bands;
7004
7057
  float f = fract(across * bands);
7005
7058
  float edge = min(f, 1.0 - f) * px;
7006
7059
  col *= 1.0 - uSeams * (1.0 - smoothstep(0.0, 1.5, edge));
7007
- fragColor = vec4(col, 1.0);
7060
+ fragColor = vec4(col, pic.a);
7008
7061
  }`
7009
7062
  ),
7010
7063
  presets: [
@@ -7046,7 +7099,8 @@ uniform float uNoise; // @control min=0 max=1 default=0.6 label="Tracking n
7046
7099
  uniform float uJitter; // @control min=0 max=1 default=0.5 label="Line jitter"
7047
7100
  uniform float uBleed; // @control min=0 max=0.03 default=0.008 label="Chroma bleed"
7048
7101
  uniform bool uOsd; // @control default=true label="On-screen display"
7049
- uniform vec3 uOsdColor; // @control color default=#F8FAFC label="Display colour"`,
7102
+ uniform vec3 uOsdColor; // @control color default=#F8FAFC label="Display colour"
7103
+ ${BACKDROP_CONTROLS}`,
7050
7104
  HASH,
7051
7105
  PICTURE,
7052
7106
  COVER,
@@ -7105,11 +7159,11 @@ float osd(vec2 fragCoord, float phase, float tau) {
7105
7159
  st.y += rw * 0.01 * (hash12(vec2(frame, 5.0)) - 0.5);
7106
7160
  float bleed = uBleed * (1.0 + 2.0 * rw);
7107
7161
  float t = tau * 1.5;
7108
- vec3 col = vec3(
7109
- picture(uImage, swayUv(uImage, st + vec2(bleed, 0.0), t, 1.4)).r,
7110
- picture(uImage, swayUv(uImage, st, t, 1.4)).g,
7111
- picture(uImage, swayUv(uImage, st - vec2(bleed, 0.0), t, 1.4)).b
7112
- );
7162
+ vec4 r = pictureRGBA(uImage, swayUv(uImage, st + vec2(bleed, 0.0), t, 1.4));
7163
+ vec4 g = pictureRGBA(uImage, swayUv(uImage, st, t, 1.4));
7164
+ vec4 b = pictureRGBA(uImage, swayUv(uImage, st - vec2(bleed, 0.0), t, 1.4));
7165
+ vec3 col = vec3(r.r, g.g, b.b);
7166
+ float alpha = max(max(r.a, g.a), b.a);
7113
7167
  // Static in the bars, a few white dropouts, and a washed-out tape look.
7114
7168
  float grain = hash12(fragCoord + frame * 17.0);
7115
7169
  col = mix(col, vec3(grain), bar * 0.75);
@@ -7122,10 +7176,12 @@ float osd(vec2 fragCoord, float phase, float tau) {
7122
7176
  col *= 1.0 - 0.35 * dot(v, v);
7123
7177
  if (uOsd) {
7124
7178
  float shadow = osd(fragCoord + vec2(-2.0, 2.0), phase, tau);
7179
+ float text = osd(fragCoord, phase, tau);
7125
7180
  col = mix(col, vec3(0.0), shadow * 0.5);
7126
- col = mix(col, uOsdColor, osd(fragCoord, phase, tau));
7181
+ col = mix(col, uOsdColor, text);
7182
+ alpha = max(alpha, max(shadow * 0.5, text));
7127
7183
  }
7128
- fragColor = vec4(clamp(col, 0.0, 1.0), 1.0);
7184
+ fragColor = vec4(clamp(col, 0.0, 1.0), alpha);
7129
7185
  }`
7130
7186
  ),
7131
7187
  presets: [
@@ -7150,7 +7206,8 @@ uniform float uHold; // @control min=0.5 max=3 default=2.2 label="Freeze
7150
7206
  uniform float uDesaturate; // @control min=0 max=1 default=0.85 label="Desaturate"
7151
7207
  uniform float uZoom; // @control min=0 max=0.25 default=0.08 label="Zoom"
7152
7208
  uniform vec3 uRing; // @control color default=#A5F3FC label="Shockwave"
7153
- uniform float uMotion; // @control min=0 max=2 default=1.4 label="Motion"`,
7209
+ uniform float uMotion; // @control min=0 max=2 default=1.4 label="Motion"
7210
+ ${BACKDROP_CONTROLS}`,
7154
7211
  PICTURE,
7155
7212
  COVER,
7156
7213
  SWAY,
@@ -7173,17 +7230,18 @@ uniform float uMotion; // @control min=0 max=2 default=1.4 label="Motion"`,
7173
7230
  float front = (dist - radius) / (0.045 * iResolution.y);
7174
7231
  float ring = exp(-front * front) * (1.0 - release);
7175
7232
  vec2 warp = d / max(dist, 1.0) * ring * 0.018;
7176
- vec3 live = picture(uImage, swayUv(uImage, uv - warp, iTime, uMotion));
7233
+ vec4 live = pictureRGBA(uImage, swayUv(uImage, uv - warp, iTime, uMotion));
7177
7234
  // Behind the wave: the moment the freeze began, zooming in towards the centre the longer it is held.
7178
7235
  vec2 c = centre / iResolution.xy;
7179
7236
  float zoom = 1.0 + uZoom * smoothstep(0.0, 1.0, since / max(uHold, 0.1));
7180
- vec3 frozen = picture(uImage, swayUv(uImage, c + (uv - warp - c) / zoom, start, uMotion));
7181
- float grey = dot(frozen, ${LUMA});
7182
- frozen = mix(frozen, vec3(grey) * vec3(0.9, 0.98, 1.1), uDesaturate);
7183
- frozen *= 1.0 + 0.025 * sin(dist / iResolution.y * 60.0 - since * 4.0) * inside;
7184
- vec3 col = mix(live, frozen, inside);
7185
- col += uRing * ring * 0.7;
7186
- fragColor = vec4(col, 1.0);
7237
+ vec4 frozen = pictureRGBA(uImage, swayUv(uImage, c + (uv - warp - c) / zoom, start, uMotion));
7238
+ float grey = dot(frozen.rgb, ${LUMA});
7239
+ frozen.rgb = mix(frozen.rgb, vec3(grey) * vec3(0.9, 0.98, 1.1), uDesaturate);
7240
+ frozen.rgb *= 1.0 + 0.025 * sin(dist / iResolution.y * 60.0 - since * 4.0) * inside;
7241
+ vec4 pic = mix(live, frozen, inside);
7242
+ // The ring is light added over the picture, so it also shows where the picture is clear.
7243
+ float alpha = max(pic.a, clamp(ring * 0.7, 0.0, 1.0));
7244
+ fragColor = vec4((pic.rgb * pic.a + uRing * ring * 0.7) / max(alpha, 1e-4), alpha);
7187
7245
  }`
7188
7246
  ),
7189
7247
  presets: [
@@ -7206,7 +7264,8 @@ uniform float uTwist; // @control min=0 max=4 default=1.2 label="Twist"
7206
7264
  uniform float uSpeed; // @control min=0 max=2 default=0.5 label="Speed"
7207
7265
  uniform vec3 uColor; // @control color default=#3B82F6 label="Tunnel colour"
7208
7266
  uniform float uTint; // @control min=0 max=1 default=0.55 label="Colour strength"
7209
- uniform vec3 uCore; // @control color default=#F0F9FF label="Core"`,
7267
+ uniform vec3 uCore; // @control color default=#F0F9FF label="Core"
7268
+ ${BACKDROP_CONTROLS}`,
7210
7269
  HASH,
7211
7270
  PICTURE,
7212
7271
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
@@ -7219,7 +7278,8 @@ uniform vec3 uCore; // @control color default=#F0F9FF label="Core"`,
7219
7278
  // Round the tube (seamless: twice round the mirrored image), twisting with depth; along it, rushing closer.
7220
7279
  float u = a / 6.28318 + uTwist * 0.15 * z + iTime * 0.125;
7221
7280
  float v = z * 0.5 + iTime * uSpeed;
7222
- vec3 img = picture(uImage, vec2(u * 2.0, v));
7281
+ vec4 pic = pictureRGBA(uImage, vec2(u * 2.0, v));
7282
+ vec3 img = pic.rgb;
7223
7283
  float l = dot(img, vec3(0.2126, 0.7152, 0.0722));
7224
7284
  vec3 col = mix(img, uColor * l * 2.2, uTint);
7225
7285
  // Rings where the tube's sections meet.
@@ -7231,13 +7291,14 @@ uniform vec3 uCore; // @control color default=#F0F9FF label="Core"`,
7231
7291
  // Thin in screen space at any distance from the core.
7232
7292
  float across = abs(fract(u * 64.0) - 0.5) / 64.0 * 6.28318 * r;
7233
7293
  float thin = 1.0 - smoothstep(0.0015, 0.005, across);
7234
- col += mix(uCore, uColor, 0.4) * dash * step(0.75, h) * thin * smoothstep(0.05, 0.25, r);
7294
+ float streak = dash * step(0.75, h) * thin * smoothstep(0.05, 0.25, r);
7295
+ col += mix(uCore, uColor, 0.4) * streak;
7235
7296
  // Depth haze into the glowing core; the near rim darkens.
7236
7297
  float haze = 1.0 - smoothstep(0.0, 0.2, r);
7237
7298
  col = mix(col, uCore, haze);
7238
7299
  col += uCore * 0.002 / (r * r + 0.01);
7239
7300
  col *= 1.0 - 0.45 * smoothstep(0.5, 0.95, r);
7240
- fragColor = vec4(clamp(col, 0.0, 1.0), 1.0);
7301
+ fragColor = vec4(clamp(col, 0.0, 1.0), clamp(max(pic.a, max(haze, streak)), 0.0, 1.0));
7241
7302
  }`
7242
7303
  ),
7243
7304
  presets: [
@@ -7259,7 +7320,8 @@ var speedRamp = {
7259
7320
  uniform sampler2D uImage; // @control filter=linear wrap=mirror default=${SAMPLE_IMAGES.landscape} label="Image"
7260
7321
  uniform float uRamp; // @control min=0 max=0.95 default=0.9 label="Ramp strength"
7261
7322
  uniform float uBlur; // @control min=0 max=0.25 default=0.1 label="Motion blur (shutter s)"
7262
- uniform float uPan; // @control min=0.1 max=1 default=0.5 label="Pan speed"`,
7323
+ uniform float uPan; // @control min=0.1 max=1 default=0.5 label="Pan speed"
7324
+ ${BACKDROP_CONTROLS}`,
7263
7325
  PICTURE,
7264
7326
  COVER,
7265
7327
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
@@ -7274,15 +7336,17 @@ uniform float uPan; // @control min=0.1 max=1 default=0.5 label="Pan spee
7274
7336
  vec2 base = 0.5 + (uv - 0.5) * cover / zoom + vec2(uPan * tau, 0.02 * sin(w * 2.0 * tau));
7275
7337
  // Blur along the pan, as long as the picture moves while the shutter is open (16 taps at most).
7276
7338
  float len = min(uPan * speed * uBlur, 0.25);
7277
- vec3 col = vec3(0.0);
7339
+ // Summed premultiplied, so clear parts of the picture do not darken the blur.
7340
+ vec4 sum = vec4(0.0);
7278
7341
  for (int i = 0; i < 16; i++) {
7279
7342
  float k = float(i) / 15.0 - 0.5;
7280
- col += picture(uImage, base - vec2(len * k, 0.0));
7343
+ vec4 c = pictureRGBA(uImage, base - vec2(len * k, 0.0));
7344
+ sum += vec4(c.rgb * c.a, c.a);
7281
7345
  }
7282
- col /= 16.0;
7346
+ vec3 col = sum.rgb / max(sum.a, 1e-4);
7283
7347
  vec2 v = uv - 0.5;
7284
7348
  col *= 1.0 - (0.2 + 0.25 * max(speed - 1.0, 0.0)) * dot(v, v) * 2.0;
7285
- fragColor = vec4(col, 1.0);
7349
+ fragColor = vec4(col, sum.a / 16.0);
7286
7350
  }`
7287
7351
  ),
7288
7352
  presets: [
@@ -7303,7 +7367,8 @@ uniform sampler2D uImage; // @control filter=linear wrap=mirror default=${SAMPL
7303
7367
  uniform float uRate; // @control min=0.2 max=4 default=1 label="Bursts per second"
7304
7368
  uniform float uHold; // @control min=0 max=1 default=0.55 label="Hold length"
7305
7369
  uniform float uFlash; // @control min=0 max=1 default=0.6 label="Flash"
7306
- uniform float uMotion; // @control min=0 max=2 default=1.6 label="Motion"`,
7370
+ uniform float uMotion; // @control min=0 max=2 default=1.6 label="Motion"
7371
+ ${BACKDROP_CONTROLS}`,
7307
7372
  HASH,
7308
7373
  PICTURE,
7309
7374
  COVER,
@@ -7328,13 +7393,14 @@ uniform float uMotion; // @control min=0 max=2 default=1.6 label="Motion"`,
7328
7393
  kick = exp(-since * 10.0);
7329
7394
  }
7330
7395
  vec2 st = 0.5 + (uv - 0.5) / (1.0 + 0.04 * kick);
7331
- vec3 col = picture(uImage, swayUv(uImage, st, t * 2.0, uMotion));
7396
+ vec4 pic = pictureRGBA(uImage, swayUv(uImage, st, t * 2.0, uMotion));
7397
+ vec3 col = pic.rgb;
7332
7398
  if (frozen) {
7333
7399
  col = mix(col, vec3(dot(col, ${LUMA})), 0.25);
7334
7400
  col = (col - 0.5) * 1.1 + 0.5;
7335
7401
  }
7336
7402
  col = mix(col, vec3(1.0), kick * uFlash * 0.85);
7337
- fragColor = vec4(clamp(col, 0.0, 1.0), 1.0);
7403
+ fragColor = vec4(clamp(col, 0.0, 1.0), max(pic.a, kick * uFlash * 0.85));
7338
7404
  }`
7339
7405
  ),
7340
7406
  presets: [
@@ -7362,8 +7428,12 @@ vec2 coverScale(sampler2D tex) {
7362
7428
  return frameAspect > imageAspect ? vec2(1.0, imageAspect / frameAspect) : vec2(frameAspect / imageAspect, 1.0);
7363
7429
  }
7364
7430
 
7431
+ vec4 coverRGBA(sampler2D tex, vec2 uv) {
7432
+ return pictureRGBA(tex, 0.5 + (uv - 0.5) * coverScale(tex));
7433
+ }
7434
+
7365
7435
  vec3 cover(sampler2D tex, vec2 uv) {
7366
- return picture(tex, 0.5 + (uv - 0.5) * coverScale(tex));
7436
+ return coverRGBA(tex, uv).rgb;
7367
7437
  }
7368
7438
 
7369
7439
  float luma(vec3 c) {
@@ -7390,7 +7460,8 @@ uniform vec2 uToB; // @control min=0 max=1 default=0.725,0.514 label="To
7390
7460
  uniform vec2 uToC; // @control min=0 max=1 default=0.672,0.364 label="To: mouth"
7391
7461
  uniform vec2 uToD; // @control min=0 max=1 default=0.672,0.25 label="To: chin"
7392
7462
  uniform vec2 uToE; // @control min=0 max=1 default=0.672,0.778 label="To: hairline"
7393
- uniform bool uShowPoints; // @control default=false label="Show points"`,
7463
+ uniform bool uShowPoints; // @control default=false label="Show points"
7464
+ ${BACKDROP_CONTROLS}`,
7394
7465
  PICTURE,
7395
7466
  COMMON,
7396
7467
  `// Points are given in image uv; the warp works in frame space (height 1, x scaled by the aspect).
@@ -7445,18 +7516,23 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
7445
7516
  vec2 m[5];
7446
7517
  for (int i = 0; i < 5; i++) m[i] = mix(a[i], b[i], t);
7447
7518
  vec2 x = fragCoord / iResolution.y;
7448
- vec3 colA = picture(uFrom, toImage(warp(x, m, a), scaleA));
7449
- vec3 colB = picture(uTo, toImage(warp(x, m, b), scaleB));
7450
- vec3 col = mix(colA, colB, smoothstep(0.2, 0.8, t));
7519
+ vec4 colA = pictureRGBA(uFrom, toImage(warp(x, m, a), scaleA));
7520
+ vec4 colB = pictureRGBA(uTo, toImage(warp(x, m, b), scaleB));
7521
+ vec4 pic = mix(colA, colB, smoothstep(0.2, 0.8, t));
7522
+ vec3 col = pic.rgb;
7523
+ float marks = 0.0;
7451
7524
  if (uShowPoints) {
7452
7525
  float px = 1.0 / iResolution.y;
7453
7526
  for (int i = 0; i < 5; i++) {
7454
7527
  float d = length(x - m[i]);
7455
- col = mix(col, vec3(0.0), smoothstep(px * 1.5, 0.0, abs(d - 0.012)) * 0.6);
7456
- col = mix(col, vec3(1.0, 0.85, 0.2), smoothstep(px, 0.0, abs(d - 0.012)));
7528
+ float shadow = smoothstep(px * 1.5, 0.0, abs(d - 0.012)) * 0.6;
7529
+ float ring = smoothstep(px, 0.0, abs(d - 0.012));
7530
+ col = mix(col, vec3(0.0), shadow);
7531
+ col = mix(col, vec3(1.0, 0.85, 0.2), ring);
7532
+ marks = max(marks, max(shadow, ring));
7457
7533
  }
7458
7534
  }
7459
- fragColor = vec4(col, 1.0);
7535
+ fragColor = vec4(col, max(pic.a, marks));
7460
7536
  }`
7461
7537
  ),
7462
7538
  presets: [{ name: "Show points", values: { uShowPoints: true, uAnimate: false, uProgress: 0.5 } }]
@@ -7554,7 +7630,8 @@ var transitionLiquidMorph = {
7554
7630
  ${pair(SAMPLE_IMAGES.landscape, SAMPLE_IMAGES.city)}
7555
7631
  uniform float uStrength; // @control min=0 max=0.5 default=0.1 label="Strength"
7556
7632
  uniform float uScale; // @control min=0.5 max=8 default=2.5 label="Scale"
7557
- uniform float uFlow; // @control min=0 max=2 default=0.4 label="Flow speed"`,
7633
+ uniform float uFlow; // @control min=0 max=2 default=0.4 label="Flow speed"
7634
+ ${BACKDROP_CONTROLS}`,
7558
7635
  HASH,
7559
7636
  NOISE,
7560
7637
  FBM,
@@ -7570,10 +7647,10 @@ uniform float uFlow; // @control min=0 max=2 default=0.4 label="Flow speed
7570
7647
  vec2 flow = vec2(fbm(q + 4.0 * first + vec2(1.7, 9.2) + time * 0.5, 4), fbm(q + 4.0 * first + vec2(8.3, 2.8) - time * 0.5, 4)) - 0.5;
7571
7648
  vec2 d = flow * uStrength * s * 2.0;
7572
7649
  // From is carried downstream, To arrives from upstream; neither moves at the ends.
7573
- vec3 a = cover(uFrom, uv + d);
7574
- vec3 b = cover(uTo, uv - d);
7650
+ vec4 a = coverRGBA(uFrom, uv + d);
7651
+ vec4 b = coverRGBA(uTo, uv - d);
7575
7652
  float k = smoothstep(0.25, 0.75, t + (flow.x + flow.y) * 0.6 * s);
7576
- fragColor = vec4(mix(a, b, k), 1.0);
7653
+ fragColor = mix(a, b, k);
7577
7654
  }`
7578
7655
  ),
7579
7656
  presets: [
@@ -7593,7 +7670,8 @@ var transitionDisplacement = {
7593
7670
  `// Displacement: From's luminance drives a directional warp out, To's luminance the same warp in reverse.
7594
7671
  ${pair(SAMPLE_IMAGES.city, SAMPLE_IMAGES.landscape)}
7595
7672
  uniform float uStrength; // @control min=0 max=1 default=0.4 label="Strength"
7596
- uniform float uAngle; // @control min=0 max=360 default=0 label="Direction (\xB0)"`,
7673
+ uniform float uAngle; // @control min=0 max=360 default=0 label="Direction (\xB0)"
7674
+ ${BACKDROP_CONTROLS}`,
7597
7675
  PICTURE,
7598
7676
  COMMON,
7599
7677
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
@@ -7602,9 +7680,9 @@ uniform float uAngle; // @control min=0 max=360 default=0 label="Direction
7602
7680
  vec2 dir = vec2(cos(radians(uAngle)), sin(radians(uAngle))) * vec2(iResolution.y / iResolution.x, 1.0);
7603
7681
  float la = luma(cover(uFrom, uv));
7604
7682
  float lb = luma(cover(uTo, uv));
7605
- vec3 a = cover(uFrom, uv + dir * t * uStrength * la);
7606
- vec3 b = cover(uTo, uv - dir * (1.0 - t) * uStrength * lb);
7607
- fragColor = vec4(mix(a, b, smoothstep(0.2, 0.8, t)), 1.0);
7683
+ vec4 a = coverRGBA(uFrom, uv + dir * t * uStrength * la);
7684
+ vec4 b = coverRGBA(uTo, uv - dir * (1.0 - t) * uStrength * lb);
7685
+ fragColor = mix(a, b, smoothstep(0.2, 0.8, t));
7608
7686
  }`
7609
7687
  ),
7610
7688
  presets: [
@@ -7625,19 +7703,20 @@ var transitionLuma = {
7625
7703
  ${pair(SAMPLE_IMAGES.landscape, SAMPLE_IMAGES.city)}
7626
7704
  uniform float uSoftness; // @control min=0.01 max=0.5 default=0.1 label="Softness"
7627
7705
  uniform bool uLightFirst; // @control default=false label="Light first"
7628
- uniform int uSource; // @control min=0 max=1 default=0 label="Brightness of (0 From, 1 To)"`,
7706
+ uniform int uSource; // @control min=0 max=1 default=0 label="Brightness of (0 From, 1 To)"
7707
+ ${BACKDROP_CONTROLS}`,
7629
7708
  PICTURE,
7630
7709
  COMMON,
7631
7710
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
7632
7711
  vec2 uv = fragCoord / iResolution.xy;
7633
7712
  float t = progress();
7634
- vec3 a = cover(uFrom, uv);
7635
- vec3 b = cover(uTo, uv);
7636
- float l = clamp(luma(uSource == 0 ? a : b), 0.0, 1.0);
7713
+ vec4 a = coverRGBA(uFrom, uv);
7714
+ vec4 b = coverRGBA(uTo, uv);
7715
+ float l = clamp(luma(uSource == 0 ? a.rgb : b.rgb), 0.0, 1.0);
7637
7716
  if (uLightFirst) l = 1.0 - l;
7638
7717
  // The front sweeps from below the darkest pixel to above the lightest, so both ends are clean.
7639
7718
  float front = mix(-uSoftness, 1.0 + uSoftness, t);
7640
- fragColor = vec4(mix(a, b, smoothstep(l - uSoftness, l + uSoftness, front)), 1.0);
7719
+ fragColor = mix(a, b, smoothstep(l - uSoftness, l + uSoftness, front));
7641
7720
  }`
7642
7721
  ),
7643
7722
  presets: [
@@ -7659,7 +7738,8 @@ ${pair(SAMPLE_IMAGES.landscape, SAMPLE_IMAGES.city)}
7659
7738
  uniform float uParticle; // @control min=6 max=80 default=20 label="Particle size (px)"
7660
7739
  uniform float uScatter; // @control min=0 max=0.8 default=0.45 label="Scatter"
7661
7740
  uniform float uSpin; // @control min=0 max=2 default=0.6 label="Spin & curl"
7662
- uniform vec3 uBackground; // @control color default=#0B0B12 label="Background"`,
7741
+ uniform vec3 uBackground; // @control color default=#0B0B12 label="Background"
7742
+ ${BACKDROP_CONTROLS}`,
7663
7743
  HASH,
7664
7744
  ROTATE,
7665
7745
  PICTURE,
@@ -7680,7 +7760,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
7680
7760
  vec2 p = fragCoord / uParticle;
7681
7761
  vec2 grid = iResolution.xy / uParticle;
7682
7762
  vec2 base = floor(p);
7683
- vec3 col = uBackground;
7763
+ vec4 col = vec4(uBackground, uBackdropAlpha);
7684
7764
  float top = -1.0;
7685
7765
  vec2 hitFrom = vec2(0.0);
7686
7766
  vec2 hitTo = vec2(0.0);
@@ -7715,12 +7795,11 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
7715
7795
  }
7716
7796
  }
7717
7797
  if (top > -1.0) {
7718
- vec3 a = cover(uFrom, hitFrom / grid);
7719
- vec3 b = cover(uTo, hitTo / grid);
7720
- vec3 particle = mix(a, b, hitT) * (1.0 + 0.15 * sin(3.14159 * hitT));
7798
+ vec4 particle = mix(coverRGBA(uFrom, hitFrom / grid), coverRGBA(uTo, hitTo / grid), hitT);
7799
+ particle.rgb *= 1.0 + 0.15 * sin(3.14159 * hitT);
7721
7800
  col = mix(col, particle, hitCover);
7722
7801
  }
7723
- fragColor = vec4(col, 1.0);
7802
+ fragColor = col;
7724
7803
  }`
7725
7804
  ),
7726
7805
  presets: [
@@ -7741,7 +7820,8 @@ var transitionInkBleed = {
7741
7820
  ${pair(SAMPLE_IMAGES.city, SAMPLE_IMAGES.landscape)}
7742
7821
  uniform float uScale; // @control min=0.5 max=8 default=3 label="Scale"
7743
7822
  uniform float uEdge; // @control min=0.005 max=0.2 default=0.05 label="Edge width"
7744
- uniform vec3 uInk; // @control color default=#1B1F3B label="Ink"`,
7823
+ uniform vec3 uInk; // @control color default=#1B1F3B label="Ink"
7824
+ ${BACKDROP_CONTROLS}`,
7745
7825
  HASH,
7746
7826
  NOISE,
7747
7827
  FBM,
@@ -7769,12 +7849,10 @@ uniform vec3 uInk; // @control color default=#1B1F3B label="Ink"`,
7769
7849
  float soak = smoothstep(-2.0 * uEdge, 0.0, gap) * (1.0 - wet);
7770
7850
  float pool = wet * smoothstep(uEdge, 0.0, gap);
7771
7851
  vec2 bleed = vec2(soak - pool) * 0.004;
7772
- vec3 a = cover(uFrom, uv + bleed);
7773
- vec3 b = cover(uTo, uv - bleed);
7774
- vec3 col = mix(a, b, wet);
7775
- col = mix(col, col * 0.82, soak * 0.6);
7776
- col = mix(col, uInk, pool * 0.85);
7777
- fragColor = vec4(col, 1.0);
7852
+ vec4 col = mix(coverRGBA(uFrom, uv + bleed), coverRGBA(uTo, uv - bleed), wet);
7853
+ col.rgb = mix(col.rgb, col.rgb * 0.82, soak * 0.6);
7854
+ col = mix(col, vec4(uInk, 1.0), pool * 0.85);
7855
+ fragColor = col;
7778
7856
  }`
7779
7857
  ),
7780
7858
  presets: [
@@ -8531,7 +8609,8 @@ uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE
8531
8609
  uniform float uZoomStart; // @control min=1 max=3 default=1.05 label="Start zoom"
8532
8610
  uniform float uZoomEnd; // @control min=1 max=3 default=1.4 label="End zoom"
8533
8611
  uniform float uAngle; // @control min=0 max=360 default=20 label="Pan direction (\xB0)"
8534
- uniform float uDuration; // @control min=2 max=30 default=6 label="Duration (s)"`,
8612
+ uniform float uDuration; // @control min=2 max=30 default=6 label="Duration (s)"
8613
+ ${BACKDROP_CONTROLS}`,
8535
8614
  PICTURE,
8536
8615
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
8537
8616
  vec2 uv = fragCoord / iResolution.xy;
@@ -8547,8 +8626,7 @@ uniform float uDuration; // @control min=2 max=30 default=6 label="Duration (s
8547
8626
  // Pan as far as the window allows in the chosen direction, from one side to the other.
8548
8627
  float a = radians(uAngle);
8549
8628
  vec2 pan = vec2(cos(a), sin(a)) * (1.0 - window) * 0.5 * (2.0 * k - 1.0);
8550
- vec3 col = picture(uImage, 0.5 + (uv - 0.5) * window + pan);
8551
- fragColor = vec4(col, 1.0);
8629
+ fragColor = pictureRGBA(uImage, 0.5 + (uv - 0.5) * window + pan);
8552
8630
  }`
8553
8631
  ),
8554
8632
  presets: [
@@ -8570,7 +8648,8 @@ uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE
8570
8648
  uniform float uCell; // @control min=4 max=32 default=9 label="Cell size (px)"
8571
8649
  uniform int uColourMode; // @control min=0 max=1 default=0 label="Colour (0 ink, 1 original)"
8572
8650
  uniform vec3 uInk; // @control color default=#A3E635 label="Ink"
8573
- uniform vec3 uBackground; // @control color default=#0A0F0A label="Background"`,
8651
+ uniform vec3 uBackground; // @control color default=#0A0F0A label="Background"
8652
+ ${BACKDROP_CONTROLS}`,
8574
8653
  PICTURE,
8575
8654
  `// 5 \xD7 7 glyphs, lightest to densest: bit (x + 5y) of .x holds rows 0\u20133 (top first), of .y rows 4\u20136.
8576
8655
  const ivec2 GLYPHS[10] = ivec2[10](
@@ -8598,14 +8677,15 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
8598
8677
  vec2 cell = vec2(uCell, uCell * 9.0 / 7.0);
8599
8678
  vec2 id = floor(fragCoord / cell);
8600
8679
  vec2 local = fract(fragCoord / cell);
8601
- vec3 src = picture(uImage, (id + 0.5) * cell / iResolution.xy);
8680
+ vec4 pic = pictureRGBA(uImage, (id + 0.5) * cell / iResolution.xy);
8681
+ vec3 src = pic.rgb;
8602
8682
  float l = pow(clamp(dot(src, vec3(0.2126, 0.7152, 0.0722)), 0.0, 1.0), 0.8);
8603
8683
  int level = int(min(l * 10.0, 9.0));
8604
8684
  int gx = int(floor(local.x * 7.0)) - 1;
8605
8685
  int gy = int(floor((1.0 - local.y) * 9.0)) - 1;
8606
8686
  float ink = glyphBit(GLYPHS[level], gx, gy);
8607
8687
  vec3 tint = uColourMode == 1 ? src * 1.25 : uInk;
8608
- fragColor = vec4(mix(uBackground, tint, ink), 1.0);
8688
+ fragColor = vec4(mix(uBackground, tint, ink), pic.a);
8609
8689
  }`
8610
8690
  ),
8611
8691
  presets: [
@@ -8630,7 +8710,8 @@ uniform float uProgress; // @control min=0 max=1 default=0.5 label="Progress (
8630
8710
  uniform float uCount; // @control min=3 max=30 default=8 label="Shards (across height)"
8631
8711
  uniform float uSpread; // @control min=0 max=2 default=0.6 label="Spread"
8632
8712
  uniform float uSpin; // @control min=0 max=6.28 default=1.5 label="Spin"
8633
- uniform vec3 uBackground; // @control color default=#0B0B12 label="Background"`,
8713
+ uniform vec3 uBackground; // @control color default=#0B0B12 label="Background"
8714
+ ${BACKDROP_CONTROLS}`,
8634
8715
  HASH,
8635
8716
  ROTATE,
8636
8717
  PICTURE,
@@ -8666,7 +8747,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
8666
8747
  // Shard centres move out from the middle; shards keep their size, so gaps open between them.
8667
8748
  float grow = 1.0 + uSpread * k;
8668
8749
  vec2 base = floor(p / grow);
8669
- vec3 col = uBackground;
8750
+ vec4 col = vec4(uBackground, uBackdropAlpha);
8670
8751
  float top = -1.0;
8671
8752
  for (int j = -2; j <= 2; j++) {
8672
8753
  for (int i = -2; i <= 2; i++) {
@@ -8681,14 +8762,14 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
8681
8762
  if (s.x == c.x && s.y == c.y && h.y > top) {
8682
8763
  top = h.y;
8683
8764
  vec2 uv = q / uCount * iResolution.y / iResolution.xy + 0.5;
8684
- vec3 shard = picture(uImage, uv);
8685
- shard *= 1.0 - 0.35 * k * abs(sin(a));
8686
- shard += k * 0.35 * smoothstep(0.06, 0.0, s.z);
8765
+ vec4 shard = pictureRGBA(uImage, uv);
8766
+ shard.rgb *= 1.0 - 0.35 * k * abs(sin(a));
8767
+ shard.rgb += k * 0.35 * smoothstep(0.06, 0.0, s.z);
8687
8768
  col = shard;
8688
8769
  }
8689
8770
  }
8690
8771
  }
8691
- fragColor = vec4(col, 1.0);
8772
+ fragColor = col;
8692
8773
  }`
8693
8774
  ),
8694
8775
  presets: [
@@ -8708,7 +8789,8 @@ var imageMelt = {
8708
8789
  uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label="Image"
8709
8790
  uniform float uAmount; // @control min=0 max=0.6 default=0.18 label="Amount"
8710
8791
  uniform float uSpeed; // @control min=0 max=2 default=0.35 label="Flow speed"
8711
- uniform float uScale; // @control min=1 max=20 default=6 label="Scale"`,
8792
+ uniform float uScale; // @control min=1 max=20 default=6 label="Scale"
8793
+ ${BACKDROP_CONTROLS}`,
8712
8794
  HASH,
8713
8795
  NOISE,
8714
8796
  FBM,
@@ -8722,10 +8804,10 @@ uniform float uScale; // @control min=1 max=20 default=6 label="Scale"`,
8722
8804
  float drop = uAmount * (0.7 * runs + 0.6 * drips) * (0.35 + 0.65 * (1.0 - uv.y));
8723
8805
  float wobble = (vnoise(vec2(uv.y * uScale * 2.0 - t, uv.x * uScale)) - 0.5) * uAmount * 0.12;
8724
8806
  vec2 st = clamp(uv + vec2(wobble, drop), 0.0, 1.0);
8725
- vec3 col = picture(uImage, st);
8807
+ vec4 pic = pictureRGBA(uImage, st);
8726
8808
  // A little sheen along the leading edges of the runs.
8727
- col += 0.12 * smoothstep(0.55, 0.9, drips) * uAmount * 4.0;
8728
- fragColor = vec4(col, 1.0);
8809
+ vec3 col = pic.rgb + 0.12 * smoothstep(0.55, 0.9, drips) * uAmount * 4.0;
8810
+ fragColor = vec4(col, pic.a);
8729
8811
  }`
8730
8812
  ),
8731
8813
  presets: [
@@ -8748,7 +8830,8 @@ uniform vec3 uInkA; // @control color default=#FF48B0 label="Ink A"
8748
8830
  uniform vec3 uInkB; // @control color default=#0078BF label="Ink B"
8749
8831
  uniform float uOffset; // @control min=0 max=20 default=4 label="Misregistration (px)"
8750
8832
  uniform float uGrain; // @control min=0 max=1 default=0.4 label="Grain"
8751
- uniform vec3 uPaper; // @control color default=#F5EFE0 label="Paper"`,
8833
+ uniform vec3 uPaper; // @control color default=#F5EFE0 label="Paper"
8834
+ ${BACKDROP_CONTROLS}`,
8752
8835
  HASH,
8753
8836
  NOISE,
8754
8837
  PICTURE,
@@ -8764,8 +8847,8 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
8764
8847
  float fibre = hash12(fragCoord);
8765
8848
  float mottle = vnoise(fragCoord / 6.0);
8766
8849
  // Two separations: ink A where the picture lacks green, ink B where it lacks red.
8767
- vec3 a = picture(uImage, uv + shift);
8768
- vec3 b = picture(uImage, uv - shift);
8850
+ vec4 a = pictureRGBA(uImage, uv + shift);
8851
+ vec4 b = pictureRGBA(uImage, uv - shift);
8769
8852
  float dA = posterize(1.0 - a.g, levels, (hash12(fragCoord + 17.0) - 0.5) * uGrain * 0.6);
8770
8853
  float dB = posterize(1.0 - b.r, levels, (hash12(fragCoord + 53.0) - 0.5) * uGrain * 0.6);
8771
8854
  // Ink never lays down evenly: mottled, with paper showing through.
@@ -8773,7 +8856,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
8773
8856
  vec3 col = uPaper * (1.0 - uGrain * 0.08 * fibre);
8774
8857
  col *= mix(vec3(1.0), uInkA, dA * cover);
8775
8858
  col *= mix(vec3(1.0), uInkB, dB * cover);
8776
- fragColor = vec4(col, 1.0);
8859
+ fragColor = vec4(col, max(a.a, b.a));
8777
8860
  }`
8778
8861
  ),
8779
8862
  presets: [
@@ -8794,7 +8877,8 @@ var imageThermal = {
8794
8877
  uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label="Image"
8795
8878
  uniform int uMode; // @control min=0 max=1 default=0 label="Palette (0 thermal, 1 night vision)"
8796
8879
  uniform float uNoise; // @control min=0 max=0.5 default=0.04 label="Noise"
8797
- uniform float uContrast; // @control min=0.5 max=3 default=1.3 label="Contrast"`,
8880
+ uniform float uContrast; // @control min=0.5 max=3 default=1.3 label="Contrast"
8881
+ ${BACKDROP_CONTROLS}`,
8798
8882
  HASH,
8799
8883
  PICTURE,
8800
8884
  `vec3 heat(float t) {
@@ -8809,9 +8893,9 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
8809
8893
  vec2 uv = fragCoord / iResolution.xy;
8810
8894
  // Heat spreads, so take a soft average around the pixel.
8811
8895
  vec2 px = 2.0 / iResolution.xy;
8812
- vec3 src = 0.4 * picture(uImage, uv) + 0.15 * (picture(uImage, uv + vec2(px.x, 0.0)) + picture(uImage, uv - vec2(px.x, 0.0)) +
8813
- picture(uImage, uv + vec2(0.0, px.y)) + picture(uImage, uv - vec2(0.0, px.y)));
8814
- float l = dot(src, vec3(0.2126, 0.7152, 0.0722));
8896
+ vec4 src = 0.4 * pictureRGBA(uImage, uv) + 0.15 * (pictureRGBA(uImage, uv + vec2(px.x, 0.0)) + pictureRGBA(uImage, uv - vec2(px.x, 0.0)) +
8897
+ pictureRGBA(uImage, uv + vec2(0.0, px.y)) + pictureRGBA(uImage, uv - vec2(0.0, px.y)));
8898
+ float l = dot(src.rgb, vec3(0.2126, 0.7152, 0.0722));
8815
8899
  l = clamp((l - 0.5) * uContrast + 0.5, 0.0, 1.0);
8816
8900
  float frame = floor(iTime * 24.0);
8817
8901
  float grain = hash12(fragCoord + frame * 37.1) - 0.5;
@@ -8826,7 +8910,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
8826
8910
  vec2 d = (uv - 0.5) * vec2(iResolution.x / iResolution.y, 1.0);
8827
8911
  col *= smoothstep(0.75, 0.3, length(d));
8828
8912
  }
8829
- fragColor = vec4(col, 1.0);
8913
+ fragColor = vec4(col, src.a);
8830
8914
  }`
8831
8915
  ),
8832
8916
  presets: [
@@ -8849,25 +8933,28 @@ uniform float uThreshold; // @control min=0 max=1 default=0.15 label="Threshold
8849
8933
  uniform vec3 uGlow; // @control color default=#22D3EE label="Glow colour"
8850
8934
  uniform float uWidth; // @control min=0.5 max=4 default=1.5 label="Glow width (px)"
8851
8935
  uniform float uDim; // @control min=0 max=1 default=0.75 label="Dim image"
8852
- uniform float uPulse; // @control min=0 max=1 default=0.4 label="Pulse"`,
8936
+ uniform float uPulse; // @control min=0 max=1 default=0.4 label="Pulse"
8937
+ ${BACKDROP_CONTROLS}`,
8853
8938
  PICTURE,
8854
- `float luma(vec2 uv) {
8855
- return dot(picture(uImage, uv), vec3(0.2126, 0.7152, 0.0722));
8939
+ `// Brightness and alpha: an edge is where either changes (an icon's outline is in its alpha).
8940
+ vec2 lumaAlpha(vec2 uv) {
8941
+ vec4 c = pictureRGBA(uImage, uv);
8942
+ return vec2(dot(c.rgb, vec3(0.2126, 0.7152, 0.0722)), c.a);
8856
8943
  }
8857
8944
 
8858
8945
  // Sobel edge strength, sampling d apart (in uv).
8859
8946
  float sobel(vec2 uv, vec2 d) {
8860
- float tl = luma(uv + vec2(-d.x, d.y));
8861
- float t = luma(uv + vec2(0.0, d.y));
8862
- float tr = luma(uv + d);
8863
- float l = luma(uv - vec2(d.x, 0.0));
8864
- float r = luma(uv + vec2(d.x, 0.0));
8865
- float bl = luma(uv - d);
8866
- float b = luma(uv - vec2(0.0, d.y));
8867
- float br = luma(uv + vec2(d.x, -d.y));
8868
- float gx = tr + 2.0 * r + br - tl - 2.0 * l - bl;
8869
- float gy = tl + 2.0 * t + tr - bl - 2.0 * b - br;
8870
- return length(vec2(gx, gy));
8947
+ vec2 tl = lumaAlpha(uv + vec2(-d.x, d.y));
8948
+ vec2 t = lumaAlpha(uv + vec2(0.0, d.y));
8949
+ vec2 tr = lumaAlpha(uv + d);
8950
+ vec2 l = lumaAlpha(uv - vec2(d.x, 0.0));
8951
+ vec2 r = lumaAlpha(uv + vec2(d.x, 0.0));
8952
+ vec2 bl = lumaAlpha(uv - d);
8953
+ vec2 b = lumaAlpha(uv - vec2(0.0, d.y));
8954
+ vec2 br = lumaAlpha(uv + vec2(d.x, -d.y));
8955
+ vec2 gx = tr + 2.0 * r + br - tl - 2.0 * l - bl;
8956
+ vec2 gy = tl + 2.0 * t + tr - bl - 2.0 * b - br;
8957
+ return max(length(vec2(gx.x, gy.x)), length(vec2(gx.y, gy.y)));
8871
8958
  }
8872
8959
 
8873
8960
  void mainImage(out vec4 fragColor, in vec2 fragCoord) {
@@ -8877,10 +8964,13 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
8877
8964
  float halo = smoothstep(uThreshold, uThreshold + 0.5, sobel(uv, d * 3.0));
8878
8965
  vec2 c = (uv - 0.5) * vec2(iResolution.x / iResolution.y, 1.0);
8879
8966
  float pulse = 1.0 - uPulse * (0.5 + 0.5 * sin(iTime * 3.14159265 - length(c) * 12.0));
8880
- vec3 col = picture(uImage, uv) * (1.0 - uDim);
8881
- col += uGlow * (line + 0.45 * halo) * pulse;
8882
- col += vec3(1.0) * line * line * 0.35 * pulse;
8883
- fragColor = vec4(col, 1.0);
8967
+ vec4 pic = pictureRGBA(uImage, uv);
8968
+ float glow = (line + 0.45 * halo) * pulse;
8969
+ vec3 light = uGlow * glow + vec3(1.0) * line * line * 0.35 * pulse;
8970
+ // The glow is light added over the picture, so it also shows where the picture is clear.
8971
+ float alpha = max(pic.a, clamp(glow, 0.0, 1.0));
8972
+ vec3 col = (pic.rgb * (1.0 - uDim) * pic.a + light) / max(alpha, 1e-4);
8973
+ fragColor = vec4(col, alpha);
8884
8974
  }`
8885
8975
  ),
8886
8976
  presets: [
@@ -8901,25 +8991,28 @@ uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE
8901
8991
  uniform float uFocus; // @control min=0 max=1 default=0.45 label="Focus position"
8902
8992
  uniform float uBand; // @control min=0 max=0.6 default=0.12 label="Focus band width"
8903
8993
  uniform float uBlur; // @control min=0 max=30 default=12 label="Blur (px)"
8904
- uniform float uSaturation; // @control min=0 max=2 default=1.4 label="Saturation"`,
8994
+ uniform float uSaturation; // @control min=0 max=2 default=1.4 label="Saturation"
8995
+ ${BACKDROP_CONTROLS}`,
8905
8996
  PICTURE,
8906
8997
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
8907
8998
  vec2 uv = fragCoord / iResolution.xy;
8908
8999
  float away = smoothstep(uBand * 0.5, uBand * 0.5 + 0.3, abs(uv.y - uFocus));
8909
9000
  float radius = away * uBlur;
8910
9001
  // A 24-tap golden-angle disc.
8911
- vec3 sum = vec3(0.0);
9002
+ // Summed premultiplied, so clear parts of the picture do not darken the blur.
9003
+ vec4 sum = vec4(0.0);
8912
9004
  for (int i = 0; i < 24; i++) {
8913
9005
  float fi = float(i);
8914
9006
  float r = sqrt((fi + 0.5) / 24.0) * radius;
8915
9007
  float a = fi * 2.39996;
8916
- sum += picture(uImage, uv + r * vec2(cos(a), sin(a)) / iResolution.xy);
9008
+ vec4 c = pictureRGBA(uImage, uv + r * vec2(cos(a), sin(a)) / iResolution.xy);
9009
+ sum += vec4(c.rgb * c.a, c.a);
8917
9010
  }
8918
- vec3 col = sum / 24.0;
9011
+ vec3 col = sum.rgb / max(sum.a, 1e-4);
8919
9012
  float l = dot(col, vec3(0.2126, 0.7152, 0.0722));
8920
9013
  col = mix(vec3(l), col, uSaturation);
8921
9014
  col = clamp((col - 0.5) * 1.08 + 0.5, 0.0, 1.0);
8922
- fragColor = vec4(col, 1.0);
9015
+ fragColor = vec4(col, sum.a / 24.0);
8923
9016
  }`
8924
9017
  ),
8925
9018
  presets: [
@@ -8939,7 +9032,8 @@ var imageSlitScan = {
8939
9032
  uniform sampler2D uImage; // @control filter=linear wrap=mirror default=${SAMPLE_IMAGES.landscape} label="Image"
8940
9033
  uniform int uDirection; // @control min=0 max=1 default=0 label="Direction (0 horizontal, 1 vertical)"
8941
9034
  uniform float uStretch; // @control min=0 max=1 default=0.45 label="Stretch"
8942
- uniform float uSpeed; // @control min=0 max=3 default=0.6 label="Speed"`,
9035
+ uniform float uSpeed; // @control min=0 max=3 default=0.6 label="Speed"
9036
+ ${BACKDROP_CONTROLS}`,
8943
9037
  HASH,
8944
9038
  NOISE,
8945
9039
  PICTURE,
@@ -8954,13 +9048,15 @@ uniform float uSpeed; // @control min=0 max=3 default=0.6 label="Speed"`,
8954
9048
  float along = slit + (a.x - slit) * (1.0 - uStretch * delay);
8955
9049
  along += uStretch * 0.15 * (delay - 0.5);
8956
9050
  // Smear: average a few taps along the streak.
8957
- vec3 col = vec3(0.0);
9051
+ // Summed premultiplied, so clear parts of the picture do not darken the smear.
9052
+ vec4 sum = vec4(0.0);
8958
9053
  for (int i = 0; i < 8; i++) {
8959
9054
  float s = along - float(i) * uStretch * 0.006;
8960
9055
  vec2 st = uDirection == 0 ? vec2(s, a.y) : vec2(a.y, s);
8961
- col += picture(uImage, st);
9056
+ vec4 c = pictureRGBA(uImage, st);
9057
+ sum += vec4(c.rgb * c.a, c.a);
8962
9058
  }
8963
- fragColor = vec4(col / 8.0, 1.0);
9059
+ fragColor = vec4(sum.rgb / max(sum.a, 1e-4), sum.a / 8.0);
8964
9060
  }`
8965
9061
  ),
8966
9062
  presets: [
@@ -8983,7 +9079,8 @@ uniform int uShape; // @control min=0 max=1 default=0 label="Tile shape (
8983
9079
  uniform float uSize; // @control min=10 max=160 default=48 label="Tile size (px)"
8984
9080
  uniform bool uAuto; // @control default=true label="Animate"
8985
9081
  uniform float uProgress; // @control min=0 max=1 default=1 label="Progress (when still)"
8986
- uniform vec3 uBackground; // @control color default=#0F172A label="Background"`,
9082
+ uniform vec3 uBackground; // @control color default=#0F172A label="Background"
9083
+ ${BACKDROP_CONTROLS}`,
8987
9084
  HASH,
8988
9085
  PICTURE,
8989
9086
  `// Hexagon tiles 1 unit across the flats: h is the offset from the tile's centre.
@@ -9037,14 +9134,14 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
9037
9134
  vec2 hf = vec2(h.x / max(squash, 0.001), h.y);
9038
9135
  float e = uShape == 0 ? hexEdge(hf) : triEdge(hf, up);
9039
9136
  float gap = 0.1 * (1.0 - flip);
9040
- vec3 col = uBackground * 0.6;
9137
+ vec4 col = vec4(uBackground * 0.6, uBackdropAlpha);
9041
9138
  if (e < 1.0 - gap + 0.001) {
9042
- vec3 face = flip < 0.5
9043
- ? uBackground * (0.9 + 0.2 * hash12(centre * 3.17)) + 0.04
9044
- : picture(uImage, (centre + hf) * uSize / iResolution.xy);
9045
- col = face * (0.5 + 0.5 * squash);
9139
+ vec4 face = flip < 0.5
9140
+ ? vec4(uBackground * (0.9 + 0.2 * hash12(centre * 3.17)) + 0.04, uBackdropAlpha)
9141
+ : pictureRGBA(uImage, (centre + hf) * uSize / iResolution.xy);
9142
+ col = vec4(face.rgb * (0.5 + 0.5 * squash), face.a);
9046
9143
  }
9047
- fragColor = vec4(col, 1.0);
9144
+ fragColor = col;
9048
9145
  }`
9049
9146
  ),
9050
9147
  presets: [
@@ -9064,7 +9161,8 @@ var imageChromaticLens = {
9064
9161
  uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label="Image"
9065
9162
  uniform float uDistortion; // @control min=-0.5 max=1 default=0.25 label="Distortion"
9066
9163
  uniform float uAberration; // @control min=0 max=0.1 default=0.025 label="Aberration"
9067
- uniform float uVignette; // @control min=0 max=1 default=0.4 label="Vignette"`,
9164
+ uniform float uVignette; // @control min=0 max=1 default=0.4 label="Vignette"
9165
+ ${BACKDROP_CONTROLS}`,
9068
9166
  PICTURE,
9069
9167
  `// Where a lens with barrel strength k shows the point c (centred, aspect-corrected) from.
9070
9168
  vec2 lens(vec2 c, float k, vec2 aspect) {
@@ -9078,13 +9176,12 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
9078
9176
  vec2 uv = fragCoord / iResolution.xy;
9079
9177
  vec2 aspect = vec2(iResolution.x / iResolution.y, 1.0);
9080
9178
  vec2 c = (uv - 0.5) * aspect;
9081
- vec3 col = vec3(
9082
- picture(uImage, lens(c, uDistortion + uAberration, aspect)).r,
9083
- picture(uImage, lens(c, uDistortion, aspect)).g,
9084
- picture(uImage, lens(c, uDistortion - uAberration, aspect)).b
9085
- );
9179
+ vec4 r = pictureRGBA(uImage, lens(c, uDistortion + uAberration, aspect));
9180
+ vec4 g = pictureRGBA(uImage, lens(c, uDistortion, aspect));
9181
+ vec4 b = pictureRGBA(uImage, lens(c, uDistortion - uAberration, aspect));
9182
+ vec3 col = vec3(r.r, g.g, b.b);
9086
9183
  col *= 1.0 - uVignette * smoothstep(0.25, 0.95, length(c));
9087
- fragColor = vec4(col, 1.0);
9184
+ fragColor = vec4(col, max(max(r.a, g.a), b.a));
9088
9185
  }`
9089
9186
  ),
9090
9187
  presets: [
@@ -9104,7 +9201,8 @@ var imageOilPaint = {
9104
9201
  uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.portrait} label="Image"
9105
9202
  uniform int uRadius; // @control min=1 max=8 default=4 label="Brush radius (px)"
9106
9203
  uniform float uSharpness; // @control min=0.5 max=8 default=4 label="Sharpness"
9107
- uniform float uSaturation; // @control min=0 max=2 default=1.15 label="Saturation"`,
9204
+ uniform float uSaturation; // @control min=0 max=2 default=1.15 label="Saturation"
9205
+ ${BACKDROP_CONTROLS}`,
9108
9206
  PICTURE,
9109
9207
  `void mainImage(out vec4 fragColor, in vec2 fragCoord) {
9110
9208
  vec2 uv = fragCoord / iResolution.xy;
@@ -9144,7 +9242,7 @@ uniform float uSaturation; // @control min=0 max=2 default=1.15 label="Saturatio
9144
9242
  vec3 col = (m0 * w.x + m1 * w.y + m2 * w.z + m3 * w.w) / (w.x + w.y + w.z + w.w);
9145
9243
  float l = dot(col, vec3(0.2126, 0.7152, 0.0722));
9146
9244
  col = mix(vec3(l), col, uSaturation);
9147
- fragColor = vec4(col, 1.0);
9245
+ fragColor = vec4(col, pictureRGBA(uImage, uv).a);
9148
9246
  }`
9149
9247
  ),
9150
9248
  presets: [
@@ -10444,4 +10542,4 @@ export {
10444
10542
  effectControls,
10445
10543
  effectDefaults
10446
10544
  };
10447
- //# sourceMappingURL=chunk-36OCXXPY.js.map
10545
+ //# sourceMappingURL=chunk-L4NFQM5N.js.map