# Black hole

A black hole seen close to edge-on: a thin disk lensed over and under a black shadow, a sharp photon ring and slow streaks flowing round.

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

## Installation [#installation]

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

## Usage [#usage]

```tsx
import { BlackHole } from "@/components/asciicn/black-hole";

export function Hero() {
  return (
    <section className="relative isolate h-[560px]">
      <BlackHole className="absolute inset-0 -z-10" />
    </section>
  );
}
```

The scene runs on the GPU as one field. Each sample sends a ray from a distant camera and bends it through the curved space around the hole. Where the ray crosses the plane of the disk, it picks up light. Rays that pass close to the hole and fall in leave the shadow empty.

The arcs over and under the shadow are the far side of the disk. Light from behind the hole bends round it and reaches you from above and below, so the disk seems to wrap the shadow. A thin photon ring sits on the shadow's edge, one or two samples wide in any charset.

The disk orbits anticlockwise seen from above, so its left side comes toward you. That side is brighter and hotter, which is called Doppler beaming. Each radius turns at its own rate, so the streaks shear slowly as they flow. Stars behind the hole stay single glyphs far out and stretch into short arcs near the edge.

The hole stays round at any aspect ratio. In a wide box the disk fits across. In a tall or narrow box the disk runs off both sides and the shadow keeps a fair share of the width.

## Examples [#examples]

### Behind a heading [#behind-a-heading]

`scale` shrinks the hole so the heading sits on clear ground below it. The heading is real text on top; the scene sits behind it at `-z-10`.

```tsx title="examples/black-hole-hero.tsx"
import { BlackHole } from "@/components/asciicn/black-hole";

export default function BlackHoleHero() {
  return (
    <section className="relative isolate flex h-[500px] w-full flex-col items-center justify-end overflow-hidden pb-8">
      <BlackHole scale={0.62} speed={0.7} className="absolute inset-0 -z-10" />
      <div className="text-center">
        <p className="text-muted-foreground font-mono text-xs tracking-[0.3em] uppercase">
          Event horizon
        </p>
        <h3 className="mt-2 text-3xl font-medium tracking-[-0.03em] text-balance">
          Everything falls in eventually
        </h3>
      </div>
    </section>
  );
}
```

### Braille, warm tint [#braille-warm-tint]

Braille packs eight dots into each glyph, so the photon ring and the streaks get much finer. `tint="warm"` swaps the theme colours for a fixed palette from deep red to white-hot.

```tsx title="examples/black-hole-braille.tsx"
import { BlackHole } from "@/components/asciicn/black-hole";

export default function BlackHoleBraille() {
  return (
    <BlackHole
      charset="braille"
      tint="warm"
      tilt={12}
      cell={10}
      scale={1.2}
      className="aspect-video w-full max-w-2xl"
    />
  );
}
```

### Tilt [#tilt]

`tilt` is the angle above the disk plane. At 0 the near disk is a straight line across the shadow. Higher values open the disk into an ellipse and push the lensed arc up.

```tsx title="examples/black-hole-tilts.tsx"
import { BlackHole } from "@/components/asciicn/black-hole";

const TILTS = [0, 8, 24] as const;

export default function BlackHoleTilts() {
  return (
    <div className="grid w-full grid-cols-1 gap-3 sm:grid-cols-3">
      {TILTS.map((tilt) => (
        <figure
          key={tilt}
          className="relative isolate aspect-[4/3] overflow-hidden rounded-md border"
        >
          <BlackHole tilt={tilt} cell={10} className="absolute inset-0 -z-10" />
          <figcaption className="bg-background/80 absolute bottom-2 left-2 rounded-md px-2 py-0.5 font-mono text-xs">
            tilt={"{"}
            {tilt}
            {"}"}
          </figcaption>
        </figure>
      ))}
    </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. `dither` defaults to `"bayer4"` for Braille and quadrants, so the faint outer disk keeps a dot gradient.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `tilt` | `number` | `8` | How far above the disk plane you look from, in degrees. 0 is edge-on. |
| `speed` | `number` | `1` | How fast the disk turns. 0 holds it still. |
| `diskSize` | `number` | `1` | Outer radius of the disk, from 0.5 to 2 times the default. |
| `scale` | `number` | `1` | How much the hole fills the box, from 0.5 to 1.5. |
| `beaming` | `number` | `1` | How much brighter the side turning toward you is, from 0 (even) to 2. |
| `tint` | `"theme" \| "warm"` | `"theme"` | "theme" draws ink with an amber-hot inner disk. "warm" uses a fixed palette from deep red to white-hot. |
| `stars` | `boolean` | `true` | Stars behind the hole, bent and stretched near its edge. |
| `ink` | `AsciiColor` | `"--ascii-ink"` | The cool outer disk and the stars in the theme tint. Also the glyph colour in mono. |
| `accent` | `AsciiColor` | `"orange"` | The hot inner disk and the photon ring in the theme tint. |
| `charset` | `CharsetName` | `"standard"` | Which glyphs draw the tones. |
| `colorMode` | `"mono" \| "truecolor" \| "ansi16" \| "duotone" \| "palette"` | `"truecolor"` | How glyphs are coloured. "mono" when a phosphor is set and no mode is. |
| `paper` | `AsciiColor` | `"--ascii-paper"` | The surface behind the glyphs, or the phosphor's glass. |
| `cell` | `number` | `12` | Glyph cell height in CSS pixels. |
| `fps` | `number` | `30` | Frame rate cap. |
| `stillTime` | `number` | `6` | The moment drawn under reduced motion, in seconds. |
| `fallback` | `ReactNode` | `a ring and a disk` | Shown without WebGL and until the first frame. |
| `handle` | `RefObject<AsciiHandle \| null>` |  | Filled with a handle for export. |

## Notes [#notes]

* The canvas is decorative and hidden from assistive technology. Put your real heading and copy beside or on top of it.
* Under reduced motion it draws one still frame at `stillTime`. It pauses offscreen and in hidden tabs.
* It ignores the pointer, so moving the mouse over it changes nothing.
* Every sample traces its own ray, up to 128 steps. A full-screen Braille canvas is the heaviest case; a ramp charset or a larger `cell` costs much less.
* In light mode the shadow is the paper, so it reads as a white hole in dark glyphs. The warm tint deepens its colours on light paper so amber stays visible.
* Without WebGL2 it shows `fallback`: by default an amber ring and a band in the theme's colours.