Embedding Space Viz
A 2D embedding-space sandbox. Documents are dots clustered by meaning; a
query is a vector pointing out from origin. Three modes — pick from preset
queries (explore), drag a query freely to see live cosine + angle
(investigate), or take a challenge to find a boundary spot where the
top-K result set spans at least two different clusters (challenge).
Select a query to see where it lands in embedding space. Each dot is a document, clustered by meaning.
Use the query buttons above to search embedding space.
Customize
Retrieval
3
Installation
npx shadcn@latest add https://craftbits.dev/r/embedding-space-viz.jsonUsage
import { EmbeddingSpaceViz } from "@craft-bits/viz/embedding-space-viz";
<EmbeddingSpaceViz />Drop in your own document set:
<EmbeddingSpaceViz
documents={[
{ id: "a1", label: "Quicksort", x: 120, y: 140, cluster: "algos" },
{ id: "a2", label: "Heapsort", x: 160, y: 100, cluster: "algos" },
{ id: "p1", label: "Dynamic typing", x: 600, y: 360, cluster: "pl" },
]}
clusterLabels={[
{ label: "Algorithms", x: 140, y: 60 },
{ label: "Languages", x: 600, y: 300 },
]}
clusterColors={{
algos: { fill: "var(--cb-info)", stroke: "var(--cb-info)" },
pl: { fill: "var(--cb-success)", stroke: "var(--cb-success)" },
}}
/>Drive the mode from outside (controlled mode):
const [mode, setMode] = useState("explore");
<EmbeddingSpaceViz mode={mode} onModeChange={setMode} />Understanding the component
- 2D plotted SVG. Documents live at fixed
(x, y)coordinates in SVG user space. Cluster geometry is encoded by the input — there is no layout algorithm, the caller chooses positions. A dashed crosshair marks the canvas centre (origin for the cosine math). - Mode tabs. Three top-level modes —
explore,investigate,challenge. Switching modes clears all interaction state. - Explore mode. Renders preset query pills above the canvas. Selecting one drops a glowing query dot, then after ~800ms (or instantly under reduced motion) the top-K connection lines stagger in with cosine similarity badges at their midpoints.
- Negation demo. A preset query can declare a
negationCompareindex; when that preset is active, a dashed red line draws between the two query positions with the cosine similarity printed — showing that "NOT" barely shifts an embedding. - Investigate mode. The canvas becomes a click-and-drag surface. The query dot follows the pointer, the top-K results recompute on every frame, and a dashed gauge in the centre shows the angle (degrees) and cosine between the query vector and the nearest document.
- Challenge mode. A goal panel asks the visitor to place a query where the top-K spans ≥ 2 clusters.
Start challengeenables dragging;Check placementevaluates and surfaces a pass/fail badge. - Cluster-aware highlights. Hovering a document draws a dashed vector to it from canvas centre; matched documents glow in their cluster's colour; nearby documents fade up proportional to distance.
- Reduced motion. Under
prefers-reduced-motion: reduce, every spring collapses to instant, the connection lines snap in without staggering, and the query-dot sonar pulse does not mount.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
documents | EmbeddingSpaceVizDocument[] | built-in 12-doc set | Documents plotted in embedding space. |
presetQueries | EmbeddingSpaceVizPresetQuery[] | built-in 4-query set | Pills surfaced in explore mode. |
clusterLabels | EmbeddingSpaceVizClusterLabel[] | built-in 3-cluster set | Decorative cluster labels rendered behind dots. |
clusterColors | Record<string, { fill, stroke }> | cooking / tech / travel | Per-cluster colour overrides. |
topK | number | 3 | Number of nearest documents to highlight. |
mode | "explore" | "investigate" | "challenge" | — | Controlled mode. Pair with onModeChange. |
defaultMode | "explore" | "investigate" | "challenge" | "explore" | Uncontrolled initial mode. |
onModeChange | (next: Mode) => void | — | Fires on mode switch. |
viewBoxWidth | number | 800 | SVG viewBox width (user units). |
viewBoxHeight | number | 500 | SVG viewBox height (user units). |
transition | Transition | SPRINGS.snap | Override the spring used for dot / line state transitions. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The SVG is
role="img"with anaria-labelsummarising the layout; per-document labels are decorative. - Mode switching uses
role="tablist"/role="tab"witharia-selectedanddata-statehooks for styling. - A polite live region announces the top results whenever they change.
- Preset queries are real
<button>elements with visible labels, focus rings, and ≥ 32px hit area. - Investigate / challenge modes require pointer input; the live region tells screen-reader users to switch to explore mode for keyboard-accessible queries.
- Colour is never the only signal — matched docs scale up and labels bold, the negation comparison adds a dashed stroke + cosine label, and the cluster labels are text.
- Motion respects
prefers-reduced-motion: reduce.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/systems/EmbeddingSpaceViz.tsx). The source was a lesson component wrapped in the project'sWidgetchrome (with built-in undo/redo history, eyebrow / premise / caption header, and reset button), with hard dependencies onSvgLabel,ModeStrip,ChallengeBtn, andFeedbackBadgefrom the lesson chrome layer, plus per-track palette tokens. The viz extract replaces every palette reference withvar(--cb-*)semantic tokens, re-keys motion to canonicalSPRINGS.snap/SPRINGS.smooth/SPRINGS.bouncyand scalarSTAGGERfrom@craft-bits/core/motion, drops the Widget chrome, replacesSvgLabelwith plain<text>, inlines a localChallengeButton, surfaces the document set / preset queries / cluster labels / colours / topK as props, and exposes the interaction mode as a controlled-or-uncontrolled Radix-style prop.