asciify-engine 1.0.35 → 1.0.37

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 (2) hide show
  1. package/README.md +44 -288
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -16,7 +16,7 @@
16
16
  - **Media → ASCII** — images, videos, and animated GIFs
17
17
  - **13 art styles** — Standard, Blocks, Circles, Braille, Katakana, Dense, and more
18
18
  - **4 color modes** — Grayscale, Full Color, Matrix, Accent
19
- - **10 animated backgrounds** — Wave, Rain, Stars, Pulse, Noise, Grid, Aurora, Silk, Void, Morph
19
+ - **14 animated backgrounds** — Wave, Rain, Stars, Pulse, Noise, Grid, Aurora, Silk, Void, Morph, Fire, DNA, Terrain, Circuit
20
20
  - **Interactive hover effects** — Spotlight, Flashlight, Magnifier, Force Field, Neon, Fire, Ice, Gravity, Shatter, Ghost
21
21
  - **Light & dark mode** — via `colorScheme: 'auto'` or explicit `light` / `dark`
22
22
  - **Embed generation** — self-contained HTML output (static or animated)
@@ -28,8 +28,6 @@
28
28
  npm install asciify-engine
29
29
  ```
30
30
 
31
- > **90% of use cases need only two functions:** `asciify()` to convert images/video/GIFs to ASCII art, and `asciiBackground()` for animated backgrounds. Start there.
32
-
33
31
  ## Quick Start
34
32
 
35
33
  ### Image → ASCII
@@ -58,6 +56,25 @@ await asciify('photo.jpg', canvas, {
58
56
  });
59
57
  ```
60
58
 
59
+ > **Canvas sizing:** Set `width` and `height` as HTML attributes on the `<canvas>` element — these control the pixel grid. CSS sizing alone (`style="width:100%"`) won't work (the canvas will be 0×0). Use `canvas.width = el.clientWidth` to fill a container.
60
+
61
+ ### Webcam → ASCII
62
+
63
+ ```ts
64
+ import { asciifyWebcam } from 'asciify-engine';
65
+
66
+ const canvas = document.querySelector('canvas')!;
67
+
68
+ // Requests camera access, starts a live rAF loop, returns stop()
69
+ const stop = await asciifyWebcam(canvas, {
70
+ artStyle: 'terminal',
71
+ mirror: true, // horizontal flip (selfie mode)
72
+ });
73
+
74
+ // Release camera + cancel loop
75
+ stop();
76
+ ```
77
+
61
78
  ### GIF Animation
62
79
 
63
80
  ```ts
@@ -174,8 +191,8 @@ asciiBackground('#hero', { type: 'aurora', colorScheme: 'auto' });
174
191
  asciiBackground('#hero', { type: 'wave', colorScheme: 'light' });
175
192
 
176
193
  // Stop / clean up
177
- const stop = asciiBackground('#hero', { type: 'stars' });
178
- stop();
194
+ const { destroy } = asciiBackground('#hero', { type: 'stars' });
195
+ destroy();
179
196
  ```
180
197
 
181
198
  ### Background Types
@@ -192,6 +209,10 @@ stop();
192
209
  | `silk` | Silky fluid swirls following the cursor |
193
210
  | `void` | Gravitational singularity — characters spiral inward toward cursor |
194
211
  | `morph` | Characters morph between shapes driven by noise |
212
+ | `fire` | Upward-drifting flame columns with heat-gradient character mapping |
213
+ | `dna` | Rotating double-helix strands with base-pair characters |
214
+ | `terrain` | Procedural heightmap landscape with parallax depth layers |
215
+ | `circuit` | PCB trace network that pulses with traveling electric signals |
195
216
 
196
217
  ### `asciiBackground` Options
197
218
 
@@ -326,14 +347,15 @@ animId = requestAnimationFrame(tick);
326
347
 
327
348
  | Function | Returns | Description |
328
349
  |---|---|---|
329
- | `asciify(source, canvas, opts?)` | `Promise<void>` | Convert an image/video/canvas (or URL) to ASCII and render handles loading automatically |
350
+ | `asciify(source, canvas, opts?)` | `Promise<void>` | Render a **single** ASCII frame from an image (or URL). For animated loops use `asciifyGif` / `asciifyVideo` |
330
351
  | `asciifyGif(source, canvas, opts?)` | `Promise<() => void>` | Fetch a GIF, convert, and start an animation loop — returns `stop()` |
