asciify-engine 4.23.0 → 4.25.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.
- package/CHANGELOG.md +315 -0
- package/README.md +71 -711
- package/dist/core.cjs +1 -1
- package/dist/core.js +1 -1
- package/dist/field.cjs +1 -1
- package/dist/field.d.cts +4 -1
- package/dist/field.d.ts +4 -1
- package/dist/field.js +1 -1
- package/dist/hover.cjs +1 -1
- package/dist/hover.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/kit/chunk-2D2SG6FW.js +1 -0
- package/dist/kit/chunk-2JQ4MRIP.js +1 -0
- package/dist/kit/chunk-2KFBIOVS.js +1 -0
- package/dist/kit/chunk-2TQW2PIG.js +1 -0
- package/dist/kit/chunk-3F6FTBMN.js +1 -0
- package/dist/kit/chunk-4SHQA3CO.js +1 -0
- package/dist/kit/chunk-4XT5IKM3.js +1 -0
- package/dist/kit/chunk-6L2GAV5C.js +1 -0
- package/dist/kit/chunk-6XNIZ52V.js +1 -0
- package/dist/kit/chunk-6YVZZ3DO.js +1 -0
- package/dist/kit/chunk-A6EDU3UT.js +1 -0
- package/dist/kit/chunk-A73S2BCE.js +1 -0
- package/dist/kit/chunk-ANLBW5RI.js +1 -0
- package/dist/kit/chunk-B6GZ645M.js +1 -0
- package/dist/kit/chunk-BBOSHBZK.js +1 -0
- package/dist/kit/chunk-BJRPY7UB.js +1 -0
- package/dist/kit/chunk-CA5J25SV.js +1 -0
- package/dist/kit/chunk-CR7QS3B3.js +1 -0
- package/dist/kit/chunk-D4KCZSIP.js +1 -0
- package/dist/kit/chunk-EHXSXMBE.js +1 -0
- package/dist/kit/chunk-EHYFSX5K.js +1 -0
- package/dist/kit/chunk-EJ3W4FN6.js +1 -0
- package/dist/kit/chunk-EK4GIXIO.js +1 -0
- package/dist/kit/chunk-EPVZIGEZ.js +1 -0
- package/dist/kit/chunk-EWPT56TR.js +1 -0
- package/dist/kit/chunk-FIWIUXNV.js +1 -0
- package/dist/kit/chunk-G4BQYQJ6.js +1 -0
- package/dist/kit/chunk-GCIZKMJL.js +1 -0
- package/dist/kit/chunk-H4DBJULW.js +1 -0
- package/dist/kit/chunk-HMW5NRUV.js +1 -0
- package/dist/kit/chunk-IUX46UDR.js +1 -0
- package/dist/kit/chunk-JAM46FF6.js +1 -0
- package/dist/kit/chunk-JRSCTMFM.js +1 -0
- package/dist/kit/chunk-KBXVIAWS.js +1 -0
- package/dist/kit/chunk-KJJJORN6.js +1 -0
- package/dist/kit/chunk-KS3TL3S2.js +1 -0
- package/dist/kit/chunk-KTYCGGRO.js +1 -0
- package/dist/kit/chunk-L3P7ZDSK.js +1 -0
- package/dist/kit/chunk-LUOIH2EM.js +1 -0
- package/dist/kit/chunk-MIA6TUQC.js +1 -0
- package/dist/kit/chunk-MRDEO5HZ.js +1 -0
- package/dist/kit/chunk-NPJ77LDC.js +1 -0
- package/dist/kit/chunk-ODMQERGZ.js +1 -0
- package/dist/kit/chunk-ONVONNUH.js +1 -0
- package/dist/kit/chunk-OVCIHOOS.js +1 -0
- package/dist/kit/chunk-P6S6HSN3.js +1 -0
- package/dist/kit/chunk-PCGRRX5P.js +1 -0
- package/dist/kit/chunk-PDNKLLBI.js +1 -0
- package/dist/kit/chunk-PFNFW4ZI.js +1 -0
- package/dist/kit/chunk-PFQBEWCF.js +1 -0
- package/dist/kit/chunk-PND73YEV.js +1 -0
- package/dist/kit/chunk-PZAQLUAS.js +1 -0
- package/dist/kit/chunk-QKZYCROD.js +1 -0
- package/dist/kit/chunk-QTVMD62E.js +1 -0
- package/dist/kit/chunk-QZHZWO7N.js +1 -0
- package/dist/kit/chunk-SAQJAH2I.js +1 -0
- package/dist/kit/chunk-SE6MQQLB.js +1 -0
- package/dist/kit/chunk-SQGJ263V.js +1 -0
- package/dist/kit/chunk-SVXRMHUJ.js +1 -0
- package/dist/kit/chunk-UO4U32P7.js +1 -0
- package/dist/kit/chunk-V6GEQ5Y5.js +1 -0
- package/dist/kit/chunk-VFV7HZ3G.js +1 -0
- package/dist/kit/chunk-WT3XOWAI.js +1 -0
- package/dist/kit/chunk-XDNCXCR7.js +1 -0
- package/dist/kit/chunk-XPXMCXYI.js +1 -0
- package/dist/kit/chunk-XVB3B3FV.js +1 -0
- package/dist/kit/chunk-XXPDXJMQ.js +1 -0
- package/dist/kit/chunk-XYLLL2PQ.js +1 -0
- package/dist/kit/chunk-YBBYCUFP.js +1 -0
- package/dist/kit/chunk-YDP6566E.js +1 -0
- package/dist/kit/chunk-YL2AXWRA.js +1 -0
- package/dist/kit/chunk-ZCHGV2CV.js +1 -0
- package/dist/kit/chunk-ZYYLWSPY.js +1 -0
- package/dist/kit.cjs +1 -1
- package/dist/kit.d.cts +3 -1
- package/dist/kit.d.ts +3 -1
- package/dist/kit.js +1 -1
- package/dist/studio.cjs +1 -1
- package/dist/studio.js +1 -1
- package/package.json +3 -2
- package/skills/asciify-engine/SKILL.md +3 -3
- package/skills/asciify-engine/references/applying-asciify.md +1 -1
- package/skills/asciify-engine/references/kit.md +2 -2
- package/skills/asciify-engine/references/performance.md +33 -0
package/README.md
CHANGED
|
@@ -1,751 +1,111 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://asciify.org">
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/ayangabryl/asciify-engine/main/.github/assets/logo-dark.png">
|
|
5
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/ayangabryl/asciify-engine/main/.github/assets/logo-light.png">
|
|
6
|
+
<img alt="Asciify" src="https://raw.githubusercontent.com/ayangabryl/asciify-engine/main/.github/assets/logo-light.png" width="220">
|
|
7
|
+
</picture>
|
|
8
|
+
</a>
|
|
8
9
|
</p>
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
### Media first, optional backgrounds
|
|
13
|
-
|
|
14
|
-
Use `asciify-engine/core` for image, video, GIF, webcam, text, and interactive rendering. It excludes the procedural background generators. Root imports expose the same media API. Procedural scenes are downloadable templates at https://asciify.org/backgrounds.
|
|
15
|
-
|
|
16
|
-
```ts
|
|
17
|
-
import { asciifyVideo } from 'asciify-engine/core';
|
|
18
|
-
|
|
19
|
-
const stop = await asciifyVideo('/film.mp4', canvas, {
|
|
20
|
-
fitTo: canvas.parentElement,
|
|
21
|
-
objectFit: 'cover',
|
|
22
|
-
maxRenderDimension: 960,
|
|
23
|
-
fontSize: 10,
|
|
24
|
-
fps: 30,
|
|
25
|
-
options: {
|
|
26
|
-
colorMode: 'accent', accentColor: '#a9b39a',
|
|
27
|
-
hoverEffect: 'trail', hoverStrength: 0.65,
|
|
28
|
-
hoverRadius: 0.18, hoverColor: '#d4ff00',
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
// On unmount / when replacing the source:
|
|
32
|
-
stop();
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
The trail follows elapsed time, fades after the pointer leaves, and changes character density through a bounded cursor wake. Image hover redraws a cached region; live video samples decoded frames while pointer motion can render between source frames. The standard image/GIF/live-video loops pause rendering outside the viewport and honor reduced motion. Small fonts keep real glyphs, and dense grids no longer silently disable hover.
|
|
36
|
-
|
|
37
|
-
Use your own CSS and source media for atmosphere; see [the copyable media background example](examples/media-background.ts). A background generator is optional. GIF decoding loads only when a GIF conversion API is called. Source maps remain available in local builds and are excluded from the npm archive.
|
|
38
|
-
|
|
39
|
-
`waveField` now animates the supplied media rather than replacing it with a generated scene. For the previous procedural scene, download the Wave template from https://asciify.org/backgrounds.
|
|
40
|
-
|
|
41
|
-
Prepared `AsciiFrame` objects are treated as immutable for drawing caches. Replace the frame object after editing cells, or call `clearAsciifyCaches()`. Always call the returned cleanup function, including when an async mount resolves after a component has unmounted.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
**[▶ Live Playground](https://asciify.org) · [npm](https://www.npmjs.com/package/asciify-engine)**
|
|
45
|
-
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
## Overview
|
|
11
|
+
<p align="center">ASCII components, backgrounds and media for the web, drawn in real characters on the GPU.</p>
|
|
49
12
|
|
|
50
|
-
|
|
13
|
+
<p align="center">
|
|
14
|
+
<a href="https://www.npmjs.com/package/asciify-engine"><img alt="npm version" src="https://img.shields.io/npm/v/asciify-engine?style=flat-square&labelColor=171716&color=3d3d3a"></a>
|
|
15
|
+
<a href="https://bundlejs.com/?q=asciify-engine"><img alt="Bundle size" src="https://img.shields.io/bundlejs/size/asciify-engine?style=flat-square&labelColor=171716&color=3d3d3a&label=gzip"></a>
|
|
16
|
+
<a href="https://www.npmjs.com/package/asciify-engine"><img alt="Downloads" src="https://img.shields.io/npm/dm/asciify-engine?style=flat-square&labelColor=171716&color=3d3d3a"></a>
|
|
17
|
+
<a href="https://github.com/ayangabryl/asciify-engine/blob/main/LICENSE"><img alt="MIT license" src="https://img.shields.io/npm/l/asciify-engine?style=flat-square&labelColor=171716&color=3d3d3a"></a>
|
|
18
|
+
</p>
|
|
51
19
|
|
|
52
|
-
|
|
53
|
-
|
|
20
|
+
<p align="center">
|
|
21
|
+
<a href="https://asciify.org">Website</a> ·
|
|
22
|
+
<a href="https://asciify.org/docs">Docs</a> ·
|
|
23
|
+
<a href="https://asciify.org/components">Components</a> ·
|
|
24
|
+
<a href="https://asciify.org/editor">Studio</a>
|
|
25
|
+
</p>
|
|
54
26
|
|
|
55
|
-
|
|
27
|
+
<p align="center">
|
|
28
|
+
<a href="https://asciify.org">
|
|
29
|
+
<picture>
|
|
30
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/ayangabryl/asciify-engine/main/.github/assets/hero-dark.webp">
|
|
31
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/ayangabryl/asciify-engine/main/.github/assets/hero-light.webp">
|
|
32
|
+
<img alt="The asciify.org home page: an ASCII field behind the headline and three live kit components" src="https://raw.githubusercontent.com/ayangabryl/asciify-engine/main/.github/assets/hero-light.webp" width="100%">
|
|
33
|
+
</picture>
|
|
34
|
+
</a>
|
|
35
|
+
</p>
|
|
56
36
|
|
|
57
|
-
|
|
37
|
+
Asciify is a framework-agnostic engine for ASCII on the web. Use it for interface components (loading buttons, toasts, tabs, AI states, charts, page transitions), for backgrounds behind your content, and for turning images, GIFs, video and text into characters. Components take their ink from your CSS tokens in light and dark, follow their element's size and radius, honour reduced motion and pause off screen.
|
|
58
38
|
|
|
59
|
-
##
|
|
39
|
+
## Install
|
|
60
40
|
|
|
61
41
|
```bash
|
|
62
|
-
npm
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Works with any modern bundler (Vite, webpack, esbuild, Rollup) and any framework — React, Vue, Svelte, Angular, Next.js, or vanilla JS.
|
|
66
|
-
|
|
67
|
-
### Website hovers, included in npm
|
|
68
|
-
|
|
69
|
-
Since **1.2.0**, import the enhanced interactions through `asciify-engine/hover`. No extra download is needed. This optional entry leaves root and `/core` browser bundles unchanged.
|
|
70
|
-
|
|
71
|
-
```js
|
|
72
|
-
import { asciify } from 'asciify-engine/core';
|
|
73
|
-
import { mountHover } from 'asciify-engine/hover';
|
|
74
|
-
|
|
75
|
-
// <div id="art" style="position:relative;width:fit-content">
|
|
76
|
-
// <canvas id="ascii" style="display:block"></canvas>
|
|
77
|
-
// </div>
|
|
78
|
-
const host = document.querySelector('#art');
|
|
79
|
-
const canvas = document.querySelector('#ascii');
|
|
80
|
-
const charset = ' .:-=+*#%@';
|
|
81
|
-
const stop = await asciify('/image.jpg', canvas, {
|
|
82
|
-
fontSize: 7,
|
|
83
|
-
options: { charset, charAspect: 0.58, hoverStrength: 0, animationStyle: 'none' },
|
|
84
|
-
});
|
|
85
|
-
const hover = mountHover(host, canvas, {
|
|
86
|
-
effect: 'trail', strength: 0.55, radius: 0.2,
|
|
87
|
-
fontSize: 7, charAspect: 0.58, charset,
|
|
88
|
-
});
|
|
89
|
-
// Switch: hover.update({ effect: 'water' });
|
|
90
|
-
// Redraw a still: hover.invalidate();
|
|
91
|
-
// On unmount: hover.destroy(); stop();
|
|
42
|
+
npm i asciify-engine
|
|
92
43
|
```
|
|
93
44
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
For video/GIF, set `animated: true, fps: 24` and coordinate `paused` with playback. For a still, optional `motion: 'caustics' | 'current' | 'reform'` (or the other IDs in `STUDIO_MOTIONS`) adds living-image motion; `fineDither: true` enables stationary grain. `mountStudioHover` remains an alias for the existing standalone integration. The module observes reduced motion, sleeps when idle/offscreen, and uses Canvas 2D when WebGL is unavailable (simpler spatial refraction). Call `destroy()` on unmount; use `hover.canvas` for overlay layering or snapshots.
|
|
97
|
-
|
|
98
|
-
See the [complete integration and lifecycle guide](skills/asciify-engine/references/studio-effects.md). A [standalone ES module](https://asciify.org/background-sources/studio-hover.js) remains available for projects without a bundler.
|
|
99
|
-
|
|
100
|
-
## Agent Skill
|
|
101
|
-
|
|
102
|
-
This repository ships a standards-friendly Agent Skill at `skills/asciify-engine`. Install it with the current skills CLI:
|
|
45
|
+
Building with a coding agent? Add the skill, then describe what you want:
|
|
103
46
|
|
|
104
47
|
```bash
|
|
105
|
-
npx skills add
|
|
48
|
+
npx skills add ayangabryl/asciify-engine
|
|
106
49
|
```
|
|
107
50
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
The current hosted skill is available at [asciify.org/skill](https://asciify.org/skill), including a [layered HTML/ASCII hero example](https://asciify.org/skill/references/layered-hero.md). The hosted copy can receive documentation updates between npm releases.
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
## Converting Media to ASCII
|
|
115
|
-
|
|
116
|
-
### Images
|
|
117
|
-
|
|
118
|
-
`imageToAsciiFrame` accepts any `HTMLImageElement`, `HTMLVideoElement`, or `HTMLCanvasElement` and returns a single ASCII frame.
|
|
119
|
-
|
|
120
|
-
```ts
|
|
121
|
-
import { imageToAsciiFrame, renderFrameToCanvas, DEFAULT_OPTIONS } from 'asciify-engine';
|
|
122
|
-
|
|
123
|
-
const img = new Image();
|
|
124
|
-
img.crossOrigin = 'anonymous';
|
|
125
|
-
img.src = 'photo.jpg';
|
|
126
|
-
|
|
127
|
-
img.onload = () => {
|
|
128
|
-
const canvas = document.getElementById('ascii') as HTMLCanvasElement;
|
|
129
|
-
const ctx = canvas.getContext('2d')!;
|
|
130
|
-
const opts = { ...DEFAULT_OPTIONS, fontSize: 10, colorMode: 'fullcolor' as const };
|
|
131
|
-
|
|
132
|
-
const { frame } = imageToAsciiFrame(img, opts, canvas.width, canvas.height);
|
|
133
|
-
renderFrameToCanvas(ctx, frame, opts, canvas.width, canvas.height);
|
|
134
|
-
};
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### Animated GIFs
|
|
138
|
-
|
|
139
|
-
`gifToAsciiFrames` parses a GIF `ArrayBuffer` and returns one `AsciiFrame` per GIF frame, preserving the original frame rate.
|
|
51
|
+
## Quick start
|
|
140
52
|
|
|
141
53
|
```ts
|
|
142
|
-
import {
|
|
143
|
-
|
|
144
|
-
const buffer = await fetch('animation.gif').then(r => r.arrayBuffer());
|
|
145
|
-
const canvas = document.getElementById('ascii') as HTMLCanvasElement;
|
|
146
|
-
const opts = { ...DEFAULT_OPTIONS, fontSize: 8 };
|
|
147
|
-
|
|
148
|
-
const { frames, fps } = await gifToAsciiFrames(buffer, opts, canvas.width, canvas.height);
|
|
149
|
-
|
|
150
|
-
let frameIndex = 0;
|
|
151
|
-
setInterval(() => {
|
|
152
|
-
renderFrameToCanvas(canvas.getContext('2d')!, frames[frameIndex], opts, canvas.width, canvas.height);
|
|
153
|
-
frameIndex = (frameIndex + 1) % frames.length;
|
|
154
|
-
}, 1000 / fps);
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### Video
|
|
158
|
-
|
|
159
|
-
`asciifyVideo` streams video as live ASCII art in real time. Instant start, constant memory, unlimited duration.
|
|
160
|
-
|
|
161
|
-
> ⚠️ Never set the backing `<video>` element to `display: none` — browsers skip GPU frame decoding. When given a URL string, `asciifyVideo` handles this automatically.
|
|
162
|
-
|
|
163
|
-
```ts
|
|
164
|
-
import { asciifyVideo } from 'asciify-engine';
|
|
165
|
-
|
|
166
|
-
const canvas = document.getElementById('ascii') as HTMLCanvasElement;
|
|
167
|
-
|
|
168
|
-
// Minimal
|
|
169
|
-
const stop = await asciifyVideo('/clip.mp4', canvas);
|
|
170
|
-
|
|
171
|
-
// Fit canvas to a container and re-size automatically on resize:
|
|
172
|
-
const stop = await asciifyVideo('/clip.mp4', canvas, {
|
|
173
|
-
fitTo: '#hero', // or an HTMLElement
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
// Full-bleed hero framing without custom CSS width hacks:
|
|
177
|
-
const stop = await asciifyVideo('/hero.mp4', canvas, {
|
|
178
|
-
fitTo: '#hero',
|
|
179
|
-
objectFit: 'contain',
|
|
180
|
-
objectPosition: 'center bottom',
|
|
181
|
-
width: '100vw',
|
|
182
|
-
height: '100%',
|
|
183
|
-
bleed: { x: '2vw' },
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
// Lifecycle hooks — ready state, timers, etc.:
|
|
187
|
-
const stop = await asciifyVideo('/clip.mp4', canvas, {
|
|
188
|
-
fitTo: '#hero',
|
|
189
|
-
fontSize: 6,
|
|
190
|
-
onReady: () => setLoading(false),
|
|
191
|
-
onFrame: () => setElapsed(t => t + 1),
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
// Pre-extract all frames before playback (frame-perfect loops, short clips):
|
|
195
|
-
const stop = await asciifyVideo('/clip.mp4', canvas, { preExtract: true });
|
|
196
|
-
|
|
197
|
-
// Scroll-scrub video time using native browser scroll:
|
|
198
|
-
const stop = await asciifyVideo('/hero.mp4', canvas, {
|
|
199
|
-
fitTo: '#hero',
|
|
200
|
-
scroll: true,
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
// Use GSAP ScrollTrigger when it is already in your app:
|
|
204
|
-
const stop = await asciifyVideo('/hero.mp4', canvas, {
|
|
205
|
-
fitTo: '#hero',
|
|
206
|
-
scroll: {
|
|
207
|
-
gsap,
|
|
208
|
-
ScrollTrigger,
|
|
209
|
-
start: 'top bottom',
|
|
210
|
-
end: 'bottom top',
|
|
211
|
-
scrub: 1,
|
|
212
|
-
speed: 1.5,
|
|
213
|
-
},
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
// Clean up:
|
|
217
|
-
stop();
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
---
|
|
223
|
-
|
|
224
|
-
## Rendering Options
|
|
225
|
-
|
|
226
|
-
All conversion and render functions accept an `AsciiOptions` object. Spread `DEFAULT_OPTIONS` as a base and override what you need.
|
|
227
|
-
|
|
228
|
-
| Option | Type | Default | Description |
|
|
229
|
-
|---|---|---|---|
|
|
230
|
-
| `fontSize` | `number` | `7` (low-level); `10` (one-call) | Character cell size in pixels. Smaller values increase density and detail. |
|
|
231
|
-
| `colorMode` | `'grayscale' \| 'fullcolor' \| 'matrix' \| 'accent'` | `'grayscale'` | Determines how pixel color is mapped to character color. |
|
|
232
|
-
| `charset` | `string` | Standard ramp | Characters ordered from dense to sparse, representing brightness levels. |
|
|
233
|
-
| `brightness` | `number` | `0` | Brightness adjustment from `-1` (darker) to `1` (lighter). |
|
|
234
|
-
| `contrast` | `number` | `1` | Contrast multiplier applied before character mapping. |
|
|
235
|
-
| `invert` | `boolean \| 'auto'` | `false` | Inverts the luminance mapping — light areas become dense, dark areas sparse. Set to `'auto'` to auto-detect from OS color scheme (light mode → invert, dark mode → normal). |
|
|
236
|
-
| `renderMode` | `'ascii' \| 'dots'` | `'ascii'` | Render as text characters or circular dot particles. |
|
|
237
|
-
| `hoverEffect` | `string` | `'none'` | Interactive effect driven by cursor position. See hover effects below. |
|
|
238
|
-
| `hoverStrength` | `number` | `0` | Effect intensity (0–1). `0` = hover disabled. |
|
|
239
|
-
| `hoverRadius` | `number` | `0.2` | Effect radius relative to canvas size (0–1). |
|
|
240
|
-
| `chromaKey` | `true \| 'blue-screen' \| {r,g,b} \| string \| null` | `null` | Remove a background colour. `true` = heuristic green screen (any shade). `'blue-screen'` = heuristic blue screen. Custom: `{r,g,b}` or any CSS hex string keyed by Euclidean distance. `null` to disable. |
|
|
241
|
-
| `chromaKeyTolerance` | `number` | `60` | Euclidean RGB distance threshold for chroma-key detection. `0` = exact match, higher = more pixels removed (max useful ~100). |
|
|
242
|
-
| `chromaKeyTrimPadding` | `number` | `0.002` | Normalized safety padding around auto-trimmed keyed video foregrounds. Use `0` for edge-tight marketing heroes. |
|
|
243
|
-
| `chromaKeyTrimLuminanceThreshold` | `number` | `0` | Minimum source luminance counted as auto-trim foreground. Raise slightly when invisible edge noise prevents keyed subjects from filling a preserved-ratio layout. |
|
|
244
|
-
| `chromaKeyTrimMode` | `'range' \| 'frame' \| 'off'` | `'range'` | `'range'` keeps one stable crop for the playback range. `'frame'` remeasures each rendered frame and expands back to the render aspect for moving scrubbed heroes. `'off'` disables keyed auto-trim. |
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
### Source Crop
|
|
248
|
-
|
|
249
|
-
Use `sourceCrop` to remove empty source edges before ASCII sampling. The recommended API is CSS-like insets; the engine sizes `asciifyVideo` from the resolved crop aspect so the output does not stretch:
|
|
250
|
-
|
|
251
|
-
```ts
|
|
252
|
-
await asciifyVideo('/hero.mp4', canvas, {
|
|
253
|
-
options: {
|
|
254
|
-
sourceCrop: { top: 0.08, bottom: 0.22 },
|
|
255
|
-
},
|
|
256
|
-
});
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
Crop from the sides the same way:
|
|
260
|
-
|
|
261
|
-
```ts
|
|
262
|
-
await asciifyVideo('/hero.mp4', canvas, {
|
|
263
|
-
options: {
|
|
264
|
-
sourceCrop: { left: 0.1, right: 0.1 },
|
|
265
|
-
},
|
|
266
|
-
});
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
Use any side together:
|
|
270
|
-
|
|
271
|
-
```ts
|
|
272
|
-
sourceCrop: { top: 0.08, right: 0.12, bottom: 0.22, left: 0.12 }
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
`sourceCrop: { height: 0.7 }` keeps 70% of the source centered. `preserveAspect` only applies to single-dimension `width`/`height` crops. Side insets (`top`, `right`, `bottom`, `left`) define the crop window directly, then `asciifyVideo` uses that crop aspect for layout and render dimensions so a top/bottom crop can become a wide hero band without stretching.
|
|
276
|
-
|
|
277
|
-
When `sourceCrop` and `chromaKey` are both enabled for video, `asciifyVideo` performs a one-time foreground trim inside the crop window. For scrubbed or trimmed video it samples the playback range and uses the union of the keyed foreground bounds. Keyed empty margins are removed before layout sizing, so green-screen or transparent-looking subjects can fill wide hero canvases without custom CSS hacks or per-frame zoom.
|
|
278
|
-
|
|
279
|
-
Use `chromaKeyTrimMode: 'frame'` when the subject moves across a scroll-scrubbed hero and a single range crop still leaves an edge visually short. The engine remeasures the visible foreground per rendered frame and expands the crop to the render aspect, so the result stays proportionate while following the actual subject.
|
|
280
|
-
|
|
281
|
-
### Canvas Layout
|
|
282
|
-
|
|
283
|
-
Use layout options on `asciifyVideo` when the ASCII canvas needs to fill a hero, preview frame, or fixed viewport. These affect only the displayed canvas box; they do not change source sampling or ASCII detail.
|
|
284
|
-
|
|
285
|
-
```ts
|
|
286
|
-
await asciifyVideo('/hero.mp4', canvas, {
|
|
287
|
-
fitTo: '#hero',
|
|
288
|
-
objectFit: 'contain',
|
|
289
|
-
objectPosition: 'center bottom',
|
|
290
|
-
width: '100vw',
|
|
291
|
-
height: '100%',
|
|
292
|
-
bleed: { x: '2vw' },
|
|
293
|
-
options: {
|
|
294
|
-
sourceCrop: { top: 0.1, bottom: 0.18 },
|
|
295
|
-
},
|
|
296
|
-
});
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
- `objectFit`: `'contain' | 'cover' | 'fill' | 'none' | 'scale-down'`
|
|
300
|
-
- `objectPosition`: any CSS object-position value, such as `'center bottom'`
|
|
301
|
-
- `scale`: visual overfill using CSS `scale`, preserving existing transforms
|
|
302
|
-
- `width` / `height`: CSS lengths or pixel numbers for the visible canvas
|
|
303
|
-
- `bleed`: extra layout overfill around the fitted canvas. Use `{ x: '2vw' }` for full-bleed ASCII heroes where glyph side bearings or character-cell quantization leave a thin visual edge gap.
|
|
304
|
-
|
|
305
|
-
Use `sourceCrop` to reframe the input media. Use `objectFit`, `objectPosition`, `scale`, and `bleed` to place the rendered canvas in the layout. For high-detail scroll scrubbers, keep `preExtract` off and set `maxCachedFrames` to bound memory while the engine caches nearby text frames.
|
|
306
|
-
|
|
307
|
-
### Chroma Key (Green/Blue Screen)
|
|
308
|
-
|
|
309
|
-
Remove a solid background colour from any source — images, GIFs, or video — so the canvas background shows through keyed pixels.
|
|
310
|
-
|
|
311
|
-
```ts
|
|
312
|
-
import { asciify, DEFAULT_OPTIONS } from 'asciify-engine';
|
|
313
|
-
|
|
314
|
-
// Green screen — zero config, just set true:
|
|
315
|
-
asciify(img, canvas, {
|
|
316
|
-
options: { ...DEFAULT_OPTIONS, chromaKey: true, colorMode: 'fullcolor' },
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
// Blue screen
|
|
320
|
-
asciify(img, canvas, {
|
|
321
|
-
options: { ...DEFAULT_OPTIONS, chromaKey: 'blue-screen', chromaKeyTolerance: 70 },
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
// Custom RGB key
|
|
325
|
-
asciify(img, canvas, {
|
|
326
|
-
options: { ...DEFAULT_OPTIONS, chromaKey: { r: 0, g: 180, b: 90 }, chromaKeyTolerance: 50 },
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
// Live video with green screen
|
|
330
|
-
asciifyVideo('/footage.mp4', canvas, {
|
|
331
|
-
fitTo: '#container',
|
|
332
|
-
options: { ...DEFAULT_OPTIONS, chromaKey: true, colorMode: 'fullcolor' },
|
|
333
|
-
});
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
**Tolerance guide:**
|
|
337
|
-
- `40–60` — tight key, natural green screen under good lighting
|
|
338
|
-
- `60–80` — broader key, wrinkled fabric or uneven lighting
|
|
339
|
-
- `80–120` — aggressive; expect some spill into the subject
|
|
340
|
-
|
|
341
|
-
### Color Modes
|
|
342
|
-
|
|
343
|
-
| Mode | Description |
|
|
344
|
-
|---|---|
|
|
345
|
-
| `grayscale` | Classic monochrome ASCII. Character brightness maps to source luminance. |
|
|
346
|
-
| `fullcolor` | Each character inherits the original pixel color from the source. |
|
|
347
|
-
| `matrix` | Monochrome green — inspired by classic terminal aesthetics. |
|
|
348
|
-
| `accent` | Single accent color applied uniformly across all characters. |
|
|
349
|
-
|
|
350
|
-
### Hover Effects
|
|
351
|
-
|
|
352
|
-
Interactive effects that respond to cursor movement. Pass the effect name to `hoverEffect` and supply the cursor position to `renderFrameToCanvas` at render time.
|
|
353
|
-
|
|
354
|
-
Available effects: `spotlight` · `flashlight` · `magnifier` · `force-field` · `neon` · `fire` · `ice` · `gravity` · `shatter` · `ghost`
|
|
355
|
-
|
|
356
|
-
```ts
|
|
357
|
-
canvas.addEventListener('mousemove', (e) => {
|
|
358
|
-
const rect = canvas.getBoundingClientRect();
|
|
359
|
-
renderFrameToCanvas(ctx, frame, opts, canvas.width, canvas.height, Date.now() / 1000, {
|
|
360
|
-
x: e.clientX - rect.left,
|
|
361
|
-
y: e.clientY - rect.top,
|
|
362
|
-
});
|
|
363
|
-
});
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
---
|
|
54
|
+
import { buttonLoading } from 'asciify-engine/kit';
|
|
55
|
+
import { asciify } from 'asciify-engine';
|
|
367
56
|
|
|
368
|
-
|
|
57
|
+
const save = buttonLoading(document.querySelector('#save')!);
|
|
58
|
+
save.start('Saving');
|
|
59
|
+
await saveDraft();
|
|
60
|
+
save.succeed('Saved');
|
|
369
61
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
// Download a template from https://asciify.org/backgrounds
|
|
374
|
-
import { asciiBackground } from './backgrounds.js';
|
|
375
|
-
|
|
376
|
-
const { destroy } = asciiBackground('#hero', {
|
|
377
|
-
type: 'rain',
|
|
378
|
-
colorScheme: 'auto', // follows OS dark/light mode
|
|
379
|
-
speed: 1.0,
|
|
380
|
-
density: 0.55,
|
|
381
|
-
accentColor: '#d4ff00',
|
|
62
|
+
const stop = await asciify('/photo.jpg', document.querySelector('canvas')!, {
|
|
63
|
+
fontSize: 8,
|
|
64
|
+
options: { colorMode: 'fullcolor' },
|
|
382
65
|
});
|
|
383
|
-
|
|
384
|
-
// Stop and clean up when no longer needed
|
|
385
|
-
destroy();
|
|
66
|
+
// On unmount: save.destroy(); stop();
|
|
386
67
|
```
|
|
387
68
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
| Type | Description |
|
|
391
|
-
|---|---|
|
|
392
|
-
| `wave` | Flowing sine-wave field with layered noise turbulence |
|
|
393
|
-
| `rain` | Vertical column rain with a glowing leading character and fading trail |
|
|
394
|
-
| `stars` | Parallax star field that reacts to cursor position |
|
|
395
|
-
| `pulse` | Concentric ripple bursts emanating from the cursor |
|
|
396
|
-
| `noise` | Smooth value-noise field with organic, fluid motion |
|
|
397
|
-
| `grid` | Geometric grid that warps and brightens near the cursor |
|
|
398
|
-
| `aurora` | Sweeping borealis-style color bands drifting across the field |
|
|
399
|
-
| `silk` | Fluid swirl simulation following cursor movement |
|
|
400
|
-
| `void` | Gravitational singularity — characters spiral inward toward the cursor |
|
|
401
|
-
| `morph` | Characters morph between shapes over time, driven by noise |
|
|
402
|
-
|
|
403
|
-
### Background Options
|
|
404
|
-
|
|
405
|
-
| Option | Type | Default | Description |
|
|
406
|
-
|---|---|---|---|
|
|
407
|
-
| `type` | `string` | `'wave'` | Which background renderer to use. |
|
|
408
|
-
| `colorScheme` | `'auto' \| 'light' \| 'dark'` | `'dark'` | `'auto'` reacts to OS theme changes in real time. |
|
|
409
|
-
| `fontSize` | `number` | `13` | Character size in pixels. |
|
|
410
|
-
| `speed` | `number` | `1` | Global animation speed multiplier. |
|
|
411
|
-
| `density` | `number` | `0.55` | Fraction of grid cells that are active (0–1). |
|
|
412
|
-
| `accentColor` | `string \| 'auto'` | varies | Highlight or leading-character color (any CSS hex string). Set to `'auto'` to auto-detect: probes `--accent-color`, `--color-accent`, `--accent`, `--primary` CSS variables on `:root`, then falls back to OS color scheme (dark ink in light mode, light ink in dark mode). |
|
|
413
|
-
| `color` | `string` | — | Override the body character color. |
|
|
414
|
-
|
|
415
|
-
---
|
|
69
|
+
## Features
|
|
416
70
|
|
|
417
|
-
|
|
71
|
+
- **Kit** (`/kit`): 31 interface components and the curated asciify.org backgrounds, one call each.
|
|
72
|
+
- **Media** (`asciify-engine`, `/core`): images, GIFs, video, webcam and text to ASCII on a canvas, with scroll-scrubbed video, chroma key and source crop.
|
|
73
|
+
- **GPU fields** (`/field`): full-bleed WebGL2 character fields with trail, fluid and lantern cursor responses, in any font and charset.
|
|
74
|
+
- **Hover and motion** (`/hover`): 19 pointer effects and 10 still-image motions for media on your page.
|
|
75
|
+
- **Studio** (`/studio`): 24 render styles, tonal curves, source warps, layered projects and PNG, JPEG, GIF, MP4 and WebM export.
|
|
76
|
+
- **Agent skill**: the house design, every component's prompt and snippet, and the full API, for Claude Code, Cursor, Codex and other agents.
|
|
77
|
+
- **Built for host pages**: no work at import, safe under SSR, tree-shaken per component, TypeScript types, ESM and CommonJS.
|
|
418
78
|
|
|
419
|
-
|
|
420
|
-
import { useEffect, useRef } from 'react';
|
|
421
|
-
import { imageToAsciiFrame, renderFrameToCanvas, DEFAULT_OPTIONS } from 'asciify-engine';
|
|
79
|
+
## Docs
|
|
422
80
|
|
|
423
|
-
|
|
424
|
-
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
81
|
+
Everything else lives at **[asciify.org/docs](https://asciify.org/docs)**: the API reference, options, framework guides, chroma key, layering, Studio and GPU fields. The agent skill's [references](skills/asciify-engine/references) cover the same ground for agents.
|
|
425
82
|
|
|
426
|
-
|
|
427
|
-
const canvas = canvasRef.current;
|
|
428
|
-
if (!canvas) return;
|
|
83
|
+
## Performance
|
|
429
84
|
|
|
430
|
-
|
|
431
|
-
img.crossOrigin = 'anonymous';
|
|
432
|
-
img.src = src;
|
|
433
|
-
img.onload = () => {
|
|
434
|
-
const opts = { ...DEFAULT_OPTIONS, fontSize: 10, colorMode: 'fullcolor' as const };
|
|
435
|
-
const { frame } = imageToAsciiFrame(img, opts, canvas.width, canvas.height);
|
|
436
|
-
renderFrameToCanvas(canvas.getContext('2d')!, frame, opts, canvas.width, canvas.height);
|
|
437
|
-
};
|
|
438
|
-
}, [src]);
|
|
85
|
+
Each optional entry loads only when imported, and a route that uses one kit component downloads that component, not the whole kit. What the engine promises a host page, and how it is measured, is in [performance.md](skills/asciify-engine/references/performance.md).
|
|
439
86
|
|
|
440
|
-
|
|
441
|
-
}
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
---
|
|
445
|
-
|
|
446
|
-
## Embed Generation
|
|
447
|
-
|
|
448
|
-
Generate self-contained HTML that can be hosted anywhere or dropped directly into a page — no runtime dependency required.
|
|
87
|
+
## Contributing
|
|
449
88
|
|
|
450
|
-
|
|
451
|
-
import { generateEmbedCode, generateAnimatedEmbedCode } from 'asciify-engine';
|
|
452
|
-
|
|
453
|
-
// Static — produces a single-file HTML with the ASCII art baked in
|
|
454
|
-
const staticHtml = generateEmbedCode(frame, options);
|
|
89
|
+
Issues and pull requests are welcome. To work on the engine:
|
|
455
90
|
|
|
456
|
-
|
|
457
|
-
|
|
91
|
+
```bash
|
|
92
|
+
npm ci
|
|
93
|
+
npm test
|
|
94
|
+
npm run build
|
|
458
95
|
```
|
|
459
96
|
|
|
460
|
-
|
|
97
|
+
`npm run test:browser` serves the browser regression page. Changes are listed in [CHANGELOG.md](CHANGELOG.md).
|
|
461
98
|
|
|
462
|
-
##
|
|
99
|
+
## Sponsors
|
|
463
100
|
|
|
464
|
-
|
|
465
|
-
|---|---|---|
|
|
466
|
-
| `asciify` | `(source, canvas, options?)` | `Promise<void>` |
|
|
467
|
-
| `asciifyVideo` | `(source, canvas, options?)` | `Promise<() => void>` |
|
|
468
|
-
| `asciifyGif` | `(source, canvas, options?)` | `Promise<() => void>` |
|
|
469
|
-
| `asciifyWebcam` | `(canvas, options?)` | `Promise<() => void>` |
|
|
470
|
-
| `asciiBackground` (downloaded template) | `(selector, options)` | `{ destroy() }` |
|
|
471
|
-
| `imageToAsciiFrame` | `(source, options, w?, h?)` | `{ frame, cols, rows }` |
|
|
472
|
-
| `renderFrameToCanvas` | `(ctx, frame, options, w, h, time?, hoverPos?)` | `void` |
|
|
473
|
-
| `gifToAsciiFrames` | `(buffer, options, w, h, onProgress?)` | `Promise<{ frames, cols, rows, fps }>` |
|
|
474
|
-
| `videoToAsciiFrames` | `(video, options, w, h, fps?, maxSec?, onProgress?)` | `Promise<{ frames, cols, rows, fps }>` |
|
|
475
|
-
| `generateEmbedCode` | `(frame, options)` | `string` |
|
|
476
|
-
| `generateAnimatedEmbedCode` | `(frames, options, fps)` | `string` |
|
|
101
|
+
<!-- sponsors:start -->
|
|
477
102
|
|
|
478
|
-
|
|
103
|
+
Asciify is free and open source. [Sponsor its development](https://asciify.org/sponsor) on GitHub Sponsors.
|
|
479
104
|
|
|
480
|
-
|
|
105
|
+
<p align="center"><a href="https://asciify.org/sponsor">Your logo here</a></p>
|
|
481
106
|
|
|
482
|
-
|
|
107
|
+
<!-- sponsors:end -->
|
|
483
108
|
|
|
484
109
|
## License
|
|
485
110
|
|
|
486
|
-
MIT ©
|
|
487
|
-
|
|
488
|
-
> ☕ [Buy me a coffee](https://www.buymeacoffee.com/asciify) — if this saved you time, I'd appreciate it!
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
## Hero finish
|
|
492
|
-
|
|
493
|
-
Version 1.3.0 adds text-shaped charcoal cutouts (`textMask`), peripheral scanline/fringe control (`edgeEffect`), fixed accent ink, and velocity-driven Trail source flow. These additions ship in **npm 1.3.0**. Upgrade older applications before using them. See the [complete API, layering example, lifecycle, and fallback limits](skills/asciify-engine/references/hero-finish.md).
|
|
494
|
-
|
|
495
|
-
## Studio composition tools
|
|
496
|
-
|
|
497
|
-
The `asciify-engine/studio` entry adds a reusable composition pipeline: alternate render styles, palette dithering, crop and masks, source/blurred/gradient backdrops, combined optical effects, saved looks, text fonts, and PNG/JPG/GIF/MP4/WebM export. Existing ASCII defaults and `/core` and `/hover` imports stay unchanged. Fonts and video encoders load on demand.
|
|
498
|
-
|
|
499
|
-
```ts
|
|
500
|
-
import { mountStudio } from 'asciify-engine/studio';
|
|
501
|
-
|
|
502
|
-
const studio = await mountStudio(canvas, '/your-video.mp4', {
|
|
503
|
-
settings: {
|
|
504
|
-
style: 'ascii',
|
|
505
|
-
backdrop: { mode: 'blurred', opacity: 0.4 },
|
|
506
|
-
},
|
|
507
|
-
});
|
|
508
|
-
studio.update({ hover: { effect: 'water', radius: 0.35 } });
|
|
509
|
-
// On unmount:
|
|
510
|
-
studio.destroy();
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
See the [Studio API guide](skills/asciify-engine/references/studio-workspace.md) for complete settings, import/export, media ownership, browser requirements, and performance budgets.
|
|
514
|
-
|
|
515
|
-
### 2.0.1 size and export fixes
|
|
516
|
-
|
|
517
|
-
`studioGrid` exposes the effective grid and minimum cell size for a bounded preview. Dither uses `dither.scale`; other styles use `cellSize`. Pass `time` to `exportStudio` for a PNG/JPEG of the current frame. Animation exports continue to start at zero. See the Studio guide for the shared preview/export density contract.
|
|
518
|
-
|
|
519
|
-
### Still-image motion — 3.0
|
|
520
|
-
|
|
521
|
-
Use `motion: { type: 'current', speed: 1 }` with `/studio`, `motion: 'current'` with `/hover`, or `animationStyle: 'current'` with core. Choose one motion owner. The curated set is Off (`none`), Caustics (`caustics`, fixed-grid light), Slow Current (`current`, local flow), and Reveal & Reform (`reform`, staggered dissolve). All loop every 12 / speed seconds and preserve your chosen colors. Breathe, Fire, Rainbow and other old ambient motion IDs are retired. Old Studio presets restore with motion Off; update application-owned core options explicitly. Hover effects and optical finishes are independent. See [the 3.0 migration guide](skills/asciify-engine/references/upgrading.md).
|
|
522
|
-
|
|
523
|
-
## 4.0: optional background templates
|
|
524
|
-
|
|
525
|
-
Procedural backgrounds no longer ship in the npm package or root exports. Download only the template you want from [the background gallery](https://asciify.org/backgrounds); its copied code and lifecycle belong to your application. Existing 3.x applications can remain pinned while migrating. `/backgrounds`, `asciiBackground`, `BACKGROUND_TYPES`, and procedural `render*Background` exports are removed; text/media background APIs remain. The default import and `/core` now expose the same media-first API. `CHARACTER_SETS` is the lightweight curated catalog shared by the hero and editor.
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
## 4.1.0: current catalogs only
|
|
529
|
-
|
|
530
|
-
This release removes `CHARSETS`, `ART_STYLE_PRESETS`, `CHARSET_SEQUENCES`, `LIVING_STYLE_PRESETS`, and their key types. **Although numbered 4.1.0 at the maintainer's request, these removals are breaking changes.** Update imports before upgrading. Removed core `artStyle` shortcuts throw with migration guidance.
|
|
531
|
-
|
|
532
|
-
Use `STUDIO_STYLES` from `asciify-engine/studio` for the 15 current render styles: ASCII, Braille, Dots, Lines, Blocks, Dither, Cross, Diagonal, Diamond, Mixed, Pixel, Mosaic, Lego, Voxel and Disco. Set `settings.style` with `mountStudio` / `createStudioRenderer`; these styles are actual rendering methods, not character ramps. Core media APIs remain ASCII-first.
|
|
533
|
-
|
|
534
|
-
Use `CHARACTER_SETS.standard.chars` (or another curated entry) from root or `/core` for an ASCII ramp; custom `options.charset` and `charsetFrames` arrays remain supported. Use explicit `options.colorMode`, `renderMode`, and hover/motion settings instead of the removed combined presets. No legacy catalog has been moved to a hidden compatibility bundle.
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
### Studio detail controls (4.2)
|
|
538
|
-
|
|
539
|
-
The optional `asciify-engine/studio` entry includes 24 render styles, including hex mosaic, LED matrix and CMYK print. Dither supports pixel scale, custom palettes, RGB/luminance mapping, pattern speed and direction. Use `studioGrid` to report effective density and opt into a larger `maxCells` budget for one-pixel output; defaults stay performance-bounded. Mounts expose `setAdaptive` for an explicit detail/performance choice. Still-image motion has `amount`, and finishing adds `rgbSplit` and `sharpen` (WebGL required). See the [complete controls and export guidance](skills/asciify-engine/references/studio-workspace.md#fine-detail-and-controls-42). No AI service or server upload is introduced.
|
|
540
|
-
|
|
541
|
-
### More motion for still images (4.3)
|
|
542
|
-
|
|
543
|
-
Studio includes six still-image motions plus Off: Caustics, Slow Current, Reveal & Reform, Sheen, Tidal Rings, and Living Grain. Sheen and Living Grain preserve the grid. Motion speed and strength are adjustable. Hover adds Magnetic Pull and Scatter alongside Trail, Water, Contour, Dissolve, Silk, and Vortex. Use `hover.effect` in Studio or `effect` in the optional `/hover` module; configure radius and strength separately. Existing defaults remain unchanged.
|
|
544
|
-
|
|
545
|
-
### Studio correctness and performance (4.3.1)
|
|
546
|
-
|
|
547
|
-
Stationary compositions are reused during animated finishing. Hidden dither settings and zero-strength motion no longer keep still previews awake. Ordered dithering uses cached thresholds with byte-identical output; Scatter caches its spatial noise. CMYK now responds to tonal hovers and still motion. For a selected PNG/JPEG frame after changing motion speed, pass `time: player.time` and `motionTime: player.motionTime` to `exportStudio`. See the packaged project-integration skill reference for a live component and independent image/video export.
|
|
548
|
-
|
|
549
|
-
### More ways to bring photos to life (4.4)
|
|
550
|
-
|
|
551
|
-
Eleven pointer interactions and ten autonomous motions, plus Off. Emboss presses paired light and shadow into a fixed character grid; Elastic stores directional momentum and springs back; Rake combs the source into alternating ribbons. Parallax, Woven Flow, Print Shift and Contour Light join the still-image motions. Contour Light follows the actual source tones.
|
|
552
|
-
|
|
553
|
-
```js
|
|
554
|
-
import { STUDIO_HOVERS, STUDIO_MOTIONS } from 'asciify-engine/studio';
|
|
555
|
-
// value / label / group / description: use these to build a complete picker.
|
|
556
|
-
player.update({
|
|
557
|
-
motion: { type: 'parallax', speed: 1, amount: .7 },
|
|
558
|
-
hover: { effect: 'elastic', radius: .45, strength: .65 },
|
|
559
|
-
});
|
|
560
|
-
```
|
|
561
|
-
|
|
562
|
-
All motion choices repeat every `12 / speed` seconds. Still exports preserve the selected preview phase; animated exports include motion without pointer events. Grain, dust, glitch, animated dither and source video have separate timing. No external AI service is used. See [effect integration](skills/asciify-engine/references/studio-effects.md).
|
|
563
|
-
|
|
564
|
-
### Tonal detail and blue-noise dithering (4.5)
|
|
565
|
-
|
|
566
|
-
Studio adds `dither.algorithm: 'blue-noise'`: a stable precomputed dispersed pattern with no runtime generation or asset download. `noise` remains white noise. Choose the algorithm from `DITHER_ALGORITHMS`; drift and shimmer remain independently controlled.
|
|
567
|
-
|
|
568
|
-
Recover usable shadow/highlight separation before conversion with `color.gamma`, `shadows`, `highlights`, `blackPoint` and `whitePoint`. All Studio styles share these controls and retain neutral defaults. They are a bounded monotone tonal curve, not arbitrary per-channel curve editing. See [exact ranges, sampling budgets and a live-project example](skills/asciify-engine/references/studio-workspace.md#tonal-detail-and-blue-noise-45).
|
|
569
|
-
|
|
570
|
-
### Channel curves and source noise reduction (4.6)
|
|
571
|
-
|
|
572
|
-
Studio adds editable RGB, red, green and blue curves, with up to 16 control points each, plus optional edge-preserving source noise reduction. They apply before every render style and travel with saved settings and exports. Defaults preserve existing output. Curves use small cached lookups; still images reuse filtered pixels while hover/motion run. Dense video filtering is an explicit CPU tradeoff.
|
|
573
|
-
|
|
574
|
-
```ts
|
|
575
|
-
player.update({color:{
|
|
576
|
-
denoise:.25,
|
|
577
|
-
curves:{rgb:[[0,0],[.25,.2],[.75,.8],[1,1]]},
|
|
578
|
-
}});
|
|
579
|
-
const settings = player.settings; // detached snapshot for save/export
|
|
580
|
-
```
|
|
581
|
-
|
|
582
|
-
See [settings, normalization, processing order and performance guidance](skills/asciify-engine/references/studio-workspace.md#channel-curves-and-source-noise-46). Curves are local source controls, not another art-style alias or AI restoration.
|
|
583
|
-
|
|
584
|
-
### Ordered source shaping (4.7)
|
|
585
|
-
|
|
586
|
-
Combine up to eight source transformations before ASCII, dither or any Studio style: Twirl, Pinch, Spherize, Ripple, Zigzag, Shear, Smudge, Polar and Fragment. Edit strength, position and radius, plus direction/frequency where applicable. Reorder or disable stages without losing their settings; choose extended or transparent edges. Generate controls from `STUDIO_WARPS` and `MAX_STUDIO_WARPS`.
|
|
587
|
-
|
|
588
|
-
```ts
|
|
589
|
-
player.update({
|
|
590
|
-
warps: [{type:'sphere',amount:.3,radius:.7}, {type:'shear',amount:.15,angle:25}],
|
|
591
|
-
warpEdge:'clamp',
|
|
592
|
-
motion:{type:'parallax',amount:.5},
|
|
593
|
-
});
|
|
594
|
-
```
|
|
595
|
-
|
|
596
|
-
The stack is static shaping; motion and pointer hover remain independent. Coordinate maps are cached across video frames, and stills reuse shaped pixels during interaction. Empty stacks are free of additional pixel work. Defaults and root/core/hover behavior remain unchanged. Dither motion also gains alpha-correct interpolation at transparent edges. See [parameters, order, persistence/export and measured CPU tradeoffs](skills/asciify-engine/references/source-shaping.md).
|
|
597
|
-
|
|
598
|
-
### Layered projects (4.8)
|
|
599
|
-
|
|
600
|
-
Compose up to eight image, video, GIF or text layers with independent styles, motion, hover, placement, opacity and blend modes. `mountStudioProject` owns one playback loop and a shared cell/raster budget; hidden and unchanged layers skip work. Root/core/hover entry points remain unchanged.
|
|
601
|
-
|
|
602
|
-
```ts
|
|
603
|
-
import { mountStudioProject } from 'asciify-engine/studio';
|
|
604
|
-
const player = await mountStudioProject(canvas, { layers: [
|
|
605
|
-
{ id: 'photo', source: 'image', settings: { motion: { type: 'parallax' } } },
|
|
606
|
-
{ id: 'title', source: 'text', placement: { width: .8, height: .25 },
|
|
607
|
-
settings: { ink: '#b3ed82', hover: { effect: 'elastic' } } },
|
|
608
|
-
] }, { image: '/your-photo.webp', text: { text: 'HELLO', font: 'Small' } });
|
|
609
|
-
player.updateLayer('photo', { settings: { hover: { effect: 'water' } } });
|
|
610
|
-
// player.destroy() on unmount
|
|
611
|
-
```
|
|
612
|
-
|
|
613
|
-
`exportStudioProject` owns independent export media, so it does not disturb the live preview. Save layouts with `serializeStudioProject`, retain/rebind source assets separately, and restore with `parseStudioProject`. See the [layered-project guide](skills/asciify-engine/references/layered-projects.md) and [typed live/export example](examples/layered-composition.ts) for geometry, picking, ownership, cancellation and performance limits. This is an optional local composition API, not a claim of complete competitor-style or FPS parity.
|
|
614
|
-
|
|
615
|
-
### Print and illustration (4.9)
|
|
616
|
-
|
|
617
|
-
Stipple, Engraving, Crosshatch, Woodblock, Risograph and Pointillism add distinct print geometry to `/studio`. All share hover, living-image motion, masks, source shaping, saved settings and exports. Tune coverage, mark weight, angle, repeatable texture, light/dark ink polarity and two-ink registration with `print`. Use `STUDIO_PRINT_STYLES` for labels and [the print guide](skills/asciify-engine/references/print-styles.md) for a live still-image example, bounds and performance limits. ASCII remains the default.
|
|
618
|
-
|
|
619
|
-
## 4.10: more life for still images
|
|
620
|
-
|
|
621
|
-
Three additional pointer responses—Lens wake, Smudge and Ripple rings—and four autonomous motions—Relight, Shimmer, Breeze and Unfold—bring the catalogs to 14 each, plus Off. Source images, art styles and colors remain yours. Six editable combinations in `STUDIO_ANIMATION_PRESETS` help choose a starting point without replacing your look.
|
|
622
|
-
|
|
623
|
-
```ts
|
|
624
|
-
import { STUDIO_ANIMATION_PRESETS } from 'asciify-engine/studio';
|
|
625
|
-
const preset = STUDIO_ANIMATION_PRESETS.find(p => p.id === 'quiet-light')!;
|
|
626
|
-
player.update({ motion: preset.motion, hover: preset.hover });
|
|
627
|
-
```
|
|
628
|
-
|
|
629
|
-
Use one Studio player for live rendering, settings persistence and independent animation export. See [the complete interaction guide](skills/asciify-engine/references/studio-effects.md#lively-images-410) and [living-image helper](examples/living-image.ts). CPU simulation is bounded and wakes settle; device frame rates and visual fidelity still require browser measurement. Existing defaults and saved IDs are unchanged.
|
|
630
|
-
|
|
631
|
-
## 4.11: GPU glyph fields
|
|
632
|
-
|
|
633
|
-
`asciify-engine/field` renders an image, a video or generated motion as characters entirely on the GPU, with three cursor responses: `trail` (the Asciify Trail, the same ink field as the studio's, now on the GPU), `fluid` (an incompressible ink-in-water wake that curls into eddies) and `lantern` (a soft light with no distortion). Use it for full-bleed heroes and brand fields that must stay smooth while people move over them. Characters are drawn texel for texel from a glyph atlas, so any font and any charset work, including your own name.
|
|
634
|
-
|
|
635
|
-
```ts
|
|
636
|
-
import { mountGlyphField } from 'asciify-engine/field';
|
|
637
|
-
|
|
638
|
-
const field = mountGlyphField(canvas, {
|
|
639
|
-
source: '/media/hero.mp4',
|
|
640
|
-
charset: ' .,:-+ACME',
|
|
641
|
-
color: 'ink', ink: '#f3ba32', shade: 1,
|
|
642
|
-
fluid: { radius: 0.08, refraction: 0.03, glow: 0.6 },
|
|
643
|
-
});
|
|
644
|
-
// field.update({ charset: ' .:ASCIIFY' }); field.destroy();
|
|
645
|
-
```
|
|
646
|
-
|
|
647
|
-
It needs WebGL2 on a GPU: `isGlyphFieldSupported()` is false without it or on software WebGL, and slow devices step down to 1× pixels and then still ambient motion. Keep a poster or the `/studio` renderer as the fallback. A still field stops drawing once its wake settles. Measured in Playwright Chromium on an Apple M4 Pro at 1440 × 900 and device pixel ratio 2, a full-viewport field held 120 fps (9 ms 95th-percentile frame) under continuous pointer movement; measure your own targets. See the [field guide](skills/asciify-engine/references/glyph-field.md) and [brand field example](examples/glyph-field.ts). It is a separate entry: the root, `/core`, `/hover` and `/studio` never load it.
|
|
648
|
-
|
|
649
|
-
## 4.23: dialogs always render, sharper motion cues, cleaner hovers
|
|
650
|
-
|
|
651
|
-
- Kit fields inside an open modal dialog always get a WebGL context: fields covered by the dialog give theirs up and come back when it closes. A released field hands its canvas back at once (Safari counts dead contexts toward its limit), and a field whose context is lost recovers onto a fresh canvas.
|
|
652
|
-
- Depth is fused from focus, haze, a horizon prior, texture and saliency with an edge-aware filter; Drift finds water (including non-blue seas by reflection), sky, cloud and falling water, and holds everything else still. The classic core renderer shares the same motion code. Analysis follows grid changes and reads the ungraded photo.
|
|
653
|
-
- Water settles cleanly; Ripple and Emboss draw their own rings on dark or transparent ground; glyph fields take `scope`; video subjects update as the video plays; the hover catalog (`HOVER_CONTROLS`, `DEFAULT_HOVER`, `resolveHover`) is exported once. On the GPU path, Trail keeps the artwork's ground and Water moves whole cells.
|
|
654
|
-
|
|
655
|
-
## 4.22: photo-aware motion and finish
|
|
656
|
-
|
|
657
|
-
- A new set of still-image motions read the photo's own light, form, highlights, detail and estimated depth: `relight`, `sheen` (specular sweep), `glow` (highlight bloom), `shimmer` (glints), `parallax`, `push`, `focus`, `mist`, `current` (drift) and `develop`.
|
|
658
|
-
- Retired motions keep working through `DEPRECATED_STUDIO_MOTIONS`: caustics → sheen, grain → shimmer, trace → relight, tidal/weave/breeze → current, reform/print/unfold → develop.
|
|
659
|
-
- Optical finish: bloom is a wide soft glow around highlights, and grain is fine film grain weighted to the midtones.
|
|
660
|
-
- Animation presets are re-paired with the new motions, plus a new Depth study.
|
|
661
|
-
|
|
662
|
-
## 4.21: premium hovers that know the subject
|
|
663
|
-
|
|
664
|
-
- Six new hovers: `loupe`, `reveal`, `repel`, `tilt`, `haze` and `bleed`; `scatter`, `elastic` and `ripple` are refined, and `rake` is deprecated (it plays `silk`).
|
|
665
|
-
- Every hover takes `decay` (how long it lingers) and `scope: 'all' | 'subject' | 'outside'`: the subject comes from transparency, a drawn mask or an even plain ground, with a feathered edge; a busy full-bleed photo acts as `all`.
|
|
666
|
-
- Hovers ease in, settle when the pointer stops, follow touch drags and stay off under reduced motion.
|
|
667
|
-
- `mountHover` now runs on the GPU in Chrome (a reserved word in its shader forced the 2D fallback).
|
|
668
|
-
|
|
669
|
-
## 4.20: sharper presets, 61 looks, twelve backgrounds
|
|
670
|
-
|
|
671
|
-
- Studio presets `ascii` (Signature ASCII) and `night` (Night signal) are retuned: finer 4px cells, brighter inks, lifted midtones and a faint glow.
|
|
672
|
-
- The woodblock print style no longer leaves pale seams between cells.
|
|
673
|
-
- `aurora` joins the deprecated backgrounds (closest current preset: `mesh`), leaving twelve curated presets; `halo` fits tall stages, `caustics` is calmer in small cards and `wordField` reads on small dark stages.
|
|
674
|
-
- The skill's `looks.md` covers 61 looks, adding Projects looks for album covers, editorial portraits, product heroes, posters, postcards, botanical plates, lookbooks, game box art, stickers and phone wallpapers, each with its export size and frame.
|
|
675
|
-
|
|
676
|
-
## 4.19: a curated set of premium backgrounds
|
|
677
|
-
|
|
678
|
-
- New presets `halo`, `ripple`, `mesh`, `interference`, `constellation` and `cascade`; `caustics`, `contour` and `aurora` are rebuilt on the GPU. All follow `setGlobalLook`, keep a quiet zone behind content and hold a still frame under reduced motion.
|
|
679
|
-
- `cursor: false` turns pointer interaction off for every preset. `mark: 'glyph' | 'dot'` draws any preset in characters or dots; `swell` keeps dots by default.
|
|
680
|
-
- Dated presets (`dna`, `circuit`, `fire`, `grid`, `morph`, `noise`, `pulse`, `rain`, `silk`, `stars`, `terrain`, `veil`, `void`, `wave`, `weave`, `dune`, `etch`, `facet`, `moire`, `halftone`) stay exported but are deprecated; the kit reference names the closest current preset for each.
|
|
681
|
-
- Switching presets no longer leaves the previous preset's poster covering the new one.
|
|
682
|
-
- `pageDissolve` draws its settled blocks in one masked pass and snapshots at 1x on touch devices, so the wave stays light on phones.
|
|
683
|
-
- The skill's `looks.md` now covers 47 artwork looks, every render style, plus composed (blur, transparent, gradient) and finished (vignette, grain, bloom, scanlines) looks.
|
|
684
|
-
|
|
685
|
-
## 4.18: 24 render styles in every live field, artwork looks, smoother components
|
|
686
|
-
|
|
687
|
-
- Glyph fields draw the Studio's tile and print styles: `mark` accepts `pixel`, `mosaic`, `lego`, `voxel`, `disco`, `dither`, `hex`, `led`, `cmyk`, `stipple`, `engraving`, `crosshatch`, `woodblock`, `risograph` and `pointillism` (see `FIELD_MARKS`), one shader branch each.
|
|
688
|
-
- `setGlobalLook({ style })` re-skins every opted-in kit field in any of the 24 Studio styles (`KIT_STYLES`); `charset` still works alongside.
|
|
689
|
-
- New background preset `swell`: a halftone dot field in slow crossing waves, quiet behind the content, swelling under the pointer.
|
|
690
|
-
- Reworked kit components: `blockChart` (crisp glyph columns, scrubbing, morphing ranges), `commandPalette` (gliding selection, resolving rows, AI shimmer), `toastStack` (no outlines, layered stack, swipe to dismiss), `pill` and `tabs` (glyph status dots, gliding indicator, rolling figures), `pageDissolve` (one continuous wave), `imageGenerate` (one develop curve), `accordion` (one spring per panel, hover intent), `pullRefresh` (an iOS-sized ring), `dropzone` (a quiet ring that comes alive on drag-over).
|
|
691
|
-
- The skill gains `references/looks.md`: 22 artwork looks with prompts and settings for wallpapers, posters and hero art.
|
|
692
|
-
- Studio defaults are paper and graphite instead of yellow; the default characters are Classic ASCII.
|
|
693
|
-
|
|
694
|
-
## 4.17: a thinking label that rolls
|
|
695
|
-
|
|
696
|
-
- `thinking` drops the dot bands and the dotted rule. A wave runs through the status label instead, each letter rolling up through glyphs and back in the label's own font; when the answer starts, the label rolls into `Thought for 2s`. New options `roll`, `wave` and `glyphs`; `lines` and `rule` are ignored.
|
|
697
|
-
|
|
698
|
-
## 4.16: smooth shading in every charset, rolling streamed text
|
|
699
|
-
|
|
700
|
-
- Glyph fields gain `blend` (default `'auto'`): with nine or fewer characters, a photo or video mixes neighbouring characters in a fixed 4x4 pattern for the shades between them, so binary, dots and block sets shade like a full ramp. Drawn interface shapes stay crisp. It costs one comparison per cell and never shimmers.
|
|
701
|
-
- `streamText(el, { transition: 'roll' })` rolls each character up through glyphs into the crisp letter, like the odometer, with the caret on the front of the roll.
|
|
702
|
-
|
|
703
|
-
## 4.15: every charset at the same weight, inks from your tokens
|
|
704
|
-
|
|
705
|
-
- `setGlobalLook({ charset })` measures the set's ink against classic ASCII and balances it: block and solid sets draw smaller glyphs and keep their detail, sets without light glyphs ease off, and faint sets draw larger, bolder glyphs. Tones are untouched.
|
|
706
|
-
- Kit inks default to `--ascii-ink-strong`, and with that unset, to the element's own text colour, so components follow the page's tokens instead of a fixed grey.
|
|
707
|
-
- `skeleton` reads its blocks' shapes from CSS (an image's radius; one bar per line of text, as wide as the words) and its shimmer is a continuous wave until `resolve()`.
|
|
708
|
-
- A hidden (`display: none`) field gives its WebGL context to fields on screen.
|
|
709
|
-
|
|
710
|
-
## 4.14: backgrounds in the kit, site-quality components
|
|
711
|
-
|
|
712
|
-
- **`background(el, { preset })`** brings all 26 asciify.org backgrounds into the kit: drift, halftone, wordField and fluid on the GPU field, and 22 canvas presets (caustics, contour, facet, etch, moire, weave, veil, dune, wave, aurora, noise, silk, rain, stars, pulse, grid, voidWell, morph, fire, dna, terrain, circuit). Presets are separate imports, so a page pays only for the ones it uses, and each matches the /backgrounds page's rendering.
|
|
713
|
-
- **The site now runs on the kit.** Every component demo on asciify.org is the kit itself, so what you see is what you install. Where the kit was weaker than the hand-built demos, the kit was raised to match.
|
|
714
|
-
- **Performance and integration:** fields mount lazily near the viewport and release far from it, a page-wide budget keeps at most 12 WebGL contexts live, `poster` shows a still for first paint and as the no-GPU fallback, `setGlobalLook({ charset })` re-skins every glyph field at once with a short sweep, and `update({ ink, look })` merges per instance.
|
|
715
|
-
- **New guide:** [applying-asciify.md](skills/asciify-engine/references/applying-asciify.md) teaches agents the house design: which component or background to use, text over fields, ink, radius, motion, performance and a finishing checklist.
|
|
716
|
-
|
|
717
|
-
## 4.13: the kit — interface components in one call
|
|
718
|
-
|
|
719
|
-
`asciify-engine/kit` turns Asciify's interface components into short, customisable calls on the GPU field. Each one follows its element's size and border radius, takes ink from your CSS variables in light and dark, honours reduced motion, pauses off screen, leaves your HTML working without WebGL2, and cleans up with `destroy()`.
|
|
720
|
-
|
|
721
|
-
```ts
|
|
722
|
-
import { beam, buttonLoading, statusOrb } from 'asciify-engine/kit';
|
|
723
|
-
|
|
724
|
-
const b = beam(card, { palette: 'colorful', mode: 'progress' });
|
|
725
|
-
b.progress(0.6); b.success();
|
|
726
|
-
|
|
727
|
-
const save = buttonLoading(saveButton, { text: 'Save' });
|
|
728
|
-
save.start('Saving'); await request; save.succeed('Saved');
|
|
729
|
-
```
|
|
730
|
-
|
|
731
|
-
Thirty components ship in the kit: beam, decode, odometer, buttonLoading, statusOrb, skeleton, pill, tabs, imageGenerate, voiceOrb, commandPalette, slideToConfirm, likeBurst, blockChart, pageDissolve, swipeDeck, codeInput, toastStack, thinking, streamText, compare, accordion, dropzone, disintegrate, pullRefresh, fieldBackground, wordmark, asciiVideo, artPanel and pricingGlow. Unused components tree-shake away; each adds about 2–4 kB gzipped on top of a 3.5 kB shared core and the `/field` entry. See [kit.md](skills/asciify-engine/references/kit.md), and [components.md](skills/asciify-engine/references/components.md) for the prompt and snippet of every component.
|
|
732
|
-
|
|
733
|
-
## 4.12: halftone fields for interfaces
|
|
734
|
-
|
|
735
|
-
`mark: 'dot'` turns the GPU field into a halftone: one circle per cell, its area following the tone, crisp at any pixel ratio. The new `drift` scene draws a few broad, slow lobes; `clear` keeps the field quiet around an element so copy stays readable; `pulse()` sends a ring through the dots when your interface wants to answer an event.
|
|
736
|
-
|
|
737
|
-
```ts
|
|
738
|
-
const field = mountGlyphField(canvas, {
|
|
739
|
-
mark: 'dot', scene: 'drift', cellSize: 14, dotScale: 0.64,
|
|
740
|
-
ink: '#9a988f', background: 'transparent',
|
|
741
|
-
clear: { element: document.querySelector('#hero .copy') },
|
|
742
|
-
});
|
|
743
|
-
saveButton.addEventListener('click', () => field.pulse({ element: saveButton }));
|
|
744
|
-
```
|
|
745
|
-
|
|
746
|
-
Also in 4.12:
|
|
747
|
-
|
|
748
|
-
- **Even quality across styles.** Character styles stretch each image's tones to its own range and pick glyphs by measured ink, so dark or flat images keep their subject in every character set and look. Set `color.levels: 'none'` for the previous linear mapping. Adaptive resolution only steps down while frames are genuinely over budget, and returns to full detail when motion settles.
|
|
749
|
-
- **Faster fields.** Image sources decode off the main thread and are scaled to what the cells can show; scene maths runs once per cell; shaders compile without blocking. `ready` resolves for fields mounted off screen, and a shader link failure is reported through `onError`.
|
|
750
|
-
- **4.12.1:** fields size themselves from layout, so a parent's transform (a dialog scaling open) no longer stretches them.
|
|
751
|
-
- **31 ready-made components in the agent skill** ([components.md](skills/asciify-engine/references/components.md)): controls, feedback, AI states, transitions, data, media, text and pages, each with the prompt and the reference code. See them running at [asciify.org/components](https://asciify.org/components).
|
|
111
|
+
[MIT](LICENSE) © ayangabryl
|