For the complete documentation index, see 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.

Charsets

The ten built-in glyph sets, how Braille and quadrants pack several samples into one cell, and how to bring your own ramp.

A charset is the set of glyphs a renderer draws tones with. Every component that draws a field takes a charset prop, and the same picture reads very differently in each one:

<AsciiImage src="/portrait.jpg" charset="braille" />

Built-in charsets

charsetGlyphsSamples per cellGood for
standard" .:-=+*#%@"1Photos and anything that should read as ASCII art
dense70 glyphs, . to $1Smooth tone on large grids
blocks" ░▒▓█"1Bold fills, the ANSI-art look
quadrants▘▝▀▖▌▞▛▗▚▐▜▄▙▟█2×2Blocky shapes with twice the detail
braille256 dot patterns, ⠁ to ⣿2×4Fine lines, plots, wireframes
cp437IBM PC symbols, · to █1DOS-era texture
petsciiCommodore lines and blocks1Retro computer screens
katakanaHalf-width katakana and digits1Signage, glyph streams
dots" ·∙•⦁●", five sizes1Halftone print and painted scenes
edge- / \ | on edges, standard inside1Line drawings and outlines

Ramps are ordered by how much ink each glyph puts on the cell in a typical monospace font, so they read correctly even where coverage can't be measured, such as in an AsciiPre rendered on the server. CHARSET_NAMES lists the ids in picker order and CHARSETS[id] holds each set's label, kind, glyphs and sample grid.

Halftone dots

dots draws five round dots after the empty cell, from geometry rather than the font, so they are round in every font. Their radii are 0.07, 0.13, 0.24, 0.33 and 0.42 of the cell. The two smallest are specks: a dark sky drawn with them reads as quiet air, and the large dots stay for the light. With dither set, a tone between two sizes mixes them in a fixed pattern, so gradients don't band.

Set square with dots. Default cells are about twice as tall as wide, so the rows of dots sit twice as far apart as the columns and a picture reads as stripes. Square cells lay the dots on an even lattice. See Cell metrics and Painted scenes.

<AsciiShader
  field={FIELD}
  colorField={COLOR_FIELD}
  charset="dots"
  square
  cell={7}
  dither="bayer4"
/>

Sub-cell charsets

Braille and quadrant glyphs aren't a ramp. Each one is a tiny bitmap: Braille has 8 dots in 2 columns of 4, quadrants have 4 squares in a 2×2 grid. The engine samples the field once per dot, lights each dot whose tone is above threshold (0.5 by default), and packs the lit dots into one glyph. Click the samples below:

Braille glyphs start at U+2800, and each dot adds one bit: dots 1 to 3 run down the left column (0x01, 0x02, 0x04), dots 4 to 6 down the right (0x08, 0x10, 0x20), and dots 7 and 8 sit on the bottom row (0x40, 0x80). brailleChar(mask) builds the glyph and BRAILLE_DOT_BITS[row][column] gives each dot's bit. Quadrants use quadrantChar(mask) with top-left 1, top-right 2, bottom-left 4 and bottom-right 8.

Because the sample grid is finer than the cell grid, a field must not assume one sample per glyph. In GLSL, res is measured in sample rows; on the CPU, createToneField(columns, rows, charset) sizes the field for you.

Lower threshold lights more dots and thickens thin features. With dither set, each dot uses the matrix threshold at its sample instead, which turns soft tones into an even scatter of dots.

Edge glyphs

The edge charset runs a 3×3 Sobel filter on the graded tones. Where the gradient is stronger than edgeThreshold (0.22 by default), the cell draws the glyph that runs across it: | for a left-right change, - for top-bottom, / and \ for diagonals. Everywhere else it falls back to the standard ramp. edgeChar(gx, gy) does the direction step on its own, with y pointing down.

Custom ramps

Pass chars with your own glyphs, from empty to densest. It wins over charset. A ramp needs at least two glyphs; anything shorter falls back to standard.

<AsciiImage src="/logo.png" chars=" .oO@" />

Start with a space so empty tones stay empty. Order the rest by how much ink they put on the cell, not alphabetically. The safest way to check is to look: a smooth gradient should brighten steadily with no glyph that jumps out.

Calibration

A font draws : and ; at almost the same weight, so an evenly spaced ramp wastes levels on glyphs you can't tell apart. The GPU renderer measures how much of the cell each glyph inks in your actual font (the glyph atlas does this once per font and size) and spaces the ramp by that coverage, blended with even spacing so every glyph stays reachable. mapTones on the CPU spaces glyphs evenly by default, which is what you want for exported text, because the reader's font is unknown. So at tone 0.5 the two can pick different glyphs. To match the GPU exactly in the browser, pass positions: charsetRampPositions({ charset, metrics }) to mapTones.

Helpers

HelperReturns
resolveCharset(charset, chars?)The Charset to draw with, custom ramp first
rampChar(charset, tone)The ramp glyph nearest a tone
eighthsBar(value, width)A row of eighth blocks, such as "███▎ " for 0.55 of 6
EIGHTHS_UP, EIGHTHS_RIGHT" ▁▂▃▄▅▆▇█" and " ▏▎▍▌▋▊▉█", for bars and progress
BOX.light / heavy / double / rounded / asciiCorners, edges and crossings for frames
SPINNERSFrame sequences: braille, orbit, arc, bar, quadrant, tick