Back to blog
Tauri React Rust Photography

Building a High-Speed Photo Culling Tool with Tauri and React

How I built Glimpse to help stage photographers review thousands of photos in minutes

8 min read

10,000+

Images Supported

RAW

NEF, ARW, CR2, CR3...

Free

Open Source (MIT)

The Problem

As a stage photographer, I often shoot thousands of photos during a single performance. After each show, I face the daunting task of reviewing all these images to find the best shots - usually under tight deadlines.

Existing tools like Adobe Bridge or Lightroom are powerful, but they're:

  • 1. Slow to load - Waiting for thousands of thumbnails to generate is painful
  • 2. Feature-heavy - I don't need editing tools during the culling phase
  • 3. Mouse-dependent - Keyboard-first workflow is much faster for selection

I needed a tool focused on one thing: quickly reviewing and selecting photos. So I built Glimpse.

Why Tauri + React + Rust?

After evaluating Electron, Flutter, and native options, I chose Tauri 2.0 for several reasons:

Rust Backend

Rust's performance is critical for image processing. Pure-Rust RAW decoders (rawloader + imagepipe) handle NEF, ARW, CR2, CR3, and more without external dependencies.

React Frontend

React's component model and ecosystem (especially @tanstack/react-virtual) make building complex UIs straightforward.

Small Bundle Size

Unlike Electron, Tauri uses the system's native webview. The final app is under 15MB compared to 150MB+ for Electron apps.

Cross-Platform

Single codebase builds for macOS (ARM64 + x64) and Windows. GitHub Actions handles automated releases.

Key Technical Challenges

1. RAW File Support

Web browsers cannot decode RAW files natively. The Rust backend generates JPEG thumbnails and previews on-the-fly using pure-Rust libraries:

// Supported RAW formats via rawloader + imagepipe
NEF (Nikon), ARW (Sony), CR2/CR3 (Canon),
RAF (Fujifilm), ORF (Olympus), RW2 (Panasonic),
PEF (Pentax), DNG (Adobe), SRW (Samsung)

The processing pipeline: decode RAW sensor data, apply demosaicing, convert to sRGB colorspace, then encode as JPEG. All done in parallel using rayon.

2. Virtual Scrolling for 10,000+ Images

Rendering thousands of DOM elements would destroy performance. Instead, I use @tanstack/react-virtual to only render visible items:

How it works:

  1. 1. Calculate which rows are visible based on scroll position
  2. 2. Render only those rows (typically 20-30 items)
  3. 3. Use CSS transforms to position items correctly
  4. 4. Recycle DOM nodes as user scrolls

Result: smooth 60fps scrolling even with 10,000+ images loaded.

3. Thumbnail Caching System

Generating thumbnails is expensive, especially for RAW files. Glimpse uses SQLite to track cached thumbnails:

First Load

Generate thumbnails in parallel, store in app data directory, record metadata in SQLite

Subsequent Loads

Check file hash against cache, serve existing thumbnails instantly

4. Keyboard-First Navigation

Speed is everything when culling photos. Glimpse is designed for keyboard-first workflow:

Key Action
Arrow keys Navigate between images
1-5 Set rating (star level)
X Mark as rejected
Enter Open detail view
C Compare selected images
Esc Go back

Performance Optimizations

CSS Grid with ResizeObserver

Instead of recalculating layout on every resize, I use ResizeObserver to efficiently detect container size changes and update the grid column count only when necessary.

Parallel Thumbnail Generation (Rayon)

Rust's rayon crate enables parallel iteration. Thumbnail generation automatically scales to all available CPU cores.

Lazy Preview Loading

High-resolution previews (2000x2000) are only generated when entering detail view, not during initial folder scan.

Async IPC with Tauri

All Rust commands run asynchronously via Tauri's IPC, keeping the UI responsive during heavy processing.

Architecture Overview

Frontend (React + TypeScript)
  |
  |-- ThumbnailGrid.tsx    (Virtual scrolling grid)
  |-- DetailView.tsx       (Full-screen viewer)
  |-- CompareView.tsx      (Side-by-side comparison)
  |
  v  Tauri IPC (invoke)
  |
Backend (Rust)
  |
  |-- commands.rs          (Tauri command handlers)
  |-- image_processor.rs   (RAW decode, thumbnail gen)
  |-- database.rs          (SQLite: sessions, labels, cache)
  |
  v
SQLite Database
  |-- sessions (folder tracking)
  |-- labels   (ratings, rejections)
  |-- cache    (thumbnail metadata)

Try Glimpse

Glimpse is free and open source. Download it, try it with your photos, and let me know what you think!

Tech Stack Summary