Skip to content

How to Implement a Cosine Similarity Function in TypeScript for Vector Comparison

Published: at 

To understand how an AI can understand that the word “cat” is similar to “kitten,” you must realize cosine similarity. In short, with the help of embeddings, we can represent words as vectors in a high-dimensional space. If the word “cat” is represented as a vector [1, 0, 0], the word “kitten” would be represented as [1, 0, 1]. Now, we can use cosine similarity to measure the similarity between the two vectors. In this blog post, we will break down the concept of cosine similarity and implement it in TypeScript.

💡 Note

I won’t explain how embeddings work in this blog post, but only how to use them.

Why Cosine Similarity Matters for Modern Web Development

When you build applications with any of these features, you directly work with vector mathematics:

All of these require you to compare vectors, and cosine similarity offers one of the most effective methods to do so. Let me break down this concept from the ground up.

First, Let’s Understand Vectors

Vectors form the foundation of many AI operations. But what exactly makes a vector?

We express a vector v\vec{v} in nn-dimensional space as:

v=[v1,v2,...,vn]\vec{v} = [v_1, v_2, ..., v_n]

Each viv_i stands for a component or coordinate in the ii-th dimension.

While we’ve only examined 2D vectors here, modern embeddings contain hundreds of dimensions.

What Is Cosine Similarity and How Does It Work?

Now that you understand vectors, let’s examine how to measure similarity between them using cosine similarity:

Cosine Similarity Explained

Cosine similarity measures the cosine of the angle between two vectors, showing how similar they are regardless of their magnitude. The value ranges from:

With the interactive visualization above, you can:

  1. Move both vectors by dragging the colored circles at their endpoints
  2. Observe how the angle between them changes
  3. See how cosine similarity relates to this angle
  4. Note that cosine similarity depends only on the angle, not the vectors’ lengths

Simple Explanation in Plain English

The cosine similarity formula measures how similar two vectors are by examining the angle between them, not their sizes. Here’s how it works in plain English:

  1. What it does: It tells you if two vectors point in the same direction, opposite directions, or somewhere in between.

  2. The calculation:

    • First, multiply the corresponding elements of both vectors and add these products together (the dot product)
    • Then, calculate how long each vector is (its magnitude)
    • Finally, divide the dot product by the product of the two magnitudes
  3. The result:

    • If you get 1, the vectors point in exactly the same direction (perfectly similar)
    • If you get 0, the vectors stand perpendicular to each other (completely unrelated)
    • If you get -1, the vectors point in exactly opposite directions (perfectly dissimilar)
    • Any value in between indicates the degree of similarity
  4. Why it’s useful:

    • It ignores vector size and focuses only on direction
    • This means you can consider two things similar even if one is much “bigger” than the other
    • For example, a short document about cats and a long document about cats would show similarity, despite their different lengths
  5. In AI applications:

    • We convert words, documents, images, etc. into vectors with many dimensions
    • Cosine similarity helps us find related items by measuring how closely their vectors align
    • This powers features like semantic search, recommendations, and content matching

Step-by-Step Example Calculation

Let me walk you through a manual calculation of cosine similarity between two simple vectors. This helps build intuition before we implement it in code.

Given two vectors: v1=[3,4]\vec{v_1} = [3, 4] and v2=[5,2]\vec{v_2} = [5, 2]

I’ll calculate their cosine similarity step by step:

Step 1: Calculate the dot product.

v1v2=3×5+4×2=15+8=23\vec{v_1} \cdot \vec{v_2} = 3 \times 5 + 4 \times 2 = 15 + 8 = 23

Step 2: Calculate the magnitude of each vector.

v1=32+42=9+16=25=5||\vec{v_1}|| = \sqrt{3^2 + 4^2} = \sqrt{9 + 16} = \sqrt{25} = 5 v2=52+22=25+4=295.385||\vec{v_2}|| = \sqrt{5^2 + 2^2} = \sqrt{25 + 4} = \sqrt{29} \approx 5.385

Step 3: Calculate the cosine similarity by dividing the dot product by the product of magnitudes.

cos(θ)=v1v2v1v2\cos(\theta) = \frac{\vec{v_1} \cdot \vec{v_2}}{||\vec{v_1}|| \cdot ||\vec{v_2}||} =235×5.385=2326.9250.854= \frac{23}{5 \times 5.385} = \frac{23}{26.925} \approx 0.854

