Embedding
Embedding turns an input into a vector of numbers that captures its meaning. Inputs about the same thing get similar vectors, which powers semantic search, classification, clustering and finding duplicates.
createEmbedder runs Google’s
EmbeddingGemma 2, for
text in 100+ languages, images, video and audio, or
all-MiniLM-L6-v2,
a much smaller model for English text.
Quick start
Section titled “Quick start”import { function createEmbedder(config?: EmbedderModel, opts?: LoadOptions): Promise<Embedder>
createEmbedder, const models: { readonly embedding: { readonly EMBEDDINGGEMMA_2: { DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F16: { dtype: "f16"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F32: { dtype: "f32"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; }; readonly ALL_MINILM_L6_V2: { DEFAULT: { tokenizerPath: string; } & { modelPath: string; }; F16: { tokenizerPath: string; } & { dtype: "f16"; modelPath: string; }; F32: { tokenizerPath: string; } & { modelPath: string; dtype: "f32"; }; }; }; ... 6 more ...; readonly imageClassification: { readonly MOBILENETV4: { readonly DEFAULT: { modelPath: string; }; readonly SMALL: { modelPath: string; }; }; };}
models, function similarity(a: Float32Array, b: Float32Array): number
similarity } from 'runntime/zoo';
// 1. Load the model. The weights download from the Hugging Face Hub.const const embedder: Embedder
embedder = await function createEmbedder(config?: EmbedderModel, opts?: LoadOptions): Promise<Embedder>
createEmbedder(const models: { readonly embedding: { readonly EMBEDDINGGEMMA_2: { DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F16: { dtype: "f16"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F32: { dtype: "f32"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; }; readonly ALL_MINILM_L6_V2: { DEFAULT: { tokenizerPath: string; } & { modelPath: string; }; F16: { tokenizerPath: string; } & { dtype: "f16"; modelPath: string; }; F32: { tokenizerPath: string; } & { modelPath: string; dtype: "f32"; }; }; }; ... 6 more ...; readonly imageClassification: { readonly MOBILENETV4: { readonly DEFAULT: { modelPath: string; }; readonly SMALL: { modelPath: string; }; }; };}
models.embedding: { readonly EMBEDDINGGEMMA_2: { DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F16: { dtype: "f16"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F32: { dtype: "f32"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; }; readonly ALL_MINILM_L6_V2: { DEFAULT: { tokenizerPath: string; } & { modelPath: string; }; F16: { tokenizerPath: string; } & { dtype: "f16"; modelPath: string; }; F32: { tokenizerPath: string; } & { modelPath: string; dtype: "f32"; }; };}
embedding.type EMBEDDINGGEMMA_2: { DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F16: { dtype: "f16"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F32: { dtype: "f32"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; };}
EMBEDDINGGEMMA_2.type DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string;}
DEFAULT);
// 2. Turn what you search into vectors.const const docs: Float32Array<ArrayBufferLike>[]
docs = await const embedder: Embedder
embedder.Embedder.embedBatch(inputs: readonly EmbedInput[], options?: EmbedOptions): Promise<Float32Array[]>
embedBatch( [ 'Two cats sleep on a pink blanket next to two TV remotes.', 'A busy crossing with cyclists in a green bike lane.', ], { EmbedOptions.task?: "query" | "document" | "questionAnswering" | "factChecking" | "classification" | "clustering" | "similarity" | "codeRetrieval" | undefined
task: 'document' },);
// 3. Turn a search into a vector and compare.const const query: Float32Array<ArrayBufferLike>
query = await const embedder: Embedder
embedder.Embedder.embed(input: EmbedInput, options?: EmbedOptions): Promise<Float32Array>
embed('cats asleep on a sofa', { EmbedOptions.task?: "query" | "document" | "questionAnswering" | "factChecking" | "classification" | "clustering" | "similarity" | "codeRetrieval" | undefined
task: 'query' });const docs: Float32Array<ArrayBufferLike>[]
docs.Array<Float32Array<ArrayBufferLike>>.map<number>(callbackfn: (value: Float32Array<ArrayBufferLike>, index: number, array: Float32Array<ArrayBufferLike>[]) => number, thisArg?: any): number[]
map((doc: Float32Array<ArrayBufferLike>
doc) => function similarity(a: Float32Array, b: Float32Array): number
similarity(const query: Float32Array<ArrayBufferLike>
query, doc: Float32Array<ArrayBufferLike>
doc)); // [0.78, 0.57], the cats match best
// 4. Free the GPU memory when done.const embedder: Embedder
embedder.Embedder.dispose(): void
dispose();task depends on the model: EmbeddingGemma 2 uses it, all-MiniLM-L6-v2
ignores it, see Tasks. The engine must be set up first, see
Getting started.
Try it yourself
Section titled “Try it yourself”Ten support answers, embedded once by the model you pick: all-MiniLM-L6-v2,
a 45 MB download, or EmbeddingGemma 2 as a text-only model, 574 MB. Each
search embeds the query and ranks the answers by similarity(), on your
GPU.
Methods
Section titled “Methods”function createEmbedder(model?: EmbedderModel, options?: LoadOptions): Promise<Embedder>;
interface Embedder { readonly dim: number; embed(input: EmbedInput, options?: EmbedOptions): Promise<Float32Array>; embedBatch(inputs: readonly EmbedInput[], options?: EmbedOptions): Promise<Float32Array[]>; dispose(): void;}
interface EmbedOptions { task?: EmbeddingTask; dims?: number;}
function similarity(a: Float32Array, b: Float32Array): number;embed(input)turns one input into one vector, see Inputs.embedBatch(inputs)turns many inputs into one vector each, in order. For text it is much faster thanembed()in a loop.taskanddimsare per call, see Tasks and Shorter vectors.dimis the length of a vector withoutdims.similarity(a, b)compares two vectors: 1 for the same meaning, near 0 for unrelated. Vectors of text, images and audio compare with each other.dispose()frees the GPU memory.
Inputs
Section titled “Inputs”An input is one part, or a list of parts that makes one vector:
- text - a string.
- image -
{ image }, anImageBuffer, the same as in Image classification. - video -
{ video }, its frames asImageBuffers, about one per second. - audio -
{ audio }, mono samples at 16 kHz. Only the first 30 seconds count.decodeAudioreads a file, see Speech to text.
Images, video and audio need a model that reads them, see Supported models.
// Photos and text in one space.const const photos: Float32Array<ArrayBufferLike>[]
photos = await const embedder: Embedder
embedder.Embedder.embedBatch(inputs: readonly EmbedInput[], options?: EmbedOptions): Promise<Float32Array[]>
embedBatch([{ image: ImageBuffer
image: const catsPhoto: ImageBuffer
catsPhoto }, { image: ImageBuffer
image: const footballPhoto: ImageBuffer
footballPhoto }]);const const query: Float32Array<ArrayBufferLike>
query = await const embedder: Embedder
embedder.Embedder.embed(input: EmbedInput, options?: EmbedOptions): Promise<Float32Array>
embed('cats asleep on a sofa', { EmbedOptions.task?: "query" | "document" | "questionAnswering" | "factChecking" | "classification" | "clustering" | "similarity" | "codeRetrieval" | undefined
task: 'query' });const photos: Float32Array<ArrayBufferLike>[]
photos.Array<Float32Array<ArrayBufferLike>>.map<number>(callbackfn: (value: Float32Array<ArrayBufferLike>, index: number, array: Float32Array<ArrayBufferLike>[]) => number, thisArg?: any): number[]
map((photo: Float32Array<ArrayBufferLike>
photo) => function similarity(a: Float32Array, b: Float32Array): number
similarity(const query: Float32Array<ArrayBufferLike>
query, photo: Float32Array<ArrayBufferLike>
photo)); // [0.76, 0.55], the cats match best
// A list makes one vector, like a product page.const const product: Float32Array<ArrayBufferLike>
product = await const embedder: Embedder
embedder.Embedder.embed(input: EmbedInput, options?: EmbedOptions): Promise<Float32Array>
embed(['Waterproof running shoes.', { image: ImageBuffer
image: const shoePhoto: ImageBuffer
shoePhoto }]);Text longer than maxTokens is cut. An input with images, video or audio
that does not fit is an error.
task puts the instruction the model was trained with before the text. A
search embeds its queries with query and what it searches with
document. Embed images, video and audio without a task.
| Task | For |
|---|---|
query | search queries, compared with document |
questionAnswering | questions, compared with document |
factChecking | claims, compared with document |
codeRetrieval | descriptions of code, compared with document code |
document | what a search searches |
classification | every text being classified |
clustering | every text being clustered |
similarity | every text being compared |
Without task a text gets no instruction, which works but matches less
well.
Shorter vectors
Section titled “Shorter vectors”dims cuts a vector to fewer numbers, for less storage at a small cost in
quality. Compare only vectors of the same length. The lengths a model
allows are in Supported models, and the cost for
EmbeddingGemma 2 is in its
scores per length.
// A third of the storage.const const short: Float32Array<ArrayBufferLike>
short = await const embedder: Embedder
embedder.Embedder.embed(input: EmbedInput, options?: EmbedOptions): Promise<Float32Array>
embed('cats asleep on a sofa', { EmbedOptions.dims?: number | undefined
dims: 256 });const short: Float32Array<ArrayBufferLike>
short.Float32Array<ArrayBufferLike>.length: number
length; // 256Options
Section titled “Options”The first argument is the model config, a plain object with the fields
below. models holds ready-made ones: DEFAULT, the f16 weights run in
f16 where the device has shader-f16 and in f32 elsewhere; F16, the same
files with f16 forced; F32, the f32 weights run in f32. Pass one as is,
copy it with a field changed, or point at your own files.
interface EmbedderModel { readonly modelPath?: ModelPath; readonly visionPath?: ModelPath | null; readonly audioPath?: ModelPath | null; readonly tokenizerPath?: string; readonly dtype?: 'f32' | 'f16'; readonly maxTokens?: number; readonly imageTokens?: 70 | 140 | 280 | 560 | 1120;}
type ModelPath = string | RangeSource; // a URL, or bytes you already haveExamples:
import { function createEmbedder(config?: EmbedderModel, opts?: LoadOptions): Promise<Embedder>
createEmbedder, const models: { readonly embedding: { readonly EMBEDDINGGEMMA_2: { DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F16: { dtype: "f16"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F32: { dtype: "f32"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; }; readonly ALL_MINILM_L6_V2: { DEFAULT: { tokenizerPath: string; } & { modelPath: string; }; F16: { tokenizerPath: string; } & { dtype: "f16"; modelPath: string; }; F32: { tokenizerPath: string; } & { modelPath: string; dtype: "f32"; }; }; }; ... 6 more ...; readonly imageClassification: { readonly MOBILENETV4: { readonly DEFAULT: { modelPath: string; }; readonly SMALL: { modelPath: string; }; }; };}
models } from 'runntime/zoo';
// The small model, for English text.await function createEmbedder(config?: EmbedderModel, opts?: LoadOptions): Promise<Embedder>
createEmbedder(const models: { readonly embedding: { readonly EMBEDDINGGEMMA_2: { DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F16: { dtype: "f16"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F32: { dtype: "f32"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; }; readonly ALL_MINILM_L6_V2: { DEFAULT: { tokenizerPath: string; } & { modelPath: string; }; F16: { tokenizerPath: string; } & { dtype: "f16"; modelPath: string; }; F32: { tokenizerPath: string; } & { modelPath: string; dtype: "f32"; }; }; }; ... 6 more ...; readonly imageClassification: { readonly MOBILENETV4: { readonly DEFAULT: { modelPath: string; }; readonly SMALL: { modelPath: string; }; }; };}
models.embedding: { readonly EMBEDDINGGEMMA_2: { DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F16: { dtype: "f16"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F32: { dtype: "f32"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; }; readonly ALL_MINILM_L6_V2: { DEFAULT: { tokenizerPath: string; } & { modelPath: string; }; F16: { tokenizerPath: string; } & { dtype: "f16"; modelPath: string; }; F32: { tokenizerPath: string; } & { modelPath: string; dtype: "f32"; }; };}
embedding.type ALL_MINILM_L6_V2: { DEFAULT: { tokenizerPath: string; } & { modelPath: string; }; F16: { tokenizerPath: string; } & { dtype: "f16"; modelPath: string; }; F32: { tokenizerPath: string; } & { modelPath: string; dtype: "f32"; };}
ALL_MINILM_L6_V2.type DEFAULT: { tokenizerPath: string;} & { modelPath: string;}
DEFAULT);
// EmbeddingGemma 2 for text only: a smaller download.await function createEmbedder(config?: EmbedderModel, opts?: LoadOptions): Promise<Embedder>
createEmbedder({ ...const models: { readonly embedding: { readonly EMBEDDINGGEMMA_2: { DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F16: { dtype: "f16"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F32: { dtype: "f32"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; }; readonly ALL_MINILM_L6_V2: { DEFAULT: { tokenizerPath: string; } & { modelPath: string; }; F16: { tokenizerPath: string; } & { dtype: "f16"; modelPath: string; }; F32: { tokenizerPath: string; } & { modelPath: string; dtype: "f32"; }; }; }; ... 6 more ...; readonly imageClassification: { readonly MOBILENETV4: { readonly DEFAULT: { modelPath: string; }; readonly SMALL: { modelPath: string; }; }; };}
models.embedding: { readonly EMBEDDINGGEMMA_2: { DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F16: { dtype: "f16"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F32: { dtype: "f32"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; }; readonly ALL_MINILM_L6_V2: { DEFAULT: { tokenizerPath: string; } & { modelPath: string; }; F16: { tokenizerPath: string; } & { dtype: "f16"; modelPath: string; }; F32: { tokenizerPath: string; } & { modelPath: string; dtype: "f32"; }; };}
embedding.type EMBEDDINGGEMMA_2: { DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F16: { dtype: "f16"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F32: { dtype: "f32"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; };}
EMBEDDINGGEMMA_2.type DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string;}
DEFAULT, EmbedderModel.visionPath?: ModelPath | null | undefined
visionPath: null, EmbedderModel.audioPath?: ModelPath | null | undefined
audioPath: null,});- modelPath - the weights, a
model.safetensorsfile. The model is read from the file. - visionPath - the weights for images and video.
nullskips the download, and images and video become an error. - audioPath - the same for audio.
- tokenizerPath - the
tokenizer.jsonfile. - dtype -
'f16'or'f32'.f16runs the model in half precision, half the GPU memory, and needs a device withshader-f16. Default:f16where the device has it,f32elsewhere. Either loads any weight file. - maxTokens - longest input in tokens. The default and the most depend on the model, see Supported models.
- imageTokens - most tokens one image takes, 280 by default. More tokens keep more detail and take longer.
Every field is optional. A missing one comes from the model’s DEFAULT
entry. Without modelPath, the model is EmbeddingGemma 2.
Loading options
Section titled “Loading options”The second argument controls the download. Every field is optional.
interface LoadOptions { cache?: WeightCache; cacheId?: string; onProgress?: (name: string, doneBytes: number, totalBytes: number) => void; onBytes?: (chunkBytes: number) => void; signal?: AbortSignal;}Example:
import { function createEmbedder(config?: EmbedderModel, opts?: LoadOptions): Promise<Embedder>
createEmbedder, function createOpfsCache(namespace: string): Promise<WeightCache | undefined>
createOpfsCache, const models: { readonly embedding: { readonly EMBEDDINGGEMMA_2: { DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F16: { dtype: "f16"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F32: { dtype: "f32"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; }; readonly ALL_MINILM_L6_V2: { DEFAULT: { tokenizerPath: string; } & { modelPath: string; }; F16: { tokenizerPath: string; } & { dtype: "f16"; modelPath: string; }; F32: { tokenizerPath: string; } & { modelPath: string; dtype: "f32"; }; }; }; ... 6 more ...; readonly imageClassification: { readonly MOBILENETV4: { readonly DEFAULT: { modelPath: string; }; readonly SMALL: { modelPath: string; }; }; };}
models } from 'runntime/zoo';
const const embedder: Embedder
embedder = await function createEmbedder(config?: EmbedderModel, opts?: LoadOptions): Promise<Embedder>
createEmbedder(const models: { readonly embedding: { readonly EMBEDDINGGEMMA_2: { DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F16: { dtype: "f16"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F32: { dtype: "f32"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; }; readonly ALL_MINILM_L6_V2: { DEFAULT: { tokenizerPath: string; } & { modelPath: string; }; F16: { tokenizerPath: string; } & { dtype: "f16"; modelPath: string; }; F32: { tokenizerPath: string; } & { modelPath: string; dtype: "f32"; }; }; }; ... 6 more ...; readonly imageClassification: { readonly MOBILENETV4: { readonly DEFAULT: { modelPath: string; }; readonly SMALL: { modelPath: string; }; }; };}
models.embedding: { readonly EMBEDDINGGEMMA_2: { DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F16: { dtype: "f16"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F32: { dtype: "f32"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; }; readonly ALL_MINILM_L6_V2: { DEFAULT: { tokenizerPath: string; } & { modelPath: string; }; F16: { tokenizerPath: string; } & { dtype: "f16"; modelPath: string; }; F32: { tokenizerPath: string; } & { modelPath: string; dtype: "f32"; }; };}
embedding.type EMBEDDINGGEMMA_2: { DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F16: { dtype: "f16"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; }; F32: { dtype: "f32"; modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string; };}
EMBEDDINGGEMMA_2.type DEFAULT: { modelPath: string; visionPath: string; audioPath: string; tokenizerPath: string;}
DEFAULT, { LoadOptions.cache?: WeightCache | undefined
cache: await function createOpfsCache(namespace: string): Promise<WeightCache | undefined>
createOpfsCache('my-app'), LoadOptions.onProgress?: ((name: string, doneBytes: number, totalBytes: number) => void) | undefined
onProgress: (name: string
name, done: number
done, total: number
total) => var console: Console
console.Console.log(...data: any[]): void
log(`${var Math: Math
Math.Math.round(x: number): number
round((100 * done: number
done) / total: number
total)}%`),});- cache - keeps the downloaded weights and tokenizer in the browser, so
the next visit loads them without the network.
createOpfsCache(name)is the built-in one. - onProgress - called while the weights upload to the GPU, with the tensor name, bytes done and bytes total. Use it for a progress bar.
- signal - an
AbortSignal. Abort it to stop the load between steps. A step already running, like the download or the GPU upload, finishes first.
Supported models
Section titled “Supported models”| Model | Config | Inputs | Tasks | Dimensions | Max tokens | Weights |
|---|---|---|---|---|---|---|
| EmbeddingGemma 2 | models.embedding.EMBEDDINGGEMMA_2 | text in 100+ languages, images, video, audio | yes | 768, 512, 256, 128 | 2048 | f16 1.5 GB, f32 2.7 GB |
| all-MiniLM-L6-v2 | models.embedding.ALL_MINILM_L6_V2 | English text | no | 384 | 256, at most 512 | f16 45 MB, f32 91 MB |