In-Browser Semantic Search covering 70 European finals.
Visit siteSearch 70 European Cup finals' events by describing what happened. All running in your browser.
Check out a very similar project: http://brunoquared.com/project/browser-ai-search/
Every European Cup and Champions League final ever played, from Real Madrid–Reims in 1956 to the 2025 final, searchable by describing a moment rather than naming it.
Type an acrobatic strike hit with his back to goal and it gives you Bale in Kyiv. Type settled on penalties and you get the shootouts. It's a search for events.

What you get back
Each result is one final, showing the passage that matched and the section of the article it came from.
Every row carries its own evidence: the cosine similarity of the matched passage, where each of the two retrievers ranked that final, and the fused score the list is sorted by.
Above the results, all 70 finals sit on a timeline as ticks. The matching years light up, taller for higher-ranked hits.
Two retrievers, one list
Searching runs twice over the same corpus, in two completely different ways.
The semantic half embeds your query into a vector and compares it against every indexed passage. It's what lets acrobatic strike find bicycle kick since the words don't overlap at all.
The lexical half is BM25. It's what makes rare, exact things findable: a player's surname, a stadium, a scoreline. Embeddings are not so great at those.
The two ranked lists are then merged with Reciprocal Rank Fusion, weighted by how confident each list is about its own top result.
It all runs in the tab
There is no server or API. Nothing you type is transmitted anywhere.
The corpus is built ahead of time from Wikipedia's raw wikitext for all 70 finals, split into 4,029 searchable units, embedded with all-MiniLM-L6-v2, and quantised to int8 - which turns the similarity search into integer dot products and gets the whole index down to about 1.5 MB of vectors plus 1.5 MB of metadata, shipped as static files.
The model itself comes from the HuggingFace CDN on first visit, roughly 45 MB of fp16 weights, cached at first run by the browser. It runs through @huggingface/transformers on WebGPU.

You can read more about it here in my blog post.
