asciify-engine 1.0.20 → 1.0.22

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
@@ -374,7 +374,7 @@ function lerp(a, b, t) {
374
374
  function hash2(ix, iy) {
375
375
  let n = ix * 127 + iy * 311;
376
376
  n = n >> 13 ^ n;
377
- return (n * (n * n * 15731 + 789221) + 1376312589 & 2147483647) / 1073741823 - 1;
377
+ return (n * (n * n * 15731 + 789221) + 1376312589 & 2147483647) / 2147483647;
378
378
  }
379
379
  function vnoise(x, y) {
380
380
  const ix = Math.floor(x), iy = Math.floor(y);