ChonconGO 2026, the Friday itinerary beside a live map of downtown Chicago
ChonconGO 2026: the itinerary on the left stays in sync with the map on the right

Why I built it

Six of us booked an Airbnb on the Near North Side for GO Fest weekend, with a loose plan for two days and the usual pre-trip artifact: a Google Doc full of pasted addresses and a group text that scrolled a foot every time someone asked where we were eating.

That artifact is not a map. It is a list of places you copy, paste, and look up one at a time, and it tells you nothing about where anyone in the group actually is. By the second bar on Friday night, half of us were in one Lyft and half in another, and the doc could not help with that.

I wanted one screen that did three things. Show every stop on a real map. Give one-tap directions to any of them. Show me where the rest of the crew was in real time. None of the trip-planner apps I looked at did all three without an account and a pile of features I did not want, so I built it.

What it does

ChonconGO is a single-page map app for the weekend. The itinerary sits on one side, the map on the other, and the two stay in sync. Tap a stop in the list and the map flies to it. Tap a marker and its details open in the list.

The app covers a short list of jobs:

  • A day filter for Friday, Saturday, or all, plus a toggle for optional stops like breweries and bottle shops, so the map shows only what is relevant right now.
  • Marker popups with hours and a one-tap directions button for every stop.
  • Live location sharing, so anyone in the group can appear on the map as a colored dot with their name.
  • A locate button that drops your own dot and points a compass at your next stop.
  • Install to the home screen, with offline support for the whole weekend.

The first thing a new traveler sees is a short tour of those features.

The ChonconGO welcome tour listing location, live sharing, day filters, install, and easter eggs
The opening tour, which doubles as the feature list

How it works

There is no build step and no framework. ChonconGO is one static HTML file with one inline script, served from Cloudflare Pages, the same zero-build approach I use for everything else. The map is Leaflet with CARTO basemap tiles, the fonts are self-hosted, and that is the entire frontend stack.

Each stop is a plain object in a data array with a name, coordinates, hours, a category, and a note. Rendering the list and dropping the markers is one loop over that array. Theme state and a few preferences live in localStorage.

Directions were the easiest decision to get wrong. I did not want a routing engine or a directions API with a key to manage. Google Maps already has the best routing on the phones everyone in the group was carrying, and it takes a deep link:

// open native Google Maps directions from one stop to the next
function dirUrl(o, d) {
  return 'https://www.google.com/maps/dir/?api=1'
    + '&origin=' + o.lat + ',' + o.lng
    + '&destination=' + d.lat + ',' + d.lng
    + '&travelmode=driving';
}

Every directions link is that URL. It hands navigation off to the app that does it best and keeps ChonconGO out of the routing business entirely.

A marker popup for Three Dots and a Dash showing hours, a note, and directions buttons
Each popup carries hours and two buttons: in-app directions and Open in Maps

The live layer

Everything above is static and could be a flat file. The live layer is the one piece that needs a server holding shared state, and it is the part I most wanted to keep small.

When you share your location, the app opens a WebSocket to a single Cloudflare Worker. The room lives in a Durable Object, the part of the platform that can hold open WebSocket connections and shared state in one place. Everyone who shares joins the same room, and the Worker fans each position update out to the others. That is the whole backend.

The protocol is four message types: join, pos, ping, and leave. The client throttles its own broadcasts to once every 2.5 seconds and sends a heartbeat every 25 seconds so the socket stays open through idle stretches:

// only the sharer broadcasts, and never more than once every 2.5s
map.addEventListener('choncongo:pos', function (e) {
  if (!sharing) return;
  var now = Date.now();
  if (now - lastSend < 2500) return;
  lastSend = now;
  send({ t: 'pos', lat: e.detail.lat, lng: e.detail.lng });
});

ws.onopen = function () {
  retry = 0;
  sendJoin();                            // announce who I am
  hb = setInterval(function () {         // keep the socket warm
    send({ t: 'ping' });
  }, 25000);
};

Phones drop the socket constantly as they move between cells and wifi, so the client reconnects with a backoff. Presence is deliberately ephemeral. There is no account, no history, and no record of where anyone was once they close the tab. The cost of that choice is real: you cannot reopen the app on Monday and replay Saturday night. I did not want that data to exist, so I never stored it. For a weekend with friends, that is the right trade.

Pointing at the next stop

The locate button does one more thing worth calling out. Once it has your position, it points a compass arrow at your next stop. Reading a heading on the phone is the fiddly part, because iOS and everything else disagree on how to report it:

function onOrient(e) {
  var h = (typeof e.webkitCompassHeading === 'number')
    ? e.webkitCompassHeading        // iOS gives a true compass heading
    : (e.absolute && typeof e.alpha === 'number')
      ? (360 - e.alpha)             // others: derive it from absolute alpha
      : null;
  if (h != null) { heading = h; refresh(); }
}

On a phone this is the whole point of the app. The map fills the screen, the itinerary slides up from the bottom as a sheet, and a dot with a compass keeps you aimed at wherever you are headed next.

The ChonconGO mobile view with the map on top and the itinerary in a bottom sheet
On a phone the map is full-screen and the itinerary is a draggable bottom sheet

Built to work with no signal

ChonconGO installs to the home screen and runs full-screen from a manifest. A service worker caches the shell, the map tiles you have already loaded, and the fonts, so the itinerary and the map both work with no connection. That mattered more than I expected. GO Fest packs tens of thousands of phones into a few city blocks, and the network goes down with them.

The app has one small guard against itself. It only registers the service worker after a HEAD request confirms the file is actually there, which keeps it from throwing when I open the same HTML in a local preview where no service worker exists.

A few things hidden in the map

There is a layer of nonsense underneath all of this, and it was half the fun of building it. Long-pressing the locate button opens a small picker that swaps your map pin for one of a few characters. There is a dog, an ogre, a disco ball, and a Mewtwo mask. Picking one does more than change the icon. Each character quietly arms its own theme song that loops under the map, bundled and cached on first load so it keeps playing offline like everything else.

The ogre and the disco ball are a local joke. One of our tentative Friday stops was a Shrek Disco in the Swamp pop-up, so the app grew a Shrek pin and a disco pin to match, each with the track you would expect.

A couple of the map markers hide short videos, served from the same R2 bucket behind photos.corbet.dev. The app remembers which secrets you have already found in localStorage, so the count survives a reload and you can try to clear the board over the weekend. The welcome tour tells you the easter eggs exist and then leaves the rest to you, so I will do the same here.

One thing that is not hidden at all: the whole app has a dark mode, and the route between stops glows in it.

ChonconGO in dark mode, with the planned route drawn as a glowing green line
Dark mode, with the route between stops drawn as a glowing line

ChonconGO is live at choncongo.corbet.dev. The trip is over and the shared map is quiet now, but the itinerary, directions, and easter eggs all still work, and it still installs. If you are running a group trip and want the same thing, the whole app is one static page and one small Worker, which is about as little infrastructure as a live, multi-person map can run on.

The six travelers at the Pokemon GO Fest Chicago 2026 entrance banner
The crew ChonconGO was built for. Five of us shared our location. The sixth went fully off-map.