Skip to content

GlyphScene

<GlyphScene> is the visual host. It owns the <pre> strip, the sibling hit layer, and the CSS variables (--rows, --cell-h, --cell-fs, --dur) that the strip keyframes consume.

<GlyphScene> must be a child of a camera component (<GlyphCamera>, <GlyphPerspectiveCamera>, or <GlyphOrthographicCamera>).

 
Loading…
PropTypeDefaultDescription
mode"wireframe" | "solid" | "voxel""solid"Render mode
colsnumber80Grid width in character columns
rowsnumber24Grid height in character rows
cellAspectnumber2.0Character cell height ÷ width
glyphPalettestring"default"Named glyph palette
useColorsbooleantrueEmit color spans in the output
directionalLightGlyphDirectionalLightDirectional light for solid mode
ambientLightGlyphAmbientLightAmbient fill for solid mode
shadowGlyphShadowOptionsundefinedShadow-map config. undefined = off. Set alongside castShadow/receiveShadow on meshes
transformCellsTransformCellsundefinedTransform the completed cell grid before its single <pre> write
classNamestringCSS class on the outer host
styleCSSPropertiesInline styles on the outer host
childrenReactNode<GlyphMesh>, controls, hotspots

transformCells runs after rasterization, shading, and depth testing, immediately before glyphcss stringifies the grid. The hook may mutate grid.char and grid.color; glyphcss still performs one write to the scene’s <pre> for the completed frame.

In solid mode, polygons with authored uvs also expose grid.surfaceUv. This is an optional interleaved Float32Array containing the perspective-correct UV from the depth-winning surface for each cell:

import { createGlyphScene } from "glyphcss";
import type { TransformCells } from "glyphcss";
const word = Array.from("HOLA");
const mapWordToSurface: TransformCells = (grid) => {
const uv = grid.surfaceUv;
if (!uv) return;
for (let i = 0; i < grid.char.length; i++) {
const u = uv[i * 2];
const v = uv[i * 2 + 1];
if (!Number.isFinite(u) || !Number.isFinite(v)) continue;
grid.char[i] = word[((Math.floor(u * 12) % word.length) + word.length) % word.length];
}
};
const scene = createGlyphScene(host, {
camera,
mode: "solid",
transformCells: mapWordToSurface,
});

surfaceUv uses [u0, v0, u1, v1, ...]. Empty cells and cells whose winning polygon has no UVs contain NaN/non-finite coordinates, so effects must check both components. Values remain in the polygon’s authored coordinate space; they are not clamped to 0..1, and may tile beyond that range. Because the mapping follows the polygon UVs rather than screen rows and columns, patterns rotate and foreshorten with the surface. Treat the grid buffers as callback-scoped; use rasterizeToCells when they must outlive the synchronous transformCells call.

For reusable or animated appearance, prefer a mounted GlyphEffectLayer. Generic layers retain the geometry raster and avoid re-projecting the mesh on parameter-only ticks; transformCells remains the final application-specific escape hatch and runs after those layers.

import {
GlyphPerspectiveCamera,
GlyphScene,
GlyphMesh,
GlyphOrbitControls,
GlyphHotspot,
} from "@glyphcss/react";
import { cubePolygons } from "@glyphcss/core";
const cube = cubePolygons({ center: [0, 0, 0], size: 1, color: "#4488ff" });
export function App() {
return (
<GlyphPerspectiveCamera rotX={25} zoom={50} distance={3}>
<GlyphScene mode="solid" cols={100} rows={30}>
<GlyphOrbitControls drag wheel />
<GlyphMesh polygons={cube}>
<GlyphHotspot
id="top"
at={[0, 0.5, 0]}
size={[3, 2]}
onClick={() => alert("top face")}
/>
</GlyphMesh>
</GlyphScene>
</GlyphPerspectiveCamera>
);
}

Shadows are opt-in. Set shadow on <GlyphScene> to enable the shadow-map pass, then flag individual meshes with castShadow and/or receiveShadow. A mesh with both flags self-shadows. <GlyphGround /> defaults to receiveShadow=true.

import {
GlyphPerspectiveCamera,
GlyphScene,
GlyphMesh,
GlyphGround,
} from "@glyphcss/react";
const shadow = { color: "#000000", opacity: 0.25, lift: 0.05 };
const directionalLight = { direction: [0.5, 0.7, 0.5], intensity: 1 };
export function ShadowDemo() {
return (
<GlyphPerspectiveCamera rotX={45} rotY={30} zoom={50} distance={5}>
<GlyphScene mode="solid" cols={100} rows={30}
directionalLight={directionalLight}
shadow={shadow}
>
<GlyphMesh geometry="dodecahedron" color="#4488ff"
castShadow
receiveShadow
/>
<GlyphGround />
</GlyphScene>
</GlyphPerspectiveCamera>
);
}
FieldTypeDefaultDescription
colorstring"#000000"Shadow tint hex color
opacitynumber0.25Darkness 0..1 toward color
liftnumber0.05Depth bias — prevents self-shadow acne on flat lit surfaces
maxExtendnumber2000Half-extent of the light-space projection volume
MethodDescription
scene.add(polygons, transform?)Register a mesh, returns a GlyphMeshHandle
mesh.setPolygons(polygons)Replace a mesh’s geometry in place
mesh.setTransform(transform)Replace a mesh’s transform
mesh.dispose()Remove a mesh
scene.addHotspot(opts, onClick?)Register a hotspot overlay, returns a GlyphHotspotHandle
scene.setOptions(partial)Update any scene option and trigger a re-render
scene.getOptions()Return a snapshot of current options
scene.rerender()Force an immediate re-rasterize
scene.destroy()Remove the scene DOM and clear all meshes