Skip to content

xplato/Sharkfin

Repository files navigation

    Sharkfin

A better way to find files on Mac

Sharkfin Header

Sharkfin is a native macOS app for semantic search of local files. Currently only supports images (#1).

Visit websiteDownloadView releases

Features

  • Local-only: indexing, searching, and all other app functionality apart from the initial CLIP model download is entirely local.
  • Natural language searching: Search indexed files with natural language. Currently, only images are supported.
  • High performance: Indexing and searching are both highly optimized to leverage the built-in neural engine in macOS. See the screenshots and videos section below for a demo.

Demos

Search

Performing several example searches on my local index of >18,000 files (source size: ~50 GB, database size: ~100 MB).

CleanShot.2026-04-12.at.18.13.43.mp4

⚠️ Note: The intermittent white artifacts in the Liquid Glass background only appear due to a bug in the screen recording; these artifacts are not visible when using the app.

Other Search Examples

(I have an egregious amount of unique design assets from Creative Market 😅)

demo-1

demo-4

demo-3

Installation

Sharkfin is currently in pre-release testing. With that said, the current build is safe to use for personal use, just keep in mind that you may encounter bugs. If so, please open an issue.

View Sharkfin releases to download the latest release in an installable DMG.

Usage

When you open Sharkfin for the first time, you'll see a welcome screen with instructions on how to get started. The onboarding flow will guide you through the initial setup. More detailed usage information is provided below, if desired.

High-level Usage

  1. Choose your model package (ViT B/32, ViT B/16, or ViT L/14).
  2. Add directories to index.
  3. Search!
View in-depth usage details

Screenshots below include UI from Sharkfin that may differ from the UI of the current version. Functionality remains the same, unless otherwise noted.

1. Downloading CLIP Models

Sharkfin requires downloading CLIP models from Hugging Face to perform indexing. These are models I've cloned onto my personal HF account.

CleanShot 2026-04-06 at 17 33 26@2x

Note that the in-app download logic is a bit hacky because HF's Xet CDN is really unreliable. With that said, downloading does appear to be working. If you encounter issues downloading the models in-app, you can instead download them from HF manually and put them in Sharkfin's data directory.

Manually Downloading CLIP Models

Download the Vision Model

  1. Visit https://huggingface.co/xplato/clip-vit-base-patch32-vision-onnx/tree/main
  2. Download the following files (each must be downloaded individually 🙄): config.json, model.onnx, and preprocessor_config.json
  3. Open Sharkfin's data directory in Finder. You can find this by clicking the "Open in Finder" button in the Storage section of the Advanced tab in Sharkfin's settings, or by visiting this directory: /Users/<your username>/Library/Application Support/com.lgx.sharkfin
  4. Create the models directory if it doesn't exist.
  5. Within models, create a new folder: clip-vit-base-patch32-vision-onnx
  6. Move the files you downloaded from Hugging Face into that directory.

Download the Text Model

  1. Visit https://huggingface.co/xplato/clip-vit-base-patch32-text-onnx/tree/main
  2. Download the following files (each must be downloaded individually 🙄): config.json, merges.txt, model.onnx, special_tokens_map.json, tokenizer_config.json, tokenizer.json, and vocab.json
  3. Open Sharkfin's data directory in Finder. You can find this by clicking the "Open in Finder" button in the Storage section of the Advanced tab in Sharkfin's settings, or by visiting this directory: /Users/<your username>/Library/Application Support/com.lgx.sharkfin
  4. Create the models directory if it doesn't exist.
  5. Within models, create a new folder: clip-vit-base-patch32-text-onnx
  6. Move the files you downloaded from Hugging Face into that directory.

2. Adding Target Directories

Sharkfin works on directories you add to the app. In the "General" tab of Sharkfin's settings, add relevant directories in the "Directories" section. Once added, indexing will automatically be performed. You can manually re-index the directory at anytime by pressing the refresh icon next to the enable toggle.

CleanShot 2026-04-06 at 17 32 09@2x

Indexing Performance and Functionality

Depending on the number of files and their respective sizes, the initial index of the added directory could take some time, but typically it is very fast. Once added, Sharkfin will listen to file system events and automatically index new, modified, or deleted files (this behavior can be disabled in the "Advanced" tab of Sharkfin's settings).

Performing a new index is typically a lightweight operation, as any files that have already been indexed will be skipped. If you want to completely re-index a previously added directory, you can do so by clicking the trash icon of the directory. This will remove the directory from Sharkfin and delete all existing embeddings and thumbnails. You can then add it back.

CleanShot 2026-04-06 at 17 32 51@2x

Enabled Directories

The toggle in the directory row controls the enabled or disabled state of the directory. When disabled, Sharkfin will exclude files in that directory from the search results; disabling a directory simply hides results, it doesn't affect the existing indexes and embeddings.

CleanShot 2026-04-06 at 17 32 58@2x

3. Search

Once models have been downloaded and directories have been added, you can now search your files using more semantic expressions. By default, Sharkfin is activated with Shift+Command+Space, but this shortcut can be changed in the "Shortcuts" tab in Sharkfin settings.

demo-2

Welcome Screen

CleanShot 2026-04-06 at 17 07 26@2x

Implementation

View implementation details

Sharkfin uses CLIP (Contrastive Language-Image Pre-Training) to embed both images and text into a shared 512-dimensional vector space, enabling natural language search over local image files.

Indexing

Indexing is triggered either by filesystem events (via FSEvents) or manually by the user. The pipeline has three phases:

  1. Scan: FileScanner recursively walks the target directory, collecting metadata (path, size, modification date) for supported image types. No file contents are read at this stage.
  2. Diff: The indexing service compares scanned files against the database, skipping files that are already indexed and unchanged. Deleted files are removed from the database.
  3. Process (up to 8 concurrent tasks per file):
    • Load and downscale the image if needed.
    • Compute a SHA-256 content hash.
    • Preprocess the image for CLIP (resize, center-crop to 224x224, normalize with ImageNet stats, arrange in CHW layout).
    • Encode the image into a 512-dimensional, L2-normalized embedding via the CLIP vision model.
    • Generate a content-addressed thumbnail (256px max).
    • Persist the IndexedFile and FileEmbedding records in a single database transaction.

After indexing completes, the in-memory search cache is invalidated.

Embedding

CLIP inference is performed locally using two ONNX models (~350 MB vision, ~250 MB text) run via ONNX Runtime:

  • Image encoding uses the CoreML execution provider for hardware acceleration (Neural Engine/GPU). The preprocessed [1, 3, 224, 224] tensor is passed through the vision model and the output is L2-normalized.
  • Text encoding runs on CPU (to avoid CoreML dynamic-shape issues). Input text is tokenized to 77 tokens via swift-transformers, passed through the text model, and L2-normalized.

Both encoders produce unit-length vectors in the same latent space, so cosine similarity reduces to a simple dot product.

Search

When the user types a query:

  1. The query text is encoded into a 512-dim vector via the CLIP text encoder.
  2. On first search (or after cache invalidation), all stored embeddings are loaded into a contiguous in-memory cache.
  3. A single vDSP_mmul call (Apple Accelerate) computes the dot product of the query vector against all stored embeddings at once.
  4. Results below a similarity threshold (0.16) are discarded, remaining scores are normalized to a 0–1 relevance scale, sorted, and capped at 50.

Similar-image search uses the same approach, substituting a stored image embedding for the text query vector.

Storage

All data is stored locally in a SQLite database (via GRDB) with tables for directories, indexed files, embeddings (stored as raw float blobs), and index job status. Thumbnails are stored as JPEG/PNG files on disk, content-addressed by hash to avoid duplicates.

Search Quality

View search quality details

Generally, I've found the results to be pretty good—sometimes surprisingly so (see the "woman as a flamingo" example above). However, in other cases the results are quite strange. Here's an example:

CleanShot 2026-04-06 at 17 53 34@2x

It's returning rather abstract vectors. While I don't necessarily have any images of an ostensible airplane plilot, I do have images that are quite close to that, both in terms of implicit meaning and explicit text.

CleanShot 2026-04-06 at 17 56 23@2x

or, more explicitly:

CleanShot 2026-04-06 at 17 54 00@2x

Neither of these two images were included in the search results for "pilot."

Improving the quality of search results is, of course, a priority moving forward. PR #31 focuses on adding support for additional CLIP model packages of larger dimensions which should, in theory, improve search quality, at the cost of indexing speed and memory usage. These trade-offs, however, are comparatively minor relative to the quality of the results (so far).

Developing

Prerequisites

  • macOS 15.4+ (deployment target)
  • Xcode 26.4+ with Swift 5 toolchain

Getting Started

  1. Clone the repository:
git clone https://github.com/xplato/sharkfin.git
cd sharkfin
  1. Open the project in Xcode:
open sharkfin.xcodeproj
  1. Swift Package Manager dependencies will resolve automatically on first open. The project uses the following packages:

  2. Select the sharkfin scheme and build (Cmd+B) or run (Cmd+R).

About

A better way to find images on Mac • Native semantic search

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages