Vector clustering, with colors

A toy embedding pipeline: each color is a "document", and the embedding model is just hex → [r, g, b]. Because the embedding space is genuinely 3D, the cube on the right is the vector space — no PCA/t-SNE squashing required.

1 Corpus

    2 Embed

    Turns every hex string into a vector and places it in the cube.

    Teaching notes
    • The layout is the embedding, not the clustering. Points never move when you cluster — k-means only discovers groups the embedding already placed near each other, and labels them. If the embedding is good, the clusters exist before anyone looks for them.
    • Reading the raw cube: the origin corner is black, the far corner is white, the three axes are pure red, green, and blue, and the main diagonal is the grayscale ramp. Direction ≈ hue; distance from the origin ≈ brightness.
    • Euclidean distance measures how far apart two colors are in the cube — it sees both hue and brightness.
    • Cosine similarity only measures the angle between vectors. Dark red [0.55, 0, 0] and pure red [1, 0, 0] point the same direction — cosine similarity 1.0 despite being far apart.
    • That means cosine collapses a whole dimension: the 3D cube flattens onto the 2D surface of a sphere of directions. Toggle "show what cosine sees" to watch it happen.
    • Black is the zero vector — it has no direction, so its cosine similarity to anything is undefined. It gets stuck at the origin.
    • All grays point the same diagonal direction, so under cosine they're identical to white.
    • Combining vectors is a technique too. Select two dots: the Mix row averages their vectors and decodes the midpoint back into a color (shown as the marker halfway along the dashed line). Try the same pair in RGB and then OKLab — the same average lands on different colors, because the operation inherits the space's geometry. This is why CSS's color-mix() and modern gradients interpolate in OKLab.
    How good is this embedding?
    • In one way, it's perfect. hex → [r, g, b] is lossless and invertible — the vector contains the entire color, and every dimension means something. Real embedding models never get this: they compress a whole paragraph into a few hundred numbers, so information is always thrown away.
    • But a "good" embedding means geometry that matches meaning. An embedding is good when its distances agree with the similarity you actually care about. If that's "how different do these colors look to a human?", RGB is only mediocre: it isn't perceptually uniform. The same Euclidean distance can be a glaring difference in one part of the cube and barely noticeable in another — human eyes are far more sensitive to changes in greens than in blues.
    • Better "models" exist for the same data. Color spaces like OKLab and CIELAB rearrange the exact same colors so that Euclidean distance ≈ perceived difference. Swapping RGB for OKLab is precisely what "upgrading to a better embedding model" means in ML: same corpus, better geometry — and every distance, cluster, and neighbor changes with it. Try it with the embedding model selector above and watch the dots re-arrange.
    • A different observer is a different model. The deuteranope and protanope models simulate red-green color blindness: the corpus doesn't change, but reds and greens now embed almost on top of each other — that geometry genuinely cannot tell them apart, the way a weaker text model conflates two concepts. Each dot keeps its true color, so you can see distinct inputs mapping to the same vector. (Dichromatic vision also squashes 3D color toward a 2D surface — the same dimension-collapse cosine pulled.)

    3 Cluster (k-means)

    no clustering yet

    Similarity

    Click two dots to compare their vectors.