asciify-engine 1.0.21 → 1.0.23

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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # asciify-engine
2
2
 
3
- Framework-agnostic ASCII art engine. Convert images, videos, and GIFs into ASCII art rendered on HTML canvas.
3
+ Framework-agnostic ASCII art engine. Convert images, videos, and GIFs into ASCII art rendered on HTML canvas — plus 10 animated ASCII background effects.
4
4
 
5
5
  ## Features
6
6
 
@@ -11,6 +11,8 @@ Framework-agnostic ASCII art engine. Convert images, videos, and GIFs into ASCII
11
11
  - **6 art styles** — Classic, Particles, Letters, Box Drawing, Dense Art, Terminal
12
12
  - **Color modes** — Grayscale, Full Color, Matrix, Accent
13
13
  - **Hover effects** — Spotlight, Magnify, Repel, Glow, Color Shift
14
+ - **10 animated backgrounds** — Wave, Rain, Stars, Pulse, Noise, Grid, Aurora, Silk, Void, Morph
15
+ - **Light & dark mode** — all backgrounds adapt via `lightMode` option or `colorScheme: 'auto'`
14
16
  - **Embed code generation** — self-contained HTML output
15
17
  - **Zero framework dependencies** — works with React, Angular, Vue, Svelte, vanilla JS
16
18
 
@@ -20,6 +22,92 @@ Framework-agnostic ASCII art engine. Convert images, videos, and GIFs into ASCII
20
22
  npm install asciify-engine
