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.
