Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🎧 Wavelength

Get on the same wavelength. A Chrome extension that lets you and your friends listen to YouTube Music together, in sync. Someone hits play, everyone hears it. Someone skips, everyone skips. Someone seeks, everyone seeks.

┌──────────────────┐        ┌───────────────┐        ┌──────────────────┐
│  You (Chrome)    │◄──────►│  relay server │◄──────►│  Friend (Chrome) │
│  extension + YTM │  wss   │  (server/)    │  wss   │  extension + YTM │
└──────────────────┘        └───────────────┘        └──────────────────┘

Two parts:

Folder What it is
chrome-extension/ The extension you load into Chrome (Manifest V3).
server/ A ~200-line WebSocket relay that keeps rooms in sync. Everyone in a room connects to the same server.
test/ An end-to-end test that drives two real browsers on the real YouTube Music site.

Vocabulary: a wavelength is a room; its 6-letter frequency is the code friends tune in with.


1. Install the extension (everyone does this)

  1. Download wavelength-extension.zip from the Releases page and unzip it (or clone this repo — it's the chrome-extension/ folder). Chrome 111 or newer.
  2. Open Chrome → chrome://extensions
  3. Turn on Developer mode (top right).
  4. Click Load unpacked → pick the chrome-extension/ folder.
  5. Pin Wavelength to the toolbar (puzzle-piece icon → 📌) so it's one click away.

2. Run the relay server (one person does this)

The relay is what lets browsers talk to each other. Nobody's music goes through it — only tiny "paused at 1:23" / "now playing X" messages.

Quick test on your own machine

cd server
npm install
npm start          # → ws://localhost:8787

That's enough to try it out with two Chrome profiles on one computer. Friends on other computers can't reach localhost, so for real use deploy it (next section) or expose it with a tunnel (npx localtunnel --port 8787 gives you a temporary https://… URL — use it as wss://…).

Deploy for free (so friends anywhere can join)

Any host that runs Node and gives you a public HTTPS URL works. Two easy ones:

Render — https://render.com → New → Web Service → connect MowkE/wavelength (or a fork) → Root directory server, Build npm install, Start npm start, Instance type Free. You'll get https://your-app.onrender.com → your server URL is wss://your-app.onrender.com. (Free instances sleep after ~15 min idle; the first connection takes ~30 s to wake it. Everything reconnects automatically.)

Railway — https://railway.app → New Project → Deploy from repo → set Root Directory to server. Railway assigns a https://…up.railway.app domain → use it as wss://….

Fly.io, Koyeb, a $5 VPS with node server.js behind Caddy — all fine too. The server reads the PORT env var, which every host sets for you.

Rule of thumb: ws:// only works for localhost. Anything remote must be wss:// (TLS), which the hosts above give you automatically.

3. Point the extension at your server

Click the extension → expand Server → paste wss://your-app.onrender.com → Save. Every friend does the same with the same URL (it's saved to your Chrome profile).

4. Listen together

  1. One person types a name and clicks Start a wavelength → they get a 6-letter frequency like K7XQ2P.
  2. Share the frequency, or click Copy invite link and send that. The link looks like https://music.youtube.com/?wave=K7XQ2P — a friend with the extension just opens it and they're in.
  3. Friends who got a frequency: click the extension → name → paste it → Tune in.
  4. Open https://music.youtube.com and play something. Everyone's player follows.

Anyone on the wavelength can play, pause, seek, or put on a different song — it mirrors to everyone. A small pill in the corner of YouTube Music tells you what's happening ("Priya put on Blinding Lights", "Marcus paused", "3 on your wavelength").

The ★ next to a name marks the leader (first person on the wavelength). Their player is the reference clock: every 5 s it nudges anyone who has drifted more than 2 s, and when a song ends their next song is the one everyone gets. If the leader leaves, the next-oldest member takes over automatically.


Good to know

  • Ads. On free YouTube Music, each person's pre-roll ads are different lengths. The extension knows when an ad is playing: it never broadcasts during one, holds incoming changes until it's over, then jumps to where your friends are. Premium = totally seamless.
  • Browse freely. Wandering around Home / Explore / playlists while music plays is fine — the extension tracks what's actually playing, not the page URL.
  • One YouTube Music tab. The tab that first said hello is the synced one; extra YTM tabs you open to browse are left alone. Close the synced tab and the next one takes over.
  • Autoplay blocked? If Chrome refuses to start audio (fresh profile, never clicked the site), the corner pill turns red: "Click here to get back on the wavelength." One click fixes it.
  • Reconnects. Laptop lid closed, Wi-Fi hiccup, server restarted — the extension reconnects with backoff and rejoins the same room. Your seat is held for 45 s, so a quick blip keeps your name, ★ leader status and place in the list (friends just see you dimmed as "reconnecting…"). Empty wavelengths live 10 minutes so everyone can come back.
  • Privacy. The relay sees frequencies, display names, video IDs and timestamps. It stores nothing on disk. Only people with the code can join.
  • Nothing to sign in to. No accounts, no Google login for the extension itself; it just drives the YouTube Music page you're already using.

How it works (for the curious)

  • background.js (service worker) owns the WebSocket to the relay so it survives page loads, keeps itself alive with a 20 s ping + a 30 s alarm, and relays between the server and the synced YouTube Music tab.
  • content.js runs on music.youtube.com. It watches the <video> element and asks page.js which song is playing. When you do something it sends a snapshot {videoId, position, playing, title, …}; when a friend's snapshot arrives it applies only the difference (seek if off by > 0.75 s, play/pause if different, switch songs if it's another track). Snapshots instead of commands means everything is idempotent, so echoes and races converge instead of ping-ponging.
  • page.js is a few lines injected into the page's own JS world ("world": "MAIN", Chrome 111+). It reads YouTube's player API for the real current song — correct while you browse Home or a playlist (URL has no ?v=) and even during ads — and performs song switches through YouTube Music's in-app navigation, so following a friend to a new song doesn't reload the page. If the in-app switch doesn't take within 4 s it falls back to a normal navigation.
  • server/server.js keeps rooms → members + last state, forwards snapshots, elects a leader, and hands newcomers the current state (with timestamps, so they can compute where the song is now).

Running the tests

cd server && npm test        # protocol test, ~1 s

cd test && npm install && npx playwright install chromium
npm test                     # two headless browsers on real YouTube Music, 2–8 min
HEADFUL=1 npm test           # same, but you can watch

The e2e test starts a wavelength in one browser, tunes in from another, and checks follow-navigation, pause, seek, play (both directions), track change, auto-advance at end of song, second-tab behaviour, leaving, and rejoining by invite link.

About

Get on the same wavelength — listen to YouTube Music in sync with your friends. Chrome extension + tiny relay.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages