# Colour modes

How glyphs get their colour: one phosphor ink, the source's own colour, the 16 ANSI colours, a duotone ramp or your theme palette.

> 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).

The charset decides which glyph a cell draws. The colour mode decides what colour it draws in. Every renderer takes the same `colorMode` prop:



| `colorMode` | Each glyph is drawn in                                        |
| ----------- | ------------------------------------------------------------- |
| `mono`      | One ink colour: `--ascii-ink`, a phosphor, or your `ink` prop |
| `truecolor` | The source colour under its cell, graded                      |
| `ansi16`    | That colour snapped to the nearest of the 16 ANSI colours     |
| `duotone`   | A multi-stop ramp, read by the cell's tone                    |
| `palette`   | Your `palette`, from paper to ink, read by tone               |

## Mono and phosphors [#mono-and-phosphors]

`mono` is the default for most components. Glyphs take `--ascii-ink`, which follows your theme's `--primary`, and the background is `--ascii-paper`. Pass `ink` and `paper` to override them, or pick a CRT phosphor:

| `phosphor`  | Ink       | Glass     |
| ----------- | --------- | --------- |
| `p1`        | `#33ff66` | `#020a04` |
| `p3`        | `#ffb000` | `#0c0700` |
| `ibm`       | `#f4f4f5` | `#09090b` |
| `blueprint` | `#38bdf8` | `#020a12` |

```tsx
<AsciiImage src="/portrait.jpg" phosphor="p3" />
```

## Truecolor and grading [#truecolor-and-grading]

`truecolor` gives each glyph the average colour of the samples in its cell, so a photo keeps its colours while the glyphs keep its tones. On the GPU, a field supplies colour through `colorField`; on the CPU, through the `colors` of a tone field (`sampleToField` fills them from images and video).

`grade` shapes tones and colours before anything else happens:

```tsx
<AsciiImage
  src="/street.jpg"
  colorMode="truecolor"
  grade={{ contrast: 1.3, gamma: 1.1, saturation: 1.4 }}
/>
```

* `contrast` multiplies around mid grey. 1 leaves tones alone.
* `brightness` is added after contrast, from -1 to 1.
* `gamma` above 1 lifts the midtones, below 1 sinks them.
* `saturation` scales colour around its luma: 0 is grey.

The same curve runs on the tone (which picks the glyph) and on each colour channel, so a brighter grade draws both denser glyphs and lighter colours.

## ANSI 16 [#ansi-16]

`ansi16` snaps each glyph's colour to the nearest of 16 terminal colours, measured in OKLab so the match follows what the eye sees. Pick the table with `ansi`:

* `vga`: the IBM PC text-mode palette, with its famous brown `#aa5500` in place of dark yellow.
* `xterm`: xterm's defaults, brighter and more saturated.

Frames drawn in `ansi16` export to [ANSI escape art](/docs/engine/export#ansi) that looks the same in any terminal: `toAnsi(frame, { mode: "ansi16" })` writes the classic 30 to 37 and 90 to 97 colour codes.

## Duotone ramps [#duotone-ramps]

`duotone` ignores the source colour and reads each cell's tone through a multi-stop ramp, dark to light. Every ramp starts on a colour that sits well on near-black and ends on one that holds up as the densest glyph:

```tsx title="examples/engine-ramps.tsx"
import {
  AsciiPre,
  fillToneField,
  mapTones,
  createToneField,
  RAMP_NAMES,
} from "@/lib/ascii";

const COLUMNS = 44;

// Each duotone ramp along a left-to-right tone gradient, rendered on the
// server as a <pre>: no canvas, no JavaScript.
const strip = (ramp: (typeof RAMP_NAMES)[number]) => {
  const field = createToneField(COLUMNS, 1, "blocks");
  fillToneField(field, (u) => u);
  return mapTones(field, COLUMNS, 1, {
    charset: "blocks",
    colorMode: "duotone",
    dither: "bayer4",
    ramp,
  });
};

export default function EngineRamps() {
  return (
    <div className="w-full overflow-x-auto rounded-md bg-[#07070b] p-4">
      <dl className="grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-1">
        {RAMP_NAMES.map((ramp) => (
          <div key={ramp} className="contents">
            <dt className="font-mono text-xs text-zinc-400">{ramp}</dt>
            <dd>
              <AsciiPre frame={strip(ramp)} cell={14} aria-hidden="true" />
            </dd>
          </div>
        ))}
      </dl>
    </div>
  );
}
```

```tsx
<AsciiImage src="/portrait.jpg" colorMode="duotone" ramp="glacier" />
<AsciiImage src="/portrait.jpg" colorMode="duotone" ramp={["#001018", "#00a3a3", "#e8fffb"]} />
```

`ramp` takes a name from `RAMP_NAMES` or your own list of `#rrggbb` stops.

## Palette [#palette]

`palette` reads tone through colours you choose, from paper to ink. Shader components take CSS variable names, so the palette follows your theme:

```tsx
<AsciiShader
  field={FIELD}
  colorMode="palette"
  palette={["--background", "--chart-3", "--chart-1"]}
/>
```

`mapTones` on the CPU takes the same idea as byte triples: `palette: [[16, 16, 24], [59, 75, 216], [255, 251, 234]]`.

## Transparent backgrounds [#transparent-backgrounds]

`background="transparent"` draws glyphs only, so the page shows between them. Use it to lay ASCII over a photo, a gradient or another component. `AsciiGrid` is transparent by default; set `paper` to give it a background.

## Theme tokens [#theme-tokens]

Components never hard-code colours. They read these CSS variables from the `ascii-tokens` item:

| Token                  | Default                                                      |
| ---------------------- | ------------------------------------------------------------ |
| `--ascii-ink`          | Your theme's `--primary`                                     |
| `--ascii-paper`        | Your theme's `--background`                                  |
| `--ascii-color-{name}` | Series colours: blue, purple, green, pink, orange, red, grey |
| `--ascii-glow`         | Glow strength, 0 to 1, per theme                             |

Pass a palette name such as `"green"` wherever a component takes an `AsciiColor`, and it resolves to `--ascii-color-green`. `seriesColor(color, index)` gives several lines or labels distinct hues in a fixed order.