21
23
  ```
22
24
 
25
+ ## Animated Backgrounds
26
+
27
+ Drop a live ASCII animation into any element with one call.
28
+
29
+ ```ts
30
+ import { asciiBackground } from 'asciify-engine';
31
+
32
+ // Attach to any selector — animates the element's background
33
+ asciiBackground('#hero', { type: 'rain' });
34
+
35
+ // Follows OS dark/light mode automatically
36
+ asciiBackground('#hero', { type: 'aurora', colorScheme: 'auto' });
37
+
38
+ // Force light mode (dark characters on light background)
39
+ asciiBackground('#hero', { type: 'wave', colorScheme: 'light' });
40
+
41
+ // Stop / clean up
42
+ const stop = asciiBackground('#hero', { type: 'stars' });
43
+ stop();
44
+ ```
45
+
46
+ ### Background Types
47
+
48
+ | Type | Description |
49
+ |---|---|
50
+ | `wave` | Flowing sine-wave field with noise turbulence |
51
+ | `rain` | Matrix-style vertical column rain with glowing head and fading tail |
52
+ | `stars` | Parallax star field that reacts to cursor position |
53
+ | `pulse` | Concentric ripple bursts that emanate from the cursor |
54
+ | `noise` | Smooth value-noise field with organic flow |
55
+ | `grid` | Geometric grid that warps and glows at cursor proximity |
56
+ | `aurora` | Sweeping borealis-style colour bands |
57
+ | `silk` | Silky fluid swirls following the cursor |
58
+ | `void` | Gravitational singularity — characters spiral inward toward cursor |
59
+ | `morph` | Characters morph between shapes driven by noise |
60
+
61
+ ### `asciiBackground` Options
62
+
63
+ | Option | Type | Default | Description |
64
+ |---|---|---|---|
65
+ | `type` | `string` | `'wave'` | Which background renderer to use |
66
+ | `colorScheme` | `'auto' \| 'light' \| 'dark'` | `'dark'` | `'auto'` follows OS theme live; `'light'` = dark chars on light bg |
67
+ | `fontSize` | `number` | `13` | Character size in px |
68
+ | `accentColor` | `string` | varies | Head/highlight colour (CSS colour string) |
69
+ | `color` | `string` | — | Override body character colour |
70
+ | `speed` | `number` | `1` | Global animation speed multiplier |
71
+ | `density` | `number` | `0.55` | Fraction of cells active (0–1) |
72
+ | `lightMode` | `boolean` | `false` | Dark characters on light background |
73
+
74
+ Each background type also accepts its own specific options — see the individual type exports (e.g. `RainBackgroundOptions`, `WaveBackgroundOptions`, etc.) for the full list.
75
+
76
+ ### Low-level background renderers
77
+
78
+ All renderers are also exported individually for direct canvas use:
79
+
80
+ ```ts
81
+ import {
82
+ renderRainBackground,
83
+ renderWaveBackground,
84
+ renderStarsBackground,
85
+ renderPulseBackground,
86
+ renderNoiseBackground,
87
+ renderGridBackground,
88
+ renderAuroraBackground,
89
+ renderSilkBackground,
90
+ renderVoidBackground,
91
+ renderMorphBackground,
92
+ } from 'asciify-engine';
93
+
94
+ // Example: drive the rain renderer yourself
95
+ const canvas = document.getElementById('canvas') as HTMLCanvasElement;
96
+ const ctx = canvas.getContext('2d')!;
97
+ let t = 0;
98
+ function tick() {
99
+ renderRainBackground(ctx, canvas.width, canvas.height, t, {
100
+ speed: 1.2,
101
+ density: 0.6,
102
+ accentColor: '#00ffcc',
103
+ lightMode: false,
104
+ });
105
+ t += 0.016;
106
+ requestAnimationFrame(tick);
107
+ }
108
+ tick();
109
+ ```
110
+
23
111
  ## Quick Start
24
112
 
25
113
  ### Vanilla JS
@@ -31,7 +119,6 @@ npm install asciify-engine
31
119
  imageToAsciiFrame,
32
120
  renderFrameToCanvas,
33
121
  DEFAULT_OPTIONS,
34
- ART_STYLE_PRESETS,
35
122
  } from 'asciify-engine';
36
123
 
37
124
  const img = new Image();
@@ -39,10 +126,8 @@ npm install asciify-engine
39
126
  img.src = 'https://picsum.photos/600/400';
40
127
  img.onload = () => {
41
128
  const canvas = document.getElementById('ascii');
42
- const options = { ...DEFAULT_OPTIONS, ...ART_STYLE_PRESETS.classic, fontSize: 10 };
43
- const cols = Math.floor(canvas.width / options.fontSize);
44
- const rows = Math.floor(canvas.height / (options.fontSize * 1.8));
45
- const frame = imageToAsciiFrame(img, options, cols, rows);
129
+ const options = { ...DEFAULT_OPTIONS, fontSize: 10 };
130
+ const { frame } = imageToAsciiFrame(img, options, canvas.width, canvas.height);
46
131
  renderFrameToCanvas(canvas.getContext('2d'), frame, options, canvas.width, canvas.height);
47
132
  };
48
133
  </script>
@@ -56,11 +141,9 @@ import {
56
141
  imageToAsciiFrame,
57
142
  renderFrameToCanvas,
58
143
  DEFAULT_OPTIONS,
59
- ART_STYLE_PRESETS,
60
- type ArtStyle,
61
144
  } from 'asciify-engine';
62
145
 
63
- export function AsciiImage({ src, style = 'classic' }: { src: string; style?: ArtStyle }) {
146
+ export function AsciiImage({ src }: { src: string }) {
64
147
  const ref = useRef<HTMLCanvasElement>(null);
65
148
 
66
149
  useEffect(() => {
@@ -69,13 +152,11 @@ export function AsciiImage({ src, style = 'classic' }: { src: string; style?: Ar
69
152
  img.src = src;
70
153
  img.onload = () => {
71
154
  const canvas = ref.current!;
72
- const opts = { ...DEFAULT_OPTIONS, ...ART_STYLE_PRESETS[style], fontSize: 10 };
73
- const cols = Math.floor(canvas.width / opts.fontSize);
74
- const rows = Math.floor(canvas.height / (opts.fontSize * 1.8));
75
- const frame = imageToAsciiFrame(img, opts, cols, rows);
155
+ const opts = { ...DEFAULT_OPTIONS, fontSize: 10 };
156
+ const { frame } = imageToAsciiFrame(img, opts, canvas.width, canvas.height);
76
157
  renderFrameToCanvas(canvas.getContext('2d')!, frame, opts, canvas.width, canvas.height);
77
158
  };
78
- }, [src, style]);
159
+ }, [src]);
79
160
 
80
161
  return <canvas ref={ref} width={800} height={600} />;
81
162
  }
@@ -84,12 +165,11 @@ export function AsciiImage({ src, style = 'classic' }: { src: string; style?: Ar
84
165
  ### Angular
85
166
 
86
167
  ```typescript
