How Semantic Find works

Native find-in-page matches characters. That fails the moment you remember the idea but not the wording — searching a contract for “cancelling” finds nothing when the text says “terminate.” Semantic Find fixes that without giving up anything Ctrl+F already does, and without sending a single byte of your text anywhere.

The pipeline

When the page loads, its text is split into paragraph-sized chunks and each chunk is embedded with MiniLM (all-MiniLM-L6-v2) running on-device through transformers.js — WebGPU when available, WASM otherwise. Embeddings are cached in IndexedDB, so a revisit skips both the one-time model download and re-indexing.

Every keystroke then ranks the chunks three independent ways:

  1. Literal substring — a raw scan over the text. It's the only signal that matches characters rather than tokens, so a fragment like grea matches inside “aggregate,” exactly like the native find bar.
  2. Keyword — exact, prefix, and typo-tolerant fuzzy matching via MiniSearch. refnd still finds “refund.”
  3. Semantic — cosine similarity between the query embedding and each chunk embedding. This is what catches paraphrase: “cancelling” ≈ “terminate your subscription.”

Fusion: rank positions, not raw scores

The three signals produce scores on incompatible scales, so summing them would let one dominate. Instead the ranked lists are merged with weighted Reciprocal Rank Fusion (RRF): each result earns credit based on its position in each list, weighted by how trustworthy that signal is. Crucially, a separate eligibility gate (a substring hit, a keyword hit, or cosine above a floor) decides what appears at all, while RRF only decides the order — so literal find-in-page never breaks while meaning leads the ranking.

Honest scoring

Each result shows a match percentage derived from absolute cosine similarity — not the relative RRF score — and carries a provenance tag (Exact / Close / Related / Loosely related) computed from which signals actually fired. If nothing clears the gate, you get “no results,” not a confidently wrong top hit.

Off the main thread

Embedding runs in a Web Worker (an offscreen document in the Chrome extension, which also side-steps host-page CSP for the model download), with vectors transferred back as zero-copy ArrayBuffers. Typing stays smooth even while a page is being indexed. In the extension, matches are painted with the CSS Custom Highlight API — no DOM mutations, so React apps don't fight the highlights.

Try it

The full algorithm, scoring model, and tuning knobs are documented in docs/DESIGN.md. Everything is open source under the MIT license.