Build1 distinct publisher3 min readUpdated
A static site distilled its embedding model into an 8,900-word lookup table and ranks 796 documents in single-digit milliseconds in the browser. The trade-offs are published too.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
A static site with 796 indexed pages now runs meaning-based search entirely in the reader's browser, with no server, no vector database, no index service, and no model executing when you type [1]. That is worth attention because it is a working counterexample to the architecture most teams reach for without arguing about it: embed the corpus through an API, push the vectors into a hosted index, embed the query at request time, pay per call, and add a service that can be down [2].
The constraint came first, which is usually how this goes. According to the author's write-up on dev.to, the site has a house rule that no page may send a reader's input to a third party, and it is a static build on Cloudflare's static assets, so there was no search server to add and an embedding API call from the query box was forbidden by policy [3]. The other browser-side option, shipping a sentence transformer as ONNX or WASM, is tens of megabytes plus a warm-up delay and puts a model in the critical path of a text box [4].
The method is Model2Vec: run the transformer once at build time over a fixed vocabulary and keep only the per-word output vectors, which turns runtime embedding into a dictionary lookup and an average [5]. The table here was distilled from Xenova/all-MiniLM-L6-v2 over the corpus's own vocabulary, one contextless 384-dimensional vector per word, 8,900 words at present [6]. Each document vector is the L2-normalized mean of its words' vectors, quantized to int8 and base64 encoded [7]. The query is embedded by exactly the same rule in the same code, which is what keeps queries and documents in the same space [8].
What ships is three static JSON files from the site's own origin, gzip and brotli by the host, plus one inline module: 401 lines of vanilla JavaScript using only atob, Math, Map, Set and typed arrays, with no DOM, no Buffer and no imports [9][10]. Ranking is cosine against 796 unit vectors, which is 796 dot products of length 384 [11], roughly 305,000 multiply-accumulate operations per query [12]. The author reports that unpacking every document vector takes 12 ms in Node on his machine and a full three-channel query takes 4 to 10 ms [13].
The second-order property is the one operators should copy. Because the word table is the expensive artifact and the document vectors are cheap, a build-start hook re-embeds the live corpus from the table that already exists, with no model and no network; the model is only needed to mint vectors for genuinely new words [14].
That is also where the bill arrives. A static word table has no context, so word order is gone, "dog bites man" and "man bites dog" embed identically, and negation is invisible [15]. Coverage decays as the corpus grows: 4,689 distinct words in the live corpus currently have no vector, and 459 documents dropped at least one word when their mean was taken [16]. The index reports this about itself in its own header, with vocabCovers set to false [17]. Those 459 documents are about 58 percent of the 796 [18], and the uncovered word count is now more than half the size of the covered table [19].
Two things to watch. First, whether retrieval quality visibly degrades before someone reruns the distillation, since the header flags the condition but nothing forces the fix [17][14]. Second, how the approach behaves on a less distinctive corpus; this one is described as dense, cross-referenced and deliberately weird, which flatters averaged word vectors [20]. The numbers are self-reported and the search is live at artwaste.land/ask [21].
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
A site with 796 indexed pages runs semantic search entirely in the browser, with no server, no vector database, no index service, and no model running at query time.
Shipping a sentence transformer to the browser as ONNX or WASM is tens of megabytes with a warm-up delay, and puts a model in the critical path of a text box.
On the author's machine, in Node, unpacking every document vector takes 12 ms and a full three-channel query takes 4 to 10 ms.
4,689 distinct words in the live corpus have no vector, and 459 documents therefore dropped at least one word when their mean was taken.
The author describes the default 2026 approach as: embed the corpus with an API, push the vectors into a hosted index, embed the query at request time, pay per call, and add a service that can be down.
The site has a house rule that no page may send a reader's input to a third party, and it is a static build on Cloudflare's static assets, so there is no search server to add and a query box calling an embedding API would violate the rule.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Specific and self-audited, but single-source and unverified
Every figure - 796 documents, 8,900 vocabulary words, 384 dimensions, 401 lines, 12 ms and 4-10 ms, 4,689 uncovered words, 459 affected documents - comes from one first-party post that also publishes working code and its own negative findings, which is stronger than a bare claim. But there is no second observer, no reproduction, no payload sizes, and no retrieval-quality evaluation, and the latency numbers were taken in Node rather than in a browser, so the architectural claims are better evidenced than the performance and quality claims.
One live site, no third-party uptake disclosed
Observed adoption is a single production deployment on the author's own 796-page static site at artwaste.land/ask. No other site, team, package download, fork or external user is reported, and the technique is presented as something readers might copy rather than something already copied.
Slightly understated; limits published with the wins
The headline promises semantic search without a vector database and the post delivers a mechanism plus numbers, then spends substantial space on what it costs: lost word order, invisible negation, 4,689 uncovered words, 459 degraded documents, and an engine that once returned nothing for 200 of 200 unique-word queries. That self-limiting framing sits slightly below what the evidence supports for the narrow case described. The small positive pull is the unmeasured assertion that context loss 'matters far less than intuition suggests' and the implicit generalizability of a 796-document result.
Self-promotional authorship, no commercial stake disclosed
The author is describing and linking their own site, so there is a traffic and reputation incentive to present the architecture favourably, and no independent party checked the numbers. Against that, no product, pricing, sponsorship, funding or vendor relationship appears anywhere in the source; the hosted alternative is named only as a rejected path, and the post argues against the commercial option rather than for a paid one.
Internally coherent, externally unconfirmed
Confidence is capped by the single-source cluster: the mechanism is internally consistent, arithmetically checkable and accompanied by code and self-reported limits, so the architectural claims are fairly reliable, while performance, quality and generalizability claims rest entirely on one unverified first-party measurement environment and one small corpus.
build
FFmpeg.wasm ships, but budget for a watchdog: one in ten heavy jobs deadlocked in silence1 distinct publisher
build
Anti-bot systems now score the session, which means your proxy pool is not a mitigation1 distinct publisher
build
Laravel's session cookie is why your HTML never gets cached at the edge1 distinct publisher
build
The capture returned HTTP 200. The file was a Cloudflare block page.1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 14, 2026