Flyback — Session Summary

ForgeKit · 2026-06-06 → 06-07 · flyback-nine.vercel.app

Aerial time machine built from scratch. MapboxGL, historical imagery overlays, cinematic flight mode, Montville story research. From initial commit to v2.0 in two days.

Jun 6 · 10am
Initial commit
MapboxGL map
Jun 6 · 12pm
v1.0
Slider + flight mode
Jun 6 · 3pm
Search + share
Compare, nearby, hash URLs
Jun 6 · 8pm
Splash + pins
About panel, 24 historic pins
Jun 7 · 9am
Story panels
Culver history + atlas maps
Jun 7 · 3pm
v2.0
LandStory + BuildGuide

Core Map + Time Slider

  • MapboxGL aerial imagery base
  • Historical USGS/county overlay layers
  • Time slider — drag to any available year
  • Year label above slider
  • "Return to now" / "Leave [YEAR]" button

Cinematic Flight Mode

  • Auto-drift: slow bearing rotation
  • Single long easeTo per step (no stutter)
  • Pitch + bearing oscillation
  • Resumes after user interaction ends
  • Mobile-smooth frame budget

Search + Navigation

  • City or address search
  • Fly to exact GPS coords (not county centroid)
  • Featured places — 24 locations
  • Hash URL: share any location + year
  • Explore Nearby chips

Splash + About Panel

  • Cinematic splash with staged fade-in
  • Dedication line (in memory of a pilot father)
  • Auto-geolocate on load
  • About panel slides in from left on logo click
  • Origin story + tips + Sources panel

Historic Pins (24 places)

  • GeoJSON symbol layer — zoom-aware
  • Filter by state / slug / year
  • Tap for popup
  • Selzer Farm: only shows 1952–1989
  • Inserted below symbol layers (not above)

Montville Story Research

  • Full Culver land chain: 1854→1897→1930
  • County atlas maps (3 years sourced)
  • Samuel Ingham — fife & drum corps leader
  • Steve & Kate Johnson store photo
  • Oberholtzer genealogy + PA-German context

LandStory + BuildGuide

  • Split from single BuildStory component
  • LandStory: full-screen slide deck
  • BuildGuide: research arc + stats
  • Shared SlideShow component
  • Text hide/show toggle on slides

Mobile Polish

  • Slim slider on small screens
  • Plane icon + dotted arc logo
  • Search input not expanding top bar
  • Featured places z-index above nearby chips
  • 2 nearby chips + expand toggle

Flyover fighting itself after manual map drag

Flight mode would jerk the map back to its path after the user dragged it. Multiple competing flyTo calls from different React effects — each one canceling and restarting the others.

Single navigation owner: one effect owns all flyTo calls. Separate mapPos ref from center prop. Cancel stale moveend callbacks before firing new ones. ~6 commits over 45 minutes.

Drift animation choppy on mobile and high-refresh displays

Flight mode stuttered at handoff points between animation steps. Using setInterval with overlapping easeTo calls created visible pauses every 30–60 seconds.

Single long easeTo per step (120s duration). Fire next step on moveend, not interval. Native Mapbox frame interpolation handles smoothness between steps. No more handoff stutters.

Featured places showing no slider after navigation

Clicking a featured place flew the map correctly but showed no historical imagery — slider was blank. The lat/lng geocoder path worked; the county API path didn't.

Use county API for all location selects (not just user-typed addresses). The lat/lng proxy as a fallback when county endpoint returns no coverage.

Historic pins hidden under raster overlay

Pins existed in the data and the GeoJSON layer was added, but nothing was visible. The raster overlay was inserted on top of the pin symbol layer in Mapbox's layer stack.

Insert raster overlay layers BELOW symbol/pin layers. Mapbox renders layers in insertion order — later = on top.

Stale closure on map load — pins not appearing

The historic pins effect ran (logs confirmed correct data) but pins didn't appear on the map. The map object captured in the closure was stale by the time the effect fired.

Use refs for map-dependent values instead of state. Refs don't go stale across re-renders on load.

Hash URL always resetting to Modern view

Sharing a URL with a year hash (e.g. #1970) would briefly show the historical overlay, then snap back to Modern. pendingYear was being cleared before the first hash restore completed.

Clear pendingYear only after the first hash restore fires and the overlay is confirmed visible.

Geolocation and fetch listener leaks

App degraded over time — layers were added multiple times, geolocation listeners piled up. Each navigation triggered a new listener without cleaning up the old one.

Plug all listeners on unmount. Disable antialias/fade, cap tile cache for mobile memory. Return cleanup functions from all effects that add map listeners.

🗺️

MapboxGL as the foundation

Mapbox's layer system made historical overlays trivial — insert a raster source, set opacity, done. Flight mode animations (easeTo, bearing, pitch) are native capabilities. No custom math required.

🔗

Hash URLs from day one

Encoding location + year in the URL hash meant sharing was free from the start. No database, no sessions — just a URL. Made testing and collaboration instant during development.

✈️

Single navigation owner pattern

Once flight mode, search, and featured places all tried to call flyTo, the map started fighting itself. Consolidating to one effect that owns all navigation eliminated the entire class of competing-animation bugs in one commit.

📜

Real historical research as the content

The Montville story wasn't UX filler — actual research sourced from county atlases, genealogy records, and land transfer chains. The app told a true story. That made it feel like software that mattered, not just a map toy.

🆓

County API as the data source

Using existing USGS/county aerial survey APIs meant no infrastructure to build or pay for. Real historical data, not stock photos, available for any location in the US.

1

Single navigation owner in MapboxGL

One effect owns flyTo. Never call flyTo from multiple places — flight mode, search, and featured places all fight each other if they each control navigation.

2

Use refs for map-dependent values in React

State goes stale in closures on initial map load. Store the map object and any map-dependent values in refs so effects always have the current value.

3

Insert raster overlays BELOW symbol layers

Mapbox renders layers in insertion order. Add raster/imagery layers before adding pin/symbol layers, or use beforeId to insert them below.

4

Plug all map listeners on unmount

Every moveend, click, and geolocation listener added inside an effect must be removed in the cleanup function. Leaks degrade performance and duplicate layers over time.

5

Hash URLs for shareable state from day one

Encode what matters (location + year) in the URL hash immediately. Retrofitting shareability is expensive. Starting with it is free.

2
Days
80+
Commits
24
Historic places
3
Atlas years researched
v2.0
Shipped

Open Items

Demo prep

Geolocation opt-in — Auto-geolocating on load is fine in dev but bad UX for strangers. Add a permission prompt before firing geolocation.

Polish

Remove pin edit toolbar — Internal-only tool, confusing to real users. Hide it before any demo.

Content

More featured places — Medina / Northeast Ohio locations with dramatic before/after. The Selzer Farm story is a template.

Story

Voice narration kit — Studio narration mode for presenting the Montville story in a demo setting.

"Some tools solve problems. This one just makes you go 'whoa.'"

Flyback · flyback-nine.vercel.app · ForgeKit · 2026-06-06