331
352
  | `asciifyVideo(source, canvas, opts?)` | `Promise<() => void>` | Convert a video and start an animation loop — returns `stop()` |
332
353
  | `imageToAsciiFrame(source, options, w?, h?)` | `{ frame, cols, rows }` | Convert an image, video frame, or canvas to a raw ASCII frame |
333
354
  | `renderFrameToCanvas(ctx, frame, options, w, h, time?, hoverPos?)` | `void` | Render a raw ASCII frame to a 2D canvas context |
334
355
  | `gifToAsciiFrames(buffer, options, w, h, onProgress?)` | `{ frames, cols, rows, fps }` | Parse an animated GIF `ArrayBuffer` into ASCII frames |
335
356
  | `videoToAsciiFrames(video, options, w, h, fps?, maxDuration?, onProgress?)` | `{ frames, cols, rows, fps }` | Extract and convert video frames to ASCII |
336
- | `asciiBackground(selector, options)` | `() => void` | Mount a live animated ASCII background; returns a cleanup function |
357
+ | `asciifyWebcam(canvas, opts?)` | `Promise<() => void>` | Start a live webcam ASCII loop; returns `stop()` to cancel and release the camera |
358
+ | `asciiBackground(target, options)` | `{ destroy: () => void }` | Mount a live animated ASCII background; call `destroy()` to stop and remove |
337
359
  | `generateEmbedCode(frame, options)` | `string` | Self-contained static HTML embed |
338
360
  | `generateAnimatedEmbedCode(frames, options, fps)` | `string` | Self-contained animated HTML embed |
339
361
 
@@ -366,289 +388,23 @@ Used by `asciify()`, `asciifyGif()`, and `asciifyVideo()`:
366
388
  | `ditherStrength` | `number` | `0` | Floyd-Steinberg dither intensity (0–1) |
367
389
  | `dotSizeRatio` | `number` | `0.8` | Dot size when `renderMode === 'dots'` (fraction of cell) |
368
390
 
369
- ### Background Options
370
-
371
- | Option | Type | Default | Description |
372
- |---|---|---|---|
373
- | `type` | `string` | `'wave'` | Background renderer |
374
- | `colorScheme` | `'auto' \| 'light' \| 'dark'` | `'dark'` | Theme; `'auto'` follows OS preference |
375
- | `fontSize` | `number` | `13` | Character size |
376
- | `speed` | `number` | `1` | Animation speed multiplier |
377
- | `density` | `number` | `0.55` | Fraction of cells active (0–1) |
378
- | `accentColor` | `string` | varies | Highlight / head colour |
379
-
380
- ## License
391
+ ### Art Styles (`artStyle`)
381
392
 
