Ultra-fast, zero-cloud RAW image culler for photographers who shoot thousands.
Crafted with ❤️ by Mustafa Erdem Köşk
Apertin is a local-first, blazing-fast RAW image culling app built for photographers who come back from a shoot with 400+ RAW files and need to triage them in minutes — not hours.
It works like a card-swipe workflow for your photos. Swipe right to keep, left to trash. Every operation runs entirely on your machine — no cloud uploads, no subscriptions, no waiting.
The core engine is written in Rust and uses memory-mapped file I/O to extract embedded JPEG previews directly from RAW files at native speed. Your full-resolution RAW files never have to be decoded during culling.
| Feature | Details |
|---|---|
| 🚀 Zero-decode preview extraction | Reads the embedded JPEG directly from RAW binary — no full decode needed |
| 🔥 Swipe Mode | Keyboard-driven keep/trash workflow with animated card transitions |
| ↩️ Undo | Instantly reverse the last keep/trash/star decision with ⌘Z / Ctrl+Z or the dock button — works even from the summary screen |
| 👁️ Browse Mode | Classic gallery browser with instant prev/next navigation |
| 📂 Grid Mode | Full-screen interactive lazy-loaded thumbnail view of files with status badges |
| 📊 Split Compare View | Compare up to 4 images side-by-side with synchronized zoom/pan |
| 📈 RGB & Luma Histogram | Live-rendered high-performance exposure graphs inside EXIF panel |
| ⚡ XMP Sidecar Export | Toggle to write ratings/reject tags directly to .xmp files, leaving originals in place |
| 🎨 Adobe Lightroom Link | Right-click sidebar files to reveal in Finder or open directly in Lightroom Classic |
| 🔍 Focus Check Zoom | Press Space to enter pixel-level zoom mode across all images (including inside Grid Mode) |
| ⭐ Star Rating | Mark hero shots with ↑ for selective editing |
| 🔗 Smart Grouping | Automatically cluster burst shots and similar scenes before you cull |
| 📊 EXIF Sidebar | Camera, lens, shutter, aperture, ISO, focal length at a glance |
| 🗂️ macOS "Open With" | Right-click any folder in Finder → Open With → Apertin |
| 🖱️ Drag & Drop | Drag a folder onto the app window to start instantly |
| 🗑️ OS Trash | Trashed files go to your system recycle bin |
| ✅ Selected_to_Edit export | Kept files moved to Selected_to_Edit/ ready for Lightroom/Capture One |
| 💾 Session persistence | Progress is saved per folder — close and resume at any time |
| 🌑 Dark mode only | Premium dark UI — built for low-light post-production environments |
| 🔒 Fully local | Zero network requests, zero telemetry, zero cloud |
| Format | Camera Brand |
|---|---|
.ARW |
Sony (α series) |
.CR3 / .CR2 |
Canon (EOS series) |
.NEF |
Nikon |
.RAF |
Fujifilm |
.DNG |
Adobe / Leica / DJI |
.ORF |
Olympus |
.RW2 |
Panasonic |
.PEF |
Pentax |
.HEIC / .HEIF |
iOS / Modern mobile |
.JPG / .JPEG |
Any camera |
.PNG |
Any source |
📁 Open Folder
│
▼
⚡ Rust scans directory & extracts embedded previews (parallel, memory-mapped)
│
▼
🔥 Swipe Mode
├── → Keep (moves to Selected_to_Edit/)
├── ← Trash (sent to OS recycle bin)
└── ↑ Star (moves to Starred/)
│
▼
📋 Review Decisions
├── Inspect thumbnails
├── Restore from trash
└── Demote keeps to trash
│
▼
✅ Apply — done. Open Selected_to_Edit/ in Lightroom.
| Key | Action |
|---|---|
→ |
Keep image |
← |
Trash image |
↑ |
Toggle star |
⌘Z / Ctrl+Z |
Undo last decision |
Space |
Toggle focus zoom (persists across images) |
| Key | Action |
|---|---|
→ |
Next image |
← |
Previous image |
↑ |
Toggle star |
⌘Z / Ctrl+Z |
Undo last decision |
Space |
Toggle focus zoom |
| Key | Action |
|---|---|
Space |
Fullscreen sharpness zoom of selected thumbnail |
Double Click |
Select thumbnail and switch to Browse Mode |
Right Click |
Open file context menu (Lightroom / Finder Reveal) |
Apertin can automatically group similar photos before you start culling, so you can make one keep/trash decision for an entire burst instead of reviewing each frame individually.
⏱ Time-based grouping runs entirely in the browser — no Rust invocation needed. It reads the DateTimeOriginal EXIF field from each photo and clusters consecutive shots taken within a configurable time window.
| Preset | Gap | Typical use |
|---|---|---|
| 30 s | 30 seconds | Tight bursts, bracketed exposures |
| 2 min | 2 minutes | Scene changes during a walk-around |
| 5 min | 5 minutes | Different locations in the same session |
⬡ Visual similarity grouping is powered by a Rust backend algorithm that analyses the pixel content of each photo's embedded preview thumbnail using a DCT-based perceptual hash (pHash) and complete-linkage clustering.
| Preset | Hamming ≤ | What it matches |
|---|---|---|
| Burst | 6 | Near-identical frames, only shutter timing differs |
| Normal | 10 | Same scene with varying exposure, slight reframe |
| Loose | 15 | Similar subject from a different angle |
Apertin is built on a Rust + Svelte + Tauri stack. The separation of concerns is clean:
┌─────────────────────────────────────────┐
│ Svelte Frontend │
│ - State machine (welcome/cull/summary) │
│ - Card stack animations │
│ - Keyboard event bus │
│ - Blob URL preview rendering │
└────────────────┬────────────────────────┘
│ Tauri IPC (invoke)
┌────────────────▼────────────────────────┐
│ Rust Backend │
│ - scan_directory (parallel WalkDir) │
│ - parse_raw_file (EXIF + preview) │
│ - get_raw_preview (mmap byte read) │
│ - execute_culling_actions (fs moves) │
│ - analyze_groups (pHash + clustering) │
│ - select_folder (rfd native dialog) │
│ - get_initial_path (CLI arg / Open With)│
└─────────────────────────────────────────┘
- RAW files range from 20MB–100MB each
- Parallel preview extraction via Rayon (one thread per file)
- Memory-mapped I/O means preview bytes are read without loading the full file
- Result: 400 Sony ARW files scanned in ~1.2 seconds on M-series Mac
- Rust (stable toolchain)
- Node.js 18+
- Tauri CLI v2
# macOS dependencies (if not already installed)
xcode-select --installBecause Apertin is not yet notarised with an Apple Developer certificate, macOS will show:
"Apple could not verify 'Apertin' is free of malware…"
One-time fix — run this in Terminal:
xattr -cr /Applications/Apertin.appThen double-click the app normally. macOS will not ask again.
Tip: If you haven't moved the app to
/Applicationsyet, drag the.appfrom the mounted.dmginto Terminal instead of typing the path.
Because the installer is not yet signed with an EV certificate, Windows SmartScreen may show "Windows protected your PC."
One-time fix: click More info → Run anyway. Right-clicking a RAW file → Open with → Apertin opens its folder ready to cull, and Reveal in Explorer highlights the file directly.
Make the AppImage executable, then run it:
chmod +x Apertin_*.AppImage
./Apertin_*.AppImageOr install the .deb on Debian/Ubuntu:
sudo dpkg -i apertin_*.debOn Linux, "Reveal in file manager" opens the containing folder (most desktops have no portable verb to pre-select a file).
git clone https://github.com/erdemkosk/apertin.git
cd apertin
npm install
npm run tauri devnpm run tauri buildThe .dmg / .app bundle will appear in src-tauri/target/release/bundle/.
apertin/
├── src/ # Svelte frontend
│ ├── App.svelte # Main application component
│ ├── global.css # Design system (HSL tokens, glassmorphism)
│ ├── logo.png # App icon
│ └── main.js # Vite entry point
│
├── src-tauri/ # Rust backend
│ ├── src/
│ │ ├── main.rs # Tauri commands + CLI arg handler
│ │ └── parser.rs # RAW EXIF + preview parser
│ ├── Cargo.toml # Rust dependencies
│ └── tauri.conf.json # App config + macOS file associations
│
├── index.html # App shell
├── vite.config.js # Vite bundler config
└── package.json
Apertin's UI is built around a single principle: the photo should fill your vision, not the interface.
- Dark-first: Deep volcanic slate backgrounds (#07090e) chosen to match how photographers work in dimmed rooms
- Glassmorphism panels: Sidebar and EXIF strip use
backdrop-filter: blur()so they feel like HUD overlays, not UI chrome - Amber accent system: The
#f97316amber is the only persistent color — everything else is monochrome or semantic (green = keep, red = trash, gold = star) - Plus Jakarta Sans: Chosen over system fonts for its optical regularity and premium weight range
- Zero animations for the photo itself — only chrome elements animate. The photo is always sharp and still.
| Layer | Technology | Why |
|---|---|---|
| Desktop shell | Tauri 2 | Smaller than Electron, native webview, Rust backend |
| Frontend | Svelte | Zero-overhead reactivity, no virtual DOM |
| Backend | Rust | Memory safety + parallel performance |
| File walking | walkdir | Efficient recursive directory traversal |
| Parallelism | rayon | Work-stealing thread pool for parallel file parsing |
| Image decode | image | JPEG decode for pHash thumbnail generation |
| OS trash | trash | Cross-platform recycle bin integration |
| Native dialogs | rfd | Cross-platform Rust file dialog |
| Typography | Plus Jakarta Sans | Premium UI typeface |
- Windows support —
.msi/.exeinstallers, Explorer "reveal & select", file associations - Linux support —
.deb/.AppImagebuilds, GTK file dialog - Collection view — grid browse with zoom
- Virtualized grid — windowed rendering keeps 5000+ file folders smooth
- Undo — reverse the last keep/trash/star decision with
⌘Z/Ctrl+Z - Color label system — reject / 1-star / 2-star / pick
- In-app auto-update — one-click update via the Tauri updater
- Smart grouping — pHash + complete-linkage clustering for burst and similar-scene detection
- XMP sidecar export — write ratings back as metadata without moving files
- Lightroom Classic integration — open Selected_to_Edit directly in catalog
PRs are welcome. Open an issue to discuss large changes first.
# Run in dev mode
npm run tauri dev
# Lint frontend
npx svelte-check
# Check Rust
cd src-tauri && cargo checkPolyForm Noncommercial 1.0.0 © Mustafa Erdem Köşk
If Apertin saved you an hour of culling, give it a ⭐
Built for photographers, by a photographer.
