An interactive audio-visual web experience exploring the River Thames through sound, sight, and sensation. This project maps geolocated multimedia recordings along the Thames, presenting them through an immersive interface with categories: Listen, See, and Feel.
🌐 Live Site: https://sonicthames.github.io
README.md(this file) – overview, setup, and scriptsAGENTS.md– architecture, code patterns, and conventionsDEVELOPMENT.md– quick reference for commands, debugging, and common tasksCONTRIBUTING.md– git workflow and PR process
Sonic Thames is a React-based web application that combines interactive mapping with multimedia content to create a unique sonic exploration of the Thames. Users can:
- 🗺️ Navigate an interactive Mapbox map showing recording locations along the River Thames
- 🎧 Browse audio-visual recordings categorized by sensory experience (Listen, See, Feel)
- 📍 Explore geotagged content with detailed location information and timestamps
- 📱 Experience responsive design optimized for desktop and mobile devices
The application features recordings of various Thames locations including wooden jetties, industrial structures, wildlife habitats, and peaceful green areas, each capturing a unique moment in time.
- React 17 - UI framework
- TypeScript - Type safety
- Tailwind CSS v3 - Utility-first CSS framework
- vanilla-extract - Type-safe CSS-in-JS for design tokens
- Radix UI - Accessible UI primitives
- shadcn/ui - Composable component library
- React Router - Client-side routing
- Mapbox GL - Interactive mapping
- RxJS - Reactive programming
- fp-ts - Functional programming utilities
- io-ts - Runtime type validation
- Luxon - Date/time handling
- Vite - Build tool and dev server
- Biome - Linting and formatting
- Vitest - Testing framework
- pnpm - Package manager
- Node.js (v16 or higher recommended)
- pnpm (v9.12.3 or compatible)
- Mapbox API Token - Required for map functionality
git clone https://github.com/sonicthames/sonicthames.github.io.git
cd sonicthames.github.iopnpm installCreate a .env file in the root directory with your Mapbox token:
VITE_MAPBOX_TOKEN=your_mapbox_token_hereYou can obtain a Mapbox token by signing up at mapbox.com and creating an access token in your account settings.
pnpm devThis will start the development server at http://localhost:3001 and automatically open it in your browser.
# Start development server
pnpm dev
# Run type checking
pnpm typecheck
# Run tests
pnpm test
# Lint code
pnpm lint
# Format code
pnpm format
# Check formatting without making changes
pnpm format:check# Build for production
pnpm build
# Preview production build locally
pnpm serve
# Clean build artifacts
pnpm clean# Regenerate icon components from SVG files
pnpm build-icons
# Generate icons without cleaning
pnpm generate-icons# Deploy to GitHub Pages
pnpm deployEvent capture is handled through PostHog Cloud—no local services required.
- Copy
.env.templateto.envand add your values:VITE_MAPBOX_TOKEN– public Mapbox token for map tilesVITE_POSTHOG_KEY– PostHog project API key (write-only)VITE_POSTHOG_HOST– defaults tohttps://eu.posthog.com; adjust if your project uses another region
- Restart
pnpm devso the environment variables are picked up. - Trigger interactions (e.g., hover/click the tracked CTA on the About page) and confirm events appear in PostHog → Live events.
Analytics helpers:
- Use
TrackedCtafor buttons/links you want to monitor. - The
useHoverAnalyticshook recordscta_hoverevents with dwell time. trackCtaClickandtrackCtaHoverinsrc/lib/analytics.tsare available if you need manual instrumentation elsewhere.
Smoke test checklist:
pnpm dev- Visit the About page, hover the “Explore the map” CTA for ~1s, then click it.
- In PostHog, filter by
event = cta_clickorcta_hoverto verify ingestion.
sonicthames.github.io/
├── public/ # Static assets (logos, manifest, robots.txt)
├── scripts/ # Build and deployment scripts
├── src/
│ ├── components/ # Reusable UI components
│ │ └── ui/ # UI component library (Button, Link, Panel, Dialog, etc.)
│ ├── domain/ # Domain models and types
│ ├── icon/ # Icon components
│ │ ├── generated/ # Auto-generated icon components
│ │ └── template.js # SVGR template for icon generation
│ ├── lib/ # Utility functions and helpers
│ │ └── rxjs/ # RxJS utilities and operators
│ ├── pages/ # Page components
│ │ ├── about/ # About page
│ │ ├── contact/ # Contact page
│ │ ├── main/ # Main page with map
│ │ ├── sound/ # Individual sound detail page
│ │ └── sounds/ # Category listing pages
│ ├── styles/ # Styling system
│ │ ├── theme.css.ts # vanilla-extract design tokens and themes
│ │ ├── recipes.css.ts # vanilla-extract component recipes
│ │ └── tailwind.css # Tailwind CSS entry point
│ ├── theme/ # Legacy theme utilities (colors, spacing, media queries)
│ ├── App.tsx # Main application component
│ ├── data.json # Sound recordings data
│ ├── data.io.ts # Runtime type validation for data
│ └── index.tsx # Application entry point
├── static/
│ └── icons/ # Source SVG icons
├── index.html # HTML entry point
├── package.json # Dependencies and scripts
├── tailwind.config.ts # Tailwind configuration
├── postcss.config.js # PostCSS configuration
├── tsconfig.json # TypeScript configuration
└── vite.config.ts # Vite configuration
Sound recordings are defined in src/data.json with the following schema:
{
title: string; // Recording title
description: string[]; // Description paragraphs
marker: string; // Short marker label
category: "Listen" | "See" | "Feel";
duration: string; // ISO 8601 duration (e.g., "PT01M29S")
location: string | null; // Location name
access: string | null; // Access instructions
coordinates: LngLat;
dateTime?: string; // ISO timestamp
interval?: string; // ISO interval
videoSrc: string; // YouTube video ID
thumbnailSrc: string | null;
}The project is configured for deployment to GitHub Pages. The deployment process:
- Validates that
VITE_MAPBOX_TOKENis set (checks multiple environment variable names) - Builds the production bundle
- Deploys to the
gh-pagesbranch usinggh-pagespackage
# Set your Mapbox token
export VITE_MAPBOX_TOKEN=your_token_here
# Deploy
pnpm deployThe deployment script accepts the following environment variable names (in order of priority):
VITE_MAPBOX_TOKENMAPBOX_TOKENMAPBOX_ACCESS_TOKEN
The project includes comprehensive accessibility features:
- ARIA Labels: All interactive elements have appropriate ARIA labels
- Semantic HTML: Proper use of semantic elements
- Icon Accessibility: Custom SVG icons include
role="img",aria-label, and<title>elements - Keyboard Navigation: Full keyboard support for interactive elements
The application is optimized for modern browsers:
Production:
- Browsers with >0.2% market share
- Excludes dead browsers
- Excludes Opera Mini
Development:
- Latest Chrome
- Latest Firefox
- Latest Safari
See CONTRIBUTING.md for git workflow and PR process. For architecture and code patterns, check AGENTS.md. For development setup and commands, see DEVELOPMENT.md.
Icons are automatically generated from SVG files using SVGR with a custom template that adds accessibility features. To add new icons:
- Add your SVG file to
static/icons/ - Run
pnpm build-icons - Import from
src/icon/generated/
This project is private and not licensed for public use. Contact the repository owner for permissions.
Built with ❤️ for the Thames