# React API

`@glyphcss/react` is the React binding for the ASCII paint backend. It is a
thin layer over the imperative `glyphcss` factory API — components register
meshes / hotspots / cameras with the surrounding `<GlyphScene>`; nothing
re-renders per frame.

The React surface mirrors the Vue surface one-to-one. Anything you can do
in React you can do in Vue, with the same option names and defaults.

## Install

```bash
pnpm add @glyphcss/react
```

## Component tree

A scene is always **camera-wraps-scene**. The camera component owns the
projection state; `<GlyphScene>` rasterises into a `<pre>`; meshes, controls
and hotspots are children of the scene.

```tsx
<GlyphPerspectiveCamera rotX={65} rotY={45} zoom={50} distance={3}>
  <GlyphScene mode="solid" cols={100} rows={30}>
    <GlyphOrbitControls drag wheel />
    <GlyphMesh geometry="cube" />
    <GlyphHotspot id="top" at={[0, 0.5, 0]} size={[3, 2]} />
  </GlyphScene>
</GlyphPerspectiveCamera>
```

## Components

| Component | Role |
|---|---|
| [`<GlyphScene>`](/components/glyph-scene) | Root container — owns the `<pre>`, grid, glyph palette, lighting |
| `<GlyphSceneStatic>` | SSR/SSG — renders the compiled `<pre>` with no runtime ([Compiling to Static](/guides/compile)) |
| `<GlyphCamera>` | Default camera (alias for `<GlyphOrthographicCamera>`) |
| `<GlyphPerspectiveCamera>` | Foreshortened projection — needs a `distance` |
| `<GlyphOrthographicCamera>` | Parallel projection — best for iso / voxel scenes |
| `<GlyphMesh>` | Polygon registration. Pass `polygons={…}`, `src="…"`, or `geometry="cube"` |
| [`<GlyphEffectLayer>`](/guides/effects) | Scene-root retained appearance layer; exposes its stable core handle through a ref |
| `<GlyphGround>` | Horizontal ground plane. `receiveShadow` defaults to `true`; `castShadow` defaults to `false` |
| `<GlyphHotspot>` | 3D-anchored DOM hotspot — projects to a screen cell |
| `<GlyphOrbitControls>` | Drag-rotate + wheel-zoom around the target |
| `<GlyphMapControls>` | Drag-pan + wheel-zoom across the target plane |
| `<GlyphFirstPersonControls>` | WASD + pointer-lock first-person camera |
| `<GlyphAxesHelper>` | Renders world axes as a mesh |
| `<GlyphDirectionalLightHelper>` | Visualises the directional light vector |

## Hooks

| Hook | Returns |
|---|---|
| `useGlyphSceneContext()` | The scene + camera handles available to any descendant of `<GlyphScene>` |
| `useGlyphCamera()` | The camera handle from the surrounding camera component |
| `useGlyphMesh(polygons, options?)` | Imperative handle for a mesh (lower-level than `<GlyphMesh>`) |
| `useGlyphAnimation(opts)` | Drive a `GlyphAnimationClip` from a mesh handle |

## `<GlyphMesh>` shortcuts

`<GlyphMesh>` accepts three mutually-exclusive geometry inputs, in
descending precedence:

```tsx
// 1. Explicit polygons — full control
<GlyphMesh polygons={cubePolygons({ size: 1, color: "#4488ff" })} />

// 2. File URL — fetched + parsed by the runtime (OBJ / GLB / glTF / VOX)
<GlyphMesh src="/models/teapot.obj" />

// 3. Built-in geometry name — resolves via @glyphcss/core registry
<GlyphMesh geometry="dodecahedron" size={1.2} color="#ffcc44" />
```

The transform props (`position`, `scale`, `rotation`) apply to whichever
source you pick. `rotation` is an XYZ Euler triple in degrees.

Shadow props on `<GlyphMesh>`:

| Prop | Type | Default | Description |
|---|---|---|---|
| `castShadow` | `boolean` | `false` | This mesh casts shadows onto `receiveShadow` surfaces |
| `receiveShadow` | `boolean` | `false` | This mesh displays shadows from `castShadow` meshes. A mesh that is both casts and receives self-shadows |

Detail / density props on `<GlyphMesh>` (see the [Density & Detail guide](/guides/density/)):

| Prop | Type | Default | Description |
|---|---|---|---|
| `density` | `number` | `1` | Render this mesh at `density`× the scene resolution, in its own `<pre>` |
| `fontSize` | `number \| string` | — | Explicit cell size (px or CSS length). Overrides `density` |
| `lineHeight` | `number` | — | Explicit cell line-height. Overrides `density` |
| `transparent` | `boolean` | `false` | See-through: doesn't occlude / isn't occluded |

Shadow prop on `<GlyphScene>`:

| Prop | Type | Default | Description |
|---|---|---|---|
| `shadow` | `GlyphShadowOptions` | `undefined` | Shadow-map config. `undefined` = shadows off. See [`GlyphShadowOptions`](/api/types#shadows) |

## End-to-end example

```tsx
import {
  GlyphPerspectiveCamera,
  GlyphScene,
  GlyphMesh,
  GlyphHotspot,
  GlyphOrbitControls,
  GlyphAxesHelper,
} from "@glyphcss/react";

const directionalLight = { direction: [0.5, 0.7, 0.5], intensity: 1 };
const ambientLight = { intensity: 0.4 };

export function App() {
  return (
    <GlyphPerspectiveCamera rotX={65} rotY={45} zoom={50} distance={3}>
      <GlyphScene
        mode="solid"
        cols={120}
        rows={36}
        directionalLight={directionalLight}
        ambientLight={ambientLight}
      >
        <GlyphOrbitControls drag wheel />
        <GlyphAxesHelper size={1.5} />
        <GlyphMesh geometry="dodecahedron" color="#4488ff">
          <GlyphHotspot id="top" at={[0, 0.5, 0]} size={[3, 2]}>
            <span className="badge">top</span>
          </GlyphHotspot>
        </GlyphMesh>
      </GlyphScene>
    </GlyphPerspectiveCamera>
  );
}
```

## Re-exports from `@glyphcss/core`

`@glyphcss/react` re-exports a curated slice of `@glyphcss/core` — the polygon
factories (`cubePolygons`, `dodecahedronPolygons`, …), the `resolveGeometry`
registry, the math primitives, and the mesh parsers (`loadMesh`, `parseObj`,
`parseGltf`, `parseVox`, `parseStl`). It is not the *whole* surface: mesh ops
such as `decimatePolygons`, `dedupeOverlappingPolygons` and `recenterPolygons`,
plus the quaternion helpers, are only in `@glyphcss/core` — import those from
there directly.

## See also

- [Vue API](/api/vue) — same surface, idiomatic Vue
- [HTML API](/api/html) — `<glyph-*>` custom elements
- [Headless API](/api/headless) — the imperative factory the bindings wrap
