Under the Hood
This site is an experiment in delivering HDR photographs as close to the original scene as current technology allows. It's a work in progress — documented here so others can build on what I've figured out so far.
Philosophy
Until recently, no screen could display the full dynamic range a camera sensor captures. The workaround was tone mapping — software that compressed a wide luminance range into the narrow band a display could reproduce. The results were often kitschy: oversaturated skies, glowing halos, the "HDR photography" look that was really just SDR with the contrast cranked up.
True HDR is different. Modern displays can actually produce the luminance range the scene contained. But backwards compatibility is a challenge. Now with JPEG-XL gain maps, a single file carries both an SDR base layer and HDR enhancement data — the browser and display do the rest natively. No tone mapping artifacts. No software interpretation. Just the scene as the camera saw it, on a screen that can finally show it.
This site is built around that idea. Every photograph is mastered in JPEG-XL with full HDR gain maps, and the pipeline is designed to preserve that data end-to-end — no external CDN, no lossy re-encoding. A standard JPEG fallback is generated alongside each image for browsers that don't support JPEG-XL yet — but I'll be the first to admit the SDR versions are a pale shadow of the real thing. That's the whole point: HDR isn't a nice-to-have here, it's the photograph.
Image Pipeline
Each photograph is exported from Lightroom as two files: a JPEG-XL master with an HDR gain map, and a JPEG companion for SDR fallback. Both are processed through the same pipeline to generate responsive derivatives.
The JXL path uses libjxl — the reference JPEG-XL
codec — to decode the master into a lossless PNG intermediate,
which Sharp resizes at each breakpoint, then cjxl
re-encodes back to JPEG-XL. HDR metadata survives the entire
round-trip. The JPEG path is simpler: Sharp handles the resize
directly.
Five derivative sizes are generated per image (400, 800, 1200,
1800, 2400 pixels wide) for both formats. The browser picks the
right one via <picture> and
srcset — JXL first, JPEG as fallback.
Tech Stack
- Framework
- Astro — mostly static, with minimal SSR for private content
- Hosting
- Cloudflare Workers — edge-deployed, incremental uploads via Wrangler
- Image codec
- libjxl — reference JPEG-XL encoder/decoder
- Image processing
- Sharp — resize intermediate for derivative generation
- Maps
- MapLibre GL JS — open-source, self-hosted tiles
- Transitions
- View Transitions API — native cross-document morphing