# ASCII Scroll Reveal

> An image that resolves from coarse glyphs to the photo as it scrolls into view.

Category: Images. Docs: https://asciicn.sumak.dev/docs/ascii-scroll-reveal

## Installation

```bash
npx shadcn@latest add @asciicn/ascii-scroll-reveal
```

Needs the `@asciicn` registry in components.json: `"registries": { "@asciicn": "https://asciicn.sumak.dev/r/{name}.json" }`.

## Usage

```tsx
import { AsciiScrollReveal } from "@/components/ascii/ascii-scroll-reveal"

<AsciiScrollReveal
  src="/work/cover.jpg"
  alt="Project cover"
  from={32}
  to={6}
/>
```

## Examples

### Stay in glyphs

`reveal="ascii"` stops at the finest grid instead of fading to the photo. With braille and a palette it reads like a print.

```tsx
import { palettes } from "@/lib/ascii"

<div className="h-72 overflow-y-auto">
  <AsciiScrollReveal
    src="/garden.jpg"
    alt="Marble columns among flowers"
    reveal="ascii"
    charset="braille"
    from={24}
    to={5}
    color={palettes.cyanotype}
  />
</div>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `src` | `string` |  | Image URL. Cross-origin images need CORS headers. |
| `alt` | `string` |  | Accessible label for the canvas. |
| `from` | `number` | `32` | Cell size in pixels as the image enters from below. |
| `to` | `number` | `6` | Cell size once the image's centre reaches the middle of the view. |
| `reveal` | `"photo" | "ascii"` | `"photo"` | Photo cross-fades to the original over the last quarter. ASCII stays glyphs. |
| `onProgress` | `(progress: number) => void` |  | Scroll progress from 0 to 1, at most once per frame. |

Also accepts every `AsciiCanvas` prop:

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `charset` | `CharsetName | string` | `"standard"` | Named ramp or your own string, empty to dense. "braille" packs 2×4 dots per cell, "quadrants" 2×2 blocks. |
| `fontSize` | `number` | `12` | Cell size in CSS pixels. Smaller means more detail. |
| `dither` | `"none" | "bayer" | "floyd"` | `"none"` | Quantisation. Floyd–Steinberg for photos, Bayer for a retro look. |
| `color` | `"ink" | "source" | string[]` | `"ink"` | Ink uses the CSS colour (theme with text-*). Source keeps pixel colours. An array maps tone onto gradient stops, e.g. palettes.thermal. |
| `edges` | `number` | `0` | Outline contours with | / - \ where the tone gradient beats this strength. Try 0.3. Ramps only. |
| `opacity` | `boolean` | `false` | Fade each glyph by its tone. |
| `brightness` | `number` | `0` | Added after contrast, -1 to 1. |
| `contrast` | `number` | `1` | Multiplier around mid grey. |
| `gamma` | `number` | `1` | Tone curve exponent. |
| `invert` | `boolean` | `false` | Swap ink and blank. |
| `animate` | `boolean` | `auto` | On for fields, frames and video. Off under reduced motion, except video. |
| `fps` | `number` | `30` | Frame cap. |
| `ref` | `Ref<AsciiHandle>` |  | Exposes text(), svg(), png() and the canvas. |

## Notes

- **Scroll container.** Progress follows the nearest ancestor that scrolls vertically, or the viewport when there is none. It reaches 1 when the image is centred and stays there as it scrolls out the top.
- **Accessibility.** The canvas has `role="img"` and takes `alt` as its label. The fade-in photo is decorative (`alt=""`), so the label isn't read twice.
- **Motion.** Under `prefers-reduced-motion` the image shows its final state straight away and ignores scrolling.
- **Performance.** Scroll is read once per frame, and only while the image is on screen. The canvas redraws only when the cell size crosses a whole pixel; the photo fade is a CSS variable.