Therefore, the cosine similarity between vectors

v1\vec{v_1} and v2\vec{v_2} is approximately 0.854, which shows that these vectors point in roughly the same direction.

Why Use Cosine Similarity?

Cosine similarity offers particular advantages in AI applications because:

  1. It ignores magnitude: You can find similarity between two documents even if one contains many more words than the other
  2. It handles high dimensions efficiently: It scales effectively to the hundreds or thousands of dimensions used in AI embeddings
  3. It captures semantic meaning: The angle between vectors often correlates well with conceptual similarity

Building a Cosine Similarity Function in TypeScript

Let me implement a cosine similarity function in TypeScript. This gives you complete control and understanding of the process.

/**
 * Calculates the cosine similarity between two vectors
 * @param vecA First vector
 * @param vecB Second vector
 * @returns A value between -1 and 1, where 1 means identical
 */
function function calculateCosineSimilarity(vecA: number[], vecB: number[]): number
Calculates the cosine similarity between two vectors
@paramvecA First vector@paramvecB Second vector@returnsA value between -1 and 1, where 1 means identical
calculateCosineSimilarity
(vecA: number[]
First vector
@paramvecA First vector
vecA
: number[], vecB: number[]
Second vector
@paramvecB Second vector
vecB
: number[]): number {
// Verify vectors are of the same length if (vecA: number[]
First vector
@paramvecA First vector
vecA
.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
!== vecB: number[]
Second vector
@paramvecB Second vector
vecB
.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
) {
throw new
var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error
("Vectors must have the same dimensions");
} // Calculate dot product let let dotProduct: numberdotProduct = 0; for (let let i: numberi = 0; let i: numberi < vecA: number[]
First vector
@paramvecA First vector
vecA
.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
; let i: numberi++) {
let dotProduct: numberdotProduct += vecA: number[]
First vector
@paramvecA First vector
vecA
[let i: numberi] * vecB: number[]
Second vector
@paramvecB Second vector
vecB
[let i: numberi];
} // Calculate magnitudes let let magnitudeA: numbermagnitudeA = 0; let let magnitudeB: numbermagnitudeB = 0; for (let let i: numberi = 0; let i: numberi < vecA: number[]
First vector
@paramvecA First vector
vecA
.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
; let i: numberi++) {
let magnitudeA: numbermagnitudeA += vecA: number[]
First vector
@paramvecA First vector
vecA
[let i: numberi] * vecA: number[]
First vector
@paramvecA First vector
vecA
[let i: numberi];
let magnitudeB: numbermagnitudeB += vecB: number[]
Second vector
@paramvecB Second vector
vecB
[let i: numberi] * vecB: number[]
Second vector
@paramvecB Second vector
vecB
[let i: numberi];
} let magnitudeA: numbermagnitudeA = var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.sqrt(x: number): number
Returns the square root of a number.
@paramx A numeric expression.
sqrt
(let magnitudeA: numbermagnitudeA);
let magnitudeB: numbermagnitudeB = var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.sqrt(x: number): number
Returns the square root of a number.
@paramx A numeric expression.
sqrt
(let magnitudeB: numbermagnitudeB);
// Handle zero magnitude if (let magnitudeA: numbermagnitudeA === 0 || let magnitudeB: numbermagnitudeB === 0) { return 0; // or throw an error, depending on your requirements } // Calculate and return cosine similarity return let dotProduct: numberdotProduct / (let magnitudeA: numbermagnitudeA * let magnitudeB: numbermagnitudeB); }

A More Efficient Implementation

I can improve our implementation using array methods for a more concise, functional approach:

