@autumnsgrove/gossamer 0.0.1 → 0.1.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 (50) hide show
  1. package/dist/animation.d.ts +80 -0
  2. package/dist/animation.d.ts.map +1 -0
  3. package/dist/characters.d.ts +49 -0
  4. package/dist/characters.d.ts.map +1 -0
  5. package/dist/index.d.ts +37 -11
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1284 -2
  8. package/dist/index.js.map +1 -1
  9. package/dist/patterns.d.ts +100 -0
  10. package/dist/patterns.d.ts.map +1 -0
  11. package/dist/renderer.d.ts +113 -0
  12. package/dist/renderer.d.ts.map +1 -0
  13. package/dist/style.css +124 -0
  14. package/dist/svelte/GossamerBorder.svelte.d.ts +1 -0
  15. package/dist/svelte/GossamerClouds.svelte.d.ts +1 -0
  16. package/dist/svelte/GossamerImage.svelte.d.ts +1 -0
  17. package/dist/svelte/GossamerOverlay.svelte.d.ts +1 -0
  18. package/dist/svelte/GossamerText.svelte.d.ts +1 -0
  19. package/dist/svelte/index.d.ts +20 -0
  20. package/dist/svelte/index.d.ts.map +1 -0
  21. package/dist/svelte/index.js +3651 -0
  22. package/dist/svelte/index.js.map +1 -0
  23. package/dist/svelte/presets.d.ts +38 -0
  24. package/dist/svelte/presets.d.ts.map +1 -0
  25. package/dist/utils/canvas.d.ts +73 -0
  26. package/dist/utils/canvas.d.ts.map +1 -0
  27. package/dist/utils/image.d.ts +74 -0
  28. package/dist/utils/image.d.ts.map +1 -0
  29. package/dist/utils/performance.d.ts +86 -0
  30. package/dist/utils/performance.d.ts.map +1 -0
  31. package/package.json +23 -5
  32. package/src/animation.test.ts +254 -0
  33. package/src/animation.ts +243 -0
  34. package/src/characters.test.ts +148 -0
  35. package/src/characters.ts +164 -0
  36. package/src/index.test.ts +115 -0
  37. package/src/index.ts +133 -11
  38. package/src/patterns.test.ts +273 -0
  39. package/src/patterns.ts +316 -0
  40. package/src/renderer.ts +309 -0
  41. package/src/svelte/GossamerBorder.svelte +326 -0
  42. package/src/svelte/GossamerClouds.svelte +269 -0
  43. package/src/svelte/GossamerImage.svelte +266 -0
  44. package/src/svelte/GossamerOverlay.svelte +232 -0
  45. package/src/svelte/GossamerText.svelte +239 -0
  46. package/src/svelte/index.ts +75 -0
  47. package/src/svelte/presets.ts +174 -0
  48. package/src/utils/canvas.ts +210 -0
  49. package/src/utils/image.ts +275 -0
  50. package/src/utils/performance.ts +282 -0
