← All work

all roads

Finds the fairest place for a group to meet by ranking real places on the trip of whoever has it worst, not on the midpoint of a map.

Year
2025, redesigned 2026
Made with
Plain HTML, CSS and JavaScript, one Firebase Function, Google Maps Platform
Links
Live siteSource on GitHub
all roads with three people entered (Manayunk, Citizens Bank Park, Fishtown) searching for tacos; the map shows their pins and numbered results, with number 1 near Center City
Manayunk, South Philly and Fishtown want tacos. Number 1 is the fairest one.
Why

Every time family, friends or coworkers needed somewhere to meet, I did it by hand: open a map, guess a spot in the middle, check everyone's trip, try another spot. all roads does that part for me.

The obvious answer is the geographic midpoint, but straight-line distance isn't travel time. So the midpoint never decides anything. Every person gets a real drive, transit or walking time to every candidate.

How it works

A search is two Google calls on the backend: one to find candidate places, one to time every person's trip to every candidate.

Search area→Text Search→Filter→Distance Matrix→Rank

Where to look

The backend averages everyone's coordinates into a centroid and measures how far the farthest person is from it. The search radius is half that spread, clamped between 1.5 and 50 km, so a group across one neighborhood and a group across a state both get a sensible area.

What to throw out

Places Text Search treats location as a suggestion, so anything more than 2 × radius + 2 km from the middle gets dropped, along with anything permanently closed. Then a brand filter: if you search "starbucks," you want Starbucks, not the coffee shop next door. When enough results contain every word of the query in their name, only those stay. "Coffee" rarely matches names, so it passes through untouched.

The top 20 go into one Distance Matrix pass covering everyone × every candidate, chunked to fit the API's per-request limits and run in parallel.

Rank by the worst trip

Venues sort by their longest trip, rounded to the minute, with the average breaking ties. The first version minimized total travel time instead, which will happily send one friend on a 50-minute drive to save the other two ten minutes each. In the search up top, El Toro wins on total time, 42 minutes to 43. Ranking by the worst trip gives whoever is coming from Manayunk two minutes back, and nobody has to hear about it.

1. Tacos Daily Fresh20, 14, 9 min. Worst trip 20
2. Tacos El Toro22, 16, 4 min. Worst 22, average 14
3. Tacos Estilo Oaxaca22, 15, 8 min. Worst 22, average 15
4. Mr. Tacos Birria24, 6, 18 min. Worst 24
The search up top, driving. Trips from Manayunk, Citizens Bank Park and Fishtown, in that order.

Links are the state

The URL holds everyone's rounded coordinates and labels, the query and the mode, so a shared link just re-runs the search. Links from before the 2026 redesign used a base64 ?state= param, and those still decode.

Stack

FrontendPlain HTML, CSS and JavaScript on GitHub Pages. No framework, no build step
BackendOne Firebase Functions v2 HTTPS function, Node 22
Google Maps PlatformMaps JavaScript, Places, Distance Matrix, Geocoding
KeysThe browser key only does maps and autocomplete; everything that costs money runs server-side
What it taught me
  1. 1

    Fair isn't the same as efficient

    Minimizing total travel time can stick one person with the worst trip.

  2. 2

    "Location bias" means bias

    Text Search returns results well outside your radius, so the distance filter has to be yours.

  3. 3

    Count round trips, not functions

    Five endpoints and a matrix call per venue became one function, one search and one batched matrix.

  4. 4

    A static site can't keep a secret

    The fix was a server-side key and a browser key locked to the domain.

  5. 5

    Share links are an API

    Once people have sent links around, the old format has to keep working.