function function cosineSimilarity(vecA: number[], vecB: number[]): numbercosineSimilarity(vecA: number[]vecA: number[], vecB: number[]vecB: number[]): number {
  if (vecA: number[]vecA.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
!== vecB: number[]vecB.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
) {
throw new
var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error
("Vectors must have the same dimensions");
} // Calculate dot product: A·B = Σ(A[i] * B[i]) const const dotProduct: numberdotProduct = vecA: number[]vecA.Array<number>.reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number (+2 overloads)
Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
@paramcallbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.@paraminitialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
reduce
((sum: numbersum, a: numbera, i: numberi) => sum: numbersum + a: numbera * vecB: number[]vecB[i: numberi], 0);
// Calculate magnitude of vector A: |A| = √(Σ(A[i]²)) const const magnitudeA: numbermagnitudeA = var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.sqrt(x: number): number
Returns the square root of a number.
@paramx A numeric expression.
sqrt
(vecA: number[]vecA.Array<number>.reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number (+2 overloads)
Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
@paramcallbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.@paraminitialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
reduce
((sum: numbersum, a: numbera) => sum: numbersum + a: numbera * a: numbera, 0));
// Calculate magnitude of vector B: |B| = √(Σ(B[i]²)) const const magnitudeB: numbermagnitudeB = var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.sqrt(x: number): number
Returns the square root of a number.
@paramx A numeric expression.
sqrt
(vecB: number[]vecB.Array<number>.reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number (+2 overloads)
Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
@paramcallbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.@paraminitialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
reduce
((sum: numbersum, b: numberb) => sum: numbersum + b: numberb * b: numberb, 0));
// Check for zero magnitude if (const magnitudeA: numbermagnitudeA === 0 || const magnitudeB: numbermagnitudeB === 0) { return 0; } // Calculate cosine similarity: (A·B) / (|A|*|B|) return const dotProduct: numberdotProduct / (const magnitudeA: numbermagnitudeA * const magnitudeB: numbermagnitudeB); }

💪 Tip

Using Math.hypot() for Performance Optimization

You can optimize vector magnitude calculations using the built-in Math.hypot() function, which calculates the square root of the sum of squares more efficiently:

function function cosineSimilarityOptimized(vecA: number[], vecB: number[]): numbercosineSimilarityOptimized(vecA: number[]vecA: number[], vecB: number[]vecB: number[]): number {
  if (vecA: number[]vecA.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
!== vecB: number[]vecB.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
) {
throw new
var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error
("Vectors must have the same dimensions");
} // Calculate dot product: A·B = Σ(A[i] * B[i]) const const dotProduct: numberdotProduct = vecA: number[]vecA.Array<number>.reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number (+2 overloads)
Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
@paramcallbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.@paraminitialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
reduce
((sum: numbersum, a: numbera, i: numberi) => sum: numbersum + a: numbera * vecB: number[]vecB[i: numberi], 0);
// Calculate magnitudes using Math.hypot() const const magnitudeA: numbermagnitudeA = var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.hypot(...values: number[]): number
Returns the square root of the sum of squares of its arguments.
@paramvalues Values to compute the square root for. If no arguments are passed, the result is +0. If there is only one argument, the result is the absolute value. If any argument is +Infinity or -Infinity, the result is +Infinity. If any argument is NaN, the result is NaN. If all arguments are either +0 or −0, the result is +0.
hypot
(...vecA: number[]vecA);
const const magnitudeB: numbermagnitudeB = var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.hypot(...values: number[]): number
Returns the square root of the sum of squares of its arguments.
@paramvalues Values to compute the square root for. If no arguments are passed, the result is +0. If there is only one argument, the result is the absolute value. If any argument is +Infinity or -Infinity, the result is +Infinity. If any argument is NaN, the result is NaN. If all arguments are either +0 or −0, the result is +0.
hypot
(...vecB: number[]vecB);
// Check for zero magnitude if (const magnitudeA: numbermagnitudeA === 0 || const magnitudeB: numbermagnitudeB === 0) { return 0; } // Calculate cosine similarity: (A·B) / (|A|*|B|) return const dotProduct: numberdotProduct / (const magnitudeA: numbermagnitudeA * const magnitudeB: numbermagnitudeB); }

This approach is not only more concise but can be significantly faster, especially for larger vectors, as Math.hypot() is highly optimized by modern JavaScript engines.

Testing Our Implementation

Let’s see how our function works with some example vectors:

// Example 1: Similar vectors pointing in roughly the same direction
const const vecA: number[]vecA = [3, 4];
const const vecB: number[]vecB = [5, 2];
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v22.x/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
(`Similarity: ${cosineSimilarity(const vecA: number[]vecA, const vecB: number[]vecB).toFixed(3)}`);
// Output: Similarity: 0.857 // Example 2: Perpendicular vectors const const vecC: number[]vecC = [1, 0]; const const vecD: number[]vecD = [0, 1]; var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v22.x/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
(`Similarity: ${cosineSimilarity(const vecC: number[]vecC, const vecD: number[]vecD).toFixed(3)}`);
// Output: Similarity: 0.000 // Example 3: Opposite vectors const const vecE: number[]vecE = [2, 3]; const const vecF: number[]vecF = [-2, -3]; var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v22.x/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
(`Similarity: ${cosineSimilarity(const vecE: number[]vecE, const vecF: number[]vecF).toFixed(3)}`);
// Output: Similarity: -1.000

Mathematically, we can verify these results:

For Example 1: cosine similarity=3×5+4×232+42×52+22=15+825×29=235×290.857\text{cosine similarity} = \frac{3 \times 5 + 4 \times 2}{\sqrt{3^2 + 4^2} \times \sqrt{5^2 + 2^2}} = \frac{15 + 8}{\sqrt{25} \times \sqrt{29}} = \frac{23}{5 \times \sqrt{29}} \approx 0.857

For Example 2: cosine similarity=1×0+0×112+02×02+12=01×1=0\text{cosine similarity} = \frac{1 \times 0 + 0 \times 1}{\sqrt{1^2 + 0^2} \times \sqrt{0^2 + 1^2}} = \frac{0}{1 \times 1} = 0

For Example 3: cosine similarity=2×(2)+3×(3)22+32×(2)2+(3)2=4913×13=1313=1\text{cosine similarity} = \frac{2 \times (-2) + 3 \times (-3)}{\sqrt{2^2 + 3^2} \times \sqrt{(-2)^2 + (-3)^2}} = \frac{-4 - 9}{\sqrt{13} \times \sqrt{13}} = \frac{-13}{13} = -1

Complete TypeScript Solution

Here’s a complete TypeScript solution that includes our cosine similarity function along with some utility methods:

class class VectorUtilsVectorUtils {
  /**
   * Calculates the cosine similarity between two vectors
   */
  static VectorUtils.cosineSimilarity(vecA: number[], vecB: number[]): number
Calculates the cosine similarity between two vectors
cosineSimilarity
(vecA: number[]vecA: number[], vecB: number[]vecB: number[]): number {
if (vecA: number[]vecA.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
!== vecB: number[]vecB.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
) {
throw new
var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error
(`Vector dimensions don't match: ${vecA: number[]vecA.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
} vs ${vecB: number[]vecB.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
}`);
} const const dotProduct: numberdotProduct = vecA: number[]vecA.Array<number>.reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number (+2 overloads)
Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
@paramcallbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.@paraminitialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
reduce
((sum: numbersum, a: numbera, i: numberi) => sum: numbersum + a: numbera * vecB: number[]vecB[i: numberi], 0);
const const magnitudeA: numbermagnitudeA = var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.sqrt(x: number): number
Returns the square root of a number.
@paramx A numeric expression.
sqrt
(vecA: number[]vecA.Array<number>.reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number (+2 overloads)
Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
@paramcallbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.@paraminitialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
reduce
((sum: numbersum, a: numbera) => sum: numbersum + a: numbera * a: numbera, 0));
const const magnitudeB: numbermagnitudeB = var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.sqrt(x: number): number
Returns the square root of a number.
@paramx A numeric expression.
sqrt
(vecB: number[]vecB.Array<number>.reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number (+2 overloads)
Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
@paramcallbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.@paraminitialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
reduce
((sum: numbersum, b: numberb) => sum: numbersum + b: numberb * b: numberb, 0));
if (const magnitudeA: numbermagnitudeA === 0 || const magnitudeB: numbermagnitudeB === 0) { return 0; } return const dotProduct: numberdotProduct / (const magnitudeA: numbermagnitudeA * const magnitudeB: numbermagnitudeB); } /** * Calculates the dot product of two vectors */ static VectorUtils.dotProduct(vecA: number[], vecB: number[]): number
Calculates the dot product of two vectors
dotProduct
(vecA: number[]vecA: number[], vecB: number[]vecB: number[]): number {
if (vecA: number[]vecA.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
!== vecB: number[]vecB.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
) {
throw new
var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error
(`Vector dimensions don't match: ${vecA: number[]vecA.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
} vs ${vecB: number[]vecB.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
}`);
} return vecA: number[]vecA.Array<number>.reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number (+2 overloads)
Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
@paramcallbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.@paraminitialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
reduce
((sum: numbersum, a: numbera, i: numberi) => sum: numbersum + a: numbera * vecB: number[]vecB[i: numberi], 0);
} /** * Calculates the magnitude (length) of a vector */ static VectorUtils.magnitude(vec: number[]): number
Calculates the magnitude (length) of a vector
magnitude
(vec: number[]vec: number[]): number {
return var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.sqrt(x: number): number
Returns the square root of a number.
@paramx A numeric expression.
sqrt
(vec: number[]vec.Array<number>.reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number (+2 overloads)
Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
@paramcallbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.@paraminitialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
reduce
((sum: numbersum, v: numberv) => sum: numbersum + v: numberv * v: numberv, 0));
} /** * Normalizes a vector (converts to unit vector) */ static VectorUtils.normalize(vec: number[]): number[]
Normalizes a vector (converts to unit vector)
normalize
(vec: number[]vec: number[]): number[] {
const const mag: numbermag = this.VectorUtils.magnitude(vec: number[]): number
Calculates the magnitude (length) of a vector
magnitude
(vec: number[]vec);
if (const mag: numbermag === 0) { return
var Array: ArrayConstructor
(arrayLength?: number) => any[] (+2 overloads)
Array
(vec: number[]vec.Array<number>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
).Array<any>.fill(value: any, start?: number, end?: number): any[]
Changes all array elements from `start` to `end` index to a static `value` and returns the modified array
@paramvalue value to fill array section with@paramstart index to start filling the array at. If start is negative, it is treated as length+start where length is the length of the array.@paramend index to stop filling the array at. If end is negative, it is treated as length+end.
fill
(0);
} return vec: number[]vec.Array<number>.map<number>(callbackfn: (value: number, index: number, array: number[]) => number, thisArg?: any): number[]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
map
(v: numberv => v: numberv / const mag: numbermag);
} /** * Converts cosine similarity to angular distance in degrees */ static VectorUtils.similarityToDegrees(similarity: number): number
Converts cosine similarity to angular distance in degrees
similarityToDegrees
(similarity: numbersimilarity: number): number {
// Clamp similarity to [-1, 1] to handle floating point errors const const clampedSimilarity: numberclampedSimilarity = var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.max(...values: number[]): number
Returns the larger of a set of supplied numeric expressions.
@paramvalues Numeric expressions to be evaluated.
max
(-1, var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.min(...values: number[]): number
Returns the smaller of a set of supplied numeric expressions.
@paramvalues Numeric expressions to be evaluated.
min
(1, similarity: numbersimilarity));
return var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.acos(x: number): number
Returns the arc cosine (or inverse cosine) of a number.
@paramx A numeric expression.
acos
(const clampedSimilarity: numberclampedSimilarity) * (180 / var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.PI: number
Pi. This is the ratio of the circumference of a circle to its diameter.
PI
);
} }

The angular distance formula uses the inverse cosine function:

θ=cos1(cosine similarity)×180π\theta = \cos^{-1}(\text{cosine similarity}) \times \frac{180}{\pi}

Where θ\theta represents the angle in degrees between the two vectors.

Using Cosine Similarity in Real Web Applications

When you work with AI in web applications, you’ll often need to calculate similarity between vectors. For example:

  1. Finding similar products:

    function function findSimilarProducts(product: Product, allProducts: Product[]): Product[]findSimilarProducts(product: Productproduct: type Product = /*unresolved*/ anyProduct, allProducts: Product[]allProducts: type Product = /*unresolved*/ anyProduct[]): type Product = /*unresolved*/ anyProduct[] {
      return allProducts: Product[]allProducts
        .Array<Product>.filter(predicate: (value: Product, index: number, array: Product[]) => unknown, thisArg?: any): Product[] (+1 overload)
    Returns the elements of an array that meet the condition specified in a callback function.
    @parampredicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value.
    filter
    (p: Productp => p: Productp.id !== product: Productproduct.id) // Exclude the current product
    .
    Array<Product>.map<{
        product: Product;
        similarity: any;
    }>(callbackfn: (value: Product, index: number, array: Product[]) => {
        product: Product;
        similarity: any;
    }, thisArg?: any): {
        product: Product;
        similarity: any;
    }[]
    Calls a defined callback function on each element of an array, and returns an array that contains the results.
    @paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
    map
    (p: Productp => ({
    product: Productproduct: p: Productp, similarity: anysimilarity: VectorUtils.cosineSimilarity(product: Productproduct.embedding, p: Productp.embedding) })) .
    Array<{ product: Product; similarity: any; }>.sort(compareFn?: ((a: {
        product: Product;
        similarity: any;
    }, b: {
        product: Product;
        similarity: any;
    }) => number) | undefined): {
        product: Product;
        similarity: any;
    }[]
    Sorts an array in place. This method mutates the array and returns a reference to the same array.
    @paramcompareFn Function used to determine the order of the elements. It is expected to return a negative value if the first argument is less than the second argument, zero if they're equal, and a positive value otherwise. If omitted, the elements are sorted in ascending, ASCII character order. ```ts [11,2,22,1].sort((a, b) => a - b) ```
    sort
    ((
    a: {
        product: Product;
        similarity: any;
    }
    a
    ,
    b: {
        product: Product;
        similarity: any;
    }
    b
    ) =>
    b: {
        product: Product;
        similarity: any;
    }
    b
    .similarity: anysimilarity -
    a: {
        product: Product;
        similarity: any;
    }
    a
    .similarity: anysimilarity) // Sort by similarity (highest first)
    .
    Array<{ product: Product; similarity: any; }>.slice(start?: number, end?: number): {
        product: Product;
        similarity: any;
    }[]
    Returns a copy of a section of an array. For both start and end, a negative index can be used to indicate an offset from the end of the array. For example, -2 refers to the second to last element of the array.
    @paramstart The beginning index of the specified portion of the array. If start is undefined, then the slice begins at index 0.@paramend The end index of the specified portion of the array. This is exclusive of the element at the index 'end'. If end is undefined, then the slice extends to the end of the array.
    slice
    (0, 5) // Get top 5 similar products
    .
    Array<{ product: Product; similarity: any; }>.map<Product>(callbackfn: (value: {
        product: Product;
        similarity: any;
    }, index: number, array: {
        product: Product;
        similarity: any;
    }[]) => Product, thisArg?: any): Product[]
    Calls a defined callback function on each element of an array, and returns an array that contains the results.
    @paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
    map
    (
    result: {
        product: Product;
        similarity: any;
    }
    result
    =>
    result: {
        product: Product;
        similarity: any;
    }
    result
    .product: Productproduct);
    }
  2. Semantic search:

    function function semanticSearch(queryEmbedding: number[], documentEmbeddings: DocumentWithEmbedding[]): SearchResult[]semanticSearch(queryEmbedding: number[]queryEmbedding: number[], documentEmbeddings: DocumentWithEmbedding[]documentEmbeddings: type DocumentWithEmbedding = /*unresolved*/ anyDocumentWithEmbedding[]): type SearchResult = /*unresolved*/ anySearchResult[] {
      return documentEmbeddings: DocumentWithEmbedding[]documentEmbeddings
        .
    Array<DocumentWithEmbedding>.map<{
        document: DocumentWithEmbedding;
        relevance: any;
    }>(callbackfn: (value: DocumentWithEmbedding, index: number, array: DocumentWithEmbedding[]) => {
        document: DocumentWithEmbedding;
        relevance: any;
    }, thisArg?: any): {
        document: DocumentWithEmbedding;
        relevance: any;
    }[]
    Calls a defined callback function on each element of an array, and returns an array that contains the results.
    @paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
    map
    (doc: DocumentWithEmbeddingdoc => ({
    document: DocumentWithEmbeddingdocument: doc: DocumentWithEmbeddingdoc, relevance: anyrelevance: VectorUtils.cosineSimilarity(queryEmbedding: number[]queryEmbedding, doc: DocumentWithEmbeddingdoc.embedding) })) .
    Array<{ document: DocumentWithEmbedding; relevance: any; }>.filter(predicate: (value: {
        document: DocumentWithEmbedding;
        relevance: any;
    }, index: number, array: {
        document: DocumentWithEmbedding;
        relevance: any;
    }[]) => unknown, thisArg?: any): {
        document: DocumentWithEmbedding;
        relevance: any;
    }[] (+1 overload)
    Returns the elements of an array that meet the condition specified in a callback function.
    @parampredicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value.
    filter
    (
    result: {
        document: DocumentWithEmbedding;
        relevance: any;
    }
    result
    =>
    result: {
        document: DocumentWithEmbedding;
        relevance: any;
    }
    result
    .relevance: anyrelevance > 0.7) // Only consider relevant results
    .
    Array<{ document: DocumentWithEmbedding; relevance: any; }>.sort(compareFn?: ((a: {
        document: DocumentWithEmbedding;
        relevance: any;
    }, b: {
        document: DocumentWithEmbedding;
        relevance: any;
    }) => number) | undefined): {
        document: DocumentWithEmbedding;
        relevance: any;
    }[]
    Sorts an array in place. This method mutates the array and returns a reference to the same array.
    @paramcompareFn Function used to determine the order of the elements. It is expected to return a negative value if the first argument is less than the second argument, zero if they're equal, and a positive value otherwise. If omitted, the elements are sorted in ascending, ASCII character order. ```ts [11,2,22,1].sort((a, b) => a - b) ```
    sort
    ((
    a: {
        document: DocumentWithEmbedding;
        relevance: any;
    }
    a
    ,
    b: {
        document: DocumentWithEmbedding;
        relevance: any;
    }
    b
    ) =>
    b: {
        document: DocumentWithEmbedding;
        relevance: any;
    }
    b
    .relevance: anyrelevance -
    a: {
        document: DocumentWithEmbedding;
        relevance: any;
    }
    a
    .relevance: anyrelevance);
    }

Using OpenAI Embedding Models with Cosine Similarity

While the examples above used simple vectors for clarity, real-world AI applications typically use embedding models that transform text and other data into high-dimensional vector spaces.

OpenAI provides powerful embedding models that you can easily incorporate into your applications. These models transform text into vectors with hundreds or thousands of dimensions that capture semantic meaning:

// Example of using OpenAI embeddings with our cosine similarity function
async function function compareTextSimilarity(textA: string, textB: string): Promise<number>compareTextSimilarity(textA: stringtextA: string, textB: stringtextB: string): interface Promise<T>
Represents the completion of an asynchronous operation
Promise
<number> {
// Get embeddings from OpenAI API const const responseA: ResponseresponseA = await function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response> (+1 overload)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/fetch)
fetch
('https://api.openai.com/v1/embeddings', {
RequestInit.method?: string | undefined
A string to set request's method.
method
: 'POST',
RequestInit.headers?: HeadersInit | undefined
A Headers object, an object literal, or an array of two-item arrays to set request's headers.
headers
: {
'Authorization': `Bearer ${var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnv
The `process.env` property returns an object containing the user environment. See [`environ(7)`](http://man7.org/linux/man-pages/man7/environ.7.html). An example of this object looks like: ```js { TERM: 'xterm-256color', SHELL: '/usr/local/bin/bash', USER: 'maciej', PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin', PWD: '/Users/maciej', EDITOR: 'vim', SHLVL: '1', HOME: '/Users/maciej', LOGNAME: 'maciej', _: '/usr/local/bin/node' } ``` It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other `Worker` threads. In other words, the following example would not work: ```bash node -e 'process.env.foo = "bar"' &#x26;&#x26; echo $foo ``` While the following will: ```js import { env } from 'node:process'; env.foo = 'bar'; console.log(env.foo); ``` Assigning a property on `process.env` will implicitly convert the value to a string. **This behavior is deprecated.** Future versions of Node.js may throw an error when the value is not a string, number, or boolean. ```js import { env } from 'node:process'; env.test = null; console.log(env.test); // => 'null' env.test = undefined; console.log(env.test); // => 'undefined' ``` Use `delete` to delete a property from `process.env`. ```js import { env } from 'node:process'; env.TEST = 1; delete env.TEST; console.log(env.TEST); // => undefined ``` On Windows operating systems, environment variables are case-insensitive. ```js import { env } from 'node:process'; env.TEST = 1; console.log(env.test); // => 1 ``` Unless explicitly specified when creating a `Worker` instance, each `Worker` thread has its own copy of `process.env`, based on its parent thread's `process.env`, or whatever was specified as the `env` option to the `Worker` constructor. Changes to `process.env` will not be visible across `Worker` threads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy of `process.env` on a `Worker` instance operates in a case-sensitive manner unlike the main thread.
@sincev0.1.27
env
.string | undefinedOPENAI_API_KEY}`,
'Content-Type': 'application/json', }, RequestInit.body?: BodyInit | null | undefined
A BodyInit object or null to set request's body.
body
: var JSON: JSON
An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.
JSON
.JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)
Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
@paramvalue A JavaScript value, usually an object or array, to be converted.@paramreplacer A function that transforms the results.@paramspace Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
stringify
({
model: stringmodel: 'text-embedding-3-large', input: stringinput: textA: stringtextA }) }); const const responseB: ResponseresponseB = await function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response> (+1 overload)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/fetch)
fetch
('https://api.openai.com/v1/embeddings', {
RequestInit.method?: string | undefined
A string to set request's method.
method
: 'POST',
RequestInit.headers?: HeadersInit | undefined
A Headers object, an object literal, or an array of two-item arrays to set request's headers.
headers
: {
'Authorization': `Bearer ${var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnv
The `process.env` property returns an object containing the user environment. See [`environ(7)`](http://man7.org/linux/man-pages/man7/environ.7.html). An example of this object looks like: ```js { TERM: 'xterm-256color', SHELL: '/usr/local/bin/bash', USER: 'maciej', PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin', PWD: '/Users/maciej', EDITOR: 'vim', SHLVL: '1', HOME: '/Users/maciej', LOGNAME: 'maciej', _: '/usr/local/bin/node' } ``` It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other `Worker` threads. In other words, the following example would not work: ```bash node -e 'process.env.foo = "bar"' &#x26;&#x26; echo $foo ``` While the following will: ```js import { env } from 'node:process'; env.foo = 'bar'; console.log(env.foo); ``` Assigning a property on `process.env` will implicitly convert the value to a string. **This behavior is deprecated.** Future versions of Node.js may throw an error when the value is not a string, number, or boolean. ```js import { env } from 'node:process'; env.test = null; console.log(env.test); // => 'null' env.test = undefined; console.log(env.test); // => 'undefined' ``` Use `delete` to delete a property from `process.env`. ```js import { env } from 'node:process'; env.TEST = 1; delete env.TEST; console.log(env.TEST); // => undefined ``` On Windows operating systems, environment variables are case-insensitive. ```js import { env } from 'node:process'; env.TEST = 1; console.log(env.test); // => 1 ``` Unless explicitly specified when creating a `Worker` instance, each `Worker` thread has its own copy of `process.env`, based on its parent thread's `process.env`, or whatever was specified as the `env` option to the `Worker` constructor. Changes to `process.env` will not be visible across `Worker` threads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy of `process.env` on a `Worker` instance operates in a case-sensitive manner unlike the main thread.
@sincev0.1.27
env
.string | undefinedOPENAI_API_KEY}`,
'Content-Type': 'application/json', }, RequestInit.body?: BodyInit | null | undefined
A BodyInit object or null to set request's body.
body
: var JSON: JSON
An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.
JSON
.JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)
Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
@paramvalue A JavaScript value, usually an object or array, to be converted.@paramreplacer A function that transforms the results.@paramspace Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
stringify
({
model: stringmodel: 'text-embedding-3-large', input: stringinput: textB: stringtextB }) }); const const embeddingA: anyembeddingA = (await const responseA: ResponseresponseA.Body.json(): Promise<any>
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json)
json
()).data[0].embedding;
const const embeddingB: anyembeddingB = (await const responseB: ResponseresponseB.Body.json(): Promise<any>
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json)
json
()).data[0].embedding;
// Calculate similarity using our function return VectorUtils.cosineSimilarity(const embeddingA: anyembeddingA, const embeddingB: anyembeddingB); }

⚠️ Warning

In a production environment, you should pre-compute embeddings for your content (like blog posts, products, or documents) and store them in a vector database (like Pinecone, Qdrant, or Milvus). Re-computing embeddings for every user request as shown in this example wastes resources and slows performance. A better approach: embed your content once during indexing, store the vectors, and only embed the user’s query when performing a search.

OpenAI’s latest embedding models like text-embedding-3-large have up to 3,072 dimensions, capturing extremely nuanced semantic relationships between words and concepts. These high-dimensional embeddings enable much more accurate similarity measurements than simpler vector representations.

For more information on OpenAI’s embedding models, including best practices and implementation details, check out their documentation at https://platform.openai.com/docs/guides/embeddings.

Conclusion

Understanding vectors and cosine similarity provides practical tools that empower you to work effectively with modern AI features. By implementing these concepts in TypeScript, you gain a deeper understanding and precise control over calculating similarity in your applications. The interactive visualizations we’ve explored help you build intuition about these mathematical concepts, while the TypeScript implementation gives you the tools to apply them in real-world scenarios. Whether you build recommendation systems, semantic search, or content-matching features, the foundation you’ve gained here will help you implement more intelligent, accurate, and effective AI-powered features in your web applications.

Questions or thoughts?

Follow me on X for more TypeScript, Vue, and web dev insights! Feel free to DM me with:

  • Questions about this article
  • Topic suggestions
  • Feedback or improvements
Connect on X

Related Posts