| name | embedding-visualizer |
| description | Explore high-dimensional text embeddings visually. Use when you need to: upload texts and compute embeddings, reduce dimensions with UMAP or t-SNE, explore semantic clusters in 2D or 3D scatter plots, find nearest neighbors by text query, label clusters, or compare two datasets side-by-side. Triggers include "visualize embeddings", "explore semantic clusters", "find similar texts", "embedding visualization", "UMAP plot", or any task requiring interactive exploration of text embedding spaces. |
embedding-visualizer
Self-hosted tool for exploring text embeddings visually. Upload texts, compute embeddings via an OpenAI-compatible API, reduce to 2D or 3D with UMAP or t-SNE, and explore semantic clusters interactively.
When to use
- Exploring semantic structure in a text corpus
- Discovering topic clusters in documents
- Finding nearest neighbors by semantic similarity
- Comparing two corpora side-by-side
- Labeling semantic clusters for downstream analysis
- Debugging embedding quality visually
Prerequisites
- Node.js 20+
- pnpm
EMBED_ENCRYPTION_KEY set (32-byte hex)
- An OpenAI-compatible embedding API key
Quick Start
pnpm install
cp .env.example .env
pnpm start
pnpm --filter dashboard dev
Server: http://localhost:4100
Dashboard: http://localhost:4101
Uploading Texts
File upload (curl)
curl -X POST http://localhost:4100/api/datasets \
-H 'Content-Type: application/json' \
-d '{"name": "My Dataset", "description": "Optional description"}'
curl -X POST http://localhost:4100/api/datasets/ds_abc123/upload \
-F 'file=@reviews.txt'
Accepted file formats:
.txt - one text per line
.csv - first column used as text
.json - array of strings
Paste texts (JSON body)
curl -X POST http://localhost:4100/api/datasets/ds_abc123/embed \
-H 'Content-Type: application/json' \
-d '{"texts": ["First text", "Second text", "Third text"]}'
Running a Projection
curl -X POST http://localhost:4100/api/datasets/ds_abc123/project \
-H 'Content-Type: application/json' \
-d '{"method": "umap", "dims": 2, "params": {"n_neighbors": 15, "min_dist": 0.1}}'
curl http://localhost:4100/api/datasets/ds_abc123/projection
Nearest Neighbor Search
curl -X POST http://localhost:4100/api/nearest \
-H 'Content-Type: application/json' \
-d '{"dataset_id": "ds_abc123", "text": "excellent product quality", "k": 10}'
curl -X POST http://localhost:4100/api/nearest \
-H 'Content-Type: application/json' \
-d '{"dataset_id": "ds_abc123", "point_id": "pt_8f3c2a1", "k": 10}'
Response:
[
{ "id": "pt_...", "content": "...", "label": "Positive", "similarity": 0.94 },
...
]
Label Management
curl http://localhost:4100/api/datasets/ds_abc123/labels
curl -X POST http://localhost:4100/api/datasets/ds_abc123/labels \
-H 'Content-Type: application/json' \
-d '{"label": "Positive", "point_ids": ["pt_8f3c2a1", "pt_3d7e9b2"]}'
curl -X DELETE http://localhost:4100/api/datasets/ds_abc123/labels/Positive
Export
curl "http://localhost:4100/api/datasets/ds_abc123/export?format=json" \
-o dataset.json
curl "http://localhost:4100/api/datasets/ds_abc123/export?format=csv" \
-o dataset.csv
curl "http://localhost:4100/api/datasets/ds_abc123/export?format=json&label=Positive" \
-o positive.json
API Reference
| Endpoint | Method | Description |
|---|
/api/datasets | GET | List all datasets |
/api/datasets | POST | Create dataset |
/api/datasets/:id | GET | Get dataset details |
/api/datasets/:id | DELETE | Delete dataset |
/api/datasets/:id/upload | POST | Upload text file |
/api/datasets/:id/embed | POST | Embed pasted texts |
/api/datasets/:id/points | GET | All points with coords |
/api/datasets/:id/project | POST | Start projection |
/api/datasets/:id/projection | GET | Projection status |
/api/datasets/:id/labels | GET | List labels |
/api/datasets/:id/labels | POST | Assign label |
/api/datasets/:id/labels/:label | DELETE | Delete label |
/api/nearest | POST | Nearest neighbor search |
/api/settings | GET | Get settings |
/api/settings | PATCH | Update settings |
/health | GET | Health check |
Environment Variables
| Variable | Description | Default |
|---|
EMBED_PORT | Server port | 4100 |
EMBED_DASHBOARD_PORT | Dashboard dev port | 4101 |
EMBED_DATA_DIR | SQLite and uploads directory | ~/.embedding-visualizer |
EMBED_ENCRYPTION_KEY | 32-byte hex for API key encryption | required |
EMBED_MODEL | Default embedding model | text-embedding-3-small |
EMBED_BASE_URL | Embedding API base URL | https://api.openai.com/v1 |
EMBED_BATCH_SIZE | Texts per API call | 100 |
EMBED_MAX_FILE_SIZE | Max upload size in bytes | 10485760 |
EMBED_LOG_LEVEL | debug, info, warn, error | info |
EMBED_DEV | Dev mode | 0 |
Troubleshooting
Embedding fails with 401
The API key is not set or has expired. Go to Settings in the dashboard and update the key.
Projection is slow
UMAP and t-SNE run server-side in Node.js. For 1,000+ points expect 10-60 seconds. Reduce n_neighbors or n_iter to speed up.
File upload rejected
Check the file is a supported format (.txt, .csv, .json) and under 10 MB. Files with a non-text MIME type are rejected.
Rate limiting (429 errors)
Reduce EMBED_BATCH_SIZE to 20-50 and retry. The client applies exponential backoff automatically.