Open a Parquet file with two million rows of embeddings in a notebook, hand the 2D projection to matplotlib or Plotly, and watch what happens. The plot takes twenty seconds to paint, a dense core swallows the middle, and every pan redraws from scratch. So you downsample to ten thousand points, lose the tails and the rare clusters — which are the whole reason you opened the file — and quietly tell yourself the picture is representative.
The usual escape is to stand up infrastructure: push the vectors into a store, wire a backend that streams tiles to a frontend, and now you maintain a service to look at a scatter plot. Apple's Embedding Atlas takes a different bet. It runs the whole pipeline — analytical query engine, cross-filtering, GPU rendering — inside a single browser tab, on your machine, with nothing uploaded. The interesting part isn't the machine learning. It's the data engineering that keeps the interaction honest at scale.
The number that sets it apart
Most embedding viewers quietly cap themselves in the low tens of thousands of points. Embedding Atlas publishes a benchmark instead of a marketing adjective. On an M1 Pro rendering into a 1600×1600 canvas at 2x scale, it holds 4 million points at 60fps and stays interactive — around 25fps — past 10 million points. Density mode costs almost nothing at that resolution; only when you push to a 3840×2160 canvas does it start to bite (for 5 million points, frame rate slips from 46 to 33fps).
That ceiling changes how you work. You stop pre-filtering the data to make the tool cooperate and start looking at all of it — the full distribution, including the sparse regions where mislabeled examples and out-of-domain drift actually live.
A database in the browser tab
The reason a scatter plot chokes at scale usually isn't the drawing — it's everything around it. Brush a region and the tool has to recompute which points fall inside, then update every linked chart. Do that against two million rows in JavaScript and you feel every frame.
Embedding Atlas sidesteps this by embedding a real analytical database in the page. Queries run against DuckDB compiled to WebAssembly, executing columnar aggregations in-browser at speeds JavaScript loops can't touch. On top sits Mosaic, which coordinates the linked views: when you filter one chart, Mosaic turns the interaction into a query that DuckDB answers, and the scatter plot, the histograms, and the count plots all update against the same engine. That is why cross-filtering across bar, line, bubble, count-plot, and eCDF panels stays responsive over millions of rows — the filtering is SQL, not a hand-rolled predicate walk.
This is the detail worth internalizing: the hard scaling problem in embedding visualization is a query problem, and the answer here is to ship an OLAP database to the client rather than call one over the network.
Rendering that doesn't cheat
Drawing millions of semi-transparent dots colored by category runs straight into a classic bug: alpha blending depends on draw order, so overlapping clusters render differently depending on which category the GPU happened to process last. Embedding Atlas uses a WebGPU pipeline with order-independent transparency — a vertex shader turns each data entry into a quad, and a fragment shader draws the circle and writes the extra information needed to composite categories correctly regardless of order. The colors you see reflect the data, not a rasterization accident.
For density, it doesn't bin into a heatmap and call it done. A compute shader runs kernel density estimation using the Deriche approximation, and fragment shaders render the levels and contours on top. The result is a smooth density field you can leave switched on while you pan, plus automatic clustering driven by that same 2D density, with labels placed by a map-style de-overlapping algorithm that stays stable as you zoom in and out. You get a labeled terrain map of the embedding space instead of an undifferentiated cloud.
Getting a dataset on screen
The friction budget is small. The Python package ships a command-line tool that computes embeddings, projects them, and opens a local server:
pip install embedding-atlas
# Embed a text column, project to 2D, serve at http://localhost:5055/
embedding-atlas reviews.parquet --text review_body
# Point it straight at a Hugging Face dataset
embedding-atlas some-org/some-dataset --text text --sample 200000
Give it a text column and it embeds with SentenceTransformers (all-MiniLM-L6-v2 by default), then reduces to two dimensions with UMAP; image and audio columns fall back to sensible defaults (google/vit-base-patch16-224, laion/clap-htsat-fused). Already have projection coordinates? Skip the compute entirely and pass them straight through:
embedding-atlas points.parquet --x umap_x --y umap_y
Inside a notebook, it's a widget over a DataFrame, so the exploration lives next to the code that produced the vectors:
from embedding_atlas.widget import EmbeddingAtlasWidget
EmbeddingAtlasWidget(df)
There's a Streamlit component in the same package, and for building your own UI, the npm side exposes EmbeddingView, EmbeddingViewMosaic, and the full EmbeddingAtlas component with React and Svelte entry points. An MCP server is included too, so an agent can query the same view you're looking at.
Where it fits, and where it doesn't
This is an exploration tool, not a vector database. It reads your data and lets you interrogate it; it isn't a serving layer, and it won't manage an index you query from production. Because everything runs client-side, your working set has to fit in browser memory — the --sample flag exists for a reason, and for very large corpora you'll precompute embeddings and projections offline and feed in the coordinates. Nearest-neighbor search is real-time and local to the loaded set, aimed at "show me points like this one" during analysis rather than low-latency retrieval at request time.
The pitch is narrow and honest: understand what's in your embeddings before you trust them downstream.
The takeaway isn't "Apple shipped a nicer scatter plot." It's the architecture. When your data outgrows a notebook chart, the reflex is to add a server. Embedding Atlas shows the other move — push a columnar query engine and a GPU renderer into the client and let the browser do the heavy lifting. Next time an embedding QA task has you downsampling to keep a plot alive, point the CLI at the raw Parquet file and look at the whole dataset instead. The clusters you were about to throw away are usually the ones worth seeing.
Sources: Embedding Atlas (Apple) · Overview & docs · apple/embedding-atlas on GitHub · Embedding Atlas: Low-Friction, Interactive Embedding Visualization (arXiv:2505.06386) · Apple Machine Learning Research