Haozhe Li

AI enthusiast / Full-stack / Product / ...

Introducing Pictures: An AI-Powered Gallery for Photographers

Project Link: haozheli.pictures

Overview

Photographers often need a platform to systematically showcase their work. However, as their portfolio grows, traditional retrieval and recommendation methods become inefficient and cumbersome. As a creator, you want your audience to see the works that truly interest them, not get lost in countless images.

Pictures was created for that very reason.
By leveraging multimodal AI technology, this project enables a cross-language, cross-modality natural language search experience. Whether starting from an image or a short description, users can quickly find the most semantically relevant photography in the Gallery—ensuring that every piece truly gets seen.


Key Features

The following features make Pictures stand apart from traditional photo showcase websites.

In Pictures, simply type a short description into the search bar. The system understands your intent and finds the most relevant works.
No more worrying about “precise keywords”—our AI understands semantics and supports fuzzy matching to return the best results.

The search function also natively supports multilingual input, completely removing language barriers.

If you’re particularly fond of a photo, image-to-image search helps you discover more similar masterpieces.
Click “Find Similar,” and the system automatically retrieves and returns photos that are most visually and semantically similar—no manual input needed.

Random Recommendations

Looking for inspiration? Try Random Recommendations.
Our recommendation system intelligently curates photography that aligns with your tastes, making discovery effortless and enjoyable.

Personalized Homepage

The homepage is no longer a static scroll through the database. It now serves personalized recommendations tailored to each visitor.

Under the hood, the feed is built from a dual-pool strategy that balances exploitation and exploration:

  • Exploitation pool: proven, high-quality photographs and works that match what you have already shown interest in.
  • Exploration pool: fresh and lesser-seen photographs, so new work gets a real chance to be discovered.

We deliberately tuned the ratio between the two pools so that great photos and brand-new photos can both show up. Established favorites do not crowd out newcomers, and exploration does not drown out quality. The result is a homepage that feels relevant on the first screen and still keeps surprising you.


Technical Details

Pictures combines multiple cutting-edge technologies to deliver a high-quality, low-latency search and recommendation experience.

Hybrid Embedding and Hybrid Retrieval

This project adopts a hybrid embedding and hybrid retrieval strategy.
The core idea: represent and understand each image in multiple ways, then combine results from different perspectives during search.

The overall process is as follows:

Flow Chart of Pictures
Pictures Flow Chart
  1. Metadata Embedding
    First, extract image metadata such as EXIF, title, and description, and embed them using:
    • BM25 to generate sparse vectors for keyword-level matching.
    • multilingual MiniLM (paraphrase) to generate dense vectors for capturing semantic information and enabling fuzzy, cross-language search.
  2. Visual Embedding
    Use Jina CLIP v1 to embed the visual content, generating dense vectors that capture both visual and semantic information.
  3. Vector Storage
    All three vectors are upserted into the vector database, forming the foundation of retrieval.
  4. Search and Result Fusion
    During search, the user’s query is embedded in all three forms simultaneously. The results are merged using a weighted strategy and the RRF (Reciprocal Rank Fusion) algorithm, returning the most comprehensive and relevant output.

Multi-Layer Caching Design

Embedding models are computationally expensive, and vector retrieval introduces latency.
To minimize response time and ensure a smooth user experience, Pictures implements a three-layer Redis caching architecture.

Cache Layer of Pictures
Pictures Cache Layer

Layer 1: Front-End Cache (Next.js)
When users visit the website, the front end first fetches homepage recommendations directly from Redis instead of making immediate backend calls.
This approach:

  • Avoids high latency from serverless backend cold starts.
  • Significantly reduces backend computation load.

Layer 2: Backend Cache (Service Layer)
The backend caches recommendation results so that identical or similar requests can be quickly served, improving throughput.

Layer 3: Embedding Cache (Embedding Layer)
Query embeddings are cached so that repeated queries reuse existing vectors instead of recomputing them, speeding up subsequent searches.


Self-Hosted Infrastructure on Railway

The entire Pictures backend is deployed on Railway and runs on our own stack, with no third-party managed AI or vector APIs in the loop:

  • A custom backend service.
  • Qdrant, self-hosted, as the vector database.
  • Redis, self-hosted, for the caching layers described above.
  • Our own embedding service.

Embedding service. Images are embedded with Jina CLIP v1, and text is embedded with multilingual MiniLM (paraphrase). Both models are quantized and exported to ONNX, so they run fast on plain CPU with no GPU required. On top of that, we wrote our own dynamic batching + request queue: concurrent requests are grouped into batches for throughput, and when the queue is full the service replies with a 503 to protect itself instead of falling over.

Same-region private networking. The backend, Qdrant, Redis and the embedding service all live in the same Railway region and talk to each other over Railway's internal network, so service-to-service calls never touch the public internet and stay very fast.

Benchmark. Measured from inside Railway against the embedding service, on CPU only:

  • multilingual MiniLM (text): 12 ms p50 / 18 ms p99 for a single request, peaking at ~197 req/s

  • Jina CLIP v1 (image): 27 ms p50 / 40 ms p99 for a single request, peaking at ~75 req/s

  • Mixed (Jina + MiniLM together): ~53 ms p50 / 77 ms p99 at concurrency 2, reaching ~83 req/s combined

  • A text embedding comes back in about 12 ms, and an image embedding in about 27 ms.

  • Even with 128 concurrent clients hammering both models at once, the service sustained roughly 80 req/s on CPU.

  • Zero failed requests (no non-200 responses) across every phase of the run, and the queue never filled.

  • Memory stayed flat at around 1.2 GB from the first request to the last, with no leaks and no growth under load.

At high concurrency, latency is mostly time spent waiting in the queue rather than model compute, which is exactly the trade the queue is designed to make.


Distributed Architecture and Global Access

To ensure consistent, fast access for users worldwide, Pictures uses a distributed infrastructure:

  • Image assets are hosted on Cloudflare R2, leveraging its global distributed storage and CDN network for fast image delivery.
  • The website front end is deployed on Vercel, providing low-latency and scalable access through its CDN and Fluid Compute system.

Thanks to this architecture, Pictures serves users across the globe with stability and efficiency.

This work is licensed underCC BY-NC-SA 4.0. Generative AI may be used for text polishing, translation, etc.
Haozhe Li