Modern photo gallery for photographers, with S3/GitHub sync, EXIF details, maps, and a WebGL viewer.
See the code
Afilmory
A modern, high-performance photo gallery platform for photographers
Official SaaS • Documentation • Live Examples • Self-Hosting
Afilmory (/əˈfɪlməri/, "uh-FIL-muh-ree") is a comprehensive photo gallery solution that combines Auto Focus (AF), Aperture (light control), Film (vintage medium), and Memory (captured moments). Built with React + TypeScript, it offers automatic photo synchronization from multiple storage sources, a GPU-accelerated image viewer (WebGPU first, WebGL fallback) with HDR gain-map support, professional EXIF metadata display, and a native iOS companion app.
👉 Get Started at afilmory.art - Zero setup, live in minutes!
The easiest way to create your photo gallery. No deployment, no servers, no maintenance required.
Why Choose SaaS?
For developers who need full control over their deployment:
Docker (Recommended)
# See our Docker deployment guide
https://github.com/Afilmory/docker
Manual Installation
# 1. Clone and install
git clone https://github.com/Afilmory/Afilmory.git
cd Afilmory
pnpm install
# 2. Configure
cp config.example.json config.json
cp builder.config.default.ts builder.config.ts
# Edit both files with your settings
# 3. Build manifest and thumbnails
pnpm run build:manifest
# 4. Start the application
pnpm dev
For detailed self-hosting instructions, see DEVELOPMENT.md and Documentation.
See Afilmory in action:
navigator.gpu is unavailable; smooth zoom, pan, pinch, and double-tap cycling between fit, fill, and 100%afilmory/
├── apps/
│ ├── web/ # Gallery SPA (Vite + React 19 + React Router 7)
│ ├── ssr/ # Next.js host: serves the SPA, injects manifest, OG/SEO
│ ├── mobile/ # Native iOS app (Swift / UIKit, XcodeGen)
│ ├── site/ # Marketing site (Astro)
│ ├── docs/ # Documentation site (Vite + MDX)
│ └── promo/ # Promo videos (Remotion)
├── be/ # Backend services
│ ├── apps/
│ │ ├── core/ # Core API server (Hono + @tsuki-hono)
│ │ ├── dashboard/ # Admin dashboard SPA
│ │ └── oauth-gateway/# Standalone OAuth broker
│ └── packages/
│ ├── db/ # Database schemas & migrations (Drizzle ORM)
│ ├── redis/ # Redis client
│ ├── task-queue/ # Job queue
│ └── utils/ # Shared backend utilities
├── packages/
│ ├── builder/ # Photo processing pipeline CLI
│ ├── webgl-viewer/ # GPU image viewer (WebGPU + WebGL fallback)
│ ├── viewer-motion/ # Framework-agnostic viewer motion primitives
│ ├── renderer/ # OG image renderer (Satori + resvg)
│ ├── data/ # PhotoLoader + bundled manifest
│ ├── typing/ # Shared photo/manifest types
│ ├── sdk/ # Public SDK + share embed script
│ ├── ui/ # Shared UI components
│ ├── hooks/ # React hooks library
│ └── utils/ # Utility functions
└── plugins/ # ESLint / Vite tooling plugins
dynamic-range: high displaystsyringe DIproject.yml as the project source of truthexiftool-vendored)Designed with adapter pattern for flexibility:
# Install dependencies
pnpm install
# Copy configuration files
cp config.example.json config.json
cp builder.config.default.ts builder.config.ts
# Set up environment variables
cp .env.template .env
# Edit .env with your credentials
# Development
pnpm dev # Start web + SSR
pnpm dev:be # Start backend services
pnpm --filter web dev # Web app only
pnpm --filter @afilmory/ssr dev # SSR only
pnpm --filter @afilmory/dashboard dev # Admin dashboard
pnpm dev:mobile # Build and run the iOS app in Simulator
pnpm site:dev # Marketing site
# Build
pnpm build # Build production web app
pnpm build:manifest # Generate photo manifest (incremental)
pnpm build:manifest -- --force # Full rebuild
pnpm build:manifest -- --force-manifest # Regenerate manifest only
pnpm build:manifest -- --force-thumbnails # Regenerate thumbnails only
# Documentation
pnpm docs:dev # Start docs dev server
pnpm docs:build # Build documentation
# Code Quality
pnpm lint # Lint and fix
pnpm format # Format code
pnpm type-check # Type checking
config.json - Site presentation config:
{
"name": "My Gallery",
"title": "My Photography",
"description": "Capturing beautiful moments",
"url": "https://gallery.example.com",
"accentColor": "#007bff",
"author": {
"name": "Your Name",
"url": "https://example.com",
"avatar": "https://example.com/avatar.jpg"
},
"social": {
"github": "username",
"twitter": "username"
},
"map": ["maplibre"],
"mapStyle": "builtin",
"mapProjection": "mercator"
}
builder.config.ts - Photo processing config:
import { defineBuilderConfig } from '@afilmory/builder'
export default defineBuilderConfig(() => ({
storage: {
provider: 's3',
bucket: 'my-photos',
region: 'us-east-1',
// ... other S3 settings
},
system: {
processing: {
defaultConcurrency: 10,
enableLivePhotoDetection: true,
},
observability: {
showProgress: true,
showDetailedStats: true,
},
},
}))
Implement the StorageProvider interface:
import type { StorageProvider, StorageObject } from '@afilmory/builder'
class MyStorageProvider implements StorageProvider {
async getFile(key: string): Promise<Buffer | null> {
// Your implementation
}
async listImages(): Promise<StorageObject[]> {
// Your implementation
}
// ... other required methods
}
Create a plugin that hooks into the build lifecycle:
import type { BuilderPlugin } from '@afilmory/builder'
export default function myPlugin(): BuilderPlugin {
return {
name: 'my-plugin',
hooks: {
beforeBuild: async ({ logger }) => {
logger.main.info('Starting build')
},
afterSaveManifest: async ({ payload }) => {
// payload.manifest, payload.cameras, payload.lenses
},
},
}
}
Register it in builder.config.ts under plugins.
We welcome contributions! See AGENTS.md for architecture notes and DESIGN.md for the web design system.
git checkout -b feature/amazing-feature)pnpm lint && pnpm type-check)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)Attribution Network License (ANL) v1.0 © 2025 Afilmory Team
See LICENSE for more details.
Built with love by the Afilmory team and contributors. Special thanks to all photographers using Afilmory to share their work with the world.
If this project helps you, please give it a ⭐️ Star!
4,834 followers · starred Jun 2025
1,045 followers · starred Dec 2025
12,637 followers · starred Oct 2025
264 followers · starred Dec 2025
Modern photo gallery for photographers, with S3/GitHub sync, EXIF details, maps, and a WebGL viewer.
See the code
Afilmory
A modern, high-performance photo gallery platform for photographers
Official SaaS • Documentation • Live Examples • Self-Hosting
Afilmory (/əˈfɪlməri/, "uh-FIL-muh-ree") is a comprehensive photo gallery solution that combines Auto Focus (AF), Aperture (light control), Film (vintage medium), and Memory (captured moments). Built with React + TypeScript, it offers automatic photo synchronization from multiple storage sources, a GPU-accelerated image viewer (WebGPU first, WebGL fallback) with HDR gain-map support, professional EXIF metadata display, and a native iOS companion app.
👉 Get Started at afilmory.art - Zero setup, live in minutes!
The easiest way to create your photo gallery. No deployment, no servers, no maintenance required.
Why Choose SaaS?
For developers who need full control over their deployment:
Docker (Recommended)
# See our Docker deployment guide
https://github.com/Afilmory/docker
Manual Installation
# 1. Clone and install
git clone https://github.com/Afilmory/Afilmory.git
cd Afilmory
pnpm install
# 2. Configure
cp config.example.json config.json
cp builder.config.default.ts builder.config.ts
# Edit both files with your settings
# 3. Build manifest and thumbnails
pnpm run build:manifest
# 4. Start the application
pnpm dev
For detailed self-hosting instructions, see DEVELOPMENT.md and Documentation.
See Afilmory in action:
navigator.gpu is unavailable; smooth zoom, pan, pinch, and double-tap cycling between fit, fill, and 100%afilmory/
├── apps/
│ ├── web/ # Gallery SPA (Vite + React 19 + React Router 7)
│ ├── ssr/ # Next.js host: serves the SPA, injects manifest, OG/SEO
│ ├── mobile/ # Native iOS app (Swift / UIKit, XcodeGen)
│ ├── site/ # Marketing site (Astro)
│ ├── docs/ # Documentation site (Vite + MDX)
│ └── promo/ # Promo videos (Remotion)
├── be/ # Backend services
│ ├── apps/
│ │ ├── core/ # Core API server (Hono + @tsuki-hono)
│ │ ├── dashboard/ # Admin dashboard SPA
│ │ └── oauth-gateway/# Standalone OAuth broker
│ └── packages/
│ ├── db/ # Database schemas & migrations (Drizzle ORM)
│ ├── redis/ # Redis client
│ ├── task-queue/ # Job queue
│ └── utils/ # Shared backend utilities
├── packages/
│ ├── builder/ # Photo processing pipeline CLI
│ ├── webgl-viewer/ # GPU image viewer (WebGPU + WebGL fallback)
│ ├── viewer-motion/ # Framework-agnostic viewer motion primitives
│ ├── renderer/ # OG image renderer (Satori + resvg)
│ ├── data/ # PhotoLoader + bundled manifest
│ ├── typing/ # Shared photo/manifest types
│ ├── sdk/ # Public SDK + share embed script
│ ├── ui/ # Shared UI components
│ ├── hooks/ # React hooks library
│ └── utils/ # Utility functions
└── plugins/ # ESLint / Vite tooling plugins
dynamic-range: high displaystsyringe DIproject.yml as the project source of truthexiftool-vendored)Designed with adapter pattern for flexibility:
# Install dependencies
pnpm install
# Copy configuration files
cp config.example.json config.json
cp builder.config.default.ts builder.config.ts
# Set up environment variables
cp .env.template .env
# Edit .env with your credentials
# Development
pnpm dev # Start web + SSR
pnpm dev:be # Start backend services
pnpm --filter web dev # Web app only
pnpm --filter @afilmory/ssr dev # SSR only
pnpm --filter @afilmory/dashboard dev # Admin dashboard
pnpm dev:mobile # Build and run the iOS app in Simulator
pnpm site:dev # Marketing site
# Build
pnpm build # Build production web app
pnpm build:manifest # Generate photo manifest (incremental)
pnpm build:manifest -- --force # Full rebuild
pnpm build:manifest -- --force-manifest # Regenerate manifest only
pnpm build:manifest -- --force-thumbnails # Regenerate thumbnails only
# Documentation
pnpm docs:dev # Start docs dev server
pnpm docs:build # Build documentation
# Code Quality
pnpm lint # Lint and fix
pnpm format # Format code
pnpm type-check # Type checking
config.json - Site presentation config:
{
"name": "My Gallery",
"title": "My Photography",
"description": "Capturing beautiful moments",
"url": "https://gallery.example.com",
"accentColor": "#007bff",
"author": {
"name": "Your Name",
"url": "https://example.com",
"avatar": "https://example.com/avatar.jpg"
},
"social": {
"github": "username",
"twitter": "username"
},
"map": ["maplibre"],
"mapStyle": "builtin",
"mapProjection": "mercator"
}
builder.config.ts - Photo processing config:
import { defineBuilderConfig } from '@afilmory/builder'
export default defineBuilderConfig(() => ({
storage: {
provider: 's3',
bucket: 'my-photos',
region: 'us-east-1',
// ... other S3 settings
},
system: {
processing: {
defaultConcurrency: 10,
enableLivePhotoDetection: true,
},
observability: {
showProgress: true,
showDetailedStats: true,
},
},
}))
Implement the StorageProvider interface:
import type { StorageProvider, StorageObject } from '@afilmory/builder'
class MyStorageProvider implements StorageProvider {
async getFile(key: string): Promise<Buffer | null> {
// Your implementation
}
async listImages(): Promise<StorageObject[]> {
// Your implementation
}
// ... other required methods
}
Create a plugin that hooks into the build lifecycle:
import type { BuilderPlugin } from '@afilmory/builder'
export default function myPlugin(): BuilderPlugin {
return {
name: 'my-plugin',
hooks: {
beforeBuild: async ({ logger }) => {
logger.main.info('Starting build')
},
afterSaveManifest: async ({ payload }) => {
// payload.manifest, payload.cameras, payload.lenses
},
},
}
}
Register it in builder.config.ts under plugins.
We welcome contributions! See AGENTS.md for architecture notes and DESIGN.md for the web design system.
git checkout -b feature/amazing-feature)pnpm lint && pnpm type-check)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)Attribution Network License (ANL) v1.0 © 2025 Afilmory Team
See LICENSE for more details.
Built with love by the Afilmory team and contributors. Special thanks to all photographers using Afilmory to share their work with the world.
If this project helps you, please give it a ⭐️ Star!
4,834 followers · starred Jun 2025
1,045 followers · starred Dec 2025
12,637 followers · starred Oct 2025
264 followers · starred Dec 2025