Real-time campus occupancy for university students.
Problem · How It Works · Tech Stack · Setup · Docs · Status
This project is under active development. Core infrastructure is in place — features are being built sprint-by-sprint. See the roadmap below for what's done and what's next.
UniSpace gives every university student real-time visibility into campus occupancy so they never waste time walking to a full building again. It combines crowdsourced, privacy-preserving location data with Google Maps Popular Times to deliver a live occupancy heatmap — check it before you leave, not after you arrive.
No accounts. No hardware. No tracking. Just open the app and see where the space is.
University students have no reliable way to know whether a study space is available before physically going there. During peak hours, students try 5–8 buildings before finding a free spot, wasting 30–40 minutes each time.
This is especially costly for:
- Commuter students — limited campus time between classes
- Students with disabilities — each failed attempt costs 10–15 minutes of physical effort
- Students with anxiety — walking into a packed room and leaving is stressful
- Group coordinators — finding a table for 5 requires more effort than solo study
- Night owls — need to know which buildings are open and occupied (safety)
- Open the app — no account required. A full-viewport heatmap shows every building on campus.
- Check occupancy — buildings are colour-coded from green (empty) to red (packed), with percentage labels and trend arrows (filling / emptying / stable).
- Tap a building — bottom sheet shows floor-by-floor breakdown, amenities, 24-hour prediction chart, and a "Usually X% at this time" insight.
- "Find me a spot" — filter by amenities (WiFi, power, quiet, accessible), walking distance, and max occupancy. Results ranked by a scoring algorithm.
- Set alerts — push notification when a building drops below your chosen threshold.
Pulse always shows the best available data:
| Priority | Source | When it's used |
|---|---|---|
| 1 | Live crowdsourced | Active UniSpace users in the building |
| 2 | Crowd reports | Anonymous 1-5 busyness reports (30-min decay) |
| 3 | Google current popularity | Real-time busyness from Google Places API |
| 4 | UniSpace predicted | EWMA model trained on historical occupancy |
| 5 | Google typical popularity | Weekly busyness histogram from Google |
| 6 | No data | Grey polygon — "No data available" |
Privacy is a core architectural constraint, not an afterthought:
- GPS coordinates never leave the device. Zone detection is client-side via Turf.js — only a
zone_idis broadcast. - No accounts required for viewing, recommendations, or alerts.
- Session IDs rotate every 30 minutes and are never persisted to any database.
- No analytics or tracking libraries. Zero third-party telemetry.
- All position data expires after 30 minutes. Nothing is retained.
For the full privacy specification, see
PRD.mdSection 13.
| Layer | Technology |
|---|---|
| Frontend | React 19 + TypeScript, Vite 8 (PWA) |
| Styling | Tailwind CSS v4 with UoM design tokens |
| Map | Mapbox GL JS (dark-v11 base) |
| Backend | Supabase — Postgres, Realtime, Edge Functions (Deno) |
| External data | Google Places API |
| Charts | Recharts |
| Animations | Framer Motion |
| Geospatial | Turf.js (client-side point-in-polygon) |
| State | Zustand (where needed) |
- Node.js 18+
- pnpm (
npm install -g pnpm) - A Supabase project
- A Mapbox account (free tier sufficient)
- A Google Cloud project with Places API enabled
# 1. Clone
git clone https://github.com/br9704/UniSpace.git
cd UniSpace
# 2. Install dependencies
pnpm install
# 3. Configure environment
cp .env.example .env.local
# Edit .env.local with your Supabase URL, anon key, and Mapbox token
# 4. Apply database migrations (requires Supabase CLI)
supabase db push
# 5. Seed the database (UoM Parkville — 18 buildings)
# Run supabase/seed/001_uom_parkville.sql and 002_google_popular_times.sql via Supabase SQL Editor or CLI
# 6. Run tests
pnpm test
# 7. Start dev server
pnpm dev| Variable | Where | Description |
|---|---|---|
VITE_SUPABASE_URL |
.env.local |
Supabase project URL |
VITE_SUPABASE_ANON_KEY |
.env.local |
Supabase anonymous/public key |
VITE_MAPBOX_TOKEN |
.env.local |
Mapbox GL JS access token |
GOOGLE_PLACES_API_KEY |
Supabase secrets | Google Places API key (server-side only) |
VAPID_PUBLIC_KEY |
Supabase secrets | Web Push VAPID public key |
VAPID_PRIVATE_KEY |
Supabase secrets | Web Push VAPID private key |
VAPID_EMAIL |
Supabase secrets | Contact email for VAPID |
Never commit secrets. All client-side env vars use the
VITE_prefix. Server-side secrets are set in the Supabase dashboard only.
pulse/
├── src/
│ ├── components/ # Reusable UI components
│ ├── hooks/ # Custom React hooks (data fetching, realtime, geo)
│ ├── lib/ # Supabase client, Mapbox helpers, blending utilities
│ ├── pages/ # Top-level route components (HomePage, MapPage, AlertsPage)
│ ├── types/ # Shared TypeScript interfaces and enums
│ ├── stores/ # Zustand state stores
│ └── constants/ # App constants (colours, thresholds, map defaults)
├── supabase/
│ ├── migrations/ # SQL migrations (001–012), applied sequentially
│ ├── functions/ # Deno Edge Functions
│ └── seed/ # Seed data scripts
├── PRD.md # Product requirements document
├── MASTERPLAN.md # Sprint plan and progress tracker
├── CLAUDE.md # Development agent instructions
└── README.md # You are here
campuses ──< buildings ──< building_zones ──< zone_occupancy
│ │
│ └──< occupancy_history
│
├──< occupancy_predictions
├──< occupancy_reports
├──< google_popularity_cache
├──< google_popular_times
└──< user_alerts
| Table | Purpose |
|---|---|
campuses |
Campus metadata — name, centre coordinates, default zoom |
buildings |
Building details, amenity flags, hours, GeoJSON polygon |
building_zones |
Floor-level zones with polygon, capacity, and amenity data |
zone_occupancy |
Live occupancy counts per zone (Supabase Realtime enabled) |
occupancy_history |
15-minute snapshots for trend analysis and predictions |
occupancy_predictions |
Pre-computed predicted occupancy by day/hour |
google_popularity_cache |
Cached Google current popularity (30-min TTL) |
google_popular_times |
Google typical weekly popularity histogram |
occupancy_reports |
Anonymous crowd reports (1-5 busyness + optional noise, 30-min expiry) |
user_alerts |
Push notification subscriptions (keyed by push token, no user ID) |
All tables have Row Level Security enabled. Anonymous users can read; only the service role (Edge Functions) can write.
| Document | Description | Link |
|---|---|---|
| Product Requirements | Full feature specs, data models, personas, design system, privacy rules, UI screen specs | PRD.md |
| Implementation Plan | Sprint-by-sprint breakdown with progress tracking, architecture decisions, and risk log | MASTERPLAN.md |
| Agent Instructions | Coding standards, privacy rules, commit conventions, sprint protocol | CLAUDE.md |
| Environment Template | Required environment variables with descriptions | .env.example |
University of Melbourne — Parkville
18 UoM Parkville buildings with OSM-sourced polygon outlines, amenity data, building hours, and Google Popular Times data (1,453 rows across all 7 days).
Capacity estimates are directional (~), not precise. Building data will be verified in Sprint 17.
- Sprint 0: Project scaffolding (Vite + React + TypeScript + Tailwind + PWA)
- Sprint 1: Supabase infrastructure (schema, migrations, seed data, Edge Function scaffolds)
- Sprint 2: Google Places integration and occupancy blending logic
- Sprint 3: Mapbox map with building polygons
- Sprint 4: Realtime geolocation broadcasting
- Sprint 5: Zone aggregation Edge Function
- Sprint 6: Occupancy blending (live + Google + predicted)
- Sprint 7: Live heatmap rendering + building expansion (5 → 18 buildings)
- Sprint 8: Building cards (bottom sheet)
- Sprint 9: Floor-level breakdown
- Sprint 10: Smart recommendations + full UI revamp
- Sprint 11: Prediction engine (Google baseline, 24h chart, sparkline, insights)
- Sprint 12: UI polish, theming & production readiness
- Sprint 13: Manual crowd reporting (1-5 scale, decay, blending)
- Sprint 14: Noise levels & favourites
- Sprint 15: Building photos & tips (components ready, photo assets pending)
- Sprint 16: PWA install flow + service worker
- Sprint 17: Seed data accuracy verification
- Sprint 18: MVP integration testing + Vercel deploy
- Accessibility compliance (WCAG 2.1 AA)
- Sprint 20: Push notifications & alerts
- Offline graceful degradation
- Performance optimisation
- Error states & edge cases
- EWMA prediction engine, anomaly detection, personalised recommendations
- Multi-campus support, university analytics dashboard
- Friend presence, study group matchmaking
Full sprint details in
MASTERPLAN.md
This project is under active development by Bruno Jaamaa. Development follows the sprint plan in MASTERPLAN.md with coding standards defined in CLAUDE.md.
All rights reserved. Copyright Bruno Jaamaa 2026.