A hybrid image search tool for macOS that uses CLIP + OCR to find images through natural language queries and text content.
On-device processing · Spotlight-style interface · GPU accelerated · Face recognition
Main App
Main.app.mp4
Spotlight Widget
widget.mp4
Photo management on macOS sucks. Searchy indexes your images locally and lets you search them using descriptive phrases like "sunset over mountains", "person wearing red", or text visible in images like "invoice 2024". Find photos by face, detect duplicates, and access it instantly from the menu bar with a global hotkey.
brew install --cask ausafmo/searchy/searchybrew uninstall --cask searchy
# Remove app data (~2GB: venv, model weights, indexes)
rm -rf ~/Library/Application\ Support/searchy- Download
Searchy-v4.0.dmgfrom Releases - Drag to Applications
- Right-click → Open (first time only, macOS security)
- Click Start Setup — Python & CLIP models install automatically (3-5 min)
git clone https://github.com/AusafMo/searchy.git
cd searchyOpen searchy.xcodeproj in Xcode and build (⌘R).
On first launch, Searchy will automatically:
- Install Python (if not found)
- Create an isolated virtual environment
- Download dependencies (~2GB)
- Download the CLIP model
⌘⇧Space Open Searchy
↑ ↓ Navigate results
Enter Copy and paste selected image
⌘Enter Reveal in Finder
⌘1-9 Copy and paste by position
Ctrl+1-9 Copy to clipboard only
Esc Close window
| Feature | Description |
|---|---|
| Hybrid Search | Natural language + OCR text search with adjustable weighting |
| Face Recognition | Auto-detect, cluster, name, pin, hide, merge, group faces |
| Duplicate Detection | Find visually similar images, bulk cleanup |
| Similar Image Search | Find images similar to any selected photo |
| Auto-Indexing | Watch directories with filters, incremental indexing |
| External Volumes | Index and search images on external drives |
| Model Selection | Multiple CLIP models, switch without re-indexing |
| Model TTL | Auto-unload CLIP model after idle period to free RAM/GPU, reloads from disk cache on next search |
| Update Checker | Notifies when a new version is available via Homebrew |
| Privacy | Fully local, no cloud, no telemetry, GPU accelerated via Metal |
searchy/
├── ContentView.swift # SwiftUI interface
├── searchyApp.swift # App lifecycle, setup manager, server management
├── server.py # FastAPI backend
├── generate_embeddings.py # CLIP model and embedding generation
├── face_recognition_service.py # Face detection & clustering (DeepFace)
├── image_watcher.py # File system monitor for auto-indexing
└── requirements.txt
Stack: SwiftUI + AppKit → FastAPI + Uvicorn → CLIP ViT-B/32 + DeepFace (ArcFace) → NumPy embeddings
All data stored in ~/Library/Application Support/searchy/:
image_index.bin # Embeddings + paths (pickle)
face_index.pkl # Face embeddings + clusters
venv/ # Isolated Python environment
# image_index.bin structure
{
'embeddings': np.ndarray, # Shape: (N, 512), float32, L2-normalized
'image_paths': list[str] # Absolute paths
}The FastAPI server runs on localhost:7860 (or next available port).
| Endpoint | Method | Description |
|---|---|---|
/health |
GET | Health check |
/search |
POST | Semantic + OCR hybrid search |
/text-search |
POST | Pure OCR text search |
/similar |
POST | Find similar images |
/duplicates |
POST | Find duplicate images |
/recent |
GET | Recent images |
/face-scan |
POST | Start face detection |
/face-clusters |
GET | Get face groups |
/status |
GET | Server + model loading state |
/model/ttl |
GET/POST | Get/set model TTL |
Want to replace CLIP with your own model or rewrite the backend? Just respect these contracts:
1. Embedding Index File — ~/Library/Application Support/searchy/image_index.bin
import pickle, numpy as np
data = {
'embeddings': np.ndarray, # Shape: (N, embedding_dim), float32, L2-normalized
'image_paths': list[str] # Length N, absolute paths
}
with open('image_index.bin', 'wb') as f:
pickle.dump(data, f)2. Server API Contract — FastAPI/HTTP server implementing the endpoints above.
3. Script Interface — The app calls scripts via Process():
# generate_embeddings.py — indexing
python generate_embeddings.py /path/to/folder [--fast] [--max-dimension INT] [--batch-size INT]
# Must output JSON to stdout:
{"type": "start", "total_images": N, "total_batches": N}
{"type": "progress", "batch": N, "total_batches": N, "images_processed": N, ...}
{"type": "complete", "total_images": N, "new_images": N, "total_time": float, ...}
# server.py — API server
python server.py --port PORT
# image_watcher.py — file system monitor
python image_watcher.py4. Example: Custom Model
from sentence_transformers import SentenceTransformer
import pickle, numpy as np
model = SentenceTransformer('clip-ViT-L-14') # Or any model
def index_images(paths):
embeddings = model.encode([Image.open(p) for p in paths])
embeddings = embeddings / np.linalg.norm(embeddings, axis=1, keepdims=True)
with open('image_index.bin', 'wb') as f:
pickle.dump({'embeddings': embeddings, 'image_paths': paths}, f)As long as you output the right JSON progress messages and maintain the pickle format, the Swift UI will work with any backend.
cd ~/Library/Application\ Support/searchy
source venv/bin/activate
python generate_embeddings.py /path/to/images --batch-size 64 --fast- Spotlight-style floating widget
- Global hotkey (
⌘⇧Space) - Auto-indexing with file watchers
- GPU acceleration (Metal)
- Bundled .app with auto-setup
- Duplicate detection
- Face recognition & clustering
- Similar image search
- External volume indexing
- OCR text extraction & hybrid search
- Model TTL & memory management
- Homebrew tap distribution
- In-app update notifications
- Alternative/smaller models
- Albums, tags, smart collections
- Timeline and map views
- Metadata panel (EXIF, IPTC, XMP)
macOS photo management is surprisingly lacking. Apple Photos is bloated and locks you into iCloud. Lightroom is a subscription editor. There's no lightweight, native gallery app for Mac.
Searchy aims to become the native macOS media manager that doesn't exist — with semantic search superpowers.
- Proxy-based — Your files stay where they are. We're a lens, not a file manager.
- Non-destructive — Albums, tags, ratings are metadata. Original files never touched.
- Smart search — Semantic search, face grouping, OCR, smart collections. No manual tagging.
Fully local. No cloud, no telemetry, no external requests after initial model download.
- CORS restricted to localhost
- Server binds to 127.0.0.1 only
- Dependencies pinned
- macOS 13+
- Apple Silicon
- Internet connection (first-time setup only)
- ~2GB disk space for models
Supported formats: jpg, jpeg, png, gif, bmp, tiff, webp, heic
MIT License