Bryn Athyn Cemetery
A cemetery you can walk with your phone
Expo · React Native · React 19 · Three.js · Leaflet
- 318commits, solo
- 50test files
- 1,093mapped plots
- 2products, one codebase

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.mjsfails 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







What shipped
- Live on two domains and in active use.
- 318 commits since 17 June 2026, solo.
HANDOVER_RUNBOOK.mdnames 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.
- Team portal
- team.brynathyncemetery.com