Charlie KrugThe Build Log

← All posts

Same cluster, zero shared words: a semantic map in your tab

Gistmap embeds, clusters, labels, and maps a messy list of short text entirely in the browser. A real sentence-transformer runs in WASM, so nothing you paste ever leaves the tab.

"Buy milk" and "grab something for dinner" share zero words. To a keyword search they are unrelated. To a person they are obviously the same errand. Getting a machine to see that used to mean a Python notebook or a cloud API and your data on someone else's disk. Gistmap does it in the browser tab, and the text never leaves.

Gistmap takes a pile of short, unstructured text (tasks, tweets, survey answers, bookmarks, feedback, meeting notes) and turns it into a live 2D map where similar lines cluster together and each cluster names itself. Paste newline-separated text, press Map it, and about two seconds later 150 lines have snapped from a random scatter into labeled, colored constellations.

It exists because seeing the shape of a list is an everyday act and every tool for it is overkill. A cloud LLM means your data leaves your machine and you get prose back, not a durable map. A notebook means writing Python and installing packages. A data platform means signing up and learning a UI. Gistmap does the whole NLP pipeline on-device with no account, no upload, no code.

Four steps, all in the tab

First it embeds each line with a real sentence-transformer, all-MiniLM-L6-v2, about 7 MB quantized, running in WebAssembly via Transformers.js. That is the step that makes "buy milk" and "grab dinner" land next to each other: the model maps each line to a 384-dimension vector where distance is meaning, not spelling. Then k-means clusters those vectors, auto-picking a sensible count with an elbow heuristic (a slider lets you override). Then each cluster gets a readable name. Finally PCA projects the high-dimension vectors down to two and the points animate onto the map.

The label writes itself

The labeling is the part I would point at. A cluster's centroid tells you where it sits but not what it is about, and "Cluster 3" helps no one. So Gistmap looks at the terms inside each cluster and surfaces the ones that are frequent in that cluster but rare everywhere else, the signature words. A term that shows up in every cluster (say "the") carries no information; a term concentrated in one cluster is exactly its label. It is the same intuition as TF-IDF, applied across clusters instead of documents, and it turns a colored blob into a thing you can name.

Why PCA, not UMAP

The projection is deliberately the boring choice. UMAP makes prettier separations, but it is a heavier dependency, it is stochastic, and I wanted the map reproducible: same input, same layout, every time. PCA is a few lines of linear algebra, deterministic, dependency-free, and fast enough to reproject live. It keeps the largest-variance directions of your data and flattens the rest, which is enough to give clusters visible daylight between them without shipping a bigger projector. A nicer projection is a later story.

The clustering, projection, and labeling are all pure, seeded, dependency-free modules, unit-tested in Node without the model in the loop, so the hard parts are provable and the only heavy dependency loads lazily. The honest caveat is that model download: it is a one-time ~7 MB fetch from a CDN the first time you open a tab, and Gistmap does not yet ship a service worker to cache the app shell, so a reload re-fetches it. After that first load, no network request carries your text anywhere. Embedding, clustering, labeling, and drawing all happen in your browser. Open your network panel and watch. v1 also targets short lines, not documents, and it is unsupervised discovery, so it finds groups rather than sorting into labels you supply.

Try it

Open Gistmap, hit a sample dataset or paste 150 of your own lines (a brain-dump, a backlog, a column of survey answers), and press Map it. Watch the scatter resolve into named constellations, hover a point to read its line, and drag the count slider to split or merge clusters live. Export the result as JSON or CSV once the shape looks right. The code is on GitHub if you would rather read the clustering math than trust the animation.

Gistmap is live. Free, in your browser, no signup.

This post is part of the build log: every app my automated factory ships gets written up here, honestly. Browse everything at apps.charliekrug.com. Comments are open below.

Comments

Loading comments…