Semantic Cube

Maps natural language onto a three-axis semantic space grounded in up-hierarchy theory. An LLM parses input into structured JSON; the app renders the result as an interactive 3D cube.

Purpose

Meaning is not a fixed definition — it is a position in a multidimensional space describing how a signal propagates from bodily, proximal experience toward broader contextual integration. The cube makes that position visible.

UI Components

Settings Panel

3D Cube (main area)

Input Bar (bottom)

Status / Error Area

Three Axes

Axis Range Meaning
STREAM 0.0–1.0 0 = proximal/bodily, 1 = distal/symbolic
FACTUAL 0.0–1.0 0 = no coherence beyond origin, 1 = confirmed across scales
CHARGE −1.0–1.0 −1 = propagation blocked, 0 = neutral, 1 = open/expansive

Dependencies

Package Version Purpose
react 19 UI framework
react-dom 19 DOM rendering
@react-three/fiber 9 React renderer for Three.js
@react-three/drei 10 OrbitControls, Text, Html helpers
three 0.175 3D engine (peer dep)
esm.sh/tsx In-browser TSX compiler (script loader)
daisyui 5 Component library with light/dark theming
@tailwindcss/browser 4 Utility classes

LLM Integration

Provider Detection

Checks localStorage for keys in order: OPENAI_API_KEY → DEEPSEEK_API_KEY → GEMINI_API_KEY. Uses first found.

Provider Endpoint Model
OpenAI https://api.openai.com/v1/chat/completions gpt-4o
DeepSeek https://api.deepseek.com/v1/chat/completions deepseek-chat
Gemini https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent

Conversation History

Maintains a messages array across submissions. Each submission appends {role: "user", content: input} and the LLM response {role: "assistant", content: rawJson}. The full history is sent on every call so the LLM can coherently expand and reposition all paths.

System Prompt Compression

On load, the raw SYSTEM_PROMPT string is hashed with crypto.subtle.digest('SHA-256', ...). The hash is compared to localStorage['semantic-cube:prompt-hash']. On mismatch or missing, calls https://tokenshrink.com/api/compress and caches both hash and compressed prompt in localStorage. All LLM calls use the compressed prompt.

JSON Schema

{
  "input": "raw user input",
  "concept": "concept being mapped",
  "paths": [
    {
      "id": "what this path represents",
      "nodes": [
        { "factual": 0.0, "charge": 0.0, "stream": 0.0, "label": "..." }
      ]
    }
  ]
}

Key Functions

Function Responsibility
hashPrompt(text) SHA-256 hash as hex string
getCompressedPrompt() Returns cached prompt or fetches from tokenshrink
detectProvider() Reads localStorage, returns {type, key}
callLLM(messages, systemPrompt) Dispatches to correct provider adapter
callOpenAI / callDeepSeek OpenAI-compatible chat completions
callGemini Gemini generateContent adapter
parseResponse(text) JSON.parse with error handling
Scene R3F Canvas with cube, axes, paths, controls
CubeFrame Wireframe unit cube
AxisLabels Text labels at axis ends
PathObjects Spheres and lines for all paths
NodeTooltip Html overlay on hover
App Root component, manages state

Theming

DaisyUI v5 CSS is loaded via CDN. An inline script sets data-theme on <html> to light or dark based on prefers-color-scheme and listens for changes. The Three.js Canvas uses gl={{ alpha: true }} so the DaisyUI bg-base-200 of its parent div shows through as the scene background.

User Workflow

  1. First load → settings panel if no key stored
  2. Enter key → saved to localStorage
  3. Type input → Submit
  4. App compresses prompt (once, cached), builds message history, calls LLM
  5. LLM returns full JSON with all paths so far
  6. Scene re-renders with updated paths
  7. Repeat — each submission adds to history; LLM maintains semantic coherence across all paths
  8. Reset → clears messages array and scene

Future Directions: Alternative Visualizations

  1. Parallel coordinates — three vertical rails (factual, charge, stream); each node is a polyline crossing all three. Patterns across many paths become visible as crossing/parallel lines.

  2. 2D projection triptych — three side-by-side scatter plots showing each pair of axes (factual/charge, factual/stream, charge/stream). Useful for reading two axes at a time without 3D navigation.

  3. Ternary triangle — equilateral triangle, each vertex = one axis pole. Node position reflects the interplay of all three simultaneously. Good for single-concept snapshots.

  4. Radial/compass — charge as angle (−1 = south, +1 = north), factual as radius, stream as color temperature. One circle per path; journeys draw arcs.

  5. Body silhouette — stream → body region (head/chest/gut), factual → field density (close/diffuse), charge → color temperature (warm/cool). Meaning reads as a somatic field.

  6. Gauge dashboard — three arc gauges + a bidirectional charge bar. Minimal, readable at a glance. Best for single-node responses; journeys step through nodes.

  7. Typographic layout — node labels as text, positioned vertically by stream, sized by factual, colored by charge. No geometry — meaning reads through the words themselves.

  8. Sankey/flow ribbon — for journeys (one path, many nodes): nodes as columns, flow width = charge magnitude, vertical band = factual range. Shows propagation as a physical flow.

  9. Force-directed graph — nodes as points attracting/repelling by coordinate proximity. Paths as edges. Across multiple submissions, semantic clusters emerge spatially.

  10. Timeline strip — x = submission order, y = stream value, dot size = factual, dot color = charge. Shows how the space being explored evolves across a session.