How do different embedding models relate the same people? This project embeds 68 short biographies of historical figures with several models, finds each person’s 3 closest neighbours under each model, and writes an interactive page comparing them.
From the run on 2026-10-07 with five models (text-embedding-3-small, text-embedding-3-large, qwen3-embedding-8b, mistral-embed-2312, gemini-embedding-2).
Method, full results, the project’s history and open ideas: FINDINGS.md.
Open the comparison page (and the 3D map experiment). These are the pages from the latest run, committed in output/ and published with GitHub Pages.
src/lib/model-comparison-page.ts.src/compare-models.ts).Requires Node 22 or later.
npm install
cp .env.example .env # then add your OpenRouter API key
npm start
This embeds every biography with each model listed in src/lib/embeddings.ts, prints a summary to the console and writes output/model-comparison.html, replacing the committed copy. Open that file in a browser.
A model that fails (for example, one blocked by an OpenRouter guardrail) is skipped with a warning and listed at the top of the page.
npm run map3d
Writes output/map-3d.html (also committed). It lays each model’s embeddings out with t-SNE in 2D and in 3D and measures how many of each person’s 3 true closest people are also nearest on each map. The page shows that comparison as a table and has a 3D map you can rotate, which lists each person’s true closest next to their nearest on the 2D and 3D maps.
| Path | Contents |
|---|---|
src/compare-models.ts |
Entry point for the main page: neighbours, comparison, console output |
src/map-3d.ts |
Entry point for the 3D experiment page |
src/lib/embeddings.ts |
The models to compare, the embeddings API call and nearest-neighbour search |
src/lib/model-comparison.ts |
Agreement, stability and field/gender scores |
src/lib/model-comparison-page.ts |
The main HTML page |
src/lib/map-3d-page.ts |
The 3D experiment page (rotation and perspective drawn in SVG, no 3D library) |
src/lib/page-theme.ts |
Colour tokens shared by both pages, light and dark |
src/lib/tsne-scatter.ts |
t-SNE layout in 2D or 3D (see the note in the file about @thi.ng/tsne) |
data/historical_figures.json |
The biographies (from the course) |
data/historical_figures_tags.json |
One field and a gender per person, used for section 2. Hand-assigned and editable. |