Launch offerUnlock Code Lens, Inline Agent, and Projects for $19 — normally $29.Pro $19 — normally $29
← Blog

How accurate is Code Lens? Benchmarks on 20 stacks, and EmbeddingGemma 2 on a Mac

Code Lens labels what you select on screen with the component, file and line that renders it. You click a label and your editor opens there. For that to be useful it has to be right most of the time, and it has to say nothing when the screen isn't from your project.

This post is how we measure that, what the numbers are in Bigshot 1.5, and what the local embedding model, EmbeddingGemma 2, does and doesn't add. All runs are on a 2021 MacBook Pro with an M1 Pro, in a release build.

How Code Lens works: select part of the screen, read its text, follow each line to the code that shows it, stay quiet on other apps, break ties with the image model, and open the line.

Results

Opens the line that renders the text82% of 587 screen lines (7% in Bigshot 1.4)
Opens the right file93%
Same task with grep, first match34%
Labels on another app's screen0 in 280 runs
Matching one screen on a 600-file repo220 ms median
Photo search, right capture first83% with EmbeddingGemma 2, 28% with keywords

The test set

A screenshot only has text and pixels. To open the right place, Lens has to get from a line of text on screen to the line of code a developer would edit to change it. We call that line the render site:

  • Literal text in a component or template: the line holding the text.
  • Translated text: the line that uses the key, such as t("hero.title"), stringResource(R.string.save) or {% trans %}. Not the locale file.
  • Text from a data array or JSON file: the line that renders it, such as the .map( or ForEach. Not the data line.

The corpus is 20 small apps, one per stack, each written the way that stack is usually written: React with react-i18next, Next.js with next-intl, Vue, Svelte, Angular, Astro, static HTML, Django, Rails, Laravel, Go templates, SwiftUI, a UIKit storyboard, Jetpack Compose, Flutter, React Native, WPF, a Rust terminal UI, copy kept in JSON and YAML files, and a Japanese and Chinese site. Each app has 3 to 6 screens written as the macOS text recognizer reads them, with the noise a real screen has: browser chrome, names, dates, prices, and paragraphs wrapped across lines.

That gives 97 screens and 587 lines with a known render site. A line counts as right when Lens opens it, or the line next to it.

To check that Lens stays quiet, we also run text from 14 other apps (Mail, Slack, Figma, Xcode, a Stripe checkout and others) against every fixture. Any label on those screens is a false positive.

Accuracy

Share of 587 screen lines where each tool opens the render site: Code Lens 1.5 82%, any grep match 52%, first grep match 34%, Code Lens 1.4 7%.
Share of 587 screen lines where each tool opens the render site: Code Lens 1.5 82%, any grep match 52%, first grep match 34%, Code Lens 1.4 7%.

The baseline is what you would do by hand: search the project for the text you see, case-insensitive and literal, and open the first match. That gets 34% of lines. Reading every match and picking the right one gets 52%. Search goes wrong in predictable ways. For translated text the first match is the locale file, not the component. Data arrays point at the data, not where it is shown. Text that wraps on screen or is built from parts doesn't match at all: 16% of lines have no match anywhere in the project.

Lens handles those cases by linking keys to where they are used, data to the component that renders it, and wrapped lines back into one sentence. It opens the render site for 82% of lines, and the right file for 93%.

StackLens 1.5grep, first matchEmbeddingGemma 2, right file first
Flutter + ARB100%34%75%
React + react-i18next100%8%91%
Next.js, Japanese and Chinese94%15%84%
SwiftUI + String Catalog91%2%64%
Vue 3 + vue-i18n91%41%88%
WPF (.NET 8)90%51%43%
SvelteKit + svelte-i18n89%50%65%
Astro + content collections89%67%50%
Next.js + next-intl88%7%53%
React Native (Expo)86%40%77%
Static HTML85%59%71%
Angular + ngx-translate82%30%69%
Jetpack Compose80%28%80%
Go templates78%48%57%
Laravel Blade78%18%51%
Copy in JSON and YAML70%45%65%
UIKit storyboard66%33%59%
Django + gettext62%13%72%
Rails ERB + i18n58%27%83%
Rust, ratatui33%13%46%
All 587 lines82%34%67%

The weakest stacks are the ones where screen text is assembled in code, not written in one piece: Rust format strings, Rails lazy lookups, Django blocks.

Why not embeddings alone

The last column above is a pure semantic search: embed the line you see with EmbeddingGemma 2, embed every component, and take the closest. It finds the right file first for 67% of lines and in the top three for 92%. Those numbers flatter it, since each fixture has only 3 to 10 files to choose from, and it returns a component, not a line.

The bigger problem is that it always returns something. On real screens the best match scores 0.63 to 0.94. On other apps' screens it scores 0.54 to 0.77. A cutoff above every other-app screen keeps only 49 of the 97 real ones. With no threshold that separates the two, embeddings can't say "this isn't your project".

So Lens works the other way round. Screen text has to be found in the project before anything is labelled. That is why it labelled nothing in 280 runs of other apps' text. The model ranks what text has already confirmed.

What EmbeddingGemma 2 does in Bigshot

EmbeddingGemma 2 is Google DeepMind's open multimodal embedding model, released on October 6, 2026 under Apache 2.0. Bigshot runs the 440M text and vision build through Google's LiteRT-LM runtime on the Mac's GPU. Text and images land in one 768-dimension space, so a description, a component and a screenshot can be compared directly. The model is optional: a 388 MB download in Settings, and Lens works on text alone without it.

It is used in three places:

  1. Lens tie-breaks. When a short crop's text is in several components, the screenshot picks the one it looks most like. It also proposes nearby components with no text of their own, such as an icon or a chart.
  2. Library photo search. You describe a screenshot and the Library finds it.
  3. Similar and duplicates. Image-to-image, using the same stored vectors.

In Lens: no change in accuracy on our real captures

We ran Lens on 38 real full-screen captures of Bigshot's own app and website, with and without the image. Ten have a known answer. Both ways it got 5 of the 10 right and labelled nothing on the 22 screens from other apps. The image changed which components were labelled on 7 of the 38 screens, without changing the score. Ten answerable screens is too few to show a small effect either way. On this set, text decides.

Right capture ranked first for 40 descriptions over 49 captures: image vectors 83%, OCR-text vectors 63%, keywords 28%.
Right capture ranked first for 40 descriptions over 49 captures: image vectors 83%, OCR-text vectors 63%, keywords 28%.

This is where the model earns its download. We wrote 40 plain descriptions of 49 real captures, the way you would remember a screenshot rather than quote it: "period tracker with moon phases", "Formula 1 race countdown on yellow", "tag picker dropdown under the notch".

FirstTop 3Mean reciprocal rank
EmbeddingGemma 2, query vs. image33 of 4039 of 400.90
EmbeddingGemma 2, query vs. OCR text25 of 4033 of 400.73
Keyword search over OCR text, tags and notes11 of 4014 of 400.34

Images beat the screenshot's own text by eight queries. A lot of what makes a screenshot memorable is never written on it: a waterfall photo, a yellow race track, a moon-phase ring.

Speed and memory

On the M1 Pro, Metal GPU, LiteRT-LM 0.18:

Model file388 MB, downloaded once
Load3 to 7 s
Process memory20 MB before loading, about 770 MB after
Embed a query or a screen of text25 ms median
Embed a component card24 to 28 ms median
Embed a 2.3-megapixel screenshot247 ms median, 458 ms p90
Code map of a 613-file Next.js app1.7 s on text, 10.7 s with the model (230 components)
Code map of Bigshot, 441 files2.9 s on text, 34.7 s with the model (962 components)
Lens matching per screen220 to 280 ms median, about 1.8 s for the slowest

The model cost is paid when the map is built. After that only components whose text changed are embedded again. Matching a screen takes the same time with or without the model, because comparing one vector to a thousand stored ones is cheap.

Limits

  • The fixtures are small apps written for the test, not production code. Each has 3 to 10 source files.
  • The real-capture set is small, and the photo-search descriptions were written by us after looking at the captures.
  • Lens also labels other components on screen, and those labels are less reliable than the line it opens. In the corpus, 160 of 204 labels were components that are on that screen.
  • Rust terminal UIs (33%), Rails (58%) and Django (62%) are the weakest stacks.
  • All timings are from one M1 Pro. Newer Macs will be faster; Google's model card lists 9.5 ms per text embedding on an M5.

The 20-app corpus and the test that scores it are part of Bigshot's test suite, so every change to matching is measured against the same 587 lines.