Inspiration
Hampton Roads, Virginia has 1.8 million residents, 36 performing arts venues across 7 cities, and $270 million in
annual arts and culture economic impact — yet there is no single place to discover cultural events. Events are
scattered across 7+ websites, venue social media pages, Eventbrite, Facebook, and local tourism sites. Finding what's
happening tonight can take 40 minutes and a dozen browser tabs. A 3,577-member Meetup group called "Bored Nevermore"
exists solely to help people find activities. Small community theaters are invisible compared to major venues like
Chrysler Hall. We wanted to change that.
## What it does
Hampton Roads Live is a multi-platform app (iOS, Android, Web) that aggregates 600+ real performing arts and
cultural events from 36 venues across 13 Hampton Roads cities into one unified, filterable interface.
- Events Tab: Search and filter by date, event type (Opera, Musical, Concert, Comedy, Dance, etc.), city, venue,
price, and audience tags (Date Night, Family Friendly) - Calendar View: Monthly grid with color-coded event type indicators
- My Schedule: Save events to a personal schedule, grouped by date
- Map View: Interactive map showing venue locations and nearby events
- Equal Visibility: Small community theaters get the same treatment as major venues
## How we built it
We built three native frontends:
- Web: Next.js 16 (App Router) + React 19 + Tailwind CSS + Leaflet maps, deployed on Vercel
- iOS: SwiftUI + MapKit + Supabase Swift SDK (iOS 17+)
- Android: Jetpack Compose + Material 3 + Google Maps + Supabase Kotlin SDK
All three apps connect to a shared Supabase PostgreSQL backend with Row-Level Security.
The data pipeline is a Node.js sync service that runs every 6 hours via GitHub Actions. It pulls events from
Ticketmaster API, PredictHQ API, and Eventbrite API, then scrapes 10+ local theater websites using
Cheerio (static HTML) and Playwright (JavaScript-rendered pages). Google Gemini AI enriches events with
missing prices, descriptions, images, and ticket URLs, and classifies audience tags.
A priority-based deduplication system ensures the same event from multiple sources only appears once, keeping the
highest-quality data.
## Challenges we faced
- Data Quality: Events from different sources have wildly inconsistent formats — some have prices, some don't;
some have images, some don't. We built a multi-pass enrichment pipeline with LLM-assisted extraction to fill the gaps. - Deduplication: The same event can appear on Ticketmaster, Eventbrite, and a venue's own website. We designed a
dedup_key system (normalized title + 30-minute time bucket) with source-priority ranking to keep the best version. - Cross-Platform Consistency: Maintaining a unified design system (colors, spacing, typography, event type icons)
across SwiftUI, Jetpack Compose, and React required careful coordination.
- Web Scraping Reliability: Theater websites change frequently and many use JavaScript rendering. We combined
static parsing (Cheerio) with headless browser automation (Playwright) and LLM fallback extraction to maximize
coverage.
## What we learned
- How to design a source-priority deduplication system that scales across multiple data providers
- Building a unified design language across three completely different UI frameworks (SwiftUI, Jetpack Compose, React)
- Using LLM (Google Gemini) not for generation but for practical data extraction and enrichment at scale
- The power of treating all venues equally — small community theaters have just as much to offer as major concert
halls
Built With
- cheerio
- eventbrite
- github-actions
- google-gemini-ai
- google-maps
- jetpack-compose
- kotlin
- leaflet.js
- next.js
- node.js
- playwright
- predicthq-api
- react
- supabase
- swiftui
- tailwind-css
- ticketmaster-api
- typescript
- vercel
Log in or sign up for Devpost to join the conversation.