@ansh-ui/core 0.2.0 → 0.3.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.
Files changed (54) hide show
  1. package/dist/behaviours.cjs +20 -15
  2. package/dist/behaviours.d.cts +2 -2
  3. package/dist/behaviours.d.ts +2 -2
  4. package/dist/behaviours.js +3 -2
  5. package/dist/{chunk-56NNBIAO.js → chunk-2DOA43UB.js} +1047 -18
  6. package/dist/chunk-2DOA43UB.js.map +1 -0
  7. package/dist/{chunk-7MYTSAPR.js → chunk-37GPMMMT.js} +3 -3
  8. package/dist/{chunk-7MYTSAPR.js.map → chunk-37GPMMMT.js.map} +1 -1
  9. package/dist/{chunk-23GSSIMK.cjs → chunk-4ORYTFY3.cjs} +3 -3
  10. package/dist/{chunk-23GSSIMK.cjs.map → chunk-4ORYTFY3.cjs.map} +1 -1
  11. package/dist/{chunk-MMO7KO53.cjs → chunk-FNR4B3KY.cjs} +3 -3
  12. package/dist/{chunk-MMO7KO53.cjs.map → chunk-FNR4B3KY.cjs.map} +1 -1
  13. package/dist/{chunk-BOQQHICI.js → chunk-GQH5ZHPB.js} +3 -3
  14. package/dist/{chunk-BOQQHICI.js.map → chunk-GQH5ZHPB.js.map} +1 -1
  15. package/dist/{chunk-R7UJ7MWP.cjs → chunk-OSNUN5JJ.cjs} +1070 -34
  16. package/dist/chunk-OSNUN5JJ.cjs.map +1 -0
  17. package/dist/{chunk-GHBRGMSO.cjs → chunk-PZF3YXVE.cjs} +6 -6
  18. package/dist/{chunk-GHBRGMSO.cjs.map → chunk-PZF3YXVE.cjs.map} +1 -1
  19. package/dist/{chunk-UJBXY6QC.cjs → chunk-SSAWZIO2.cjs} +150 -28
  20. package/dist/chunk-SSAWZIO2.cjs.map +1 -0
  21. package/dist/{chunk-C6XPQO2O.js → chunk-WLOGUD3G.js} +126 -5
  22. package/dist/{chunk-C6XPQO2O.js.map → chunk-WLOGUD3G.js.map} +1 -1
  23. package/dist/{chunk-KTUBCQC3.js → chunk-YHSCAGCI.js} +3 -3
  24. package/dist/{chunk-KTUBCQC3.js.map → chunk-YHSCAGCI.js.map} +1 -1
  25. package/dist/color.cjs +39 -39
  26. package/dist/color.d.cts +5 -4
  27. package/dist/color.d.ts +5 -4
  28. package/dist/color.js +3 -3
  29. package/dist/effects.cjs +53 -25
  30. package/dist/effects.d.cts +4 -4
  31. package/dist/effects.d.ts +4 -4
  32. package/dist/effects.js +4 -4
  33. package/dist/{index-CQEEPu-D.d.ts → index-DN0bQII7.d.ts} +47 -2
  34. package/dist/{index-DHOfsEIn.d.cts → index-DmJ3i1Tr.d.cts} +330 -4
  35. package/dist/{index-Dl8GCsya.d.cts → index-Dv3gw7Mb.d.cts} +47 -2
  36. package/dist/{index-CCdpefMp.d.ts → index-SO8zHlVo.d.ts} +330 -4
  37. package/dist/index.cjs +186 -154
  38. package/dist/index.d.cts +10 -9
  39. package/dist/index.d.ts +10 -9
  40. package/dist/index.js +9 -9
  41. package/dist/math.d.cts +3 -2
  42. package/dist/math.d.ts +3 -2
  43. package/dist/noise.d.cts +2 -1
  44. package/dist/noise.d.ts +2 -1
  45. package/dist/{palette-6HkVltYU.d.ts → palette-Dcomsp9R.d.cts} +2 -1
  46. package/dist/{palette-6HkVltYU.d.cts → palette-Dcomsp9R.d.ts} +2 -1
  47. package/dist/{random--4mSc5Lp.d.cts → random-YnhdaiIx.d.cts} +2 -1
  48. package/dist/{random--4mSc5Lp.d.ts → random-YnhdaiIx.d.ts} +2 -1
  49. package/dist/{reduced-motion-D0LUgZle.d.cts → reduced-motion-DaeXwgzb.d.cts} +2 -1
  50. package/dist/{reduced-motion-D0LUgZle.d.ts → reduced-motion-DaeXwgzb.d.ts} +2 -1
  51. package/package.json +1 -1
  52. package/dist/chunk-56NNBIAO.js.map +0 -1
  53. package/dist/chunk-R7UJ7MWP.cjs.map +0 -1
  54. package/dist/chunk-UJBXY6QC.cjs.map +0 -1
@@ -1,8 +1,8 @@
1
- import { toRgbString, resolvePalette, expandPalette } from './chunk-KTUBCQC3.js';
1
+ import { toRgbString, resolvePalette, expandPalette } from './chunk-YHSCAGCI.js';
2
2
  import { createNoise } from './chunk-FAVIEION.js';
3
- import { createRng } from './chunk-YFQXRC5R.js';
4
- import { getRegistry, observeResize, resolveMotion, getTicker, observeIntersection, onReducedMotionChange, trackPointer } from './chunk-BOQQHICI.js';
3
+ import { getRegistry, observeResize, resolveMotion, getTicker, observeIntersection, onReducedMotionChange, trackPointer } from './chunk-GQH5ZHPB.js';
5
4
  import { parseColor } from './chunk-QALZKHWH.js';
5
+ import { createRng } from './chunk-YFQXRC5R.js';
6
6
  import { clamp, TAU, lerp, radians } from './chunk-KU2C6GEA.js';
7
7
 
8
8
  // src/render/surface.ts
@@ -1418,8 +1418,447 @@ var constellation = defineEffect({
1418
1418
  }
1419
1419
  });
1420
1420
 
