How it works
How DigUp turns your files into one searchable space with EmbeddingGemma 2, ranks results, runs the model, and keeps its index valid.
The model
EmbeddingGemma 2 is the embedding model Google DeepMind released in October 2026. It takes text, pictures, sound or video, and turns each into a list of 768 numbers, a point in one shared space where things that mean the same land close together, whatever kind of input they came from. A photo of a dog on a beach lands next to the words "a dog on the beach", in any of the model's more than 100 languages. It's open under Apache 2.0, and at 740M parameters it's small enough to run on a laptop.
DigUp runs ggml-org's 8-bit build of it through llama.cpp. The build comes as two files: the text part (310 MB) and the image and audio encoders (555 MB). That's the one download DigUp makes.
Two facts about the model shape the design. It listens to 30 seconds of sound per input, so long recordings are read as overlapping 30-second windows. And it takes a limited number of video frames at once, so DigUp embeds a video's keyframes as pictures, one per shot, plus its soundtrack as sound.
From files to points
Nothing long is squeezed into one point. Each file is read in pieces the size the model handles well, each piece gets its own point with its place in the file, and at search time the best piece of a file is its score. That's also where the result opens.
| Kind | What becomes a point | Where it opens |
|---|---|---|
| Photo | The picture | The file |
| Screenshot | The picture, at twice a photo's detail. On a Mac, its text is read too, for exact words | The file |
| Each page's text; a page with little text, like a scan, as a picture | The page | |
| Document, spreadsheet, slides | Passages of about 1,800 characters; sheets by name and rows, slides with their notes | The passage |
| Audio | 30-second windows, one every 25 seconds, so no sentence falls between two | The moment |
| Video | A frame every 3 seconds, kept when the picture changed (about one per shot), plus the soundtrack | The moment |
| Code | Stretches of whole lines up to 1,800 characters, with the file's path in its repository | The line |
The readers that turn files into pictures, text and sound come from each system: on a Mac, macOS's own ImageIO, PDFKit and AVFoundation; on Windows and Linux, PDFium, FFmpeg, stb and miniz. They only decode files. Everything after that is the same code everywhere.
One file for the index
Every point is stored at half precision (1.5 KB each) in one SQLite file, next to a keyword index of the same text. A search compares your words with every point by plain matrix multiplication; for the tens of thousands of points a home folder makes, that takes a few milliseconds, so there's no separate vector database. Code search keeps a second file of its own.
Ranking
Your words go through the same model, with its search prompt, while you type. Each piece in the index gets a meaning score: how close it is to your words, measured against the other pieces of its kind for this search, so a picture and a page can be ranked together. Exact words add to it: the more of your words a file has, in its name or its text, the more they count, and a query that looks like an invoice number or an error code counts them double. A kind you name ("in a video") gets a small boost.
This combination was chosen by measurement. On the project's test sets, it put the right file first more often than fusing two ranked lists did (0.88 against 0.73 on the synthetic set), because rank fusion throws away how strong each match is: one common word in a terminal screenshot could beat a chart that matched by meaning.
Results below half the best score fold under "weaker matches", a display rule only.
Exact words
Keyword search runs in SQLite's full-text index over file names, document and page text, and on a Mac the text read from screenshots and scans. Words match whole, with their endings; a word of three letters or more that's in no file as typed matches as the start of longer words. Numbers of four digits or more are matched as written. The model alone found 5 of 9 exact lookups like error codes in the project's tests; with keywords, all of them.
Running the model
The model runs in a helper process next to the app. On a Mac it runs on the GPU with Metal. On Windows and Linux it runs on the graphics card with Vulkan when a quick check at setup finds that faster and giving the same results, and on the processor otherwise; searches always run on the processor there, so the panel never waits for a graphics card to wake up.
The indexing helper reads the next batch of files while the model embeds the current one, and quits when it's done. A smaller helper holds just the text part of the model while you search, and quits after ten minutes without one. If reading a file runs away, the helper stops it, notes the file, skips it and goes on.
Keeping the index valid
The index remembers which model, revision and version of its vectors made them, and never mixes them. Before any update to llama.cpp ships, a test checks that fixed inputs (texts, pictures, a recording) still give the stored reference vectors, at a cosine similarity of 0.999 or more. An update that passes keeps your index; one that didn't would make DigUp read everything again by itself.
Numbers
Measured on the developer's Mac (an M4 Pro) and the project's test files. The test sets are small, so these give a sense of scale, not a benchmark.
| What | Measured |
|---|---|
| Results after you stop typing | about 0.12 s, about 0.35 s the first time |
| A new screenshot becomes searchable | About a second after it lands |
| Right file first on the test sets | 98 of 111 searches (English, Bengali, Arabic), 109 of 111 in the top 5 |
| The app, idle | 12 to 30 MB |
| The search helper | About 250 MB, while you search |
| The indexing helper | Under about 2 GB at peak |
| Index size | A photo about 1.6 KB, a PDF page about 6 KB, an hour of video 1 to 2 MB |
The code is open: github.com/ARahim3/DigUp.