ブログに戻る
Tauri React Rust Photography

TauriとReactで高速写真セレクトツールを作った話

舞台写真家が数千枚の写真を数分でレビューするためのアプリ「Glimpse」の開発記録

8分で読めます

10,000+

枚の画像に対応

RAW

NEF, ARW, CR2, CR3...

無料

オープンソース (MIT)

課題

舞台写真家として、1回の公演で数千枚の写真を撮影することがあります。公演後、締め切りに追われながら、これらすべての画像をレビューしてベストショットを見つける作業は大変です。

Adobe BridgeやLightroomなどの既存ツールは強力ですが、以下の問題がありました:

  • 1. 読み込みが遅い - 数千枚のサムネイル生成を待つのは苦痛
  • 2. 機能が多すぎる - セレクト段階では編集機能は不要
  • 3. マウス依存 - キーボード中心の操作の方が高速

私が必要としていたのは、写真の高速レビューと選別に特化したツールでした。そこでGlimpseを作りました。

なぜTauri + React + Rustなのか?

Electron、Flutter、ネイティブオプションを評価した結果、以下の理由でTauri 2.0を選択しました:

Rustバックエンド

画像処理にはRustのパフォーマンスが不可欠。Pure-RustのRAWデコーダー(rawloader + imagepipe)でNEF、ARW、CR2、CR3などを外部依存なしで処理。

Reactフロントエンド

Reactのコンポーネントモデルとエコシステム(特に@tanstack/react-virtual)で複雑なUIを効率的に構築。

小さなバンドルサイズ

Electronと違い、Tauriはシステムのネイティブwebviewを使用。最終的なアプリは15MB未満(Electronアプリは150MB以上)。

クロスプラットフォーム

単一コードベースでmacOS(ARM64 + x64)とWindowsをビルド。GitHub Actionsで自動リリース。

技術的な課題と解決策

1. RAWファイルのサポート

Webブラウザはネイティブでは RAWファイルをデコードできません。RustバックエンドでPure-Rustライブラリを使用してJPEGサムネイルとプレビューをオンザフライで生成しています:

// rawloader + imagepipe でサポートするRAW形式
NEF (Nikon), ARW (Sony), CR2/CR3 (Canon),
RAF (Fujifilm), ORF (Olympus), RW2 (Panasonic),
PEF (Pentax), DNG (Adobe), SRW (Samsung)

処理パイプライン:RAWセンサーデータをデコード、デモザイク処理を適用、sRGB色空間に変換、JPEGにエンコード。すべてrayonを使って並列処理。

2. 10,000枚以上の画像のための仮想スクロール

数千のDOM要素をレンダリングするとパフォーマンスが著しく低下します。代わりに@tanstack/react-virtualを使用して、表示されているアイテムのみをレンダリング:

仕組み:

  1. 1. スクロール位置に基づいて表示される行を計算
  2. 2. その行のみをレンダリング(通常20〜30アイテム)
  3. 3. CSSトランスフォームでアイテムを正しく配置
  4. 4. スクロール時にDOMノードを再利用

結果:10,000枚以上の画像が読み込まれていても滑らかな60fpsスクロールを実現。

3. サムネイルキャッシュシステム

サムネイルの生成は、特にRAWファイルの場合コストが高いです。GlimpseはSQLiteを使用してキャッシュされたサムネイルを追跡:

初回読み込み

サムネイルを並列生成、アプリデータディレクトリに保存、メタデータをSQLiteに記録

2回目以降

ファイルハッシュをキャッシュと照合、既存のサムネイルを即座に提供

4. キーボードファーストのナビゲーション

写真セレクトではスピードがすべて。Glimpseはキーボードファーストのワークフロー用に設計されています:

キー アクション
矢印キー 画像間を移動
1-5 レーティング(星の数)を設定
X リジェクト(不採用)としてマーク
Enter 詳細ビューを開く
C 選択した画像を比較
Esc 戻る

パフォーマンス最適化

CSS GridとResizeObserver

リサイズのたびにレイアウトを再計算する代わりに、ResizeObserverでコンテナサイズの変更を効率的に検出し、必要な場合のみグリッドの列数を更新。

並列サムネイル生成(Rayon)

Rustのrayonクレートで並列イテレーションを実現。サムネイル生成は利用可能なすべてのCPUコアに自動スケール。

遅延プレビュー読み込み

高解像度プレビュー(2000x2000)は、初回フォルダスキャン時ではなく、詳細ビューに入った時にのみ生成。

非同期IPC(Tauri)

すべてのRustコマンドはTauriのIPCを介して非同期で実行され、重い処理中もUIの応答性を維持。

アーキテクチャ概要

フロントエンド (React + TypeScript)
  |
  |-- ThumbnailGrid.tsx    (仮想スクロールグリッド)
  |-- DetailView.tsx       (フルスクリーンビューア)
  |-- CompareView.tsx      (サイドバイサイド比較)
  |
  v  Tauri IPC (invoke)
  |
バックエンド (Rust)
  |
  |-- commands.rs          (Tauriコマンドハンドラ)
  |-- image_processor.rs   (RAWデコード、サムネイル生成)
  |-- database.rs          (SQLite: sessions, labels, cache)
  |
  v
SQLite データベース
  |-- sessions (フォルダ追跡)
  |-- labels   (レーティング、リジェクト)
  |-- cache    (サムネイルメタデータ)

Glimpseを試す

Glimpseは無料でオープンソースです。ダウンロードして、あなたの写真で試してみてください。フィードバックをお待ちしています!

技術スタックまとめ