1421
- // src/effects/atmosphere/meteors.ts
1421
+ // src/effects/particles/powder-explosion.ts
1422
1422
  var DEFAULTS5 = {
1423
+ colors: ["#ff9d5c", "#ffd76f", "#ff6b8b", "#7cc4ff"],
1424
+ speed: 1,
1425
+ intensity: 1,
1426
+ opacity: 1,
1427
+ blur: 0,
1428
+ blend: "lighter",
1429
+ seed: 59,
1430
+ interactive: true,
1431
+ resolution: 1,
1432
+ performance: "auto",
1433
+ reducedMotion: "pause",
1434
+ paused: false,
1435
+ pauseOffscreen: true,
1436
+ burstSize: 90,
1437
+ burstRate: 0.8,
1438
+ force: 420,
1439
+ drag: 0.92,
1440
+ gravity: 90,
1441
+ life: 1.9,
1442
+ size: 3,
1443
+ glow: 3.2,
1444
+ burstOnPress: true
1445
+ };
1446
+ var MAX_PARTICLES2 = 1600;
1447
+ var FADE_SHAPE = 1.4;
1448
+ var powderExplosion = defineEffect({
1449
+ name: "powder-explosion",
1450
+ defaults: DEFAULTS5,
1451
+ structuralOptions: ["burstSize"],
1452
+ setup(context, options) {
1453
+ let opts = options;
1454
+ const perBurst = Math.max(1, Math.floor(opts.burstSize));
1455
+ const concurrent = Math.max(2, Math.ceil(opts.life * opts.burstRate) + 2);
1456
+ const capacity = Math.min(MAX_PARTICLES2, perBurst * concurrent);
1457
+ const buffer = new ParticleBuffer(capacity);
1458
+ const gradients = new GradientCache(64);
1459
+ let untilNextBurst = 0;
1460
+ let wasDown = false;
1461
+ function burst(x, y) {
1462
+ const rng = context.rng;
1463
+ const colorCount = Math.max(1, context.colors.length);
1464
+ const family = rng.int(0, colorCount - 1);
1465
+ for (let i = 0; i < perBurst; i++) {
1466
+ const index = buffer.spawn();
1467
+ if (index < 0) return;
1468
+ const angle = rng.angle();
1469
+ const t = rng.next();
1470
+ const speed = opts.force * t * t * rng.range(0.85, 1.15);
1471
+ buffer.x[index] = x;
1472
+ buffer.y[index] = y;
1473
+ buffer.vx[index] = Math.cos(angle) * speed;
1474
+ buffer.vy[index] = Math.sin(angle) * speed;
1475
+ buffer.size[index] = opts.size * rng.range(0.55, 1.5);
1476
+ buffer.colorIndex[index] = rng.bool(0.75) ? family : rng.int(0, colorCount - 1);
1477
+ buffer.maxLife[index] = opts.life * rng.range(0.6, 1.3);
1478
+ buffer.life[index] = buffer.maxLife[index];
1479
+ buffer.seed[index] = rng.range(0, TAU);
1480
+ buffer.extra[index] = rng.range(0.85, 1.2);
1481
+ }
1482
+ }
1483
+ return {
1484
+ frame({ delta, quality }) {
1485
+ const { ctx, size, surface, pointer, colors } = context;
1486
+ const { width, height } = size;
1487
+ if (width < 1 || height < 1) return;
1488
+ surface.clear();
1489
+ if (colors.length === 0) return;
1490
+ const rng = context.rng;
1491
+ const rate = opts.burstRate * (opts.performance === "high" ? 1 : quality);
1492
+ untilNextBurst -= delta * rate;
1493
+ if (untilNextBurst <= 0) {
1494
+ burst(rng.range(width * 0.15, width * 0.85), rng.range(height * 0.2, height * 0.8));
1495
+ untilNextBurst = -Math.log(1 - rng.next());
1496
+ }
1497
+ const interactive = opts.interactive !== false && pointer !== null;
1498
+ if (interactive && opts.burstOnPress) {
1499
+ if (pointer.down && !wasDown) burst(pointer.x, pointer.y);
1500
+ wasDown = pointer.down;
1501
+ }
1502
+ const retain = Math.pow(1 - clamp(opts.drag, 0, 0.999), delta);
1503
+ const gravity = opts.gravity * delta;
1504
+ const alphaScale = clamp(opts.opacity * opts.intensity, 0, 1);
1505
+ const colorCount = Math.max(1, colors.length);
1506
+ ctx.globalCompositeOperation = opts.blend ?? "lighter";
1507
+ let i = 0;
1508
+ while (i < buffer.count) {
1509
+ buffer.life[i] -= delta;
1510
+ if (buffer.life[i] <= 0) {
1511
+ buffer.kill(i);
1512
+ continue;
1513
+ }
1514
+ buffer.vx[i] *= retain;
1515
+ buffer.vy[i] = buffer.vy[i] * retain + gravity;
1516
+ buffer.x[i] += buffer.vx[i] * delta;
1517
+ buffer.y[i] += buffer.vy[i] * delta;
1518
+ const age = clamp(1 - buffer.life[i] / buffer.maxLife[i], 0, 1);
1519
+ const alpha = alphaScale * Math.pow(1 - age, FADE_SHAPE);
1520
+ if (alpha > 2e-3) {
1521
+ drawGlow(
1522
+ ctx,
1523
+ buffer.x[i],
1524
+ buffer.y[i],
1525
+ buffer.size[i] * opts.glow * buffer.extra[i],
1526
+ colors[buffer.colorIndex[i] % colorCount],
1527
+ alpha,
1528
+ gradients
1529
+ );
1530
+ }
1531
+ i++;
1532
+ }
1533
+ ctx.globalCompositeOperation = "source-over";
1534
+ },
1535
+ update(next) {
1536
+ opts = {
1537
+ ...opts,
1538
+ ...next
1539
+ };
1540
+ gradients.clear();
1541
+ },
1542
+ destroy() {
1543
+ buffer.clear();
1544
+ gradients.clear();
1545
+ }
1546
+ };
1547
+ }
1548
+ });
1549
+
1550
+ // src/effects/particles/strange-attractor.ts
1551
+ var DEFAULTS6 = {
1552
+ colors: ["#7cc4ff", "#a78bfa", "#5eead4", "#fbbf7c"],
1553
+ speed: 1,
1554
+ intensity: 1,
1555
+ opacity: 1,
1556
+ blur: 0,
1557
+ blend: "lighter",
1558
+ seed: 83,
1559
+ interactive: false,
1560
+ resolution: 1,
1561
+ performance: "auto",
1562
+ reducedMotion: "slow",
1563
+ paused: false,
1564
+ pauseOffscreen: true,
1565
+ kind: "clifford",
1566
+ count: 2600,
1567
+ drift: 0.12,
1568
+ driftSpeed: 0.05,
1569
+ size: 0.9,
1570
+ pointOpacity: 0.34,
1571
+ fade: 0.12,
1572
+ scale: 0.42
1573
+ };
1574
+ var MAX_WALKERS = 8e3;
1575
+ var PARAM_SETS = {
1576
+ clifford: [
1577
+ [-1.4, 1.6, 1, 0.7],
1578
+ [1.7, 1.7, 0.06, 1.2],
1579
+ [-1.7, 1.3, -0.1, -1.21],
1580
+ [1.5, -1.8, 1.6, 0.9],
1581
+ [-1.8, -2, -0.5, -0.9]
1582
+ ],
1583
+ dejong: [
1584
+ [1.4, -2.3, 2.4, -2.1],
1585
+ [2.01, -2.53, 1.61, -0.33],
1586
+ [-2.7, -0.09, -0.86, -2.2],
1587
+ [1.641, 1.902, 0.316, 1.525]
1588
+ ],
1589
+ svensson: [
1590
+ [1.4, 1.56, 1.4, -6.56],
1591
+ [-1.78, -1.28, -0.75, 1.34],
1592
+ [1.94, 1.1, -1.63, 0.42]
1593
+ ]
1594
+ };
1595
+ var strangeAttractor = defineEffect({
1596
+ name: "strange-attractor",
1597
+ defaults: DEFAULTS6,
1598
+ structuralOptions: ["count", "kind"],
1599
+ setup(context, options) {
1600
+ let opts = options;
1601
+ const count = Math.min(MAX_WALKERS, Math.max(1, Math.floor(opts.count)));
1602
+ const px = new Float32Array(count);
1603
+ const py = new Float32Array(count);
1604
+ const colorIndex = new Uint8Array(count);
1605
+ const rng = context.rng;
1606
+ const sets = PARAM_SETS[opts.kind] ?? PARAM_SETS.clifford;
1607
+ const [baseA, baseB, baseC, baseD] = sets[rng.int(0, sets.length - 1)];
1608
+ const phaseA = rng.angle();
1609
+ const phaseB = rng.angle();
1610
+ const phaseC = rng.angle();
1611
+ const phaseD = rng.angle();
1612
+ let seeded = false;
1613
+ function seed() {
1614
+ const colorCount = Math.max(1, context.colors.length);
1615
+ for (let i = 0; i < count; i++) {
1616
+ px[i] = rng.range(-1, 1);
1617
+ py[i] = rng.range(-1, 1);
1618
+ colorIndex[i] = rng.int(0, colorCount - 1);
1619
+ }
1620
+ seeded = true;
1621
+ }
1622
+ return {
1623
+ frame({ time, quality }) {
1624
+ const { ctx, size, surface, colors } = context;
1625
+ const { width, height } = size;
1626
+ if (width < 1 || height < 1) return;
1627
+ if (colors.length === 0) return;
1628
+ if (!seeded) seed();
1629
+ const fade = clamp(opts.fade, 0, 1);
1630
+ if (fade >= 1) {
1631
+ surface.clear();
1632
+ } else if (fade > 0) {
1633
+ ctx.globalCompositeOperation = "destination-out";
1634
+ ctx.fillStyle = `rgba(0,0,0,${fade})`;
1635
+ ctx.fillRect(0, 0, width, height);
1636
+ }
1637
+ const wobble = opts.drift;
1638
+ const rate = opts.driftSpeed * time;
1639
+ const a = baseA + Math.sin(rate + phaseA) * wobble;
1640
+ const b = baseB + Math.sin(rate * 1.31 + phaseB) * wobble;
1641
+ const c = baseC + Math.sin(rate * 0.77 + phaseC) * wobble;
1642
+ const d = baseD + Math.sin(rate * 1.13 + phaseD) * wobble;
1643
+ const kind = opts.kind;
1644
+ const extent = kind === "dejong" ? 2 : 1 + Math.max(Math.abs(c), Math.abs(d));
1645
+ const span = Math.min(width, height) * opts.scale;
1646
+ const cx = width / 2;
1647
+ const cy = height / 2;
1648
+ const radius = Math.max(0.35, opts.size);
1649
+ const diameter = radius * 2;
1650
+ const walkers = opts.performance === "high" ? count : Math.max(64, Math.round(count * quality));
1651
+ const alpha = clamp(opts.pointOpacity * opts.opacity * opts.intensity, 0, 1);
1652
+ ctx.globalCompositeOperation = opts.blend ?? "lighter";
1653
+ ctx.globalAlpha = alpha;
1654
+ const colorCount = colors.length;
1655
+ let currentColor = -1;
1656
+ for (let i = 0; i < walkers; i++) {
1657
+ const x = px[i];
1658
+ const y = py[i];
1659
+ let nx;
1660
+ let ny;
1661
+ if (kind === "dejong") {
1662
+ nx = Math.sin(a * y) - Math.cos(b * x);
1663
+ ny = Math.sin(c * x) - Math.cos(d * y);
1664
+ } else if (kind === "svensson") {
1665
+ nx = d * Math.sin(a * x) - Math.sin(b * y);
1666
+ ny = c * Math.cos(a * x) + Math.cos(b * y);
1667
+ } else {
1668
+ nx = Math.sin(a * y) + c * Math.cos(a * x);
1669
+ ny = Math.sin(b * x) + d * Math.cos(b * y);
1670
+ }
1671
+ px[i] = nx;
1672
+ py[i] = ny;
1673
+ const ci = colorIndex[i] % colorCount;
1674
+ if (ci !== currentColor) {
1675
+ const colour = colors[ci];
1676
+ ctx.fillStyle = `rgb(${Math.round(colour.r * 255)},${Math.round(colour.g * 255)},${Math.round(colour.b * 255)})`;
1677
+ currentColor = ci;
1678
+ }
1679
+ ctx.fillRect(
1680
+ cx + nx / extent * span - radius,
1681
+ cy + ny / extent * span - radius,
1682
+ diameter,
1683
+ diameter
1684
+ );
1685
+ }
1686
+ ctx.globalAlpha = 1;
1687
+ ctx.globalCompositeOperation = "source-over";
1688
+ },
1689
+ resize() {
1690
+ seeded = false;
1691
+ },
1692
+ update(next) {
1693
+ opts = {
1694
+ ...opts,
1695
+ ...next
1696
+ };
1697
+ }
1698
+ };
1699
+ }
1700
+ });
1701
+
1702
+ // src/effects/particles/swarm.ts
1703
+ var DEFAULTS7 = {
1704
+ colors: ["#3b82f6", "#a78bfa", "#f472b6", "#fbbf24"],
1705
+ speed: 1,
1706
+ intensity: 1,
1707
+ opacity: 1,
1708
+ blur: 0,
1709
+ blend: "lighter",
1710
+ seed: 89,
1711
+ interactive: true,
1712
+ resolution: 1,
1713
+ performance: "auto",
1714
+ reducedMotion: "slow",
1715
+ paused: false,
1716
+ pauseOffscreen: true,
1717
+ count: 900,
1718
+ attractors: 3,
1719
+ gravity: 260,
1720
+ swirl: 0.85,
1721
+ softening: 70,
1722
+ drag: 0.45,
1723
+ attractorSpeed: 0.08,
1724
+ size: 1.4,
1725
+ fastSpeed: 320,
1726
+ pointerGravity: 1.6
1727
+ };
1728
+ var MAX_PARTICLES3 = 4e3;
1729
+ var MAX_ATTRACTORS = 8;
1730
+ var MAX_DELTA_V = 900;
1731
+ var swarm = defineEffect({
1732
+ name: "swarm",
1733
+ defaults: DEFAULTS7,
1734
+ structuralOptions: ["count", "attractors"],
1735
+ setup(context, options) {
1736
+ let opts = options;
1737
+ const capacity = Math.min(MAX_PARTICLES3, Math.max(1, Math.floor(opts.count)));
1738
+ const attractorCount = Math.min(MAX_ATTRACTORS, Math.max(1, Math.floor(opts.attractors)));
1739
+ const buffer = new ParticleBuffer(capacity);
1740
+ const ax = new Float32Array(attractorCount);
1741
+ const ay = new Float32Array(attractorCount);
1742
+ const fx = new Float32Array(attractorCount);
1743
+ const fy = new Float32Array(attractorCount);
1744
+ const phx = new Float32Array(attractorCount);
1745
+ const phy = new Float32Array(attractorCount);
1746
+ const rng = context.rng;
1747
+ for (let a = 0; a < attractorCount; a++) {
1748
+ ax[a] = rng.range(0.18, 0.34);
1749
+ ay[a] = rng.range(0.18, 0.34);
1750
+ fx[a] = rng.range(0.6, 1.4);
1751
+ fy[a] = rng.range(0.6, 1.4);
1752
+ phx[a] = rng.angle();
1753
+ phy[a] = rng.angle();
1754
+ }
1755
+ let seeded = false;
1756
+ function seed() {
1757
+ const { width, height } = context.size;
1758
+ buffer.clear();
1759
+ for (let i = 0; i < capacity; i++) {
1760
+ const index = buffer.spawn();
1761
+ if (index < 0) break;
1762
+ buffer.x[index] = rng.next() * width;
1763
+ buffer.y[index] = rng.next() * height;
1764
+ const angle = rng.angle();
1765
+ const speed = rng.range(20, 90);
1766
+ buffer.vx[index] = Math.cos(angle) * speed;
1767
+ buffer.vy[index] = Math.sin(angle) * speed;
1768
+ buffer.size[index] = opts.size * rng.range(0.7, 1.5);
1769
+ buffer.life[index] = Infinity;
1770
+ buffer.maxLife[index] = Infinity;
1771
+ }
1772
+ seeded = true;
1773
+ }
1774
+ return {
1775
+ frame({ time, delta, quality }) {
1776
+ const { ctx, size, surface, pointer, colors } = context;
1777
+ const { width, height } = size;
1778
+ if (width < 1 || height < 1) return;
1779
+ if (!seeded || buffer.count === 0) seed();
1780
+ surface.clear();
1781
+ if (colors.length === 0) return;
1782
+ const target = opts.performance === "high" ? capacity : Math.max(32, Math.floor(capacity * quality));
1783
+ if (buffer.count > target) buffer.truncate(target);
1784
+ const cx = width / 2;
1785
+ const cy = height / 2;
1786
+ const t = time * opts.attractorSpeed * TAU;
1787
+ const atx = [];
1788
+ const aty = [];
1789
+ for (let a = 0; a < attractorCount; a++) {
1790
+ atx.push(cx + Math.sin(t * fx[a] + phx[a]) * width * ax[a]);
1791
+ aty.push(cy + Math.cos(t * fy[a] + phy[a]) * height * ay[a]);
1792
+ }
1793
+ const interactive = opts.interactive !== false && pointer !== null && pointer.everInside;
1794
+ if (interactive && opts.pointerGravity !== 0) {
1795
+ atx.push(pointer.smoothX);
1796
+ aty.push(pointer.smoothY);
1797
+ }
1798
+ const gravity = opts.gravity * opts.intensity;
1799
+ const pointerBoost = opts.pointerGravity;
1800
+ const swirl = opts.swirl;
1801
+ const softening = Math.max(1, opts.softening);
1802
+ const retain = Math.pow(1 - clamp(opts.drag, 0, 0.999), delta);
1803
+ const invFast = 1 / Math.max(1, opts.fastSpeed);
1804
+ const alpha = clamp(opts.opacity * opts.intensity, 0, 1);
1805
+ const colorCount = colors.length;
1806
+ const live = buffer.count;
1807
+ const activeAttractors = atx.length;
1808
+ ctx.globalCompositeOperation = opts.blend ?? "lighter";
1809
+ ctx.globalAlpha = alpha;
1810
+ let currentColor = -1;
1811
+ for (let i = 0; i < live; i++) {
1812
+ let axSum = 0;
1813
+ let aySum = 0;
1814
+ for (let a = 0; a < activeAttractors; a++) {
1815
+ const dx = atx[a] - buffer.x[i];
1816
+ const dy = aty[a] - buffer.y[i];
1817
+ const distance = Math.hypot(dx, dy);
1818
+ const inv = 1 / (distance + softening);
1819
+ const peak = a === attractorCount && interactive ? gravity * pointerBoost : gravity;
1820
+ const strength = peak * softening * inv;
1821
+ const nx = dx * inv;
1822
+ const ny = dy * inv;
1823
+ axSum += nx * strength;
1824
+ aySum += ny * strength;
1825
+ axSum += -ny * strength * swirl;
1826
+ aySum += nx * strength * swirl;
1827
+ }
1828
+ const dvx = clamp(axSum * delta, -MAX_DELTA_V, MAX_DELTA_V);
1829
+ const dvy = clamp(aySum * delta, -MAX_DELTA_V, MAX_DELTA_V);
1830
+ buffer.vx[i] = (buffer.vx[i] + dvx) * retain;
1831
+ buffer.vy[i] = (buffer.vy[i] + dvy) * retain;
1832
+ buffer.x[i] += buffer.vx[i] * delta;
1833
+ buffer.y[i] += buffer.vy[i] * delta;
1834
+ const speed = Math.hypot(buffer.vx[i], buffer.vy[i]);
1835
+ const ci = Math.min(colorCount - 1, Math.floor(clamp(speed * invFast, 0, 1) * colorCount));
1836
+ if (ci !== currentColor) {
1837
+ const colour = colors[ci];
1838
+ ctx.fillStyle = `rgb(${Math.round(colour.r * 255)},${Math.round(colour.g * 255)},${Math.round(colour.b * 255)})`;
1839
+ currentColor = ci;
1840
+ }
1841
+ const r = buffer.size[i];
1842
+ ctx.fillRect(buffer.x[i] - r, buffer.y[i] - r, r * 2, r * 2);
1843
+ }
1844
+ ctx.globalAlpha = 1;
1845
+ ctx.globalCompositeOperation = "source-over";
1846
+ },
1847
+ resize() {
1848
+ seeded = false;
1849
+ },
1850
+ update(next) {
1851
+ opts = { ...opts, ...next };
1852
+ },
1853
+ destroy() {
1854
+ buffer.clear();
1855
+ }
1856
+ };
1857
+ }
1858
+ });
1859
+
1860
+ // src/effects/atmosphere/meteors.ts
1861
+ var DEFAULTS8 = {
1423
1862
  colors: ["#ffffff", "#a5c8ff", "#8a2be2"],
1424
1863
  speed: 1,
1425
1864
  intensity: 1,
@@ -1445,7 +1884,7 @@ var DEFAULTS5 = {
1445
1884
  var MAX_METEORS = 200;
1446
1885
  var meteors = defineEffect({
1447
1886
  name: "meteors",
1448
- defaults: DEFAULTS5,
1887
+ defaults: DEFAULTS8,
1449
1888
  structuralOptions: ["count"],
1450
1889
  setup(context, options) {
1451
1890
  let opts = options;
@@ -1833,7 +2272,7 @@ var snow = defineEffect({
1833
2272
  });
1834
2273
 
1835
2274
  // src/effects/atmosphere/fireflies.ts
1836
- var DEFAULTS6 = {
2275
+ var DEFAULTS9 = {
1837
2276
  colors: ["#ffe98a", "#c8ff8a", "#8affd4"],
1838
2277
  speed: 1,
1839
2278
  intensity: 1,
@@ -1857,7 +2296,7 @@ var DEFAULTS6 = {
1857
2296
  };
1858
2297
  var fireflies = defineEffect({
1859
2298
  name: "fireflies",
1860
- defaults: DEFAULTS6,
2299
+ defaults: DEFAULTS9,
1861
2300
  structuralOptions: ["count"],
1862
2301
  setup(context, options) {
1863
2302
  let opts = options;
@@ -1964,8 +2403,591 @@ var fireflies = defineEffect({
1964
2403
  }
1965
2404
  });
1966
2405
 
2406
+ // src/effects/atmosphere/light-rays.ts
2407
+ var DEFAULTS10 = {
2408
+ colors: ["#fff4d6", "#ffd9a0", "#ffb865"],
2409
+ speed: 1,
2410
+ intensity: 1,
2411
+ opacity: 1,
2412
+ blur: 0,
2413
+ blend: "lighter",
2414
+ seed: 41,
2415
+ interactive: true,
2416
+ resolution: 1,
2417
+ performance: "auto",
2418
+ reducedMotion: "slow",
2419
+ paused: false,
2420
+ pauseOffscreen: true,
2421
+ count: 16,
2422
+ originX: 0.5,
2423
+ originY: -0.6,
2424
+ angle: Math.PI / 2,
2425
+ spread: 1.1,
2426
+ rayWidth: 0.03,
2427
+ length: 1.4,
2428
+ falloff: 0.5,
2429
+ softness: 0.65,
2430
+ flicker: 0.55,
2431
+ flickerSpeed: 0.35,
2432
+ sway: 0.06,
2433
+ swaySpeed: 0.12,
2434
+ pointerPull: 0.25
2435
+ };
2436
+ var MAX_RAYS = 64;
2437
+ var MIN_BRIGHTNESS = 0.15;
2438
+ var MAX_SOFT_SPREAD = 2.5;
2439
+ var SOFT_LAYERS = [
2440
+ { width: 1, alpha: 0.28 },
2441
+ { width: 0.55, alpha: 0.32 },
2442
+ { width: 0.28, alpha: 0.4 }
2443
+ ];
2444
+ var lightRays = defineEffect({
2445
+ name: "light-rays",
2446
+ defaults: DEFAULTS10,
2447
+ structuralOptions: ["count"],
2448
+ setup(context, options) {
2449
+ let opts = options;
2450
+ const count = Math.min(MAX_RAYS, Math.max(1, Math.floor(opts.count)));
2451
+ const angleJitter = new Float32Array(count);
2452
+ const widthScale = new Float32Array(count);
2453
+ const noiseOffset = new Float32Array(count);
2454
+ const colorIndex = new Uint8Array(count);
2455
+ const rng = context.rng;
2456
+ for (let i = 0; i < count; i++) {
2457
+ angleJitter[i] = rng.range(-0.5, 0.5);
2458
+ widthScale[i] = rng.range(0.45, 1.55);
2459
+ noiseOffset[i] = rng.range(0, 128);
2460
+ colorIndex[i] = rng.int(0, 255);
2461
+ }
2462
+ const brightness = context.noise.fbm2D({ octaves: 2, gain: 0.55 });
2463
+ let gradients = [];
2464
+ let gradientLength = -1;
2465
+ function buildGradients(rayLength) {
2466
+ const { ctx, colors } = context;
2467
+ gradients = colors.map((color) => {
2468
+ const rgb = `${Math.round(color.r * 255)},${Math.round(color.g * 255)},${Math.round(color.b * 255)}`;
2469
+ const gradient = ctx.createLinearGradient(0, 0, Math.max(1, rayLength), 0);
2470
+ const reach = clamp(opts.falloff, 0.05, 1);
2471
+ gradient.addColorStop(0, `rgba(${rgb},${color.a * 0.7})`);
2472
+ gradient.addColorStop(reach * 0.4, `rgba(${rgb},${color.a * 0.36})`);
2473
+ gradient.addColorStop(reach, `rgba(${rgb},${color.a * 0.12})`);
2474
+ gradient.addColorStop(1, `rgba(${rgb},0)`);
2475
+ return gradient;
2476
+ });
2477
+ gradientLength = rayLength;
2478
+ }
2479
+ return {
2480
+ frame({ time, quality }) {
2481
+ const { ctx, size, surface, pointer, colors } = context;
2482
+ const { width, height } = size;
2483
+ if (width < 1 || height < 1) return;
2484
+ surface.clear();
2485
+ if (colors.length === 0) return;
2486
+ const rayLength = Math.hypot(width, height) * opts.length;
2487
+ if (rayLength !== gradientLength) buildGradients(rayLength);
2488
+ const drawn = opts.performance === "high" ? count : Math.max(3, Math.round(count * quality));
2489
+ let originX = opts.originX;
2490
+ let originY = opts.originY;
2491
+ const interactive = opts.interactive !== false && pointer !== null && pointer.everInside;
2492
+ if (interactive) {
2493
+ const pull = clamp(opts.pointerPull, 0, 1);
2494
+ originX += (pointer.smoothNx - originX) * pull;
2495
+ originY += (pointer.smoothNy - originY) * pull;
2496
+ }
2497
+ const sourceX = originX * width;
2498
+ const sourceY = originY * height;
2499
+ const sway = Math.sin(time * opts.swaySpeed * TAU) * opts.sway;
2500
+ const half = opts.spread / 2;
2501
+ const step = opts.spread / drawn;
2502
+ const flickerDepth = clamp(opts.flicker, 0, 1);
2503
+ const alphaScale = clamp(opts.opacity * opts.intensity, 0, 1);
2504
+ ctx.save();
2505
+ ctx.globalCompositeOperation = opts.blend ?? "lighter";
2506
+ ctx.translate(sourceX, sourceY);
2507
+ for (let i = 0; i < drawn; i++) {
2508
+ const rayAngle = opts.angle - half + (i + 0.5) * step + angleJitter[i] * step + sway;
2509
+ const n = brightness(noiseOffset[i], time * opts.flickerSpeed);
2510
+ const varied = 1 - flickerDepth * (1 - (n * 0.5 + 0.5));
2511
+ const alpha = clamp(Math.max(MIN_BRIGHTNESS, varied) * alphaScale, 0, 1);
2512
+ if (alpha <= 2e-3) continue;
2513
+ const halfWidth = opts.rayWidth * widthScale[i];
2514
+ const spreadFactor = 1 + clamp(opts.softness, 0, 1) * MAX_SOFT_SPREAD;
2515
+ ctx.save();
2516
+ ctx.rotate(rayAngle);
2517
+ ctx.fillStyle = gradients[colorIndex[i] % gradients.length];
2518
+ for (const layer of SOFT_LAYERS) {
2519
+ const hw = halfWidth * (1 + (spreadFactor - 1) * layer.width);
2520
+ ctx.globalAlpha = alpha * layer.alpha;
2521
+ ctx.beginPath();
2522
+ ctx.moveTo(0, 0);
2523
+ ctx.lineTo(Math.cos(-hw) * rayLength, Math.sin(-hw) * rayLength);
2524
+ ctx.lineTo(Math.cos(hw) * rayLength, Math.sin(hw) * rayLength);
2525
+ ctx.closePath();
2526
+ ctx.fill();
2527
+ }
2528
+ ctx.restore();
2529
+ }
2530
+ ctx.restore();
2531
+ ctx.globalAlpha = 1;
2532
+ ctx.globalCompositeOperation = "source-over";
2533
+ },
2534
+ resize() {
2535
+ gradientLength = -1;
2536
+ },
2537
+ update(next) {
2538
+ opts = { ...opts, ...next };
2539
+ gradientLength = -1;
2540
+ },
2541
+ destroy() {
2542
+ gradients = [];
2543
+ }
2544
+ };
2545
+ }
2546
+ });
2547
+
2548
+ // src/effects/atmosphere/smoke.ts
2549
+ var DEFAULTS11 = {
2550
+ colors: ["#8e9bb0", "#b9c3d1", "#6b7688"],
2551
+ speed: 1,
2552
+ intensity: 1,
2553
+ opacity: 1,
2554
+ blur: 0,
2555
+ // `source-over`, not `lighter` — the one place this effect deliberately breaks with the other
2556
+ // particle effects here. Smoke *occludes* light rather than emitting it, and additive blending
2557
+ // drives overlapping puffs to pure white, which reads as a lens flare rather than a plume.
2558
+ blend: "source-over",
2559
+ seed: 53,
2560
+ interactive: false,
2561
+ resolution: 0.85,
2562
+ performance: "auto",
2563
+ reducedMotion: "slow",
2564
+ paused: false,
2565
+ pauseOffscreen: true,
2566
+ count: 100,
2567
+ sourceX: 0.5,
2568
+ sourceY: 1.05,
2569
+ // Tight at the emitter: a wide spawn scatters puffs into unrelated blobs instead of a column,
2570
+ // and the curl field widens the plume on its own as it climbs.
2571
+ spread: 16,
2572
+ rise: 54,
2573
+ turbulence: 26,
2574
+ scale: 35e-4,
2575
+ size: 18,
2576
+ growth: 2.2,
2577
+ life: 4.5,
2578
+ drag: 0.55,
2579
+ density: 0.16
2580
+ };
2581
+ var MAX_PUFFS = 400;
2582
+ var FADE_IN = 0.14;
2583
+ var FIELD_DRIFT = 0.12;
2584
+ var DECAY_SHAPE = 1.6;
2585
+ var smoke = defineEffect({
2586
+ name: "smoke",
2587
+ defaults: DEFAULTS11,
2588
+ structuralOptions: ["count"],
2589
+ setup(context, options) {
2590
+ let opts = options;
2591
+ const capacity = Math.min(MAX_PUFFS, Math.max(1, Math.floor(opts.count)));
2592
+ const buffer = new ParticleBuffer(capacity);
2593
+ const gradients = new GradientCache(64);
2594
+ const curl = context.noise.curlAnimated({ octaves: 2, gain: 0.5 });
2595
+ let seeded = false;
2596
+ function place(index, ageOffset) {
2597
+ const { width, height } = context.size;
2598
+ const rng = context.rng;
2599
+ const colorCount = Math.max(1, context.colors.length);
2600
+ buffer.x[index] = opts.sourceX * width + rng.range(-opts.spread, opts.spread);
2601
+ buffer.y[index] = opts.sourceY * height + rng.range(-opts.spread, opts.spread) * 0.35;
2602
+ buffer.vx[index] = rng.range(-8, 8);
2603
+ buffer.vy[index] = -opts.rise * rng.range(0.7, 1.3);
2604
+ buffer.size[index] = opts.size * rng.range(0.65, 1.4);
2605
+ buffer.colorIndex[index] = rng.int(0, colorCount - 1);
2606
+ buffer.maxLife[index] = opts.life * rng.range(0.75, 1.25);
2607
+ buffer.life[index] = buffer.maxLife[index] * (1 - ageOffset);
2608
+ buffer.seed[index] = rng.range(0, 500);
2609
+ buffer.extra[index] = rng.range(0.7, 1.35);
2610
+ }
2611
+ function seed() {
2612
+ buffer.clear();
2613
+ for (let i = 0; i < capacity; i++) {
2614
+ const index = buffer.spawn();
2615
+ if (index < 0) break;
2616
+ place(index, context.rng.next());
2617
+ }
2618
+ seeded = true;
2619
+ }
2620
+ return {
2621
+ frame({ time, delta, quality }) {
2622
+ const { ctx, size, surface, colors } = context;
2623
+ const { width, height } = size;
2624
+ if (width < 1 || height < 1) return;
2625
+ if (!seeded || buffer.count === 0) seed();
2626
+ const target = opts.performance === "high" ? capacity : Math.max(4, Math.floor(capacity * quality));
2627
+ if (buffer.count > target) buffer.truncate(target);
2628
+ surface.clear();
2629
+ if (colors.length === 0) return;
2630
+ const count = buffer.count;
2631
+ const colorCount = Math.max(1, colors.length);
2632
+ const retain = Math.pow(1 - clamp(opts.drag, 0, 0.999), delta);
2633
+ const turbulence = opts.turbulence * opts.intensity;
2634
+ const rise = opts.rise * opts.intensity;
2635
+ const peak = clamp(opts.density, 0, 1) * clamp(opts.opacity, 0, 1);
2636
+ ctx.globalCompositeOperation = opts.blend ?? "lighter";
2637
+ for (let i = 0; i < count; i++) {
2638
+ buffer.life[i] -= delta;
2639
+ if (buffer.life[i] <= 0) {
2640
+ place(i, 0);
2641
+ continue;
2642
+ }
2643
+ const flow = curl(
2644
+ (buffer.x[i] + buffer.seed[i]) * opts.scale,
2645
+ buffer.y[i] * opts.scale,
2646
+ time * FIELD_DRIFT
2647
+ );
2648
+ buffer.vx[i] = buffer.vx[i] * retain + flow.x * turbulence * delta;
2649
+ buffer.vy[i] = buffer.vy[i] * retain + (flow.y * turbulence - rise) * delta;
2650
+ buffer.x[i] += buffer.vx[i] * delta;
2651
+ buffer.y[i] += buffer.vy[i] * delta;
2652
+ const age = clamp(1 - buffer.life[i] / buffer.maxLife[i], 0, 1);
2653
+ const fadeIn = Math.min(1, age / FADE_IN);
2654
+ const decay = Math.pow(1 - age, DECAY_SHAPE);
2655
+ const alpha = peak * fadeIn * decay;
2656
+ if (alpha <= 2e-3) continue;
2657
+ const radius = buffer.size[i] * (1 + opts.growth * age) * buffer.extra[i];
2658
+ drawGlow(
2659
+ ctx,
2660
+ buffer.x[i],
2661
+ buffer.y[i],
2662
+ radius,
2663
+ colors[buffer.colorIndex[i] % colorCount],
2664
+ alpha,
2665
+ gradients
2666
+ );
2667
+ }
2668
+ ctx.globalCompositeOperation = "source-over";
2669
+ },
2670
+ resize() {
2671
+ seeded = false;
2672
+ },
2673
+ update(next) {
2674
+ opts = { ...opts, ...next };
2675
+ gradients.clear();
2676
+ },
2677
+ destroy() {
2678
+ buffer.clear();
2679
+ gradients.clear();
2680
+ }
2681
+ };
2682
+ }
2683
+ });
2684
+
2685
+ // src/effects/geometry/floating-geometry.ts
2686
+ var ALL_SHAPES = ["triangle", "square", "pentagon", "hexagon", "circle"];
2687
+ var DEFAULTS12 = {
2688
+ colors: ["#7cc4ff", "#a78bfa", "#5eead4"],
2689
+ speed: 1,
2690
+ intensity: 1,
2691
+ opacity: 1,
2692
+ blur: 0,
2693
+ blend: "lighter",
2694
+ seed: 67,
2695
+ interactive: false,
2696
+ resolution: 1,
2697
+ performance: "auto",
2698
+ reducedMotion: "slow",
2699
+ paused: false,
2700
+ pauseOffscreen: true,
2701
+ count: 18,
2702
+ shapes: ALL_SHAPES,
2703
+ size: 46,
2704
+ drift: 26,
2705
+ spin: 0.06,
2706
+ perspective: 1,
2707
+ depth: 2.6,
2708
+ haze: 0.25,
2709
+ filled: false,
2710
+ lineWidth: 1.5
2711
+ };
2712
+ var MAX_SHAPES = 120;
2713
+ var CIRCLE = 0;
2714
+ function sidesFor(shape) {
2715
+ switch (shape) {
2716
+ case "triangle":
2717
+ return 3;
2718
+ case "square":
2719
+ return 4;
2720
+ case "pentagon":
2721
+ return 5;
2722
+ case "hexagon":
2723
+ return 6;
2724
+ default:
2725
+ return CIRCLE;
2726
+ }
2727
+ }
2728
+ var floatingGeometry = defineEffect({
2729
+ name: "floating-geometry",
2730
+ defaults: DEFAULTS12,
2731
+ structuralOptions: ["count", "shapes"],
2732
+ setup(context, options) {
2733
+ let opts = options;
2734
+ const count = Math.min(MAX_SHAPES, Math.max(1, Math.floor(opts.count)));
2735
+ const x = new Float32Array(count);
2736
+ const y = new Float32Array(count);
2737
+ const z = new Float32Array(count);
2738
+ const rotation = new Float32Array(count);
2739
+ const spinRate = new Float32Array(count);
2740
+ const radius = new Float32Array(count);
2741
+ const driftX = new Float32Array(count);
2742
+ const driftY = new Float32Array(count);
2743
+ const sides = new Uint8Array(count);
2744
+ const colorIndex = new Uint8Array(count);
2745
+ const order = new Int32Array(count);
2746
+ let seeded = false;
2747
+ function seed() {
2748
+ const { width, height } = context.size;
2749
+ const rng = context.rng;
2750
+ const colorCount = Math.max(1, context.colors.length);
2751
+ const palette = opts.shapes.length > 0 ? opts.shapes : ALL_SHAPES;
2752
+ for (let i = 0; i < count; i++) {
2753
+ x[i] = rng.next() * width;
2754
+ y[i] = rng.next() * height;
2755
+ z[i] = rng.next();
2756
+ rotation[i] = rng.angle();
2757
+ spinRate[i] = rng.range(-1, 1) * opts.spin * TAU;
2758
+ radius[i] = opts.size * rng.range(0.55, 1.45);
2759
+ const angle = rng.angle();
2760
+ driftX[i] = Math.cos(angle);
2761
+ driftY[i] = Math.sin(angle);
2762
+ sides[i] = sidesFor(palette[rng.int(0, palette.length - 1)]);
2763
+ colorIndex[i] = rng.int(0, colorCount - 1);
2764
+ order[i] = i;
2765
+ }
2766
+ const indices = Array.from(order);
2767
+ indices.sort((a, b) => z[b] - z[a]);
2768
+ for (let i = 0; i < count; i++) order[i] = indices[i];
2769
+ seeded = true;
2770
+ }
2771
+ return {
2772
+ frame({ delta, quality }) {
2773
+ const { ctx, size, surface, colors } = context;
2774
+ const { width, height } = size;
2775
+ if (width < 1 || height < 1) return;
2776
+ if (!seeded) seed();
2777
+ surface.clear();
2778
+ if (colors.length === 0) return;
2779
+ const drawn = opts.performance === "high" ? count : Math.max(3, Math.round(count * quality));
2780
+ const focal = Math.max(0.05, opts.perspective);
2781
+ const spread = Math.max(0, opts.depth);
2782
+ const drift = opts.drift * opts.intensity;
2783
+ const haze = clamp(opts.haze, 0, 1);
2784
+ const alphaScale = clamp(opts.opacity, 0, 1);
2785
+ const colorCount = Math.max(1, colors.length);
2786
+ ctx.save();
2787
+ ctx.globalCompositeOperation = opts.blend ?? "lighter";
2788
+ const firstDrawn = count - drawn;
2789
+ for (let k = 0; k < count; k++) {
2790
+ const i = order[k];
2791
+ const projected = focal / (focal + z[i] * spread);
2792
+ x[i] += driftX[i] * drift * projected * delta;
2793
+ y[i] += driftY[i] * drift * projected * delta;
2794
+ rotation[i] += spinRate[i] * delta;
2795
+ const margin = radius[i] * projected + opts.lineWidth * 2;
2796
+ if (x[i] < -margin) x[i] = width + margin;
2797
+ else if (x[i] > width + margin) x[i] = -margin;
2798
+ if (y[i] < -margin) y[i] = height + margin;
2799
+ else if (y[i] > height + margin) y[i] = -margin;
2800
+ if (k < firstDrawn) continue;
2801
+ const alpha = alphaScale * lerp(1, haze, z[i]);
2802
+ if (alpha <= 2e-3) continue;
2803
+ const r = radius[i] * projected;
2804
+ const color = colors[colorIndex[i] % colorCount];
2805
+ ctx.save();
2806
+ ctx.translate(x[i], y[i]);
2807
+ ctx.rotate(rotation[i]);
2808
+ ctx.globalAlpha = alpha;
2809
+ ctx.beginPath();
2810
+ if (sides[i] === CIRCLE) {
2811
+ ctx.arc(0, 0, r, 0, TAU);
2812
+ } else {
2813
+ const n = sides[i];
2814
+ for (let v = 0; v < n; v++) {
2815
+ const a = v / n * TAU;
2816
+ const px = Math.cos(a) * r;
2817
+ const py = Math.sin(a) * r;
2818
+ if (v === 0) ctx.moveTo(px, py);
2819
+ else ctx.lineTo(px, py);
2820
+ }
2821
+ ctx.closePath();
2822
+ }
2823
+ if (opts.filled) {
2824
+ ctx.fillStyle = toRgbString(color);
2825
+ ctx.fill();
2826
+ } else {
2827
+ ctx.lineWidth = Math.max(0.3, opts.lineWidth * projected);
2828
+ ctx.strokeStyle = toRgbString(color);
2829
+ ctx.stroke();
2830
+ }
2831
+ ctx.restore();
2832
+ }
2833
+ ctx.restore();
2834
+ ctx.globalAlpha = 1;
2835
+ ctx.globalCompositeOperation = "source-over";
2836
+ },
2837
+ resize() {
2838
+ seeded = false;
2839
+ },
2840
+ update(next) {
2841
+ opts = {
2842
+ ...opts,
2843
+ ...next
2844
+ };
2845
+ }
2846
+ };
2847
+ }
2848
+ });
2849
+
2850
+ // src/effects/geometry/orbit.ts
2851
+ var DEFAULTS13 = {
2852
+ colors: ["#7cc4ff", "#a78bfa", "#5eead4", "#fbbf7c"],
2853
+ speed: 1,
2854
+ intensity: 1,
2855
+ opacity: 1,
2856
+ blur: 0,
2857
+ blend: "lighter",
2858
+ seed: 71,
2859
+ interactive: false,
2860
+ resolution: 1,
2861
+ performance: "auto",
2862
+ reducedMotion: "slow",
2863
+ paused: false,
2864
+ pauseOffscreen: true,
2865
+ rings: 3,
2866
+ perRing: 14,
2867
+ radius: 0.42,
2868
+ innerRadius: 0.45,
2869
+ tilt: 0.34,
2870
+ orbitSpeed: 0.05,
2871
+ size: 3.2,
2872
+ glow: 3,
2873
+ counterRotate: true,
2874
+ showOrbits: true,
2875
+ orbitOpacity: 0.12,
2876
+ core: 26
2877
+ };
2878
+ var MAX_RINGS = 12;
2879
+ var MAX_PER_RING = 80;
2880
+ var DEPTH_SCALE_FAR = 0.55;
2881
+ var DEPTH_SCALE_NEAR = 1.3;
2882
+ var DEPTH_ALPHA_FAR = 0.3;
2883
+ var DEPTH_ALPHA_NEAR = 1;
2884
+ var INNER_RING_SPEEDUP = 1.8;
2885
+ var orbit = defineEffect({
2886
+ name: "orbit",
2887
+ defaults: DEFAULTS13,
2888
+ structuralOptions: ["rings", "perRing"],
2889
+ setup(context, options) {
2890
+ let opts = options;
2891
+ const rings = Math.min(MAX_RINGS, Math.max(1, Math.floor(opts.rings)));
2892
+ const perRing = Math.min(MAX_PER_RING, Math.max(1, Math.floor(opts.perRing)));
2893
+ const total = rings * perRing;
2894
+ const phase = new Float32Array(total);
2895
+ const sizeScale = new Float32Array(total);
2896
+ const colorIndex = new Uint8Array(total);
2897
+ const ringPhase = new Float32Array(rings);
2898
+ const rng = context.rng;
2899
+ const colorCount = Math.max(1, context.colors.length);
2900
+ for (let r = 0; r < rings; r++) {
2901
+ ringPhase[r] = rng.angle();
2902
+ for (let p = 0; p < perRing; p++) {
2903
+ const i = r * perRing + p;
2904
+ phase[i] = p / perRing * TAU + rng.range(-0.12, 0.12);
2905
+ sizeScale[i] = rng.range(0.7, 1.35);
2906
+ colorIndex[i] = rng.int(0, colorCount - 1);
2907
+ }
2908
+ }
2909
+ const gradients = new GradientCache(64);
2910
+ return {
2911
+ frame({ time, quality }) {
2912
+ const { ctx, size, surface, colors } = context;
2913
+ const { width, height } = size;
2914
+ if (width < 1 || height < 1) return;
2915
+ surface.clear();
2916
+ if (colors.length === 0) return;
2917
+ const cx = width / 2;
2918
+ const cy = height / 2;
2919
+ const outer = Math.min(width, height) * opts.radius;
2920
+ const inner = outer * clamp(opts.innerRadius, 0.05, 1);
2921
+ const tilt = clamp(opts.tilt, 0.02, 1);
2922
+ const alphaScale = clamp(opts.opacity * opts.intensity, 0, 1);
2923
+ const count = colors.length;
2924
+ const drawnPerRing = opts.performance === "high" ? perRing : Math.max(3, Math.round(perRing * quality));
2925
+ ctx.save();
2926
+ ctx.globalCompositeOperation = opts.blend ?? "lighter";
2927
+ const drawPoints = (near) => {
2928
+ for (let r = 0; r < rings; r++) {
2929
+ const t = rings === 1 ? 1 : r / (rings - 1);
2930
+ const ringRadius = lerp(inner, outer, t);
2931
+ const direction = opts.counterRotate && r % 2 === 1 ? -1 : 1;
2932
+ const rate = opts.orbitSpeed * TAU * direction * lerp(INNER_RING_SPEEDUP, 1, t);
2933
+ const base = ringPhase[r] + time * rate;
2934
+ for (let p = 0; p < drawnPerRing; p++) {
2935
+ const i = r * perRing + p;
2936
+ const theta = base + phase[i];
2937
+ const depth = Math.sin(theta);
2938
+ if (near !== depth > 0) continue;
2939
+ const d = depth * 0.5 + 0.5;
2940
+ const alpha = alphaScale * lerp(DEPTH_ALPHA_FAR, DEPTH_ALPHA_NEAR, d);
2941
+ if (alpha <= 2e-3) continue;
2942
+ const scale = lerp(DEPTH_SCALE_FAR, DEPTH_SCALE_NEAR, d);
2943
+ drawGlow(
2944
+ ctx,
2945
+ cx + Math.cos(theta) * ringRadius,
2946
+ cy + depth * ringRadius * tilt,
2947
+ opts.size * opts.glow * sizeScale[i] * scale,
2948
+ colors[colorIndex[i] % count],
2949
+ alpha,
2950
+ gradients
2951
+ );
2952
+ }
2953
+ }
2954
+ };
2955
+ drawPoints(false);
2956
+ if (opts.showOrbits && opts.orbitOpacity > 0) {
2957
+ const guide = colors[0];
2958
+ ctx.globalAlpha = clamp(opts.orbitOpacity * alphaScale, 0, 1);
2959
+ ctx.strokeStyle = `rgb(${Math.round(guide.r * 255)},${Math.round(guide.g * 255)},${Math.round(guide.b * 255)})`;
2960
+ ctx.lineWidth = 1;
2961
+ for (let r = 0; r < rings; r++) {
2962
+ const t = rings === 1 ? 1 : r / (rings - 1);
2963
+ const ringRadius = lerp(inner, outer, t);
2964
+ ctx.beginPath();
2965
+ ctx.ellipse(cx, cy, ringRadius, ringRadius * tilt, 0, 0, TAU);
2966
+ ctx.stroke();
2967
+ }
2968
+ ctx.globalAlpha = 1;
2969
+ }
2970
+ if (opts.core > 0) {
2971
+ drawGlow(ctx, cx, cy, opts.core, colors[0], alphaScale, gradients);
2972
+ }
2973
+ drawPoints(true);
2974
+ ctx.restore();
2975
+ ctx.globalAlpha = 1;
2976
+ ctx.globalCompositeOperation = "source-over";
2977
+ },
2978
+ update(next) {
2979
+ opts = { ...opts, ...next };
2980
+ gradients.clear();
2981
+ },
2982
+ destroy() {
2983
+ gradients.clear();
2984
+ }
2985
+ };
2986
+ }
2987
+ });
2988
+
1967
2989
  // src/effects/surfaces/waves.ts
1968
- var DEFAULTS7 = {
2990
+ var DEFAULTS14 = {
1969
2991
  colors: "abyss",
1970
2992
  speed: 1,
1971
2993
  intensity: 1,
@@ -1990,7 +3012,7 @@ var DEFAULTS7 = {
1990
3012
  };
1991
3013
  var waves = defineEffect({
1992
3014
  name: "waves",
1993
- defaults: DEFAULTS7,
3015
+ defaults: DEFAULTS14,
1994
3016
  structuralOptions: ["layers"],
1995
3017
  setup(context, options) {
1996
3018
  let opts = options;
@@ -2085,7 +3107,7 @@ var waves = defineEffect({
2085
3107
  });
2086
3108
 
2087
3109
  // src/effects/surfaces/blob.ts
2088
- var DEFAULTS8 = {
3110
+ var DEFAULTS15 = {
2089
3111
  colors: "ansh",
2090
3112
  speed: 1,
2091
3113
  intensity: 1,
@@ -2112,7 +3134,7 @@ var DEFAULTS8 = {
2112
3134
  };
2113
3135
  var blob = defineEffect({
2114
3136
  name: "blob",
2115
- defaults: DEFAULTS8,
3137
+ defaults: DEFAULTS15,
2116
3138
  structuralOptions: ["count", "segments"],
2117
3139
  setup(context, options) {
2118
3140
  let opts = options;
@@ -2214,7 +3236,7 @@ var blob = defineEffect({
2214
3236
  });
2215
3237
 
2216
3238
  // src/effects/overlays/noise-layer.ts
2217
- var DEFAULTS9 = {
3239
+ var DEFAULTS16 = {
2218
3240
  colors: ["#ffffff"],
2219
3241
  speed: 1,
2220
3242
  intensity: 1,
@@ -2241,7 +3263,7 @@ var DEFAULTS9 = {
2241
3263
  };
2242
3264
  var noiseLayer = defineEffect({
2243
3265
  name: "noise-layer",
2244
- defaults: DEFAULTS9,
3266
+ defaults: DEFAULTS16,
2245
3267
  structuralOptions: ["tileSize", "frames", "grainSize", "monochrome"],
2246
3268
  setup(context, options) {
2247
3269
  let opts = options;
@@ -2332,7 +3354,7 @@ var noiseLayer = defineEffect({
2332
3354
  });
2333
3355
 
2334
3356
  // src/effects/overlays/spotlight.ts
2335
- var DEFAULTS10 = {
3357
+ var DEFAULTS17 = {
2336
3358
  colors: ["#ffffff"],
2337
3359
  speed: 1,
2338
3360
  intensity: 0.35,
@@ -2359,7 +3381,7 @@ var DEFAULTS10 = {
2359
3381
  };
2360
3382
  var spotlight = defineEffect({
2361
3383
  name: "spotlight",
2362
- defaults: DEFAULTS10,
3384
+ defaults: DEFAULTS17,
2363
3385
  setup(context, options) {
2364
3386
  let opts = options;
2365
3387
  let presence = opts.ambient ? 1 : 0;
@@ -2431,16 +3453,23 @@ var effects = {
2431
3453
  "animated-gradient": animatedGradient,
2432
3454
  "particle-field": particleField,
2433
3455
  constellation,
3456
+ "powder-explosion": powderExplosion,
3457
+ "strange-attractor": strangeAttractor,
3458
+ swarm,
2434
3459
  meteors,
2435
3460
  rain,
2436
3461
  snow,
2437
3462
  fireflies,
3463
+ "light-rays": lightRays,
3464
+ smoke,
3465
+ "floating-geometry": floatingGeometry,
3466
+ orbit,
2438
3467
  waves,
2439
3468
  blob,
2440
3469
  "noise-layer": noiseLayer,
2441
3470
  spotlight
2442
3471
  };
2443
3472
 
2444
- export { GradientCache, ParticleBuffer, SpatialGrid, ageAndCull, animatedGradient, aurora, blob, constellation, createSurface, defineEffect, drawDot, drawGlow, effects, fireflies, integrate, meshGradient, meteors, mountEffect, noiseLayer, particleField, rain, resetFilterSupport, snow, spotlight, supportsCanvasFilter, supportsOffscreenCanvas, supportsWorkerRendering, traceClosedSpline, traceOpenSpline, traceRoundRect, waves, wrapBounds };
2445
- //# sourceMappingURL=chunk-56NNBIAO.js.map
2446
- //# sourceMappingURL=chunk-56NNBIAO.js.map
3473
+ export { GradientCache, ParticleBuffer, SpatialGrid, ageAndCull, animatedGradient, aurora, blob, constellation, createSurface, defineEffect, drawDot, drawGlow, effects, fireflies, floatingGeometry, integrate, lightRays, meshGradient, meteors, mountEffect, noiseLayer, orbit, particleField, powderExplosion, rain, resetFilterSupport, smoke, snow, spotlight, strangeAttractor, supportsCanvasFilter, supportsOffscreenCanvas, supportsWorkerRendering, swarm, traceClosedSpline, traceOpenSpline, traceRoundRect, waves, wrapBounds };
3474
+ //# sourceMappingURL=chunk-2DOA43UB.js.map
3475
+ //# sourceMappingURL=chunk-2DOA43UB.js.map