// Stage 02 — embedding lookup. // Shows a stylized vocab matrix (rows = token ids) with the active row lit up, // then the pulled-out row as a vector of numbers streaming to the right. function StageEmbed({ tokens, activeIdx, progress, active, onExplain }) { const rows = 14; // visible slice of the 50k vocab const cols = 32; // visible slice of d_model const activeId = tokens[activeIdx]?.id ?? 0; const activeRowSlot = activeId % rows; // Deterministic embedding matrix slice based on activeId const rand = React.useMemo(() => seededRand(activeId * 131 + 7), [activeId]); const matrix = React.useMemo(() => { const r = seededRand(activeId * 131 + 7); return Array.from({ length: rows }, () => Array.from({ length: cols }, () => (r() - 0.5) * 2)); }, [activeId]); const vector = matrix[activeRowSlot] || []; // reveal fraction for the extracted vector (how far it has streamed out) const reveal = active ? clamp(progress * 1.1, 0, 1) : 0; // Mouse-tracked parallax on the vocab slab. Same pattern as Stage 03 heads: // rAF-throttled mousemove inside the slab container sets parallax (-1..1 // per axis), which modulates the base rotateX(14deg) and adds a rotateY. // Magnitude kept small (~4-5 deg) so the slab feels responsive without // inducing motion sickness. const slabRef = React.useRef(null); const [parallax, setParallax] = React.useState({ x: 0, y: 0 }); React.useEffect(() => { const el = slabRef.current; if (!el) return; let raf = 0; let pending = { x: 0, y: 0 }; const onMove = (e) => { const r = el.getBoundingClientRect(); pending = { x: ((e.clientX - r.left) / r.width - 0.5) * 2, y: ((e.clientY - r.top) / r.height - 0.5) * 2, }; if (!raf) raf = requestAnimationFrame(() => { raf = 0; setParallax(pending); }); }; const onLeave = () => setParallax({ x: 0, y: 0 }); el.addEventListener('mousemove', onMove); el.addEventListener('mouseleave', onLeave); return () => { el.removeEventListener('mousemove', onMove); el.removeEventListener('mouseleave', onLeave); if (raf) cancelAnimationFrame(raf); }; }, []); // Base rotateX = 14deg. Parallax.y pulls the top toward the camera when // the cursor is near the top (negative y), pushes it back when near the // bottom. Parallax.x tilts left/right. const slabRotateX = 14 - parallax.y * 7; const slabRotateY = parallax.x * 9; return (
{/* The vocab matrix — rendered as a shallow 3D slab so the viewer feels the scale (50000 rows) of the real matrix, not just the 14-row slice we're drawing. Perspective on the outer container, rotateX on the matrix grid, mouse-tracked tilt. */}
vocab_embedding · [50000 × 4096] ↓ row #{String(activeId).padStart(5, '0')}
{matrix.map((row, ri) => row.map((v, ci) => { const isActiveRow = active && ri === activeRowSlot; // Depth fog: top rows fade into the bg. Distance from the // "bottom" row (ri = rows-1). Only applies to non-active rows. const rowDepth = (rows - 1 - ri) / (rows - 1); const fogMul = isActiveRow ? 1 : (1 - rowDepth * 0.6); return ( {fmt(v, 1)} ); }))} {/* active row highlight bar — nested inside the transformed grid so it tilts with the matrix rather than floating flat */} {active && (
)}
{/* Phosphor-green depth fog: fades the top rows into the near-bg. Matches var(--bg) / var(--bg-2) so rows dissolve into the void. */}
{/* arrow */}
{/* Extracted vector, streaming in */}
embedding vector · [4096] "{tokens[activeIdx]?.text.replace(/ /g, '␣') || '—'}"
{Array.from({ length: 16 * 7 }).map((_, i) => { const v = vector[i % vector.length] ?? 0; const appeared = reveal * 16 * 7 > i; return ( {fmt(v, 1)} ); })}
); } window.StageEmbed = StageEmbed;