A modern, interactive web application that lets you discover and listen to radio stations from around the world. Browse stations on an interactive map, search by country or genre, and enjoy streaming audio with an intuitive player interface powered by glassmorphic design and real-time geolocation features.
Live Demo: https://oldtunes.netlify.app/
Repository: https://github.com/aryansrao/OldTunes
Developer Portfolio: https://aryansrao.github.io
- Overview
- Features
- Technology Stack
- Project Structure
- How It Works
- User Interface
- Installation
- Configuration
- API Reference
- Browser Compatibility
- Usage Guide
- Contributing
- Acknowledgments
OldTunes is a location-aware radio streaming application built with modern web technologies. The application transforms global radio discovery into an interactive, visual experience by combining real-time station data, interactive mapping, and seamless audio streaming. Users can explore stations from any location, filter by preferences, and enjoy uninterrupted audio playback with an elegant, responsive interface.
The application operates entirely in the browser with no backend required, ensuring user privacy and instant performance. Station data is fetched dynamically and cached for optimal responsiveness.
- Interactive World Map: Visualize radio stations globally with Leaflet.js mapping technology
- Marker Clustering: Automatic station clustering for performance and usability
- Dynamic Station Loading: Real-time station data fetching from external sources
- Geolocation Integration: Browser-based location detection (optional)
- Advanced Search: Filter stations by name, country, genre, or language
- Audio Streaming: HTML5 audio player with support for streaming protocols
- Playback Controls: Play, pause, next, previous, and random station selection
- Volume Control: Custom slider for audio level adjustment
- Search Results: Real-time dropdown with station suggestions
- Station Info: Display current station name, country, and genre
- Zoom Controls: Custom slider for map zoom levels (2x to 18x magnification)
- Glassmorphic UI: Modern liquid glass effects with blur and transparency
- Dark Theme: Eye-friendly dark interface with high contrast
- Responsive Layout: Optimized for desktop, tablet, and mobile devices
- Loading States: Visual feedback during data loading and processing
- Smooth Animations: Fluid transitions and interactive feedback
- Client-Side Processing: All operations in browser for privacy and speed
- No Backend Required: Pure static web application
- Offline Capable: Core functionality without internet (after initial load)
- Real-Time Updates: Instant station filtering and map updates
- HTML5: Semantic markup with comprehensive structure
- CSS3: Modern styling with custom properties, Grid, and Flexbox
- Vanilla JavaScript: Pure ES6+ JavaScript, no frameworks required
-
Leaflet.js v1.9.4: Interactive mapping library
- CartoDB Dark tile layer for modern map aesthetics
- Marker clustering via Leaflet.MarkerCluster
- Custom zoom controls and attribution
-
Leaflet.MarkerCluster v1.4.1: Station marker clustering
- Dynamic cluster generation
- Zoom-responsive grouping
- Custom styling for clusters
- Poppins Font Family: Modern sans-serif from Google Fonts
- Weights: 300 (Light), 400 (Regular), 500 (Medium), 600 (SemiBold), 700 (Bold)
- Preconnect optimization for performance
- Netlify: Static site hosting with automatic deployments
- HTTPS: Secure connection enabled
- CDN: Global content distribution network
OldTunes/
│
├── index.html # Main HTML structure
│ ├── SVG filters for glassmorphic effects
│ ├── Search interface
│ ├── Map container
│ ├── Audio player controls
│ └── Station info display
│
├── app.js # Application logic
│ ├── RadioApp class (main controller)
│ ├── Map initialization
│ ├── Station loading and caching
│ ├── Search functionality
│ ├── Audio player management
│ ├── Event handlers
│ └── Utility functions
│
├── styles.css # Complete styling
│ ├── CSS custom properties (colors, sizes)
│ ├── Glassmorphic effects
│ ├── Layout and grid system
│ ├── Component styles
│ ├── Animation definitions
│ ├── Responsive breakpoints
│ └── Dark theme colors
│
├── assets/ # Static assets
│ └── (images, icons, etc.)
│
├── .git/ # Version control
│
└── README.md # Documentation
OldTunes follows a single-page application (SPA) pattern with a modular architecture:
- Page Load: Browser loads HTML, CSS, and JavaScript
- App Initialization:
RadioAppclass instantiates - Map Setup: Leaflet map renders with dark CartoDB tiles
- Data Loading: Station data fetches from external API
- UI Binding: Event listeners attached to interactive elements
- Ready State: Application ready for user interaction
class RadioApp {
constructor() // Initialize properties
init() // Run setup sequence
initMap() // Create and configure map
createZoomSlider() // Add zoom control
bindEvents() // Attach event listeners
loadStations() // Fetch station data
handleSearch() // Process search input
displayStations() // Render station markers
togglePlayPause() // Control audio playback
updatePlayButton() // Update UI button state
}Data Format Expected:
[
{
"id": "unique_identifier",
"name": "Station Name",
"country": "Country Name",
"genre": "Genre/Category",
"url": "https://stream.url:port/path",
"latitude": 28.7041,
"longitude": 77.1025,
"language": "Language"
}
]Loading Process:
- Fetch from API endpoint (URL configurable in app.js)
- Parse JSON response
- Validate station data structure
- Filter out invalid entries
- Cache stations in memory
- Process with clustering algorithm
- Create map markers
Caching Strategy:
- In-memory storage of stations array
- Filtered results cached during search
- No persistent storage (session-based)
- Re-fetch on application reload
Search Methodology:
- Input Processing: Normalize search text (lowercase, trim)
- Field Matching: Search across multiple fields:
- Station name (primary)
- Country (secondary)
- Genre (tertiary)
- Language (optional)
- Match Scoring: Prioritize exact matches over partial
- Results Sorting: Sort by relevance and alphabetically
- UI Update: Render results in dropdown below search input
Supported Search Patterns:
"BBC" // Station name match
"India" // Country match
"Jazz" // Genre match
"BBC Radio 1" // Multi-word station name
"London, UK" // Location-based search
Leaflet Map Configuration:
// Bounds: Entire world with wrapping
center: [20, 0] // Center at 20°N, 0°E (Equatorial Atlantic)
zoom: 2 // Global view (continental level)
minZoom: 2 // Prevent over-zoom out
maxZoom: 18 // Allow detailed zoom in
worldCopyJump: true // Enable map wrappingTile Layer:
- Provider: CartoDB Dark theme
- Attribution: OpenStreetMap, CartoDB
- Maximum zoom: 20
- Dark aesthetic for nighttime browsing
Marker Clustering:
- Threshold: Default clustering at various zoom levels
- Dynamic updates as map pans/zooms
- Click cluster to zoom to contents
- Individual markers at high zoom levels
- Prominent placement at top
- Glassmorphic styling with blur effects
- Real-time input validation
- Auto-focusing dropdown results
- Search icon and random button
- Full-width interactive Leaflet map
- CartoDB Dark tiles for visibility
- Station markers with clustering
- Zoom slider control
- Attribution footer
- Bottom-fixed control bar
- Play/pause button with state indication
- Previous/next station navigation
- Random station selector
- Volume slider (0-100%)
- Current station display
- Appears below search input
- Real-time station suggestions
- Scrollable results
- Click to select and play
- Click-outside to dismiss
- Current station name
- Country and genre tags
- Stream status indicator
- Loading states
--text-primary: #ffffff /* Main text color */
--text-secondary: rgba(255, 255, 255, 0.6) /* Secondary text */
--accent: #ff6b35 /* Orange accent */
--glass-tint: rgba(255, 255, 255, 0.2) /* Glass effect */
--bg: #0a0a0a /* Background */Breakpoints:
- Desktop: 1200px+ (full feature set)
- Tablet: 768px-1199px (optimized layout)
- Mobile: Below 768px (single column, touch-optimized)
Responsive Adjustments:
- Search bar width optimizations
- Player controls stack vertically on mobile
- Map height adjustment for smaller screens
- Font sizes scale for readability
- Touch-friendly button sizes (minimum 44x44px)
Glassmorphic Effects:
- Backdrop blur (16px)
- Transparency layers
- SVG filter distortions
- Gradient overlays
Transitions:
- Button hover effects
- Search result fade-in
- Map marker animations
- Volume slider smooth changes
- Loading spinner rotations
-
Clone the repository:
git clone https://github.com/aryansrao/OldTunes.git cd OldTunes -
Serve locally:
Using Python 3:
python3 -m http.server 8000
Using Node.js (http-server):
npm install -g http-server http-server -p 8000
Using PHP:
php -S localhost:8000
-
Open in browser:
http://localhost:8000
- Push code to GitHub repository
- Log in to Netlify
- Click "New site from Git"
- Select your GitHub repository
- Build settings:
- Build command: (leave empty)
- Publish directory: (leave empty)
- Deploy site
- Access at
your-site.netlify.app
- Install Vercel CLI:
npm i -g vercel - Run:
vercel - Follow configuration prompts
- Push to GitHub main branch
- Go to repository Settings > Pages
- Select main branch as source
- Access at
https://username.github.io/OldTunes
- No build process or dependencies
- No server-side code required
- Modern web browser (see Browser Compatibility)
- HTTPS for geolocation features (optional)
Edit the API endpoint in app.js:
async loadStations() {
try {
const response = await fetch('YOUR_API_ENDPOINT_HERE');
// Default uses a public radio API
// Replace with your own endpoint or data file
} catch (error) {
console.error('Error loading stations:', error);
}
}Change tile provider in initMap():
// Current: CartoDB Dark
L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
subdomains: 'abcd',
maxZoom: 20
}).addTo(this.map);
// Alternative: OpenStreetMap Default
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
maxZoom: 19
}).addTo(this.map);
// Alternative: CartoDB Light
L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
maxZoom: 20
}).addTo(this.map);Edit CSS custom properties in styles.css:
:root {
--text-primary: #ffffff;
--text-secondary: rgba(255, 255, 255, 0.6);
--accent: #ff6b35;
--glass-tint: rgba(255, 255, 255, 0.2);
}Modify player configuration in bindEvents():
// Default volume (0-1)
this.audioPlayer.volume = 0.7;
// Change volume slider range
<input type="range" id="volumeSlider" min="0" max="100" value="70">Customize in createZoomSlider():
const slider = document.getElementById('zoomSlider');
slider.min = 2;
slider.max = 18;
slider.step = 1;
slider.value = 2;Initializes the entire application.
async init() {
this.initMap();
this.bindEvents();
await this.loadStations();
this.hideLoading();
}Creates and configures the Leaflet map with CartoDB Dark tiles and world wrapping enabled.
Fetches station data from remote API, validates, caches, and displays on map.
Filters stations across name, country, genre, and language fields. Updates UI with matching results.
Renders station markers on map with clustering and click handlers for playback.
Controls audio playback state and updates button UI.
Loads and plays a specific station with URL from station data.
Plays previous station in filtered list with wrapping.
Plays next station in filtered list with wrapping.
Selects and plays random station from current filtered list.
Updates play button appearance based on playback state.
| Browser | Minimum Version | Notes |
|---|---|---|
| Chrome | 90+ | Full support |
| Firefox | 88+ | Full support |
| Safari | 14+ | Full support, iOS 14+ |
| Edge | 90+ | Full support |
| Opera | 76+ | Full support |
Required APIs: Fetch, HTML5 Audio, Geolocation (optional), Canvas/SVG, LocalStorage (optional)
HTTPS: Required for geolocation features. HTTP works on localhost.
- Open https://oldtunes.netlify.app/
- Wait for map and data to load
- Search for stations or click map markers
- By Name: "BBC Radio 1"
- By Country: "India", "UK"
- By Genre: "Jazz", "Classical"
- Search and Click: Type station name, click result
- Map Markers: Zoom and click marker on map
- Navigation: Use next/previous/random buttons
- Play/Pause: Toggle playback
- Previous/Next: Navigate stations
- Random: Discover new station
- Volume: Adjust (0-100%)
Check existing issues, then create detailed issue with:
- Browser and OS info
- Steps to reproduce
- Expected vs actual behavior
- Screenshots/error messages
Open issue with [Feature Request] tag, describe use case and benefits.
- Fork and create feature branch
- Make changes following style guidelines
- Commit and push
- Create Pull Request
- Features: Favorites, history, playlists
- UI/UX: Themes, animations, accessibility
- Performance: Optimization, caching
- Data: Station sources, categorization
- Documentation: Guides, tutorials
- Leaflet.js - Interactive mapping
- Leaflet.MarkerCluster - Station clustering
- CartoDB - Map tiles
- Google Fonts - Poppins font
- Netlify - Hosting and deployment
- Glassmorphic design trends
- Spotify and Apple Music UI patterns
- Material Design principles
- Mobile-first responsive design
Aryan S Rao
- Portfolio: https://aryansrao.github.io
- GitHub: @aryansrao
- GitHub Issues: Report bugs or request features
- GitHub Discussions: Community discussions
- Website: aryansrao.github.io
Features:
- Interactive world map with Leaflet.js
- Search across 1000+ global stations
- Real-time filtering by name, country, genre
- HTML5 audio player with controls
- Marker clustering
- Dark theme with glasmorphic UI
- Responsive mobile design
Deployment: Netlify with HTTPS and CDN
Discover the world, one station at a time.