1. Home
  2. Tools
  3. Distance playground
Tool · interactive

Distance playground

Which distance should your index use? Drag the vectors below. The rings, rays and lines show what each pgvector operator considers equally close to the query — and the ranking changes as you switch.

Tested on pgvector 0.8.7 · PostgreSQL 18.6Updated 2026-10-27
Rank by

Drag the orange query or any grey vector (keyboard: focus a point, use arrow keys). The three nearest are highlighted. Contours show the distance of the 1st, 3rd and 5th nearest.

sqlSame ranking in pgvector

What the shapes mean#

OperatorDistance"Equally close" looks likeIndex operator classUse when
<->L2 (Euclidean)Circles around the queryvector_l2_opsMagnitude carries meaning (image features, coordinates)
<=>Cosine distance = 1 − cos θRays from the origin — only the angle mattersvector_cosine_opsText embeddings whose length you want to ignore
<#>Negative inner productStraight lines perpendicular to the query — longer vectors winvector_ip_opsNormalised embeddings (fastest), or recommendation scores
<+>L1 (taxicab)Diamondsvector_l1_ops (HNSW only)Sparse-ish features, robustness to outliers

Tick Normalise and every point snaps to the unit circle. Now cosine, inner product and L2 give the same order: for unit vectors, ‖a − b‖² = 2 − 2·cos θ and a·b = cos θ. That's why the choice barely matters for OpenAI embeddings (we checked: all 500,000 vectors in our benchmark set have length 1 ± 2 × 10⁻⁷) — what matters is that the index opclass and the query operator match, or the index is ignored (see the plans).

The same numbers in Postgres#

The SQL panel reproduces the current ranking in pgvector. Values agree with the table to about six significant digits — pgvector stores float4 elements and returns float8 distances, while this page computes in JavaScript doubles. Here are the four operators on a fixed example, run on our server:

psqlpsql · PostgreSQL 18.6 · pgvector 0.8.7
demo=# SELECT l2_distance('[1,2,3]'::vector, '[4,5,6]') AS l2,
demo-#        inner_product('[1,2,3]'::vector, '[4,5,6]') AS ip,
demo-#        cosine_distance('[1,2,3]'::vector, '[4,5,6]') AS cosine,
demo-#        l1_distance('[1,2,3]'::vector, '[4,5,6]') AS l1;
        l2         | ip |        cosine        | l1 
-------------------+----+----------------------+----
 5.196152422706632 | 32 | 0.025368153802923787 |  9
(1 row)

demo=# SELECT vector_dims('[1,2,3]'::vector) AS dims,
demo-#        vector_norm('[3,4]'::vector) AS norm,
demo-#        l2_normalize('[3,4]'::vector) AS normalized,
demo-#        subvector('[1,2,3,4,5]'::vector, 2, 3) AS sub;
 dims | norm | normalized |   sub   
------+------+------------+---------
    3 |    5 | [0.6,0.8]  | [2,3,4]
(1 row)

demo=# SELECT '[1,2]'::vector + '[3,4]' AS added, '[1,2]'::vector - '[3,4]' AS subtracted,
demo-#        '[1,2]'::vector * '[3,4]' AS multiplied, '[1,2]'::vector || '[3]' AS concatenated;
 added | subtracted | multiplied | concatenated 
-------+------------+------------+--------------
 [4,6] | [-2,-2]    | [3,8]      | [1,2,3]
(1 row)

Frequently asked questions

Should I use cosine distance or inner product in pgvector?

If your embeddings are normalised to length 1 — OpenAI, Cohere and most sentence-transformer models are, or can be — they produce the same ranking, and inner product (<#> with vector_ip_ops) does slightly less arithmetic. If vector lengths vary and you don't want length to count, use cosine (<=>). Toggle Normalise all vectors above to see the rankings converge.

Why does pgvector's inner product operator return negative numbers?

<#> returns the negative inner product so that, like every other pgvector operator, a smaller value means closer and ORDER BY … ASC works with an index. Multiply by −1 to get the actual inner product.

How do I convert cosine distance to cosine similarity?

Cosine similarity = 1 − cosine distance: SELECT 1 - (embedding <=> $1) AS similarity. Keep the ORDER BY on the distance itself so the index can be used.