galr

Self-hosted private image gallery for browsing large photo collections organized in a flexible hierarchy.

Highlights

  • Zero-dependency architecture: single Node.js process, no external database or cache
  • Advanced lightbox with slideshow, pan/zoom, keyboard shortcuts, and collection navigation
  • Comprehensive organization: tags, ratings, favorites, notes, and smart filtering
  • Secure sharing via time-limited token URLs with automatic session management
  • Invite-only registration with QR code system for frictionless onboarding

A self-hosted private image gallery built for browsing large photo collections organized in a flexible hierarchy (e.g., category → collection → images). Runs as a single Node.js process with no external dependencies.

Architecture

Single-process design: Node.js with embedded SQLite (using --experimental-sqlite). No separate database server, no Redis, no complex orchestration.

Tech stack:

  • Runtime: Node.js 24 with native SQLite support
  • HTTP: Hono v4 + @hono/node-server
  • Templating: Hono JSX server-side rendering (zero client framework)
  • Database: node:sqlite (DatabaseSync) in WAL mode
  • Thumbnails: sharp for JPEG generation with configurable size and TTL
  • Styling: Vanilla dark-theme CSS
  • Client JS: Single vanilla script, no bundler

Core features

Browsing

  • Category list: Sort by A–Z, collection count, or last scanned date; filter by first letter or category tag; “New” badge on recently scanned categories
  • Category page: Responsive cover-art grid for all collections; filter by collection tag; 1–5 star ratings displayed on cards; favorites toggle; prev/next collection navigation
  • Collection page: Full image grid with click-to-open lightbox; collection-level tags, star rating, freeform notes, prev/next collection links
  • Search: Live typeahead in header (categories + collections); full search results page at /search
  • Favorites: Heart any category or collection; view all favorites at /favorites
  • Continue watching: Recently viewed collections strip on home page with reading-position progress
  • Top Picks: Collections ranked by avg_rating × vote_count × view_count at /top
  • Random collection: /random redirects to a randomly chosen collection
  • Navigation: Previous/next image (arrow keys or on-screen buttons)
  • Slideshow mode: Animated progress bar (press S to toggle, adjustable speed)
  • Pan & zoom: Scroll wheel, pinch-to-zoom, drag to pan; Z to reset
  • Full-screen toggle
  • Image info overlay (I): Filename, natural dimensions, file size
  • Cover pin (★): Promote any image to be the collection’s cover thumbnail
  • Prev/next collection jump (⏮/⏭): Navigate between collections without leaving lightbox
  • Keyboard shortcuts cheat-sheet (?): Quick reference overlay

Organization

  • Tags (collection-level): Add/remove tags on any collection; filter by tag on category page; tag cloud on collection page
  • Tags (category-level): Add/remove tags on categories; filter categories by tag on home page
  • Star ratings: Per-user 1–5 stars; average + vote count displayed everywhere
  • Collection notes: Freeform markdown-safe text annotation per collection

Sharing

Shared links: Generate time-limited (7-day) token URL for any collection. Links work without login; images and thumbnails served via short-lived galr_share cookie. Related collections and editing controls hidden on shared views.

Admin

  • Rescan: Incremental scan (processes only changed directories); full wipe-and-rescan option
  • Orphan pruning: Removes database records for images/collections/categories whose files have been deleted
  • Thumbnail cache stats: Storage usage breakdown by tier (covers vs gallery)
  • User management: List all accounts, create new users, delete users
  • QR invite system: Generate single-use invite token displayed as QR code; recipients visit link and register without needing existing login

Account

  • Session-based login: HTTP-only cookies for security
  • Change password: Available at /settings
  • Invite-only registration: Admin-controlled access

Why I built it

I wanted a fast, private gallery for large photo collections that didn’t require complex infrastructure or external services. Most gallery solutions are either cloud-hosted (privacy concerns) or require multiple services (database server, cache, workers). This runs as a single process you can start and forget.

The embedded SQLite approach means deployment is trivial: copy the binary, point it at a photo directory, and it works. No connection strings, no migration runners, no orchestration complexity.

Usages: Could be to sort photos by Year -> Month -> images for example.

Design decisions

Server-side rendering: Using Hono JSX means the server renders full HTML pages. No client-side framework complexity, faster initial page loads, works without JavaScript (except for the lightbox and live search).

Embedded database: SQLite in WAL mode provides ACID transactions and concurrent reads without requiring a separate database server. For a single-user or small-team gallery, this is more than sufficient.

Vanilla client JS: A single script handles lightbox interactions, keyboard shortcuts, and live search. No build step, no dependency management, straightforward debugging.

Thumbnail strategy: Sharp generates JPEG thumbnails on first access and caches them. Configurable TTL means old thumbnails expire automatically without manual intervention.

Status

Currently running in private testing. Not yet public-facing, but functional and stable for daily use.