RetroFM is an AI-powered historical radio simulator and broadcast mixer that recreates authentic, era-accurate radio broadcasts for any chosen historical date. By combining real historical news facts from the New York Times Archive API, music lookup and video playback via YouTube, automatic real-time audio ducking through a companion Chrome extension bridge, and customizable DJ personalities (Mike & Lisa) driven by Google Gemini Flash Text-to-Speech and native browser speech engines, RetroFM delivers an immersive, interactive recreation of listening to top-40 radio in decades past.
- Docker Desktop with Docker Compose
- Google Chrome (to load the companion Chrome extension bridge)
- Configure environment variables by copying
.env.example:Add your API keys (cp .env.example .env
GEMINI_API_KEY,YOUTUBE_API_KEY,NYT_API_KEY) to.env. Note: Never commit real API keys to version control. - Build and start the container:
docker compose up --build
- Open the application in Google Chrome:
- Local Application URL: http://localhost:3003
- Port mapping: Host
3003-> Container3000
- Start the application:
docker compose up
- Stop the application:
docker compose down
- View application logs:
docker compose logs -f retrofm
- Check container status:
docker compose ps
- Rebuild after dependency changes:
docker compose up --build
- With Docker: RetroFM runs at http://localhost:3003 (mapped from container port
3000). - Without Docker (
npm run dev): RetroFM runs on the port printed bynpm run dev(typically http://localhost:3000, or another available port). The companion extension and app communication automatically adapt to whatever port is in use.
- Node.js
>=20.19.0(tested with Nodev26.11.1andnpm v11.20.0) or Bun1.x - Google Chrome (to load the companion Chrome extension bridge)
-
Install dependencies:
npm install # or bun install -
Configure environment variables: Copy the example environment file:
cp .env.example .env
Add your API keys to
.env(see Environment Variables). -
Run the development server:
npm run dev
Open the application in Google Chrome at the URL printed in the terminal (e.g.
http://localhost:3000). -
Build for production:
npm run build npm run start
RetroFM is designed so that anyone who clones the repository can immediately run it with full DJ features and without requiring an external SQLite database.
A fresh clone includes a bundled sample dataset in public/data/sample/ with ~50 curated songs across multiple dates and decades (1960s to 2000s), complete with authentic Billboard chart rankings, songwriter/producer credits, and active band member histories.
When the full library files are not present, RetroFM detects this and automatically loads the sample data:
# 1. Clone the repository and install dependencies
git clone https://github.com/chris-schmitz/RetroFM.git
cd RetroFM
npm install # or bun install
# 2. Configure .env with your Gemini API key
cp .env.example .env
# 3. Launch the development server
npm run dev # or docker compose up -dOpen the URL in Google Chrome (http://localhost:3003 when running Docker, or the port npm run dev prints when running locally). The DJ commentary, era-accurate chart trivia, and song stories will work out of the box using the sample dataset.
For power users and contributors wanting the entire 70-year library (~1,800 verified YouTube tracks and 355,800 Billboard Hot 100 weekly chart entries spanning 1958 through today):
-
SQLite Database Warehouse: The authoritative historical data is maintained in an SQLite database at
~/retrofm/library.sqlite(containingsongs,song_facts,band_members,artist_aliases, andweekly_chart).- Weekly Chart Data (
weekly_chart):- Source: Scraped Billboard Hot 100 weekly chart compilation from the open-source repository
utdata/rwd-billboard-datamaintained by the University of Texas at Austin School of Journalism data journalism program. Specifically fetched fromhttps://raw.githubusercontent.com/utdata/rwd-billboard-data/main/data-out/hot-100-current.csvvia~/retrofm/import_charts.py. - Fetched Date: October 11, 2026 (355,800 weekly rank rows covering 3,558 consecutive chart weeks from August 4, 1958 through October 10, 2026).
- Update Mechanism: An automated GitHub Actions workflow (
action_scrape_charts.R) runs weekly in the upstream repository, scraping the latest weekly Billboard Hot 100 chart from Billboard.com and committing updated rows todata-out/hot-100-current.csv. Re-running~/retrofm/import_charts.pydownloads the latest CSV and repopulatesweekly_chart. - License / Terms: The
utdata/rwd-billboard-datacode and compilation are licensed under the MIT License. The underlying historical chart rankings are factual historical records published by Billboard (Penske Media Corporation).
- Source: Scraped Billboard Hot 100 weekly chart compilation from the open-source repository
- MusicBrainz Metadata:
- Used for canonical track durations, release dates, recording IDs (
recording_mbid), songwriter and producer credits, and band member line-ups. bun run scripts/populate-library-credits.tsqueries the MusicBrainz API (https://musicbrainz.org/ws/2/recording/${mbid}?inc=artist-rels+work-rels+recording-rels&fmt=json) to populate songwriter (written by,composed by,lyrics by) and production credits intosong_credits.bun run scripts/populate-band-members.tsqueries the MusicBrainz Artist API (https://musicbrainz.org/ws/2/artist/${mbid}?inc=artist-rels&fmt=json) to populate band membership history (member names, begin/end join years, active line-up states).src/services/music/songStoryCardService.tsandsrc/services/music/songSearch.tsquery MusicBrainz for canonical release years and artist origins.
- Used for canonical track durations, release dates, recording IDs (
- Wikipedia Data:
- Used by
src/services/music/songStoryCardService.tsvia the MediaWiki Action API (action=query&prop=extracts|revisions) to fetch page summaries and wikitext for song articles (or artist articles if no standalone song article exists). - Parses behind-the-scenes writing history, recording trivia, cover song origins, sample/interpolation sources, and cultural impact to build rich DJ story cards evaluated by Gemini and LM Studio commentary prompts.
- Used by
- Weekly Chart Data (
-
Deterministic Exporter Command: Whenever you enrich or build
library.sqlite, run:npm run export-library
This command:
- Validates referential integrity (ensuring every credit and fact maps to a valid song ID).
- Fully regenerates the verified catalog (
public/data/retrofm_library_youtube.json), extra song references (public/data/songs_extra.json), song facts, band members, and artist aliases. - Splits the 355,800 weekly chart rankings into decade JSON files (
public/data/chart_facts_<decade>.json), atomically rewriting only decades whose content changed. - Clears
.cache/song-cards/so no stale cache persists. - Prints row counts and file sizes. All exported files stay git-ignored.
Configure the following variables in your local .env file (do not commit real values to version control):
| Variable | Description | Required / Optional |
|---|---|---|
GEMINI_API_KEY |
Google Gemini API key for script generation and Flash Text-to-Speech audio synthesis. | Required |
YOUTUBE_API_KEY |
YouTube Data API v3 key for searching songs and verifying music duration metadata. | Required |
NYT_API_KEY |
New York Times API key for querying historical articles from the NYT Archive API. | Required |
GEMINI_TEXT_MODEL |
Primary Gemini model for DJ break script writing (defaults to gemini-3.8-flash). |
Optional |
GEMINI_TEXT_MODEL_FALLBACK |
Fallback Gemini model invoked if the primary model is busy or times out (e.g. gemini-2.5-flash). |
Optional |
RetroFM includes a companion Chrome extension that provides bidirectional tab communication, YouTube playback control, smooth audio ducking ramps, and tab grouping.
Tip: Web browsers prevent pages from directly linking to
chrome://URLs. RetroFM provides a convenient "Copy chrome://extensions" button right in the app header and modal.
- If you cloned the repo: In Google Chrome, navigate to
chrome://extensions/. Turn on the Developer mode toggle in the top-right corner, click Load unpacked, and select theextensionfolder inside the cloned project directory. - If running without the repo: Download the extension ZIP (click Download Extension in the RetroFM app), unzip the archive to a permanent folder (e.g.
Documents/retrofm-extension), and use Load unpacked on that folder. - Return to RetroFM and click Launch RetroFM to automatically open YouTube and group both tabs into an orange "RetroFM" Chrome Tab Group.
- Download the new version ZIP package from the RetroFM app.
- Unzip into the same folder (
Documents/retrofm-extension) and replace existing files. - Open
chrome://extensions/. - Click the Reload arrow ↻ on the RetroFM card (do not remove the card).
- Refresh your RetroFM and YouTube tabs.
src/– React application source code, UI components, and state management.src/services/audio/– CentralVolumeControllermanaging volume ducking, restoring, watchdog monitoring, and ramp IDs.src/services/music/– YouTube music search, playlist timing models, and music provider abstraction.src/services/news/– NYT Archive API provider, national focus filter, category normalizer, and interest scoring.src/services/djBreak/– Two-step DJ break script generation, anachronism audit, and caching.
extension/– Manifest V3 Chrome extension bridge source files and icons.scripts/– Utility scripts (bump-extension.ts,test-music-search.ts).server.ts– Express full-stack proxy server and Vite middleware integration.
This project is licensed under the MIT License. See the LICENSE file for the complete text.
Important Notice: The MIT License covers only the software code of this application and its companion extension. It does not cover or grant rights to any third-party music recordings, audio streams, YouTube video content, third-party trademarks, logos, or news articles retrieved from upstream APIs. All trademarks, music recordings, and news content belong exclusively to their respective copyright holders.
