Work / Case study 06
Terrarium
Real-world exploration as a living world
It started with moving to San Francisco
The idea came up when I moved to SF in May 2026. Exploring a new city brings me real moments of joy — but having never lived on the West Coast, deciding where to go was surprisingly hard. Every list app answers "what's nearby" with a wall of pins and reviews, which is information, not motivation. What I wanted was something closer to a game: pick a destination for me, make getting there feel like uncovering territory, and give me a small reward for actually showing up.
Terrarium is that, built as a native iOS app around one core loop: recommend → arrive → grow. It suggests a nearby place worth visiting, tracks the walk with a fog-of-war map, and awards a collectible specimen when it verifies you really arrived — so the city gradually becomes a map you've personally cleared.
Three surfaces
- Home — an RPG-style atlas with fog-of-war, a weather-driven sky state, and level progress that reflects how much of the world you've uncovered.
- Drift — a location-tracked walking "ramble" that lays down a breadcrumb trail and reveals hex tiles of the map as you move.
- Anchor — a concierge that ranks one nearby place to visit, lets you re-roll, and verifies arrival to award the specimen.
The engineering
The core technical problem is that the app's central event — "you actually went somewhere" — comes from the physical world, which is noisy, asynchronous, and impossible to script in a test. Arrival detection runs as an explicit expedition state machine driven by live location, with a manual check-in fallback for when GPS is being GPS. Modeling it as a state machine, rather than scattered location callbacks, is what makes it testable: every transition can be exercised with simulated coordinates.
The same discipline shapes the whole architecture. A pure Domain layer (Foundation and simd
only) holds the app's logic, and every external integration — weather, location, maps, live
venue hours — sits behind a Providing/Managing protocol backed by a
stub. The result is that the app compiles and runs fully offline, with every preview and test
running against deterministic fakes. It's the same principle you'd apply to any system that has
to be evaluated against a nondeterministic outside world: isolate the nondeterminism behind a
seam, then test the logic on your side of it.
The visible layer is a fog-of-war rendered over a Mapbox v11 map, indexed with H3 hexagonal
cells so "revealed territory" is a well-defined grid rather than a blurry radius. The codebase
is strict-concurrency Swift 6 throughout — MainActor default isolation, @Observable
view models — so the compiler, not convention, enforces that location streams and UI state can't
race.
The content is hand-curated rather than scraped: a catalog of SF and Bay Area places with verified hours, each with an authored field note — because a recommendation engine is only as good as what it's allowed to recommend.
Where it stands
Built solo in about a month: ~29,000 lines of Swift across 131 files and 135 commits. The core loop — recommendation, verified arrival, fog-of-war reveal, specimen collection — works end to end, and the app is on its way to the App Store. This page will get the link when it lands.