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>).
| Prop | Type | Default | Description |
|---|---|---|---|
mode | "wireframe" | "solid" | "voxel" | "solid" | Render mode |
cols | number | 80 | Grid width in character columns |
rows | number | 24 | Grid height in character rows |
cellAspect | number | 2.0 | Character cell height ÷ width |
glyphPalette | string | "default" | Named glyph palette |
useColors | boolean | true | Emit color spans in the output |
directionalLight | GlyphDirectionalLight | — | Directional light for solid mode |
ambientLight | GlyphAmbientLight | — | Ambient fill for solid mode |
shadow | GlyphShadowOptions | undefined | Shadow-map config. undefined = off. Set alongside castShadow/receiveShadow on meshes |
transformCells | TransformCells | undefined | Transform the completed cell grid before its single <pre> write |
className | string | — | CSS class on the outer host |
style | CSSProperties | — | Inline styles on the outer host |
children | ReactNode | — | <GlyphMesh>, controls, hotspots |
Cell transforms and surface UVs
Section titled “Cell transforms and surface UVs”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.
Full examples
Section titled “Full examples”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> );}<template> <GlyphPerspectiveCamera :rot-x="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]" @click="() => alert('top face')" /> </GlyphMesh> </GlyphScene> </GlyphPerspectiveCamera></template>
<script setup lang="ts">import { GlyphPerspectiveCamera, GlyphScene, GlyphMesh, GlyphOrbitControls, GlyphHotspot,} from "@glyphcss/vue";import { cubePolygons } from "@glyphcss/core";
const cube = cubePolygons({ center: [0, 0, 0], size: 1, color: "#4488ff" });</script>import { createGlyphPerspectiveCamera, createGlyphScene, createGlyphOrbitControls,} from "glyphcss";import { cubePolygons } from "@glyphcss/core";
const host = document.querySelector<HTMLElement>("#scene")!;
const camera = createGlyphPerspectiveCamera({ rotX: 25, zoom: 50, distance: 3 });const scene = createGlyphScene(host, { camera, mode: "solid", cols: 100, rows: 30,});
scene.add(cubePolygons({ center: [0, 0, 0], size: 1, color: "#4488ff" }));
scene.addHotspot( { id: "top", at: [0, 0.5, 0], size: [3, 2] }, () => alert("top face"),);
const controls = createGlyphOrbitControls(scene, { drag: true, wheel: true });
// Later, when done:// controls.destroy();// scene.destroy();<script type="module" src="https://esm.sh/glyphcss/elements"></script>
<glyph-camera> <glyph-scene mode="solid"> <glyph-mesh geometry="cuboctahedron"></glyph-mesh> <glyph-orbit-controls drag wheel></glyph-orbit-controls> </glyph-scene></glyph-camera>Shadows
Section titled “Shadows”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> );}<template> <GlyphPerspectiveCamera :rot-x="45" :rot-y="30" :zoom="50" :distance="5"> <GlyphScene mode="solid" :cols="100" :rows="30" :directional-light="directionalLight" :shadow="shadow" > <GlyphMesh geometry="dodecahedron" color="#4488ff" cast-shadow receive-shadow /> <GlyphGround /> </GlyphScene> </GlyphPerspectiveCamera></template>
<script setup lang="ts">import { GlyphPerspectiveCamera, GlyphScene, GlyphMesh, GlyphGround,} from "@glyphcss/vue";
const shadow = { color: "#000000", opacity: 0.25, lift: 0.05 };const directionalLight = { direction: [0.5, 0.7, 0.5], intensity: 1 };</script><glyph-perspective-camera rot-x="45" rot-y="30" zoom="50" distance="5"> <glyph-scene mode="solid" cols="100" rows="30" directional-intensity="1" shadow shadow-color="#000000" shadow-opacity="0.25" shadow-lift="0.05" > <glyph-mesh geometry="dodecahedron" color="#4488ff" cast-shadow receive-shadow ></glyph-mesh> <glyph-mesh geometry="ground" receive-shadow></glyph-mesh> </glyph-scene></glyph-perspective-camera>GlyphShadowOptions fields
Section titled “GlyphShadowOptions fields”| Field | Type | Default | Description |
|---|---|---|---|
color | string | "#000000" | Shadow tint hex color |
opacity | number | 0.25 | Darkness 0..1 toward color |
lift | number | 0.05 | Depth bias — prevents self-shadow acne on flat lit surfaces |
maxExtend | number | 2000 | Half-extent of the light-space projection volume |
Lifecycle methods (vanilla)
Section titled “Lifecycle methods (vanilla)”| Method | Description |
|---|---|
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 |