altor-vec vs fuse.js

altor-vec vs Fuse.js — Semantic Search vs Fuzzy Matching

The core difference is simple and important: Fuse.js does fuzzy text matching, while altor-vec does semantic vector similarity. Fuse.js answers, “is this string close to that string?” using approximate character-level matching associated with Bitap-style fuzzy search and edit-distance-like scoring. altor-vec answers, “is this meaning close to that meaning?” using embeddings plus HNSW nearest-neighbour search inside a 54 KB WebAssembly runtime. Those are not just different implementations of the same idea; they solve different product problems. A user typing how to cancel my plan can match a help article titled subscription management with altor-vec because the meanings are related. The same query usually will not surface that article in Fuse.js unless the literal words overlap enough to trigger its fuzzy score.

Install altor-vec: npm install altor-vec

Feature comparison table

Capabilityaltor-vecFuse.js
AlgorithmHNSW (vector similarity)Bitap / Levenshtein-style fuzzy string matching
Semantic understandingYes — meaning-basedNo — character-based
Bundle size54KB gzipped WASM~5KB minified
npm downloads/week~52 (growing)~5 million
Setup complexityMedium (need embeddings)Very low (zero config)
Works offlineYesYes
Typo toleranceVia semantic neighborsYes, native
Query cancel subscriptionend my planMatchesNo match
LanguageRust (WASM)JavaScript
LicenseMITApache-2.0

This table makes the tradeoff clear. Fuse.js wins the simplicity contest by a wide margin. You can import it, point it at an array, tune a threshold, and ship. That is exactly why it became the default client-side search library for autocomplete widgets, small docs lists, command palettes, or any experience where “close enough text match” is all you need. altor-vec asks for more setup because it expects your corpus to be embedded ahead of time. In return, it unlocks a class of search quality that fuzzy matching cannot reach: synonyms, paraphrases, indirect phrasing, and intent-level retrieval.

The most honest way to compare the two is not “new thing beats old thing.” It is “which failure mode hurts your product more?” If users mostly miss results because of typos, transpositions, or slightly wrong wording, Fuse.js may already solve the problem. If users miss results because they ask for the same idea in different language — pricing tiers vs plans, end my subscription vs cancel membership, how do I archive a board vs hide an old project — then semantic retrieval is the bigger step change. That is where altor-vec becomes a meaningful upgrade instead of a fashionable rewrite.

What changes in practice

Fuse.js mindset

Search quality depends on text overlap, field weighting, and threshold tuning. You are optimizing the score of words and substrings already present in the corpus.

altor-vec mindset

Search quality depends on the embedding model, chunking strategy, and corpus preparation. You are optimizing meaning representation, then retrieving nearest vectors.

Why teams switch

They usually do not outgrow Fuse.js because it is slow. They outgrow it because support articles, docs, or knowledge-base entries use different vocabulary than users do.

That vocabulary gap matters more than many teams expect. Product and documentation teams often name a feature one way internally and users describe it another way externally. A billing team may write “subscription lifecycle,” but users search for “pause account,” “stop charges,” or “turn off renewal.” Fuse.js can only help when enough of those words literally intersect with the stored text. altor-vec can bridge the gap because the query and the document are both mapped into embedding space before search happens.

Code comparison

altor-vec

import init, { WasmSearchEngine } from 'altor-vec';

// Embeddings generated at build time
await init();
const res = await fetch('/search-index.bin');
const engine = WasmSearchEngine.from_bytes(new Uint8Array(await res.arrayBuffer()));

// "cancel my subscription" will find "end your plan" semantically
const hits = JSON.parse(engine.search(queryEmbedding, 5));

Fuse.js

import Fuse from 'fuse.js';

const fuse = new Fuse(docs, { keys: ['title', 'content'], threshold: 0.3 });

// "cancel my subscription" will NOT find "end your plan"
const results = fuse.search('cancel my subscription');

Notice what is different here. Fuse.js searches plain source text directly at query time. That is why it is so easy to adopt. altor-vec searches vectors, not raw strings, so the hard part has already happened before the user types anything. Your content has been embedded, indexed, serialized, and shipped as a static asset. That up-front indexing work is the real cost of semantic search in the browser. But the reward is that query phrasing no longer has to mirror author phrasing. For user-facing documentation, help centers, onboarding checklists, release notes, and internal knowledge surfaces, that can be the difference between a search box people trust and one they avoid.

