Projects

Bryn Athyn Cemetery

A cemetery you can walk with your phone

Expo · React Native · React 19 · Three.js · Leaflet

brynathyncemetery.com Source on request Try the live demo June 2026 to present Sole designer and engineer
  • 318commits, solo
  • 50test files
  • 1,093mapped plots
  • 2products, one codebase
Bryn Athyn Cemetery

The problem

A cemetery is a database that people visit on foot, usually while grieving, often on a phone with one bar of signal. The records existed. What did not exist was a way to stand at the gate and find your grandmother.

So the constraints came from the setting, not from a spec. It had to work at walking pace, outdoors, with no signal. And it had to be safe enough that a stranger's contribution could never quietly rewrite a family's record.

What I built

  • Map and search across 1,093 mapped plots. Leaflet with marker clustering, hosted in a WebView on native and an iframe on web, over a GeoJSON layer built from survey data and drone imagery.
  • Turn-by-turn walking directions over a weighted graph of the cemetery's own trail segments, so a route follows the paths people actually walk instead of cutting straight across graves.
  • A 3D mode in Three.js with a touch joystick, so the grounds can be explored before anyone drives out there.
  • Offline first. Downloadable map packs and a service worker keep the whole thing usable with no signal at all.
  • A change ledger and an approval queue. Every write to a record goes through lib/changeLedger.ts. Public contributions land as proposals that a moderator reviews in one place, never as direct edits.
  • One codebase, two products. A single build flag produces either the public app or the team portal.

Architecture

Runtime
Expo SDK 54 and Expo Router 6 on React Native 0.81 and React 19, shipping native and web from one codebase.
Data
Firestore across nine collections. Every record write passes through a change ledger; offline writes queue locally and replay when signal returns.
Mapping
Leaflet and markercluster over a 1,093-feature GeoJSON, with affine coordinate calibration and terrain elevation data.
3D
Three.js through react-three-fiber and drei, checked against real drone frames at matching GPS position and heading.
Delivery
Two Cloudflare Pages projects: brynathyncemetery.com for the public app, team.brynathyncemetery.com for the portal. Both deploy from main.

Engineering discipline

The checks that make fast work safe. Every one of these is in the repo.

  • 50 test files. 45 Vitest suites over routing, coordinate transforms, the offline queue, the proposal queue and the change ledger, plus 5 Playwright end-to-end specs covering arrival, location permission, navigation, offline mode and touch controls.
  • scripts/check-public-bundle.mjs fails the build if any admin module reaches the public bundle. One flag separates two products, so the guard is not optional.
  • A bundle budget check and a design audit run as npm scripts rather than as good intentions.
  • 3D parity boards. The renderer is shot against real drone frames at the same GPS and bearing, with draw calls and triangle counts printed on the board, so a regression in the scene is visible rather than argued about.

Built with AI, gated by tests

I directed this build rather than typing it. The run log is still in the repo as CLAUDE_HANDOFF_2026-07-26.md, 406 KB of it, left in on purpose.

What I owned: the data model, the trust model (nothing public writes straight to a record), the call to ship two surfaces from one codebase, and every gate that would catch the model being wrong.

The tests are why the method is safe. A generated change that breaks the routing graph fails a Vitest suite long before it reaches a person standing in a cemetery.

Visuals

BeforeCemetery map at close zoom before the marker rework
AfterCemetery map at close zoom after the marker rework
Close zoom, before and after the marker pass. Same data, same tiles. The work was in reading a dense field of plots as individual graves rather than a smear, and in keeping the row structure legible under the pins.
BeforeCemetery map at wide zoom before clustering was tuned
AfterCemetery map at wide zoom after clustering was tuned
Wide zoom. Clustering has to summarise a thousand plots without implying a grave is somewhere it is not.
Three.js 3D walk mode with an on-screen touch joystick
3D walk mode. Three.js through react-three-fiber, driven by a touch joystick, with the 2D map one tap away.
Walk options sheet with speed control and view modes
Walk options. Overhead view, free exploration, or an auto stroll for anyone who does not want to drive the camera.
The Bryn Athyn Cemetery grounds
The place itself. Everything above is in service of standing here and finding one grave.

What shipped

  • Live on two domains and in active use.
  • 318 commits since 17 June 2026, solo.
  • HANDOVER_RUNBOOK.md names every account, who holds it today, and the order to transfer them to the church. I built it, then wrote down how to run it without me.

Let's talk

Download résumé