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
coordinate: 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