01 — the problem
150+ people, one physical floor, and a Figma file that went stale the moment it was exported
Every year, ITP Camp's Show All Things Show places 150+ participants across a real physical floor plan — camp HQ, the amphitheater, north and south lodge, the wood shop, 3D printers, the lake lounge. Historically this lived in a Figma file: someone manually placed named boxes on a map image, exported it, and hoped nobody needed a last-minute swap.
That breaks in predictable ways. No live sync — the map campers see is already stale the moment someone gets reassigned. No way to import a signup spreadsheet without retyping 150 names by hand. And no distinction between "here's the map" and "let me actually move people around" — anyone with the Figma link could accidentally nudge a pin.
02 — the actual floor
This is what 150+ spots across a real camp actually looks like
Cabins, an amphitheater, two lodges, a wood shop, an ER, a craft corner, vending machines, bathrooms, an elevator — every numbered pin below is a real assigned spot.
03 — the signature moment
From a blank dropzone to a living, searchable map
Click the empty state below and watch it come alive.
click to bring it to life ✦
Upload any floor plan image, no fixed template — then real placements, real project descriptions, live in the sidebar, with swap mode ready to trade any two spots with a click.
04 — what i built
Two linked surfaces, one live data store
Public spot-lookup
A camper types their name, the map pans to and highlights their assigned spot. Pinch-to-zoom and pan — built to work in someone's hand, walking around camp.
Admin console
The sidebar list of every spot, a swap mode (click any two pins to trade assignments), spreadsheet import, and a distinct pin type for named locations like the amphitheater versus individual camper spots.
Both read from and write to the same backend, so an admin move shows up on the public map within moments — no re-export, no re-deploy.
05 — technical decisions
Three real problems, three specific decisions
map layer
Leaflet.js
Pan, zoom, and marker interactions that work identically on a laptop and a phone — over a plain floor plan image rather than a geographic tile layer.
live sync
Vercel + Upstash Redis
Admin writes push to Redis; the public page reads from the same store. The trickiest part of the build was getting the public page's data to update reliably before its own initial local state raced ahead of what Redis actually had.
real-world data import
Header-row detection, not a clean CSV
The actual camper signup sheet had helper rows above the real header row, and inconsistent column names ("Camper(s)", "Project Title(s)"). Rather than asking organizers to manually clean the sheet every year, I built a header-row scoring system that detects the real header row regardless of what's above it, and maps loosely-named columns to the three fields that matter: spot, camper name, project title. Everything else — equipment requests, lighting notes, emails — gets left out on purpose. The tool does one job.
06 — a real edge case
Not every project fits the grid
One participant's project needed a non-standard location that didn't match any predefined spot. Rather than force it into the system, I added an "Unassigned / Other" section with a manual note field — so admins can place edge cases without breaking the main flow for everyone else.
07 — outcome
Live for 150+ participants during Show All Things Show 2026
Reduced spot management from "someone manually edits a Figma file and hopes it's still accurate" to real-time, admin-editable, camper-searchable — with a same-day handoff doc so next year's crew, who may not know the codebase at all, can redeploy their own instance from scratch in about five minutes.