# ASCII donut

A lit, ray-marched torus spinning in glyphs, with specular highlights, a rim light and drag to spin.

> 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-donut.json](https://asciicn.dev/r/ascii-donut.json), under `files[].content`.

## Installation [#installation]

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

## Usage [#usage]

```tsx
import { AsciiDonut } from "@/components/asciicn/ascii-donut";

export function Spinner() {
  return <AsciiDonut className="aspect-square w-80" />;
}
```

Every sample casts a ray from a perspective camera and marches it into the torus, so the silhouette, the hole and the near side overlapping the far side are exact at any cell size. Braille and quadrants sample 2×4 and 2×2 times per cell, which sharpens the outline further.

The shading is built for glyphs. A key light sets the body tone, a cool rim light from behind outlines the far edge, the inside of the ring sits in soft shadow, and a tight specular highlight is the only place the densest glyphs appear. In truecolor, the body takes `color`, the rim takes `rim`, and highlights go white.

At rest the ring leans toward you in a three-quarter view, with the hole open, and rocks slowly a little either way while it turns on its own axis. It never turns edge-on by itself, and it does not react to hover.

Press and drag to turn it. Let go mid-drag and it coasts, then eases back to its resting view. With keyboard focus, the arrow keys turn it.

## Examples [#examples]

### Ribbed, in Braille [#ribbed-in-braille]

`surface="ribbed"` winds grooves around the tube, which catch the light as the ring turns on its axis.

```tsx title="examples/ascii-donut-ribbed.tsx"
import { AsciiDonut } from "@/components/asciicn/ascii-donut";

export default function AsciiDonutRibbed() {
  return (
    <AsciiDonut
      surface="ribbed"
      charset="braille"
      colorMode="duotone"
      ramp="glacier"
      thickness={0.32}
      radius={0.58}
      spin={0.6}
      cell={9}
      className="aspect-[4/3] w-full max-w-xl"
    />
  );
}
```

### CP437 on a green phosphor [#cp437-on-a-green-phosphor]

Mono with the P1 phosphor and a CRT finish from `fx`.

```tsx title="examples/ascii-donut-terminal.tsx"
import { AsciiDonut } from "@/components/asciicn/ascii-donut";

export default function AsciiDonutTerminal() {
  return (
    <div className="w-full max-w-md border bg-background font-mono text-xs">
      <div className="flex items-center justify-between border-b px-3 py-1.5 text-muted-foreground">
        <span>~/render $ ./ring --cp437</span>
        <span>p1</span>
      </div>
      <AsciiDonut
        charset="cp437"
        colorMode="mono"
        phosphor="p1"
        thickness={0.55}
        floor={false}
        fx={{ bloom: 0.5, scanlines: 0.4, vignette: 0.4 }}
        cell={11}
        className="aspect-square w-full"
      />
    </div>
  );
}
```

## Props [#props]

Every [ASCII image](/docs/images/ascii-image#props) look prop also applies, with `charset` defaulting to `"standard"`, plus `fx` for a CRT finish.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `radius` | `number` | `0.5` | Ring radius as a share of half the box's shorter side. |
| `thickness` | `number` | `0.46` | Tube radius over ring radius, 0.1 to 0.9. |
| `spin` | `number` | `0.4` | Turn around the ring's own axis, in radians per second. Shows on the ribbed surface. |
| `speed` | `number` | `1` | Multiplies the spin and the slow rocking. 0 holds it still until dragged. |
| `surface` | `"smooth" \| "ribbed"` | `"smooth"` | A plain ring, or grooves wound around the tube. |
| `color` | `AsciiColor` | `"orange"` | Body colour in truecolor. |
| `rim` | `AsciiColor` | `"blue"` | Rim light colour in truecolor. |
| `light` | `number` | `315` | Where the key light comes from, in degrees clockwise from the top. |
| `floor` | `boolean` | `true` | A soft shadow on an unseen floor below the ring. |
| `colorMode` | `"mono" \| "truecolor" \| "ansi16" \| "duotone" \| "palette"` | `"truecolor"` | How glyphs are coloured. |
| `draggable` | `boolean` | `true` | Drag or use the arrow keys to turn it. |
| `cell` | `number` | `10` | Glyph cell height in CSS pixels. |
| `animate` | `boolean` | `true` | false draws one still frame. |
| `handle` | `RefObject<AsciiHandle \| null>` |  | Filled with a handle for export. |

## Notes [#notes]

* It is announced as an image. Pass `aria-label` to describe what it stands for on your page.
* Under reduced motion it draws one still frame, in the resting three-quarter view.
* Only a press and drag turns it. Moving the mouse over it changes nothing.
* It draws at up to 60 fps and pauses offscreen and in hidden tabs. Rays that miss the ring's bounding sphere skip the march, so empty space costs almost nothing.