# ASCII text

Display type drawn as ASCII art in any charset, with an optional decode, scan or wipe reveal, while the real text stays in the page.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown variants are available by appending `.md` to any URL or sending an `Accept: text/markdown` header. The asciicn agent skill, for building any ASCII effect on the engine, is at [/.well-known/agent-skills/asciicn/SKILL.md](/.well-known/agent-skills/asciicn/SKILL.md).

> **This is an example, not the only option.** It is one component built on the asciicn engine. Install it when it is what you were asked for. For any other look, don't stretch its props to fit: write your own field on `AsciiShader` ([Write your own](/docs/engine/write-your-own.md)), or draw a CPU grid with `AsciiGrid`, and read this example's source for technique. Its full source is in [https://asciicn.dev/r/ascii-text.json](https://asciicn.dev/r/ascii-text.json), under `files[].content`.

## Installation [#installation]

```bash
npx shadcn@latest add https://asciicn.dev/r/ascii-text.json
```

## Usage [#usage]

```tsx
import { AsciiText } from "@/components/asciicn/ascii-text";

export function Hero() {
  return (
    <AsciiText as="h1" charset="braille" reveal="decode" className="font-bold">
      Ship it
    </AsciiText>
  );
}
```

`AsciiText` draws your text in the element's own font (family, weight and style come from its `className`) into an offscreen canvas at `size` pixels. It averages the coverage of every glyph sample, builds a tone field and maps it to glyphs with the engine's `mapTones`, the same mapping every asciicn renderer uses. The art is a `<pre>` of real characters, so it stays sharp at any zoom and you can select and copy it.

The charset sets the look. `braille` packs 2×4 dots into each cell and gives the smoothest large type. `blocks` and `quadrants` give chunky banners. `standard` and `dense` give classic character art. `edge` traces the letters' outlines with `- / | \`.

## Examples [#examples]

### Duotone banner [#duotone-banner]

`blocks` with `colorMode="duotone"` colours every glyph by its density along a ramp, here `outrun`. A line break in the text starts a second line.

```tsx title="examples/ascii-text-banner.tsx"
import { AsciiText } from "@/components/asciicn/ascii-text";

export default function AsciiTextBanner() {
  return (
    <AsciiText
      as="h3"
      charset="blocks"
      colorMode="duotone"
      ramp="outrun"
      cell={10}
      size={88}
      className="w-full font-display font-extrabold tracking-tight"
    >
      {"SYSTEM\nONLINE"}
    </AsciiText>
  );
}
```

### Outlines [#outlines]

The `edge` charset runs a Sobel filter over the sampled type and draws a directional glyph wherever the edge is strong. Lower `edgeThreshold` picks up softer edges.

```tsx title="examples/ascii-text-outline.tsx"
import { AsciiText } from "@/components/asciicn/ascii-text";

export default function AsciiTextOutline() {
  return (
    <AsciiText
      as="h3"
      charset="edge"
      edgeThreshold={0.18}
      color="blue"
      cell={9}
      size={150}
      className="w-full font-sans font-black"
    >
      Edge
    </AsciiText>
  );
}
```

### Reveal on hover [#reveal-on-hover]

`trigger="hover"` shows the finished art and replays the reveal each time the pointer enters. Here each link wipes in from the left through quadrant glyphs. `fit={false}` keeps every label at the same size.

```tsx title="examples/ascii-text-hover.tsx"
import { AsciiText } from "@/components/asciicn/ascii-text";

const LINKS = ["Docs", "Studio", "Scenes"] as const;

export default function AsciiTextHover() {
  return (
    <nav aria-label="Sections" className="flex flex-wrap justify-center gap-8">
      {LINKS.map((label) => (
        <a
          key={label}
          href="#top"
          className="rounded-sm outline-offset-4 focus-visible:outline-2 focus-visible:outline-ring"
        >
          <AsciiText
            as="span"
            charset="quadrants"
            reveal="wipe"
            trigger="hover"
            duration={0.6}
            cell={6}
            size={56}
            fit={false}
            className="font-display font-bold"
          >
            {label}
          </AsciiText>
        </a>
      ))}
    </nav>
  );
}
```

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `string` |  | The text to draw. Line breaks start new lines. |
| `as` | `"h1" \| "h2" \| "h3" \| "h4" \| "p" \| "div" \| "span"` | `"h2"` | The element that holds the real text. |
| `size` | `number` | `96` | Font size the type is drawn at, in CSS pixels, before it is sampled into cells. |
| `cell` | `number` | `8` | Glyph cell height in CSS pixels. Smaller cells draw finer type. |
| `charset` | `CharsetName` | `"braille"` | Glyphs the type is drawn with: standard, dense, blocks, quadrants, braille, cp437, petscii, katakana, dots or edge. |
| `chars` | `string` |  | A custom ramp from empty to dense, such as " .oO@". Wins over charset. |
| `colorMode` | `"mono" \| "duotone"` | `"mono"` | mono draws every glyph in color. duotone colours glyphs by density along ramp. |
| `ramp` | `RampName` | `"mainframe"` | The duotone ramp: mainframe, sodium, heliograph, infrared, outrun, orchid, glacier, ember, toxic or vapor. |
| `color` | `AsciiColor` | `"ink"` | Glyph colour in mono mode: a palette name, a CSS variable name or any CSS colour. |
| `threshold` | `number` | `0.5` | Braille and quadrants: the coverage that lights a dot, 0 to 1. |
| `edgeThreshold` | `number` | `0.22` | Edge charset: gradient strength, 0 to 1, that draws a directional glyph. |
| `dither` | `"none" \| "bayer2" \| "bayer4" \| "bayer8" \| "blue-noise"` | `"none"` | Ordered dithering between glyph levels. |
| `reveal` | `"none" \| "decode" \| "scan" \| "wipe"` | `"none"` | decode cycles inked cells through random glyphs until they settle. scan writes rows from the top. wipe writes columns from the left behind a ragged edge. |
| `trigger` | `"mount" \| "view" \| "hover"` | `"view"` | When the reveal plays. hover shows the finished art and replays the reveal on each pointer enter. |
| `duration` | `number` | `1.2` | Reveal length in seconds. |
| `fit` | `boolean` | `true` | Shrink the type to fit the container's width. |

Other props pass through to the element.

## Notes [#notes]

* The real text is always in the element. It shows as plain type during server rendering and until the art is drawn, then becomes visually hidden, so screen readers, search engines and find-in-page read the words while sighted readers see the art. The art itself is `aria-hidden`.
* The art redraws when the element resizes and when web fonts finish loading, so it always uses the font you set.
* With reduced motion, the reveal is skipped and the finished art shows at once.
* Drawing is one canvas pass when the text, size or charset changes. Nothing runs per frame except during a reveal.