When to choose each

Choose altor-vec when:

Choose Fuse.js when:

There is no shame in picking Fuse.js. In fact, for many product surfaces it is still the correct answer. A command menu over 200 items does not need a vector index. A searchable dropdown for country names does not need semantic retrieval. If the corpus is tiny, the queries are short, and the terms are stable, the smaller and simpler tool usually wins. altor-vec earns its place when the search problem has shifted from spelling tolerance to intent matching.

Upgrading from Fuse.js

The migration path is more straightforward than it first appears because both libraries stay entirely client-side at query time. You are not moving to a hosted service. You are swapping one local search strategy for another.

  1. Install altor-vec: npm install altor-vec
  2. Generate embeddings at build time with Transformers.js and all-MiniLM-L6-v2.
  3. Replace the Fuse search call with an altor-vec search call that accepts a query embedding.
  4. Result: the same client-side deployment model and the same offline capability, but with semantic understanding instead of string-only fuzzy matching.
// build-time: generate-index.mjs
import fs from 'node:fs';
import { pipeline } from '@xenova/transformers';
import { WasmSearchEngine } from 'altor-vec/node';

const embed = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
const docs = [/* your content */];
const embeddings = [];

for (const doc of docs) {
  const out = await embed(doc.text, { pooling: 'mean', normalize: true });
  embeddings.push(...out.data);
}

const engine = WasmSearchEngine.from_vectors(new Float32Array(embeddings), 384, 16, 200, 50);
const bytes = engine.serialize();
fs.writeFileSync('./public/search-index.bin', Buffer.from(bytes));

The real application change is conceptual: instead of searching over raw strings, you search over a vector that represents the query. That means you also need a query embedding path in the browser, or you need to precompute vectors for a constrained query set depending on the product. Once that exists, the rest of the architecture remains pleasantly static. Serve the index file, load the WASM module, perform nearest-neighbour lookup locally, and render results just as you did before. No API gateway, no search cluster, no per-query invoice.

If you are migrating from Fuse.js on a docs site, a practical rollout pattern is to keep the existing UI and swap only the retrieval layer first. Use the same result renderer, the same keyboard navigation, and the same highlight treatment. Measure whether users click successful results more often or reformulate their searches less often. Semantic search should be justified by a visible improvement in failed-search rate, not by the novelty of embeddings alone.

FAQ

Is altor-vec a replacement for Fuse.js?

Not always. Fuse.js is smaller, simpler, and easier to adopt. If fuzzy text matching is enough for your corpus, it remains a great choice. altor-vec becomes the better fit when meaning matters more than string similarity and you are willing to add a build-time embedding step.

Does altor-vec handle typos like Fuse.js?

Yes, but through a different mechanism. Fuse.js handles typos directly with fuzzy matching over characters. altor-vec relies on semantic proximity in embedding space, so variants like colour and color or related phrasing can still land near each other even though the engine is not doing Bitap on the literal query string.

How much bigger is altor-vec than Fuse.js?

Fuse.js is about 5 KB minified, while altor-vec is a 54 KB gzipped WASM payload. That extra weight is not trivial. It is worth paying only when semantic retrieval quality changes the usefulness of the search experience.

Can I use altor-vec with zero build step like Fuse.js?

Not easily. This is the real tradeoff. Fuse.js can work directly on in-memory JavaScript data with essentially no indexing pipeline. altor-vec needs pre-generated embeddings, which means a build or deploy step is part of the setup.

Which one is faster at query time?

For a 10K-vector corpus, altor-vec reports sub-1 ms p95 query latency in-browser. Fuse.js is also fast for small lists, but it still behaves like a fuzzy scan across the corpus, so scaling characteristics are less favorable as document counts grow.

Does altor-vec work offline like Fuse.js?

Yes, completely. Once the WASM module and static index file are loaded, all search computation stays on-device. That preserves the same offline story many developers already like about Fuse.js.

Bottom line

Fuse.js is one of the best answers in JavaScript when you need lightweight fuzzy matching. altor-vec is one of the best answers when you want semantic retrieval in the browser without a backend. If your users fail search because of typos, stay with Fuse.js. If they fail search because their words do not match your authors' words, altor-vec is the more relevant upgrade path. The honest framing is that Fuse.js wins on simplicity and bundle size, while altor-vec wins on meaning-level retrieval.

CTA: npm install altor-vec · Star on GitHub

related resources

implement

blog

compare