package/dist/index.js CHANGED
@@ -1,3 +1,1229 @@
1
+ const DEFAULT_CHARACTERS$1 = " .:-=+*#%@";
2
+ function calculateBrightness$1(r, g, b) {
3
+ return 0.21 * r + 0.72 * g + 0.07 * b;
4
+ }
5
+ const DEFAULT_RENDER_CONFIG = {
6
+ characters: DEFAULT_CHARACTERS$1,
7
+ cellWidth: 8,
8
+ cellHeight: 12,
9
+ color: "#ffffff",
10
+ backgroundColor: "",
11
+ fontFamily: "monospace",
12
+ brightnessFunction: calculateBrightness$1
13
+ };
14
+ class GossamerRenderer {
15
+ constructor(canvas, config = {}) {
16
+ this.animationId = null;
17
+ this.lastFrameTime = 0;
18
+ this.isRunning = false;
19
+ const context = canvas.getContext("2d");
20
+ if (!context) {
21
+ throw new Error("Failed to get 2D rendering context");
22
+ }
23
+ this.ctx = context;
24
+ this.config = {
25
+ canvas,
26
+ ...DEFAULT_RENDER_CONFIG,
27
+ ...config
28
+ };
29
+ this.setupCanvas();
30
+ }
31
+ /**
32
+ * Set up the canvas with optimal rendering settings
33
+ */
34
+ setupCanvas() {
35
+ const { fontFamily, cellHeight } = this.config;
36
+ this.ctx.font = `${cellHeight}px ${fontFamily}`;
37
+ this.ctx.textBaseline = "top";
38
+ this.ctx.imageSmoothingEnabled = true;
39
+ this.ctx.imageSmoothingQuality = "high";
40
+ }
41
+ /**
42
+ * Update the renderer configuration
43
+ */
44
+ updateConfig(config) {
45
+ this.config = { ...this.config, ...config };
46
+ this.setupCanvas();
47
+ }
48
+ /**
49
+ * Resize the canvas to match new dimensions
50
+ */
51
+ resize(width, height) {
52
+ const { canvas } = this.config;
53
+ canvas.width = width;
54
+ canvas.height = height;
55
+ this.setupCanvas();
56
+ }
57
+ /**
58
+ * Get the current canvas dimensions
59
+ */
60
+ getDimensions() {
61
+ return {
62
+ width: this.config.canvas.width,
63
+ height: this.config.canvas.height
64
+ };
65
+ }
66
+ /**
67
+ * Calculate the number of cells that fit in the canvas
68
+ */
69
+ getCellCount() {
70
+ const { width, height } = this.getDimensions();
71
+ const { cellWidth, cellHeight } = this.config;
72
+ return {
73
+ cols: Math.ceil(width / cellWidth),
74
+ rows: Math.ceil(height / cellHeight)
75
+ };
76
+ }
77
+ /**
78
+ * Clear the canvas
79
+ */
80
+ clear() {
81
+ const { canvas, backgroundColor } = this.config;
82
+ if (backgroundColor) {
83
+ this.ctx.fillStyle = backgroundColor;
84
+ this.ctx.fillRect(0, 0, canvas.width, canvas.height);
85
+ } else {
86
+ this.ctx.clearRect(0, 0, canvas.width, canvas.height);
87
+ }
88
+ }
89
+ /**
90
+ * Render a single frame from image data
91
+ */
92
+ renderFrame(imageData) {
93
+ const { canvas, characters, cellWidth, cellHeight, color, brightnessFunction } = this.config;
94
+ const { width, data } = imageData;
95
+ this.clear();
96
+ this.ctx.fillStyle = color;
97
+ for (let y = 0; y < canvas.height; y += cellHeight) {
98
+ for (let x = 0; x < canvas.width; x += cellWidth) {
99
+ const brightness = this.getCellBrightness(data, x, y, width, cellWidth, cellHeight, brightnessFunction);
100
+ const charIndex = Math.floor(brightness / 255 * (characters.length - 1));
101
+ const char = characters[Math.min(charIndex, characters.length - 1)];
102
+ if (char !== " ") {
103
+ this.ctx.fillText(char, x, y);
104
+ }
105
+ }
106
+ }
107
+ }
108
+ /**
109
+ * Render ASCII from a brightness grid (for pattern-based rendering)
110
+ */
111
+ renderFromBrightnessGrid(grid) {
112
+ const { characters, cellWidth, cellHeight, color } = this.config;
113
+ this.clear();
114
+ this.ctx.fillStyle = color;
115
+ for (let row = 0; row < grid.length; row++) {
116
+ for (let col = 0; col < grid[row].length; col++) {
117
+ const brightness = grid[row][col];
118
+ const charIndex = Math.floor(brightness / 255 * (characters.length - 1));
119
+ const char = characters[Math.min(charIndex, characters.length - 1)];
120
+ if (char !== " ") {
121
+ this.ctx.fillText(char, col * cellWidth, row * cellHeight);
122
+ }
123
+ }
124
+ }
125
+ }
126
+ /**
127
+ * Render ASCII with per-cell colors (for colored image rendering)
128
+ */
129
+ renderWithColors(data) {
130
+ this.clear();
131
+ for (const { char, color, x, y } of data) {
132
+ if (char !== " ") {
133
+ this.ctx.fillStyle = color;
134
+ this.ctx.fillText(char, x, y);
135
+ }
136
+ }
137
+ }
138
+ /**
139
+ * Calculate average brightness for a cell region
140
+ */
141
+ getCellBrightness(data, startX, startY, imageWidth, cellWidth, cellHeight, brightnessFunction) {
142
+ let total = 0;
143
+ let count = 0;
144
+ for (let cy = 0; cy < cellHeight; cy++) {
145
+ for (let cx = 0; cx < cellWidth; cx++) {
146
+ const px = ((startY + cy) * imageWidth + (startX + cx)) * 4;
147
+ if (px >= 0 && px + 2 < data.length) {
148
+ total += brightnessFunction(data[px], data[px + 1], data[px + 2]);
149
+ count++;
150
+ }
151
+ }
152
+ }
153
+ return count > 0 ? total / count : 0;
154
+ }
155
+ /**
156
+ * Start an animation loop with FPS limiting
157
+ */
158
+ startAnimation(updateFn, fps = 30) {
159
+ if (this.isRunning) {
160
+ this.stopAnimation();
161
+ }
162
+ this.isRunning = true;
163
+ const frameInterval = 1e3 / fps;
164
+ this.lastFrameTime = performance.now();
165
+ const animate = (currentTime) => {
166
+ if (!this.isRunning) return;
167
+ const deltaTime = currentTime - this.lastFrameTime;
168
+ if (deltaTime >= frameInterval) {
169
+ const result = updateFn(currentTime, deltaTime);
170
+ if (result instanceof ImageData) {
171
+ this.renderFrame(result);
172
+ } else {
173
+ this.renderFromBrightnessGrid(result);
174
+ }
175
+ this.lastFrameTime = currentTime - deltaTime % frameInterval;
176
+ }
177
+ this.animationId = requestAnimationFrame(animate);
178
+ };
179
+ this.animationId = requestAnimationFrame(animate);
180
+ }
181
+ /**
182
+ * Stop the animation loop
183
+ */
184
+ stopAnimation() {
185
+ this.isRunning = false;
186
+ if (this.animationId !== null) {
187
+ cancelAnimationFrame(this.animationId);
188
+ this.animationId = null;
189
+ }
190
+ }
191
+ /**
192
+ * Check if animation is currently running
193
+ */
194
+ isAnimating() {
195
+ return this.isRunning;
196
+ }
197
+ /**
198
+ * Pause animation (can be resumed)
199
+ */
200
+ pause() {
201
+ this.isRunning = false;
202
+ if (this.animationId !== null) {
203
+ cancelAnimationFrame(this.animationId);
204
+ this.animationId = null;
205
+ }
206
+ }
207
+ /**
208
+ * Clean up and destroy the renderer
209
+ */
210
+ destroy() {
211
+ this.stopAnimation();
212
+ }
213
+ }
214
+ const DEFAULT_PATTERN_CONFIG = {
215
+ frequency: 0.05,
216
+ amplitude: 1,
217
+ speed: 0.5
218
+ };
219
+ const PERMUTATION = new Uint8Array(512);
220
+ const P = new Uint8Array([
221
+ 151,
222
+ 160,
223
+ 137,
224
+ 91,
225
+ 90,
226
+ 15,
227
+ 131,
228
+ 13,
229
+ 201,
230
+ 95,
231
+ 96,
232
+ 53,
233
+ 194,
234
+ 233,
235
+ 7,
236
+ 225,
237
+ 140,
238
+ 36,
239
+ 103,
240
+ 30,
241
+ 69,
242
+ 142,
243
+ 8,
244
+ 99,
245
+ 37,
246
+ 240,
247
+ 21,
248
+ 10,
249
+ 23,
250
+ 190,
251
+ 6,
252
+ 148,
253
+ 247,
254
+ 120,
255
+ 234,
256
+ 75,
257
+ 0,
258
+ 26,
259
+ 197,
260
+ 62,
261
+ 94,
262
+ 252,
263
+ 219,
264
+ 203,
265
+ 117,
266
+ 35,
267
+ 11,
268
+ 32,
269
+ 57,
270
+ 177,
271
+ 33,
272
+ 88,
273
+ 237,
274
+ 149,
275
+ 56,
276
+ 87,
277
+ 174,
278
+ 20,
279
+ 125,
280
+ 136,
281
+ 171,
282
+ 168,
283
+ 68,
284
+ 175,
285
+ 74,
286
+ 165,
287
+ 71,
288
+ 134,
289
+ 139,
290
+ 48,
291
+ 27,
292
+ 166,
293
+ 77,
294
+ 146,
295
+ 158,
296
+ 231,
297
+ 83,
298
+ 111,
299
+ 229,
300
+ 122,
301
+ 60,
302
+ 211,
303
+ 133,
304
+ 230,
305
+ 220,
306
+ 105,
307
+ 92,
308
+ 41,
309
+ 55,
310
+ 46,
311
+ 245,
312
+ 40,
313
+ 244,
314
+ 102,
315
+ 143,
316
+ 54,
317
+ 65,
318
+ 25,
319
+ 63,
320
+ 161,
321
+ 1,
322
+ 216,
323
+ 80,
324
+ 73,
325
+ 209,
326
+ 76,
327
+ 132,
328
+ 187,
329
+ 208,
330
+ 89,
331
+ 18,
332
+ 169,
333
+ 200,
334
+ 196,
335
+ 135,
336
+ 130,
337
+ 116,
338
+ 188,
339
+ 159,
340
+ 86,
341
+ 164,
342
+ 100,
343
+ 109,
344
+ 198,
345
+ 173,
346
+ 186,
347
+ 3,
348
+ 64,
349
+ 52,
350
+ 217,
351
+ 226,
352
+ 250,
353
+ 124,
354
+ 123,
355
+ 5,
356
+ 202,
357
+ 38,
358
+ 147,
359
+ 118,
360
+ 126,
361
+ 255,
362
+ 82,
363
+ 85,
364
+ 212,
365
+ 207,
366
+ 206,
367
+ 59,
368
+ 227,
369
+ 47,
370
+ 16,
371
+ 58,
372
+ 17,
373
+ 182,
374
+ 189,
375
+ 28,
376
+ 42,
377
+ 223,
378
+ 183,
379
+ 170,
380
+ 213,
381
+ 119,
382
+ 248,
383
+ 152,
384
+ 2,
385
+ 44,
386
+ 154,
387
+ 163,
388
+ 70,
389
+ 221,
390
+ 153,
391
+ 101,
392
+ 155,
393
+ 167,
394
+ 43,
395
+ 172,
396
+ 9,
397
+ 129,
398
+ 22,
399
+ 39,
400
+ 253,
401
+ 19,
402
+ 98,
403
+ 108,
404
+ 110,
405
+ 79,
406
+ 113,
407
+ 224,
408
+ 232,
409
+ 178,
410
+ 185,
411
+ 112,
412
+ 104,
413
+ 218,
414
+ 246,
415
+ 97,
416
+ 228,
417
+ 251,
418
+ 34,
419
+ 242,
420
+ 193,
421
+ 238,
422
+ 210,
423
+ 144,
424
+ 12,
425
+ 191,
426
+ 179,
427
+ 162,
428
+ 241,
429
+ 81,
430
+ 51,
431
+ 145,
432
+ 235,
433
+ 249,
434
+ 14,
435
+ 239,
436
+ 107,
437
+ 49,
438
+ 192,
439
+ 214,
440
+ 31,
441
+ 181,
442
+ 199,
443
+ 106,
444
+ 157,
445
+ 184,
446
+ 84,
447
+ 204,
448
+ 176,
449
+ 115,
450
+ 121,
451
+ 50,
452
+ 45,
453
+ 127,
454
+ 4,
455
+ 150,
456
+ 254,
457
+ 138,
458
+ 236,
459
+ 205,
460
+ 93,
461
+ 222,
462
+ 114,
463
+ 67,
464
+ 29,
465
+ 24,
466
+ 72,
467
+ 243,
468
+ 141,
469
+ 128,
470
+ 195,
471
+ 78,
472
+ 66,
473
+ 215,
474
+ 61,
475
+ 156,
476
+ 180
477
+ ]);
478
+ for (let i = 0; i < 256; i++) {
479
+ PERMUTATION[i] = P[i];
480
+ PERMUTATION[i + 256] = P[i];
481
+ }
482
+ function fade(t) {
483
+ return t * t * t * (t * (t * 6 - 15) + 10);
484
+ }
485
+ function lerp(a, b, t) {
486
+ return a + t * (b - a);
487
+ }
488
+ function grad(hash, x, y) {
489
+ const h = hash & 3;
490
+ const u = h < 2 ? x : y;
491
+ const v = h < 2 ? y : x;
492
+ return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v);
493
+ }
494
+ function perlinNoise2D(x, y) {
495
+ const xi = Math.floor(x) & 255;
496
+ const yi = Math.floor(y) & 255;
497
+ const xf = x - Math.floor(x);
498
+ const yf = y - Math.floor(y);
499
+ const u = fade(xf);
500
+ const v = fade(yf);
501
+ const aa = PERMUTATION[PERMUTATION[xi] + yi];
502
+ const ab = PERMUTATION[PERMUTATION[xi] + yi + 1];
503
+ const ba = PERMUTATION[PERMUTATION[xi + 1] + yi];
504
+ const bb = PERMUTATION[PERMUTATION[xi + 1] + yi + 1];
505
+ const x1 = lerp(grad(aa, xf, yf), grad(ba, xf - 1, yf), u);
506
+ const x2 = lerp(grad(ab, xf, yf - 1), grad(bb, xf - 1, yf - 1), u);
507
+ return lerp(x1, x2, v);
508
+ }
509
+ function fbmNoise(x, y, octaves = 4, persistence = 0.5) {
510
+ let total = 0;
511
+ let frequency = 1;
512
+ let amplitude = 1;
513
+ let maxValue = 0;
514
+ for (let i = 0; i < octaves; i++) {
515
+ total += perlinNoise2D(x * frequency, y * frequency) * amplitude;
516
+ maxValue += amplitude;
517
+ amplitude *= persistence;
518
+ frequency *= 2;
519
+ }
520
+ return total / maxValue;
521
+ }
522
+ function wavePattern(x, y, time, config = DEFAULT_PATTERN_CONFIG) {
523
+ const { frequency, amplitude, speed } = config;
524
+ const wave1 = Math.sin(x * frequency + time * speed);
525
+ const wave2 = Math.cos(y * frequency + time * speed * 0.7);
526
+ const wave3 = Math.sin((x + y) * frequency * 0.5 + time * speed * 0.5);
527
+ return (wave1 + wave2 + wave3) / 3 * amplitude;
528
+ }
529
+ function ripplePattern(x, y, centerX, centerY, time, config = DEFAULT_PATTERN_CONFIG) {
530
+ const { frequency, amplitude, speed } = config;
531
+ const dx = x - centerX;
532
+ const dy = y - centerY;
533
+ const distance = Math.sqrt(dx * dx + dy * dy);
534
+ return Math.sin(distance * frequency - time * speed) * amplitude;
535
+ }
536
+ function staticNoise(seed) {
537
+ if (seed !== void 0) {
538
+ const x = Math.sin(seed * 12.9898) * 43758.5453;
539
+ return x - Math.floor(x);
540
+ }
541
+ return Math.random();
542
+ }
543
+ function seededNoise2D(x, y, seed = 0) {
544
+ const n = Math.sin(x * 12.9898 + y * 78.233 + seed) * 43758.5453;
545
+ return n - Math.floor(n);
546
+ }
547
+ function generateBrightnessGrid(cols, rows, pattern, time = 0, config = DEFAULT_PATTERN_CONFIG) {
548
+ const grid = [];
549
+ const { frequency, amplitude, speed } = config;
550
+ for (let row = 0; row < rows; row++) {
551
+ grid[row] = [];
552
+ for (let col = 0; col < cols; col++) {
553
+ let value;
554
+ switch (pattern) {
555
+ case "perlin":
556
+ value = perlinNoise2D(
557
+ col * frequency + time * speed * 0.1,
558
+ row * frequency + time * speed * 0.05
559
+ );
560
+ break;
561
+ case "fbm":
562
+ value = fbmNoise(
563
+ col * frequency + time * speed * 0.1,
564
+ row * frequency + time * speed * 0.05,
565
+ 4,
566
+ 0.5
567
+ );
568
+ break;
569
+ case "waves":
570
+ value = wavePattern(col, row, time, config);
571
+ break;
572
+ case "ripple":
573
+ value = ripplePattern(col, row, cols / 2, rows / 2, time, config);
574
+ break;
575
+ case "static":
576
+ default:
577
+ value = seededNoise2D(col, row, Math.floor(time * speed * 10)) * 2 - 1;
578
+ break;
579
+ }
580
+ const normalized = (value + 1) * 0.5 * amplitude;
581
+ const brightness = Math.max(0, Math.min(255, Math.floor(normalized * 255)));
582
+ grid[row][col] = brightness;
583
+ }
584
+ }
585
+ return grid;
586
+ }
587
+ function gridToImageData(grid, cellWidth, cellHeight) {
588
+ var _a;
589
+ const rows = grid.length;
590
+ const cols = ((_a = grid[0]) == null ? void 0 : _a.length) || 0;
591
+ const width = cols * cellWidth;
592
+ const height = rows * cellHeight;
593
+ const data = new Uint8ClampedArray(width * height * 4);
594
+ for (let row = 0; row < rows; row++) {
595
+ for (let col = 0; col < cols; col++) {
596
+ const brightness = grid[row][col];
597
+ for (let cy = 0; cy < cellHeight; cy++) {
598
+ for (let cx = 0; cx < cellWidth; cx++) {
599
+ const px = ((row * cellHeight + cy) * width + (col * cellWidth + cx)) * 4;
600
+ data[px] = brightness;
601
+ data[px + 1] = brightness;
602
+ data[px + 2] = brightness;
603
+ data[px + 3] = 255;
604
+ }
605
+ }
606
+ }
607
+ }
608
+ return new ImageData(data, width, height);
609
+ }
610
+ const CHARACTER_SETS = {
611
+ standard: {
612
+ name: "Standard",
613
+ description: "Classic ASCII art character set",
614
+ characters: " .:-=+*#%@",
615
+ bestFor: ["general", "images", "patterns"]
616
+ },
617
+ dense: {
618
+ name: "Dense",
619
+ description: "High detail character set with many gradations",
620
+ characters: " .'`^\",:;Il!i><~+_-?][}{1)(|\\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$",
621
+ bestFor: ["detailed images", "portraits", "high contrast"]
622
+ },
623
+ minimal: {
624
+ name: "Minimal",
625
+ description: "Clean and simple with few characters",
626
+ characters: " .:*#",
627
+ bestFor: ["backgrounds", "subtle effects", "clean aesthetic"]
628
+ },
629
+ grove: {
630
+ name: "Grove",
631
+ description: "Organic, soft characters inspired by nature",
632
+ characters: " ·∙•◦○◉●",
633
+ bestFor: ["organic patterns", "soft backgrounds", "nature themes"]
634
+ },
635
+ dots: {
636
+ name: "Dots",
637
+ description: "Braille-like dot patterns",
638
+ characters: " ⋅∘∙●",
639
+ bestFor: ["stipple effects", "pointillism", "dotted textures"]
640
+ },
641
+ blocks: {
642
+ name: "Blocks",
643
+ description: "Block-based characters for sharp edges",
644
+ characters: " ░▒▓█",
645
+ bestFor: ["retro effects", "pixel art", "bold patterns"]
646
+ },
647
+ lines: {
648
+ name: "Lines",
649
+ description: "Line-based characters for directional effects",
650
+ characters: " -─═╌│┃",
651
+ bestFor: ["rain effects", "streaks", "motion blur"]
652
+ },
653
+ stars: {
654
+ name: "Stars",
655
+ description: "Star and sparkle characters",
656
+ characters: " ·✧✦✫✬✯★",
657
+ bestFor: ["sparkle effects", "night sky", "magical themes"]
658
+ },
659
+ nature: {
660
+ name: "Nature",
661
+ description: "Nature-themed decorative characters",
662
+ characters: " .~≈∿⌇☘",
663
+ bestFor: ["decorative", "themed effects", "organic patterns"]
664
+ },
665
+ weather: {
666
+ name: "Weather",
667
+ description: "Weather-related symbols",
668
+ characters: " ·.:*❄❅❆",
669
+ bestFor: ["snow effects", "weather simulations", "seasonal themes"]
670
+ },
671
+ binary: {
672
+ name: "Binary",
673
+ description: "Digital-style binary characters",
674
+ characters: " 01",
675
+ bestFor: ["digital effects", "matrix style", "tech themes"]
676
+ },
677
+ math: {
678
+ name: "Math",
679
+ description: "Mathematical symbols",
680
+ characters: " +-×÷=≠≈∞",
681
+ bestFor: ["abstract patterns", "tech themes", "decorative"]
682
+ }
683
+ };
684
+ function getCharacterSet(name) {
685
+ return CHARACTER_SETS[name];
686
+ }
687
+ function getCharacters(name) {
688
+ var _a;
689
+ return ((_a = CHARACTER_SETS[name]) == null ? void 0 : _a.characters) || CHARACTER_SETS.standard.characters;
690
+ }
691
+ function getCharacterSetNames() {
692
+ return Object.keys(CHARACTER_SETS);
693
+ }
694
+ function createCharacterSet(name, characters, description = "", bestFor = []) {
695
+ return {
696
+ name,
697
+ description,
698
+ characters,
699
+ bestFor
700
+ };
701
+ }
702
+ function validateCharacterSet(characters) {
703
+ if (characters.length < 2) return false;
704
+ if (characters[0] !== " ") return false;
705
+ return true;
706
+ }
707
+ function invertCharacters(characters) {
708
+ return characters.split("").reverse().join("");
709
+ }
710
+ function createAnimationLoop(options) {
711
+ const { fps = 30, onStart, onStop, onFrame } = options;
712
+ const state = {
713
+ isRunning: false,
714
+ frameId: null,
715
+ lastFrameTime: 0,
716
+ frameInterval: 1e3 / fps,
717
+ elapsedTime: 0,
718
+ frameCount: 0
719
+ };
720
+ let pausedTime = 0;
721
+ let isPaused = false;
722
+ function animate(currentTime) {
723
+ if (!state.isRunning || isPaused) return;
724
+ const deltaTime = currentTime - state.lastFrameTime;
725
+ if (deltaTime >= state.frameInterval) {
726
+ state.elapsedTime += deltaTime;
727
+ state.frameCount++;
728
+ const continueAnimation = onFrame(currentTime, deltaTime, state.frameCount);
729
+ if (continueAnimation === false) {
730
+ stop();
731
+ return;
732
+ }
733
+ state.lastFrameTime = currentTime - deltaTime % state.frameInterval;
734
+ }
735
+ state.frameId = requestAnimationFrame(animate);
736
+ }
737
+ function start() {
738
+ if (state.isRunning) return;
739
+ state.isRunning = true;
740
+ state.lastFrameTime = performance.now();
741
+ state.elapsedTime = 0;
742
+ state.frameCount = 0;
743
+ isPaused = false;
744
+ onStart == null ? void 0 : onStart();
745
+ state.frameId = requestAnimationFrame(animate);
746
+ }
747
+ function stop() {
748
+ state.isRunning = false;
749
+ isPaused = false;
750
+ if (state.frameId !== null) {
751
+ cancelAnimationFrame(state.frameId);
752
+ state.frameId = null;
753
+ }
754
+ onStop == null ? void 0 : onStop();
755
+ }
756
+ function pause() {
757
+ if (!state.isRunning || isPaused) return;
758
+ isPaused = true;
759
+ pausedTime = performance.now();
760
+ if (state.frameId !== null) {
761
+ cancelAnimationFrame(state.frameId);
762
+ state.frameId = null;
763
+ }
764
+ }
765
+ function resume() {
766
+ if (!state.isRunning || !isPaused) return;
767
+ isPaused = false;
768
+ state.lastFrameTime += performance.now() - pausedTime;
769
+ state.frameId = requestAnimationFrame(animate);
770
+ }
771
+ function getState() {
772
+ return { ...state };
773
+ }
774
+ return { start, stop, pause, resume, getState };
775
+ }
776
+ function throttle(fn, limit) {
777
+ let lastCall = 0;
778
+ let timeout = null;
779
+ return (...args) => {
780
+ const now = Date.now();
781
+ if (now - lastCall >= limit) {
782
+ lastCall = now;
783
+ fn(...args);
784
+ } else if (!timeout) {
785
+ timeout = setTimeout(() => {
786
+ lastCall = Date.now();
787
+ timeout = null;
788
+ fn(...args);
789
+ }, limit - (now - lastCall));
790
+ }
791
+ };
792
+ }
793
+ function debounce(fn, delay) {
794
+ let timeout = null;
795
+ return (...args) => {
796
+ if (timeout) {
797
+ clearTimeout(timeout);
798
+ }
799
+ timeout = setTimeout(() => {
800
+ timeout = null;
801
+ fn(...args);
802
+ }, delay);
803
+ };
804
+ }
805
+ function calculateFPS(frameTimes, sampleSize = 60) {
806
+ if (frameTimes.length < 2) return 0;
807
+ const samples = frameTimes.slice(-sampleSize);
808
+ const totalTime = samples[samples.length - 1] - samples[0];
809
+ const frameCount = samples.length - 1;
810
+ if (totalTime <= 0) return 0;
811
+ return frameCount / totalTime * 1e3;
812
+ }
813
+ const easings = {
814
+ /** Linear - no easing */
815
+ linear: (t) => t,
816
+ /** Ease in - slow start */
817
+ easeIn: (t) => t * t,
818
+ /** Ease out - slow end */
819
+ easeOut: (t) => t * (2 - t),
820
+ /** Ease in/out - slow start and end */
821
+ easeInOut: (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
822
+ /** Sine ease in */
823
+ sineIn: (t) => 1 - Math.cos(t * Math.PI / 2),
824
+ /** Sine ease out */
825
+ sineOut: (t) => Math.sin(t * Math.PI / 2),
826
+ /** Sine ease in/out */
827
+ sineInOut: (t) => -(Math.cos(Math.PI * t) - 1) / 2,
828
+ /** Bounce at end */
829
+ bounceOut: (t) => {
830
+ const n1 = 7.5625;
831
+ const d1 = 2.75;
832
+ if (t < 1 / d1) {
833
+ return n1 * t * t;
834
+ } else if (t < 2 / d1) {
835
+ return n1 * (t -= 1.5 / d1) * t + 0.75;
836
+ } else if (t < 2.5 / d1) {
837
+ return n1 * (t -= 2.25 / d1) * t + 0.9375;
838
+ } else {
839
+ return n1 * (t -= 2.625 / d1) * t + 0.984375;
840
+ }
841
+ }
842
+ };
843
+ function createCanvas(options = {}) {
844
+ const { width = 300, height = 150, highDPI = true, className, style } = options;
845
+ const canvas = document.createElement("canvas");
846
+ if (highDPI) {
847
+ const dpr = window.devicePixelRatio || 1;
848
+ canvas.width = width * dpr;
849
+ canvas.height = height * dpr;
850
+ canvas.style.width = `${width}px`;
851
+ canvas.style.height = `${height}px`;
852
+ const ctx = canvas.getContext("2d");
853
+ if (ctx) {
854
+ ctx.scale(dpr, dpr);
855
+ }
856
+ } else {
857
+ canvas.width = width;
858
+ canvas.height = height;
859
+ }
860
+ if (className) {
861
+ canvas.className = className;
862
+ }
863
+ if (style) {
864
+ Object.assign(canvas.style, style);
865
+ }
866
+ return canvas;
867
+ }
868
+ function getDevicePixelRatio() {
869
+ return typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
870
+ }
871
+ function resizeCanvasToContainer(canvas, container, highDPI = true) {
872
+ const rect = container.getBoundingClientRect();
873
+ const dpr = highDPI ? getDevicePixelRatio() : 1;
874
+ const width = rect.width;
875
+ const height = rect.height;
876
+ canvas.width = width * dpr;
877
+ canvas.height = height * dpr;
878
+ canvas.style.width = `${width}px`;
879
+ canvas.style.height = `${height}px`;
880
+ if (highDPI) {
881
+ const ctx = canvas.getContext("2d");
882
+ if (ctx) {
883
+ ctx.scale(dpr, dpr);
884
+ }
885
+ }
886
+ return { width, height };
887
+ }
888
+ function createOffscreenCanvas(width, height) {
889
+ if (typeof OffscreenCanvas !== "undefined") {
890
+ return new OffscreenCanvas(width, height);
891
+ }
892
+ const canvas = document.createElement("canvas");
893
+ canvas.width = width;
894
+ canvas.height = height;
895
+ return canvas;
896
+ }
897
+ function clearCanvas(ctx, width, height, backgroundColor) {
898
+ if (backgroundColor) {
899
+ ctx.fillStyle = backgroundColor;
900
+ ctx.fillRect(0, 0, width, height);
901
+ } else {
902
+ ctx.clearRect(0, 0, width, height);
903
+ }
904
+ }
905
+ function getImageData(ctx, x = 0, y = 0, width, height) {
906
+ const canvas = ctx.canvas;
907
+ const w = width ?? canvas.width;
908
+ const h = height ?? canvas.height;
909
+ return ctx.getImageData(x, y, w, h);
910
+ }
911
+ function optimizeContext(ctx) {
912
+ ctx.imageSmoothingEnabled = false;
913
+ ctx.globalCompositeOperation = "source-over";
914
+ }
915
+ function setupTextRendering(ctx, fontSize, fontFamily = "monospace", color = "#ffffff") {
916
+ ctx.font = `${fontSize}px ${fontFamily}`;
917
+ ctx.textBaseline = "top";
918
+ ctx.textAlign = "left";
919
+ ctx.fillStyle = color;
920
+ }
921
+ function measureTextWidth(ctx, text, fontSize, fontFamily = "monospace") {
922
+ const originalFont = ctx.font;
923
+ ctx.font = `${fontSize}px ${fontFamily}`;
924
+ const metrics = ctx.measureText(text);
925
+ ctx.font = originalFont;
926
+ return metrics.width;
927
+ }
928
+ function calculateCellSize(canvasWidth, canvasHeight, targetCols) {
929
+ const cellWidth = Math.floor(canvasWidth / targetCols);
930
+ const cellHeight = Math.floor(cellWidth * 1.5);
931
+ const cols = Math.floor(canvasWidth / cellWidth);
932
+ const rows = Math.floor(canvasHeight / cellHeight);
933
+ return { cellWidth, cellHeight, cols, rows };
934
+ }
935
+ function setBlendMode(ctx, mode) {
936
+ ctx.globalCompositeOperation = mode === "normal" ? "source-over" : mode;
937
+ }
938
+ function loadImage(src, options = {}) {
939
+ return new Promise((resolve, reject) => {
940
+ const img = new Image();
941
+ if (options.crossOrigin !== void 0) {
942
+ img.crossOrigin = options.crossOrigin;
943
+ }
944
+ img.onload = () => resolve(img);
945
+ img.onerror = () => reject(new Error(`Failed to load image: ${src}`));
946
+ img.src = src;
947
+ });
948
+ }
949
+ async function loadAndScaleImage(src, maxWidth, maxHeight, options = {}) {
950
+ const img = await loadImage(src, options);
951
+ let width = img.naturalWidth;
952
+ let height = img.naturalHeight;
953
+ if (width > maxWidth || height > maxHeight) {
954
+ const widthRatio = maxWidth / width;
955
+ const heightRatio = maxHeight / height;
956
+ const scale = Math.min(widthRatio, heightRatio);
957
+ width = Math.floor(width * scale);
958
+ height = Math.floor(height * scale);
959
+ }
960
+ return { image: img, width, height };
961
+ }
962
+ function imageToPixelData(image, width, height) {
963
+ const w = width ?? (image instanceof HTMLImageElement ? image.naturalWidth : image.width);
964
+ const h = height ?? (image instanceof HTMLImageElement ? image.naturalHeight : image.height);
965
+ const canvas = document.createElement("canvas");
966
+ canvas.width = w;
967
+ canvas.height = h;
968
+ const ctx = canvas.getContext("2d");
969
+ if (!ctx) {
970
+ throw new Error("Failed to get 2D context");
971
+ }
972
+ ctx.drawImage(image, 0, 0, w, h);
973
+ return ctx.getImageData(0, 0, w, h);
974
+ }
975
+ function extractBrightness(imageData, brightnessFunction = (r, g, b) => 0.21 * r + 0.72 * g + 0.07 * b) {
976
+ const { data, width, height } = imageData;
977
+ const brightness = new Array(width * height);
978
+ for (let i = 0; i < data.length; i += 4) {
979
+ brightness[i / 4] = brightnessFunction(data[i], data[i + 1], data[i + 2]);
980
+ }
981
+ return brightness;
982
+ }
983
+ function sampleImageCells(imageData, cellWidth, cellHeight, brightnessFunction = (r, g, b) => 0.21 * r + 0.72 * g + 0.07 * b) {
984
+ const { data, width, height } = imageData;
985
+ const cols = Math.ceil(width / cellWidth);
986
+ const rows = Math.ceil(height / cellHeight);
987
+ const result = [];
988
+ for (let row = 0; row < rows; row++) {
989
+ result[row] = [];
990
+ for (let col = 0; col < cols; col++) {
991
+ const cellData = sampleCell(data, width, col * cellWidth, row * cellHeight, cellWidth, cellHeight);
992
+ const brightness = brightnessFunction(cellData.r, cellData.g, cellData.b);
993
+ result[row][col] = {
994
+ brightness,
995
+ color: `rgb(${Math.round(cellData.r)}, ${Math.round(cellData.g)}, ${Math.round(cellData.b)})`
996
+ };
997
+ }
998
+ }
999
+ return result;
1000
+ }
1001
+ function sampleCell(data, imageWidth, startX, startY, cellWidth, cellHeight) {
1002
+ let totalR = 0;
1003
+ let totalG = 0;
1004
+ let totalB = 0;
1005
+ let totalA = 0;
1006
+ let count = 0;
1007
+ for (let y = startY; y < startY + cellHeight; y++) {
1008
+ for (let x = startX; x < startX + cellWidth; x++) {
1009
+ const px = (y * imageWidth + x) * 4;
1010
+ if (px >= 0 && px + 3 < data.length) {
1011
+ totalR += data[px];
1012
+ totalG += data[px + 1];
1013
+ totalB += data[px + 2];
1014
+ totalA += data[px + 3];
1015
+ count++;
1016
+ }
1017
+ }
1018
+ }
1019
+ if (count === 0) {
1020
+ return { r: 0, g: 0, b: 0, a: 0 };
1021
+ }
1022
+ return {
1023
+ r: totalR / count,
1024
+ g: totalG / count,
1025
+ b: totalB / count,
1026
+ a: totalA / count
1027
+ };
1028
+ }
1029
+ function rgbToHex(r, g, b) {
1030
+ const toHex = (n) => Math.max(0, Math.min(255, Math.round(n))).toString(16).padStart(2, "0");
1031
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
1032
+ }
1033
+ function hexToRgb(hex) {
1034
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
1035
+ return result ? {
1036
+ r: parseInt(result[1], 16),
1037
+ g: parseInt(result[2], 16),
1038
+ b: parseInt(result[3], 16)
1039
+ } : null;
1040
+ }
1041
+ function adjustBrightness(imageData, amount) {
1042
+ const { data, width, height } = imageData;
1043
+ const adjusted = new Uint8ClampedArray(data.length);
1044
+ for (let i = 0; i < data.length; i += 4) {
1045
+ adjusted[i] = Math.min(255, Math.max(0, data[i] + amount));
1046
+ adjusted[i + 1] = Math.min(255, Math.max(0, data[i + 1] + amount));
1047
+ adjusted[i + 2] = Math.min(255, Math.max(0, data[i + 2] + amount));
1048
+ adjusted[i + 3] = data[i + 3];
1049
+ }
1050
+ return new ImageData(adjusted, width, height);
1051
+ }
1052
+ function adjustContrast(imageData, amount) {
1053
+ const { data, width, height } = imageData;
1054
+ const adjusted = new Uint8ClampedArray(data.length);
1055
+ const factor = 259 * (amount + 255) / (255 * (259 - amount));
1056
+ for (let i = 0; i < data.length; i += 4) {
1057
+ adjusted[i] = Math.min(255, Math.max(0, factor * (data[i] - 128) + 128));
1058
+ adjusted[i + 1] = Math.min(255, Math.max(0, factor * (data[i + 1] - 128) + 128));
1059
+ adjusted[i + 2] = Math.min(255, Math.max(0, factor * (data[i + 2] - 128) + 128));
1060
+ adjusted[i + 3] = data[i + 3];
1061
+ }
1062
+ return new ImageData(adjusted, width, height);
1063
+ }
1064
+ function invertColors(imageData) {
1065
+ const { data, width, height } = imageData;
1066
+ const inverted = new Uint8ClampedArray(data.length);
1067
+ for (let i = 0; i < data.length; i += 4) {
1068
+ inverted[i] = 255 - data[i];
1069
+ inverted[i + 1] = 255 - data[i + 1];
1070
+ inverted[i + 2] = 255 - data[i + 2];
1071
+ inverted[i + 3] = data[i + 3];
1072
+ }
1073
+ return new ImageData(inverted, width, height);
1074
+ }
1075
+ function toGrayscale(imageData) {
1076
+ const { data, width, height } = imageData;
1077
+ const grayscale = new Uint8ClampedArray(data.length);
1078
+ for (let i = 0; i < data.length; i += 4) {
1079
+ const gray = 0.21 * data[i] + 0.72 * data[i + 1] + 0.07 * data[i + 2];
1080
+ grayscale[i] = gray;
1081
+ grayscale[i + 1] = gray;
1082
+ grayscale[i + 2] = gray;
1083
+ grayscale[i + 3] = data[i + 3];
1084
+ }
1085
+ return new ImageData(grayscale, width, height);
1086
+ }
1087
+ function createVisibilityObserver(element, callback, threshold = 0.1) {
1088
+ const observer = new IntersectionObserver(
1089
+ (entries) => {
1090
+ for (const entry of entries) {
1091
+ callback(entry.isIntersecting, entry);
1092
+ }
1093
+ },
1094
+ { threshold }
1095
+ );
1096
+ observer.observe(element);
1097
+ return () => {
1098
+ observer.disconnect();
1099
+ };
1100
+ }
1101
+ function createResizeObserver(element, callback, debounceMs = 100) {
1102
+ let timeout = null;
1103
+ const observer = new ResizeObserver((entries) => {
1104
+ if (timeout) {
1105
+ clearTimeout(timeout);
1106
+ }
1107
+ timeout = setTimeout(() => {
1108
+ const entry = entries[0];
1109
+ if (entry) {
1110
+ const { width, height } = entry.contentRect;
1111
+ callback(width, height, entry);
1112
+ }
1113
+ }, debounceMs);
1114
+ });
1115
+ observer.observe(element);
1116
+ return () => {
1117
+ if (timeout) {
1118
+ clearTimeout(timeout);
1119
+ }
1120
+ observer.disconnect();
1121
+ };
1122
+ }
1123
+ function prefersReducedMotion() {
1124
+ if (typeof window === "undefined") return false;
1125
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1126
+ }
1127
+ function onReducedMotionChange(callback) {
1128
+ if (typeof window === "undefined") {
1129
+ return () => {
1130
+ };
1131
+ }
1132
+ const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
1133
+ const handler = (event) => {
1134
+ callback(event.matches);
1135
+ };
1136
+ mediaQuery.addEventListener("change", handler);
1137
+ callback(mediaQuery.matches);
1138
+ return () => {
1139
+ mediaQuery.removeEventListener("change", handler);
1140
+ };
1141
+ }
1142
+ function isLowPowerMode() {
1143
+ if (typeof navigator !== "undefined" && "getBattery" in navigator) {
1144
+ return false;
1145
+ }
1146
+ if (typeof navigator !== "undefined" && navigator.hardwareConcurrency) {
1147
+ return navigator.hardwareConcurrency <= 2;
1148
+ }
1149
+ return false;
1150
+ }
1151
+ function getRecommendedFPS() {
1152
+ if (prefersReducedMotion()) {
1153
+ return 0;
1154
+ }
1155
+ if (isLowPowerMode()) {
1156
+ return 15;
1157
+ }
1158
+ return 30;
1159
+ }
1160
+ function createFPSCounter() {
1161
+ const frameTimes = [];
1162
+ const maxSamples = 60;
1163
+ let droppedFrames = 0;
1164
+ let lastFrameTime = performance.now();
1165
+ function tick() {
1166
+ const now = performance.now();
1167
+ const delta = now - lastFrameTime;
1168
+ lastFrameTime = now;
1169
+ frameTimes.push(now);
1170
+ if (frameTimes.length > maxSamples) {
1171
+ frameTimes.shift();
1172
+ }
1173
+ if (delta > 20) {
1174
+ droppedFrames += Math.floor(delta / 16.67) - 1;
1175
+ }
1176
+ }
1177
+ function getFPS() {
1178
+ if (frameTimes.length < 2) return 0;
1179
+ const oldest = frameTimes[0];
1180
+ const newest = frameTimes[frameTimes.length - 1];
1181
+ const elapsed = newest - oldest;
1182
+ if (elapsed === 0) return 0;
1183
+ return (frameTimes.length - 1) / elapsed * 1e3;
1184
+ }
1185
+ function getMetrics() {
1186
+ const fps = getFPS();
1187
+ const frameTime = fps > 0 ? 1e3 / fps : 0;
1188
+ return {
1189
+ fps: Math.round(fps * 10) / 10,
1190
+ frameTime: Math.round(frameTime * 100) / 100,
1191
+ droppedFrames
1192
+ };
1193
+ }
1194
+ function reset() {
1195
+ frameTimes.length = 0;
1196
+ droppedFrames = 0;
1197
+ lastFrameTime = performance.now();
1198
+ }
1199
+ return { tick, getFPS, getMetrics, reset };
1200
+ }
1201
+ function requestIdleCallback(callback, options) {
1202
+ const global = globalThis;
1203
+ if (typeof global.requestIdleCallback === "function") {
1204
+ return global.requestIdleCallback(callback, options);
1205
+ }
1206
+ return global.setTimeout(callback, (options == null ? void 0 : options.timeout) ?? 1);
1207
+ }
1208
+ function cancelIdleCallback(id) {
1209
+ const global = globalThis;
1210
+ if (typeof global.cancelIdleCallback === "function") {
1211
+ global.cancelIdleCallback(id);
1212
+ } else {
1213
+ global.clearTimeout(id);
1214
+ }
1215
+ }
1216
+ function isBrowser() {
1217
+ return typeof window !== "undefined" && typeof document !== "undefined";
1218
+ }
1219
+ function isCanvasSupported() {
1220
+ if (!isBrowser()) return false;
1221
+ const canvas = document.createElement("canvas");
1222
+ return !!(canvas.getContext && canvas.getContext("2d"));
1223
+ }
1224
+ function isOffscreenCanvasSupported() {
1225
+ return typeof OffscreenCanvas !== "undefined";
1226
+ }
1
1227
  const DEFAULT_CHARACTERS = " .:-=+*#%@";
2
1228
  const DEFAULT_CONFIG = {
3
1229
  characters: DEFAULT_CHARACTERS,
@@ -16,12 +1242,68 @@ function brightnessToChar(brightness, characters = DEFAULT_CHARACTERS) {
16
1242
  const index = Math.floor(brightness / 255 * (characters.length - 1));
17
1243
  return characters[Math.min(index, characters.length - 1)];
18
1244
  }
19
- const VERSION = "0.0.1";
1245
+ const VERSION = "0.1.0";
20
1246
  export {
1247
+ CHARACTER_SETS,
21
1248
  DEFAULT_CHARACTERS,
22
1249
  DEFAULT_CONFIG,
1250
+ DEFAULT_PATTERN_CONFIG,
1251
+ GossamerRenderer,
23
1252
  VERSION,
1253
+ adjustBrightness,
1254
+ adjustContrast,
24
1255
  brightnessToChar,
25
- calculateBrightness
1256
+ calculateBrightness,
1257
+ calculateCellSize,
1258
+ calculateFPS,
1259
+ cancelIdleCallback,
1260
+ clearCanvas,
1261
+ createAnimationLoop,
1262
+ createCanvas,
1263
+ createCharacterSet,
1264
+ createFPSCounter,
1265
+ createOffscreenCanvas,
1266
+ createResizeObserver,
1267
+ createVisibilityObserver,
1268
+ debounce,
1269
+ easings,
1270
+ extractBrightness,
1271
+ fbmNoise,
1272
+ generateBrightnessGrid,
1273
+ getCharacterSet,
1274
+ getCharacterSetNames,
1275
+ getCharacters,
1276
+ getDevicePixelRatio,
1277
+ getImageData,
1278
+ getRecommendedFPS,
1279
+ gridToImageData,
1280
+ hexToRgb,
1281
+ imageToPixelData,
1282
+ invertCharacters,
1283
+ invertColors,
1284
+ isBrowser,
1285
+ isCanvasSupported,
1286
+ isLowPowerMode,
1287
+ isOffscreenCanvasSupported,
1288
+ loadAndScaleImage,
1289
+ loadImage,
1290
+ measureTextWidth,
1291
+ onReducedMotionChange,
1292
+ optimizeContext,
1293
+ perlinNoise2D,
1294
+ prefersReducedMotion,
1295
+ requestIdleCallback,
1296
+ resizeCanvasToContainer,
1297
+ rgbToHex,
1298
+ ripplePattern,
1299
+ sampleImageCells,
1300
+ seededNoise2D,
1301
+ setBlendMode,
1302
+ setupTextRendering,
1303
+ staticNoise,
1304
+ throttle,
1305
+ toGrayscale,
1306
+ validateCharacterSet,
1307
+ wavePattern
26
1308
  };
27
1309
  //# sourceMappingURL=index.js.map