Skip to content

GlyphHotspot

<GlyphHotspot> renders a real, absolutely-positioned <div> over the ASCII strip, tracking a 3D anchor through every baked frame. The <div> is pointer-events: auto inside an otherwise transparent hit layer — events fire on it like any normal DOM element.

PropTypeDefaultDescription
idstringrequiredStable identifier for this hotspot
atVec3 | stringrequiredWorld-space anchor. Either [x, y, z] or a string like "vertex:42"
size[number, number][1, 1]Hitbox size in character cells
onClickMouseEventHandlerClick handler
aria-labelstringAccessibility label
classNamestringCSS class
childrenReactNodeRendered inside the <div> (use for tooltips/badges)

For an animated scene, the hotspot is projected once per frame during the bake. The resulting positions are emitted as a @keyframes rule with 0%, 1.667%, 3.333%, …, 100% waypoints — matching the strip’s steps(60, end) exactly.

When the user drags, the strip’s animation pauses and the hotspot’s transform is live-updated per pointermove. On release, both animations re-start from the new camera state.

import {
GlyphCamera,
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 HotspotDemo() {
return (
<GlyphCamera rotX={25}>
<GlyphScene mode="solid" cols={100} rows={30}>
<GlyphOrbitControls />
<GlyphMesh polygons={cube}>
{/* Hotspot on the top face */}
<GlyphHotspot
id="top"
at={[0, 0.5, 0]}
size={[5, 3]}
aria-label="Top face"
onClick={() => alert("top face clicked")}
>
<span className="badge">Top</span>
</GlyphHotspot>
{/* Hotspot on the right face */}
<GlyphHotspot
id="right"
at={[0.5, 0, 0]}
size={[5, 3]}
aria-label="Right face"
onClick={() => alert("right face clicked")}
>
<span className="badge">Right</span>
</GlyphHotspot>
</GlyphMesh>
</GlyphScene>
</GlyphCamera>
);
}

Hotspots behind the camera get opacity: 0 automatically. Add a CSS transition to fade them gracefully:

.glyph-hotspot {
transition: opacity 200ms ease;
}

The fade is free — the renderer writes opacity: 0 into the keyframe at any frame where the anchor is behind the camera.

at accepts either an explicit Vec3 or a string anchor:

AnchorResolves to
[x, y, z]World-space coordinate
"vertex:42"Vertex index 42 of the parent mesh
"face:roof"Named face (requires mesh authoring)
"centroid"Mesh centroid