382
- MIT © [asciify.org](https://asciify.org)
383
-
384
- ---
385
-
386
- <p align="left">
387
- <a href="https://www.buymeacoffee.com/asciify">☕ Buy me a coffee — if this saved you time, I'd appreciate it!</a>
388
- </p>
389
-
390
-
391
- **[▶ Live Playground](https://asciify.org) · [npm](https://www.npmjs.com/package/asciify-engine) · [Support the project ☕](https://www.buymeacoffee.com/asciify)**
392
-
393
- ## Features
394
-
395
- - **Media → ASCII** — images, videos, and animated GIFs
396
- - **13 art styles** — Standard, Blocks, Circles, Braille, Katakana, Dense, and more
397
- - **4 color modes** — Grayscale, Full Color, Matrix, Accent
398
- - **10 animated backgrounds** — Wave, Rain, Stars, Pulse, Noise, Grid, Aurora, Silk, Void, Morph
399
- - **Interactive hover effects** — Spotlight, Flashlight, Magnifier, Force Field, Neon, Fire, Ice, Gravity, Shatter, Ghost
400
- - **Light & dark mode** — via `colorScheme: 'auto'` or explicit `light` / `dark`
401
- - **Embed generation** — self-contained HTML output (static or animated)
402
- - **Zero dependencies** — works with React, Vue, Angular, Svelte, Next.js, or vanilla JS
403
-
404
- ## Install
405
-
406
- ```bash
407
- npm install asciify-engine
408
- ```
409
-
410
- ## Animated Backgrounds
411
-
412
- Drop a live ASCII animation into any element with one call.
413
-
414
- ```ts
415
- import { asciiBackground } from 'asciify-engine';
416
-
417
- // Attach to any selector — animates the element's background
418
- asciiBackground('#hero', { type: 'rain' });
419
-
420
- // Follows OS dark/light mode automatically
421
- asciiBackground('#hero', { type: 'aurora', colorScheme: 'auto' });
422
-
423
- // Force light mode (dark characters on light background)
424
- asciiBackground('#hero', { type: 'wave', colorScheme: 'light' });
425
-
426
- // Stop / clean up
427
- const stop = asciiBackground('#hero', { type: 'stars' });
428
- stop();
429
- ```
430
-
431
- ### Background Types
432
-
433
- | Type | Description |
434
- |---|---|
435
- | `wave` | Flowing sine-wave field with noise turbulence |
436
- | `rain` | Matrix-style vertical column rain with glowing head and fading tail |
437
- | `stars` | Parallax star field that reacts to cursor position |
438
- | `pulse` | Concentric ripple bursts that emanate from the cursor |
439
- | `noise` | Smooth value-noise field with organic flow |
440
- | `grid` | Geometric grid that warps and glows at cursor proximity |
441
- | `aurora` | Sweeping borealis-style colour bands |
442
- | `silk` | Silky fluid swirls following the cursor |
443
- | `void` | Gravitational singularity — characters spiral inward toward cursor |
444
- | `morph` | Characters morph between shapes driven by noise |
445
-
446
- ### `asciiBackground` Options
447
-
448
- | Option | Type | Default | Description |
449
- |---|---|---|---|
450
- | `type` | `string` | `'wave'` | Which background renderer to use |
451
- | `colorScheme` | `'auto' \| 'light' \| 'dark'` | `'dark'` | `'auto'` follows OS theme live; `'light'` = dark chars on light bg |
452
- | `fontSize` | `number` | `13` | Character size in px |
453
- | `accentColor` | `string` | varies | Head/highlight colour (CSS colour string) |
454
- | `color` | `string` | — | Override body character colour |
455
- | `speed` | `number` | `1` | Global animation speed multiplier |
456
- | `density` | `number` | `0.55` | Fraction of cells active (0–1) |
457
- | `lightMode` | `boolean` | `false` | Dark characters on light background |
458
-
459
- Each background type also accepts its own specific options — see the individual type exports (e.g. `RainBackgroundOptions`, `WaveBackgroundOptions`, etc.) for the full list.
460
-
461
- ### Low-level background renderers
462
-
463
- All renderers are also exported individually for direct canvas use:
464
-
465
- ```ts
466
- import {
467
- renderRainBackground,
468
- renderWaveBackground,
469
- renderStarsBackground,
470
- renderPulseBackground,
471
- renderNoiseBackground,
472
- renderGridBackground,
473
- renderAuroraBackground,
474
- renderSilkBackground,
475
- renderVoidBackground,
476
- renderMorphBackground,
477
- } from 'asciify-engine';
478
-
479
- // Example: drive the rain renderer yourself
480
- const canvas = document.getElementById('canvas') as HTMLCanvasElement;
481
- const ctx = canvas.getContext('2d')!;
482
- let t = 0;
483
- function tick() {
484
- renderRainBackground(ctx, canvas.width, canvas.height, t, {
485
- speed: 1.2,
486
- density: 0.6,
487
- accentColor: '#00ffcc',
488
- lightMode: false,
489
- });
490
- t += 0.016;
491
- requestAnimationFrame(tick);
492
- }
493
- tick();
494
- ```
495
-
496
- ## Quick Start
497
-
498
- ### Vanilla JS
499
-
500
- ```html
501
- <canvas id="ascii" width="800" height="600"></canvas>
502
- <script type="module">
503
- import {
504
- imageToAsciiFrame,
505
- renderFrameToCanvas,
506
- DEFAULT_OPTIONS,
507
- } from 'asciify-engine';
508
-
509
- const img = new Image();
510
- img.crossOrigin = 'anonymous';
511
- img.src = 'https://picsum.photos/600/400';
512
- img.onload = () => {
513
- const canvas = document.getElementById('ascii');
514
- const options = { ...DEFAULT_OPTIONS, fontSize: 10 };
515
- const { frame } = imageToAsciiFrame(img, options, canvas.width, canvas.height);
516
- renderFrameToCanvas(canvas.getContext('2d'), frame, options, canvas.width, canvas.height);
517
- };
518
- </script>
519
- ```
520
-
521
- ### React
522
-
523
- ```tsx
524
- import { useEffect, useRef } from 'react';
525
- import {
526
- imageToAsciiFrame,
527
- renderFrameToCanvas,
528
- DEFAULT_OPTIONS,
529
- } from 'asciify-engine';
530
-
531
- export function AsciiImage({ src }: { src: string }) {
532
- const ref = useRef<HTMLCanvasElement>(null);
533
-
534
- useEffect(() => {
535
- const img = new Image();
536
- img.crossOrigin = 'anonymous';
537
- img.src = src;
538
- img.onload = () => {
539
- const canvas = ref.current!;
540
- const opts = { ...DEFAULT_OPTIONS, fontSize: 10 };
541
- const { frame } = imageToAsciiFrame(img, opts, canvas.width, canvas.height);
542
- renderFrameToCanvas(canvas.getContext('2d')!, frame, opts, canvas.width, canvas.height);
543
- };
544
- }, [src]);
545
-
546
- return <canvas ref={ref} width={800} height={600} />;
547
- }
548
- ```
549
-
550
- ### Angular
551
-
552
- ```typescript
553
- import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
554
- import {
555
- imageToAsciiFrame,
556
- renderFrameToCanvas,
557
- DEFAULT_OPTIONS,
558
- } from 'asciify-engine';
559
-
560
- @Component({
561
- selector: 'app-ascii',
562
- template: `<canvas #canvas [width]="800" [height]="600"></canvas>`,
563
- })
564
- export class AsciiComponent implements AfterViewInit {
565
- @ViewChild('canvas') canvasRef!: ElementRef<HTMLCanvasElement>;
566
-
567
- ngAfterViewInit() {
568
- const img = new Image();
569
- img.crossOrigin = 'anonymous';
570
- img.src = 'https://picsum.photos/600/400';
571
- img.onload = () => {
572
- const canvas = this.canvasRef.nativeElement;
573
- const opts = { ...DEFAULT_OPTIONS, fontSize: 10 };
574
- const { frame } = imageToAsciiFrame(img, opts, canvas.width, canvas.height);
575
- renderFrameToCanvas(canvas.getContext('2d')!, frame, opts, canvas.width, canvas.height);
576
- };
577
- }
578
- }
579
- ```
580
-
581
- ### GIF Animation
582
-
583
- ```ts
584
- import { gifToAsciiFrames, renderFrameToCanvas, DEFAULT_OPTIONS } from 'asciify-engine';
585
-
586
- const response = await fetch('https://media.giphy.com/media/ENagATV1Gr9eg/giphy.gif');
587
- const buffer = await response.arrayBuffer();
588
-
589
- const canvas = document.getElementById('ascii') as HTMLCanvasElement;
590
- const ctx = canvas.getContext('2d')!;
591
- const options = { ...DEFAULT_OPTIONS, fontSize: 8 };
592
-
593
- const { frames, fps } = await gifToAsciiFrames(buffer, options, canvas.width, canvas.height);
594
-
595
- let i = 0;
596
- setInterval(() => {
597
- renderFrameToCanvas(ctx, frames[i], options, canvas.width, canvas.height);
598
- i = (i + 1) % frames.length;
599
- }, 1000 / fps);
600
- ```
601
-
602
- ### Video
603
-
604
- ```ts
605
- import { videoToAsciiFrames, renderFrameToCanvas, DEFAULT_OPTIONS } from 'asciify-engine';
606
-
607
- const video = document.createElement('video');
608
- video.crossOrigin = 'anonymous';
609
- video.src = '/my-video.mp4';
610
- await new Promise((r) => (video.onloadeddata = r));
611
-
612
- const canvas = document.getElementById('ascii') as HTMLCanvasElement;
613
- const options = { ...DEFAULT_OPTIONS, fontSize: 8 };
614
-
615
- const { frames, fps } = await videoToAsciiFrames(video, options, canvas.width, canvas.height, 12, 10);
616
-
617
- let i = 0;
618
- setInterval(() => {
619
- renderFrameToCanvas(canvas.getContext('2d')!, frames[i], options, canvas.width, canvas.height);
620
- i = (i + 1) % frames.length;
621
- }, 1000 / fps);
622
- ```
623
-
624
- ## API Reference
625
-
626
- ### Core Functions
627
-
628
- | Function | Returns | Description |
393
+ | Value | Color mode | Description |
629
394
  |---|---|---|
630
- | `imageToAsciiFrame(source, options, w?, h?)` | `{ frame, cols, rows }` | Convert an image, video frame, or canvas to ASCII |
631
- | `renderFrameToCanvas(ctx, frame, options, w, h, time?, hoverPos?)` | `void` | Render an ASCII frame to a 2D canvas context |
632
- | `gifToAsciiFrames(buffer, options, w, h, onProgress?)` | `{ frames, cols, rows, fps }` | Parse an animated GIF `ArrayBuffer` into ASCII frames |
633
- | `videoToAsciiFrames(video, options, w, h, fps?, maxDuration?, onProgress?)` | `{ frames, cols, rows, fps }` | Extract and convert video frames to ASCII |
634
- | `asciiBackground(selector, options)` | `() => void` | Mount a live animated ASCII background; returns a cleanup function |
635
- | `generateEmbedCode(frame, options)` | `string` | Self-contained static HTML embed |
636
- | `generateAnimatedEmbedCode(frames, options, fps)` | `string` | Self-contained animated HTML embed |
637
-
638
- ### Key Options (`AsciiOptions`)
639
-
640
- | Option | Type | Default | Description |
641
- |---|---|---|---|
642
- | `fontSize` | `number` | `10` | Character cell size in px |
643
- | `colorMode` | `'grayscale' \| 'fullcolor' \| 'matrix' \| 'accent'` | `'grayscale'` | Color output mode |
644
- | `renderMode` | `'ascii' \| 'dots'` | `'ascii'` | Render as characters or dot particles |
645
- | `charset` | `string` | standard ramp | Custom character density ramp |
646
- | `brightness` | `number` | `0` | Brightness adjustment (-1 → 1) |
647
- | `contrast` | `number` | `1` | Contrast multiplier |
648
- | `invert` | `boolean` | `false` | Invert luminance mapping |
649
- | `hoverEffect` | `string` | `'none'` | Interactive effect name |
650
- | `hoverStrength` | `number` | `0.8` | Effect intensity |
651
- | `hoverRadius` | `number` | `0.3` | Effect radius (0–1 relative to canvas) |
395
+ | `classic` | Grayscale | Standard density ramp clean, universally readable |
396
+ | `art` | Full color | 70-char dense ramp for maximum tonal detail |
397
+ | `particles` | Full color | Dot circles (`renderMode: 'dots'`) great for photos |
398
+ | `letters` | Full color | Alphabet characters with pixel-accurate color |
399
+ | `terminal` | Matrix | Classic charset with green phosphor / Matrix look |
400
+ | `claudeCode` | Accent | Box-drawing chars with accent color — technical/hacker aesthetic |
401
+ | `braille` | Full color | 256-char braille block ultra-dense, printed feel |
402
+ | `katakana` | Matrix | Half-width katakana — anime / cyberpunk aesthetic |
403
+ | `box` | Grayscale | Filled block elements `▪◾◼■█` |
404
+ | `lines` | Grayscale | Dash/em-dash ramp — minimalist typographic look |
405
+ | `circles` | Accent | Concentric circle chars with accent highlight |
406
+ | `musical` | Accent | Music notation ♩♪♫♬♭♮♯ — playful, low density |
407
+ | `emoji` | Full color | Block emoji mosaic best at larger `fontSize` |
652
408
 
653
409
  ### Background Options
654
410
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "asciify-engine",
3
- "version": "1.0.35",
3
+ "version": "1.0.37",
4
4
  "description": "Framework-agnostic ASCII art engine. Convert images, videos, and GIFs into ASCII art rendered on canvas.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",