Hit Layer Interactivity
The glyphcss hit layer is sparse — you opt-in to interactivity by registering hotspots at specific 3D anchors. No polygon-per-DOM-node overhead.
Why sparse
Section titled “Why sparse”A glyph-style “one DOM node per polygon” model gives you per-polygon events but
balloons the DOM for large meshes (a 10k-triangle GLB → 10k <div>s). glyphcss takes
the opposite trade: a single <pre> for visuals, and only the hotspots you
explicitly register become DOM nodes.
For most real apps this is what you want. You don’t need click handlers on every roof shingle; you need them on the front door.
Authoring hotspots
Section titled “Authoring hotspots”import { GlyphCamera, GlyphScene, GlyphMesh, GlyphHotspot } from "@glyphcss/react";import { octahedronPolygons } from "@glyphcss/core";
const shape = octahedronPolygons({ center: [0, 0, 0], size: 1, color: "#ffcc44" });
<GlyphCamera rotX={25}> <GlyphScene mode="solid" cols={80} rows={24}> <GlyphMesh polygons={shape}> <GlyphHotspot at={[0, 1, 0]} onClick={() => alert("top vertex")}> <span className="badge">Top</span> </GlyphHotspot> <GlyphHotspot at={[0, -1, 0]} onClick={() => alert("bottom vertex")}> <span className="badge">Bottom</span> </GlyphHotspot> </GlyphMesh> </GlyphScene></GlyphCamera>Each <GlyphHotspot> becomes:
<div class="glyph-demo__hotspot" role="button" style="position: absolute; transform: translate3d(...); width: ...; height: ..."> <span class="badge">Roof</span></div>The transform: translate3d(...) updates every frame via @keyframes matching the
strip’s steps(60, end) timing.
Anchors
Section titled “Anchors”at accepts either an explicit Vec3 or a string anchor:
| Anchor | Resolves 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 |
Visibility
Section titled “Visibility”Hotspots behind the camera get opacity: 0 automatically. Use this for “show
this label only when the user is looking at the front of the house”:
.glyph-demo__hotspot { transition: opacity 200ms ease;}The fade is free — the renderer just writes opacity: 0 into the keyframe at any
frame where the anchor is behind the camera.