87
- import { Component, ElementRef, Input, ViewChild, AfterViewInit } from '@angular/core';
168
+ import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
88
169
  import {
89
170
  imageToAsciiFrame,
90
171
  renderFrameToCanvas,
91
172
  DEFAULT_OPTIONS,
92
- ART_STYLE_PRESETS,
93
173
  } from 'asciify-engine';
94
174
 
95
175
  @Component({
@@ -98,19 +178,15 @@ import {
98
178
  })
99
179
  export class AsciiComponent implements AfterViewInit {
100
180
  @ViewChild('canvas') canvasRef!: ElementRef<HTMLCanvasElement>;
101
- @Input() src!: string;
102
- @Input() artStyle = 'classic';
103
181
 
104
182
  ngAfterViewInit() {
105
183
  const img = new Image();
106
184
  img.crossOrigin = 'anonymous';
107
- img.src = this.src;
185
+ img.src = 'https://picsum.photos/600/400';
108
186
  img.onload = () => {
109
187
  const canvas = this.canvasRef.nativeElement;
110
- const opts = { ...DEFAULT_OPTIONS, ...ART_STYLE_PRESETS[this.artStyle], fontSize: 10 };
111
- const cols = Math.floor(canvas.width / opts.fontSize);
112
- const rows = Math.floor(canvas.height / (opts.fontSize * 1.8));
113
- const frame = imageToAsciiFrame(img, opts, cols, rows);
188
+ const opts = { ...DEFAULT_OPTIONS, fontSize: 10 };
189
+ const { frame } = imageToAsciiFrame(img, opts, canvas.width, canvas.height);
114
190
  renderFrameToCanvas(canvas.getContext('2d')!, frame, opts, canvas.width, canvas.height);
115
191
  };
116
192
  }
@@ -128,10 +204,8 @@ const buffer = await response.arrayBuffer();
128
204
  const canvas = document.getElementById('ascii') as HTMLCanvasElement;
129
205
  const ctx = canvas.getContext('2d')!;
130
206
  const options = { ...DEFAULT_OPTIONS, fontSize: 8 };
131
- const cols = Math.floor(canvas.width / options.fontSize);
132
- const rows = Math.floor(canvas.height / (options.fontSize * 1.8));
133
207
 
134
- const { frames, fps } = await gifToAsciiFrames(buffer, options, cols, rows);
208
+ const { frames, fps } = await gifToAsciiFrames(buffer, options, canvas.width, canvas.height);
135
209
 
136
210
  let i = 0;
137
211
  setInterval(() => {
@@ -150,31 +224,31 @@ video.crossOrigin = 'anonymous';
150
224
  video.src = '/my-video.mp4';
151
225
  await new Promise((r) => (video.onloadeddata = r));
152
226
 
227
+ const canvas = document.getElementById('ascii') as HTMLCanvasElement;
153
228
  const options = { ...DEFAULT_OPTIONS, fontSize: 8 };
154
- const cols = 120;
155
- const rows = 50;
156
- const { frames, fps } = await videoToAsciiFrames(video, options, cols, rows, 10, 6);
229
+
230
+ const { frames, fps } = await videoToAsciiFrames(video, options, canvas.width, canvas.height, 12, 10);
157
231
 
158
232
  let i = 0;
159
233
  setInterval(() => {
160
- const ctx = document.getElementById('ascii').getContext('2d');
161
- renderFrameToCanvas(ctx, frames[i], options, 800, 600);
234
+ renderFrameToCanvas(canvas.getContext('2d')!, frames[i], options, canvas.width, canvas.height);
162
235
  i = (i + 1) % frames.length;
163
236
  }, 1000 / fps);
164
237
  ```
165
238
 
166
239
  ## API
167
240
 
168
- ### Functions
241
+ ### Core Functions
169
242
 
170
243
  | Function | Description |
171
244
  |---|---|
172
- | `imageToAsciiFrame(img, options, cols, rows)` | Convert an image element to a single ASCII frame |
173
- | `renderFrameToCanvas(ctx, frame, options, width, height)` | Render an ASCII frame onto a canvas context |
174
- | `gifToAsciiFrames(buffer, options, cols, rows, onProgress?)` | Convert a GIF `ArrayBuffer` to animated frames |
175
- | `videoToAsciiFrames(video, options, cols, rows, fps, maxDuration, onProgress?)` | Convert a `<video>` to animated frames |
245
+ | `imageToAsciiFrame(source, options, targetWidth?, targetHeight?)` | Convert an image/video/canvas to a single ASCII frame; returns `{ frame, cols, rows }` |
246
+ | `renderFrameToCanvas(ctx, frame, options, width, height, time?, hoverPos?)` | Render an ASCII frame onto a canvas context |
247
+ | `gifToAsciiFrames(buffer, options, targetWidth, targetHeight, onProgress?)` | Convert a GIF `ArrayBuffer` to animated frames; returns `{ frames, cols, rows, fps }` |
248
+ | `videoToAsciiFrames(video, options, targetWidth, targetHeight, fps?, maxDuration?, onProgress?)` | Convert a `<video>` to animated frames; returns `{ frames, cols, rows, fps }` |
176
249
  | `generateEmbedCode(frame, options)` | Generate self-contained HTML embed string |
177
250
  | `generateAnimatedEmbedCode(frames, options, fps)` | Generate animated HTML embed string |
251
+ | `asciiBackground(selector, options)` | Mount an animated ASCII background on an element; returns a cleanup function |
178
252
 
179
253
  ### Art Styles
180
254
 
package/dist/index.cjs CHANGED
@@ -398,7 +398,7 @@ function renderWaveBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
398
398
  lineHeightRatio = 1.4,
399
399
  chars = " .:-=+*#%@",
400
400
  baseColor = null,
401
- accentColor = "#d4ff00",
401
+ accentColor = void 0,
402
402
  accentThreshold = 0.52,
403
403
  mouseInfluence = 0.55,
404
404
  mouseFalloff = 2.8,
@@ -408,6 +408,7 @@ function renderWaveBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
408
408
  breathe = true,
409
409
  lightMode = false
410
410
  } = options;
411
+ const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
411
412
  const charW = fontSize * charAspect;
412
413
  const lineH = fontSize * lineHeightRatio;
413
414
  const cols = Math.ceil(width / charW);
@@ -415,8 +416,8 @@ function renderWaveBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
415
416
  const mx = mousePos.x;
416
417
  const my = mousePos.y;
417
418
  let acR = 212, acG = 255, acB = 0;
418
- if (accentColor) {
419
- const hex = accentColor.replace("#", "");
419
+ {
420
+ const hex = resolvedAccent.replace("#", "");
420
421
  if (hex.length === 6) {
421
422
  acR = parseInt(hex.slice(0, 2), 16);
422
423
  acG = parseInt(hex.slice(2, 4), 16);
@@ -1026,13 +1027,14 @@ function renderRainBackground(ctx, width, height, time, options = {}) {
1026
1027
  const {
1027
1028
  fontSize = 13,
1028
1029
  chars = "0123456789ABCDEF@#$&*+=/<>",
1029
- accentColor = "#d4ff00",
1030
+ accentColor = void 0,
1030
1031
  color,
1031
1032
  speed = 1,
1032
1033
  density = 0.55,
1033
1034
  tailLength = 14,
1034
1035
  lightMode = false
1035
1036
  } = options;
1037
+ const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
1036
1038
  const charW = fontSize * 0.62;
1037
1039
  const lineH = fontSize * 1.4;
1038
1040
  const cols = Math.ceil(width / charW);
@@ -1055,7 +1057,7 @@ function renderRainBackground(ctx, width, height, time, options = {}) {
1055
1057
  }
1056
1058
  }
1057
1059
  let acR = 212, acG = 255, acB = 0;
1058
- const ap = parseColor(accentColor);
1060
+ const ap = parseColor(resolvedAccent);
1059
1061
  if (ap) {
1060
1062
  acR = ap.r;
1061
1063
  acG = ap.g;
@@ -1091,12 +1093,13 @@ function renderStarsBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
1091
1093
  const {
1092
1094
  fontSize = 14,
1093
1095
  chars = " . \xB7 * + \xB0 \u2605",
1094
- accentColor = "#d4ff00",
1096
+ accentColor = void 0,
1095
1097
  color,
1096
1098
  speed = 1,
1097
1099
  count = 180,
1098
1100
  lightMode = false
1099
1101
  } = options;
1102
+ const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
1100
1103
  ctx.clearRect(0, 0, width, height);
1101
1104
  ctx.textBaseline = "middle";
1102
1105
  ctx.textAlign = "center";
@@ -1118,7 +1121,7 @@ function renderStarsBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
1118
1121
  }
1119
1122
  }
1120
1123
  let acR = 212, acG = 255, acB = 0;
1121
- const ap = parseColor(accentColor);
1124
+ const ap = parseColor(resolvedAccent);
1122
1125
  if (ap) {
1123
1126
  acR = ap.r;
1124
1127
  acG = ap.g;
@@ -1151,13 +1154,14 @@ function renderPulseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
1151
1154
  const {
1152
1155
  fontSize = 14,
1153
1156
  chars = ". \xB7 \u25CB \u25CE \u25CF",
1154
- accentColor = "#00ffcc",
1157
+ accentColor = void 0,
1155
1158
  color,
1156
1159
  rings = 5,
1157
1160
  speed = 1,
1158
1161
  sharpness = 4,
1159
1162
  lightMode = false
1160
1163
  } = options;
1164
+ const resolvedAccent = accentColor ?? (lightMode ? "#007a5e" : "#00ffcc");
1161
1165
  ctx.clearRect(0, 0, width, height);
1162
1166
  ctx.textBaseline = "middle";
1163
1167
  ctx.textAlign = "center";
@@ -1179,7 +1183,7 @@ function renderPulseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
1179
1183
  }
1180
1184
  }
1181
1185
  let acR = 0, acG = 255, acB = 204;
1182
- const ap = parseColor(accentColor);
1186
+ const ap = parseColor(resolvedAccent);
1183
1187
  if (ap) {
1184
1188
  acR = ap.r;
1185
1189
  acG = ap.g;
@@ -1224,7 +1228,7 @@ function renderNoiseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
1224
1228
  const {
1225
1229
  fontSize = 14,
1226
1230
  chars = " .\xB7:;=+*#%@\u2591\u2592\u2593",
1227
- accentColor = "#d4ff00",
1231
+ accentColor = void 0,
1228
1232
  color,
1229
1233
  octaves = 4,
1230
1234
  speed = 1,
@@ -1233,6 +1237,7 @@ function renderNoiseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
1233
1237
  mouseWarp = 0.3,
1234
1238
  lightMode = false
1235
1239
  } = options;
1240
+ const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
1236
1241
  const charW = fontSize * 0.62;
1237
1242
  const lineH = fontSize * 1.4;
1238
1243
  const cols = Math.ceil(width / charW);
@@ -1255,7 +1260,7 @@ function renderNoiseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
1255
1260
  }
1256
1261
  }
1257
1262
  let acR = 212, acG = 255, acB = 0;
1258
- const ap = parseColor(accentColor);
1263
+ const ap = parseColor(resolvedAccent);
1259
1264
  if (ap) {
1260
1265
  acR = ap.r;
1261
1266
  acG = ap.g;
@@ -1303,7 +1308,7 @@ function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
1303
1308
  const {
1304
1309
  fontSize = 12,
1305
1310
  chars = "\xB7-=+|/",
1306
- accentColor = "#d4ff00",
1311
+ accentColor = void 0,
1307
1312
  color,
1308
1313
  bands = 3,
1309
1314
  speed = 1,
@@ -1311,6 +1316,7 @@ function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
1311
1316
  glitch = true,
1312
1317
  lightMode = false
1313
1318
  } = options;
1319
+ const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
1314
1320
  const charW = fontSize * 0.62;
1315
1321
  const lineH = fontSize * 1.4;
1316
1322
  const cols = Math.ceil(width / charW);
@@ -1333,7 +1339,7 @@ function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
1333
1339
  }
1334
1340
  }
1335
1341
  let acR = 212, acG = 255, acB = 0;
1336
- const ap = parseColor(accentColor);
1342
+ const ap = parseColor(resolvedAccent);
1337
1343
  if (ap) {
1338
1344
  acR = ap.r;
1339
1345
  acG = ap.g;
@@ -1375,13 +1381,14 @@ function renderAuroraBackground(ctx, width, height, time, mousePos = { x: 0.5, y
1375
1381
  fontSize = 14,
1376
1382
  chars = " \xB7\u2219\u2022:;+=\u2261\u2263#@",
1377
1383
  color,
1378
- accentColor = "#d4ff00",
1384
+ accentColor = void 0,
1379
1385
  speed = 1,
1380
1386
  layers = 5,
1381
1387
  softness = 1.2,
1382
1388
  mouseRipple = 0.2,
1383
1389
  lightMode = false
1384
1390
  } = options;
1391
+ const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
1385
1392
  const charW = fontSize * 0.62;
1386
1393
  const lineH = fontSize * 1.4;
1387
1394
  const cols = Math.ceil(width / charW);
@@ -1404,7 +1411,7 @@ function renderAuroraBackground(ctx, width, height, time, mousePos = { x: 0.5, y
1404
1411
  }
1405
1412
  }
1406
1413
  let acR = 212, acG = 255, acB = 0;
1407
- const ap = parseColor(accentColor);
1414
+ const ap = parseColor(resolvedAccent);
1408
1415
  if (ap) {
1409
1416
  acR = ap.r;
1410
1417
  acG = ap.g;
@@ -1460,12 +1467,13 @@ function renderSilkBackground(ctx, width, height, time, options = {}) {
1460
1467
  const {
1461
1468
  fontSize = 13,
1462
1469
  color,
1463
- accentColor = "#d4ff00",
1470
+ accentColor = void 0,
1464
1471
  speed = 0.4,
1465
1472
  layers = 4,
1466
1473
  turbulence = 0.8,
1467
1474
  lightMode = false
1468
1475
  } = options;
1476
+ const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
1469
1477
  const charW = fontSize * 0.62;
1470
1478
  const lineH = fontSize * 1.4;
1471
1479
  const cols = Math.ceil(width / charW);
@@ -1488,7 +1496,7 @@ function renderSilkBackground(ctx, width, height, time, options = {}) {
1488
1496
  }
1489
1497
  }
1490
1498
  let acR = 212, acG = 255, acB = 0;
1491
- const ap = parseColor(accentColor);
1499
+ const ap = parseColor(resolvedAccent);
1492
1500
  if (ap) {
1493
1501
  acR = ap.r;
1494
1502
  acG = ap.g;
@@ -1535,12 +1543,13 @@ function renderVoidBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
1535
1543
  fontSize = 13,
1536
1544
  chars = " \xB7:;=+*#%@",
1537
1545
  color,
1538
- accentColor = "#d4ff00",
1546
+ accentColor = void 0,
1539
1547
  speed = 1,
1540
1548
  radius = 0.38,
1541
1549
  swirl = 3,
1542
1550
  lightMode = false
1543
1551
  } = options;
1552
+ const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
1544
1553
  const charW = fontSize * 0.62;
1545
1554
  const lineH = fontSize * 1.4;
1546
1555
  const cols = Math.ceil(width / charW);
@@ -1564,7 +1573,7 @@ function renderVoidBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
1564
1573
  }
1565
1574
  }
1566
1575
  let acR = 212, acG = 255, acB = 0;
1567
- const ap = parseColor(accentColor);
1576
+ const ap = parseColor(resolvedAccent);
1568
1577
  if (ap) {
1569
1578
  acR = ap.r;
1570
1579
  acG = ap.g;
@@ -1608,11 +1617,12 @@ function renderMorphBackground(ctx, width, height, time, options = {}) {
1608
1617
  fontSize = 14,
1609
1618
  chars = " \xB7\u2219\u2022:-=+*#",
1610
1619
  color,
1611
- accentColor = "#d4ff00",
1620
+ accentColor = void 0,
1612
1621
  speed = 0.5,
1613
1622
  harmonics = 3,
1614
1623
  lightMode = false
1615
1624
  } = options;
1625
+ const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
1616
1626
  const charW = fontSize * 0.62;
1617
1627
  const lineH = fontSize * 1.4;
1618
1628
  const cols = Math.ceil(width / charW);
@@ -1635,7 +1645,7 @@ function renderMorphBackground(ctx, width, height, time, options = {}) {
1635
1645
  }
1636
1646
  }
1637
1647
  let acR = 212, acG = 255, acB = 0;
1638
- const ap = parseColor(accentColor);
1648
+ const ap = parseColor(resolvedAccent);
1639
1649
  if (ap) {
1640
1650
  acR = ap.r;
1641
1651
  acG = ap.g;