Listen Together, Stay in Sync
Harmonize is a real-time audio synchronization platform that enables multiple users to join virtual listening rooms and experience shared audio playback without drift or delay. Create collaborative playlists, upload music, and enjoy perfectly synchronized playback with friends anywhere in the world.
- 🎼 Create Virtual Rooms: Start a listening session and share with friends via a simple link
- 🎧 Real-time Synchronization: Perfect audio sync across all connected devices using Firestore real-time updates
- 📤 Collaborative Playlists: Upload audio files and build shared playlists together
- 🎮 Host Controls: Room creator controls playback (play, pause, skip, volume)
- 👥 Live Participants: See who's listening in real-time
- 🎨 Beautiful UI: Modern, responsive design with gradient backgrounds and smooth animations
- 🔄 Auto-sync: Automatic synchronization when users join mid-playback
- Frontend: Next.js 15, React 19, TypeScript, Tailwind CSS
- Backend: Firebase (Firestore + Storage)
- Audio: Web Audio API
- Real-time: Firestore real-time listeners
- Node.js 18+
- npm or yarn
- Firebase project with Firestore and Storage enabled
cd harmonize
npm install-
Go to Firebase Console
-
Select your project:
direct-byte-473415-t7 -
Enable Firestore Database:
- Go to Firestore Database → Create Database
- Start in production mode or test mode (for development)
-
Enable Storage:
- Go to Storage → Get Started
- Set up security rules (start in test mode for development)
-
Enable Web App:
- Go to Project Settings → General
- Under "Your apps", click the web icon
</> - Register your app and copy the config
Update .env.local with your Firebase config values:
NEXT_PUBLIC_FIREBASE_API_KEY=your_actual_api_key
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=direct-byte-473415-t7.firebaseapp.com
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=direct-byte-473415-t7.appspot.com
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_id
NEXT_PUBLIC_FIREBASE_APP_ID=your_app_idTo get these values:
- Firebase Console → Project Settings (gear icon)
- Scroll down to "Your apps"
- Select your web app or create one
- Copy the config values
For testing, use these permissive rules. Update for production!
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /rooms/{roomId} {
allow read, write: if true; // Change this for production!
}
}
}rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /rooms/{roomId}/{allPaths=**} {
allow read, write: if true; // Change this for production!
}
}
}npm run devOpen http://localhost:3000 in your browser.
- Open the app homepage
- Click "Create Room"
- Enter your name and room name
- Click "Create" - you'll be redirected to the room
- Click "Join Room"
- Enter your name and the room ID (shared by the host)
- Click "Join"
- In the room, click "+ Upload" in the right panel
- Enter song title and artist name
- Select an audio file (MP3, WAV, etc.)
- Click "Upload Song"
- Wait for the upload to complete
- Host only can control playback
- Click any song in the playlist to play it
- Use play/pause, next/previous buttons
- Adjust volume with the slider
- All participants hear the same audio in perfect sync
Click "Copy Room Link" button in the Room Info panel to share with friends.
harmonize/
├── app/
│ ├── page.tsx # Landing page (Create/Join Room)
│ ├── room/[roomId]/
│ │ └── page.tsx # Room player page
│ └── layout.tsx
├── components/
│ ├── RoomInfo.tsx # Room details & participants
│ ├── MusicPlayer.tsx # Audio player with controls
│ └── Playlist.tsx # Playlist & upload UI
├── hooks/
│ └── useRoom.ts # Real-time room data hook
├── lib/
│ └── firebase.ts # Firebase configuration
├── types/
│ └── index.ts # TypeScript interfaces
└── .env.local # Environment variables
- Firestore Real-time Listeners: Room state is stored in Firestore and synced to all clients via
onSnapshot - Host Controls: Only the room creator can modify playback state
- State Broadcasting: When host changes play/pause/seek/volume, it updates Firestore
- Client Sync: All connected clients receive updates and adjust their local audio playback
- Time Sync: Current playback time is periodically synced to keep all clients in unison
Edit gradient colors in:
app/page.tsx- Landing page gradientsapp/room/[roomId]/page.tsx- Room page background- Component files for button and card styling
In components/MusicPlayer.tsx, change the sync frequency:
// Currently syncs every 2 seconds
if (isHost && room.isPlaying && Math.floor(audio.currentTime) % 2 === 0) {
updateRoom({ currentTime: audio.currentTime });
}- Verify Firebase credentials in
.env.local - Check Firestore security rules allow read/write
- Ensure room ID is correct
- Check Storage is enabled in Firebase
- Verify Storage security rules
- Check file size limits (Firebase free tier: 1GB storage)
- Check browser console for errors
- Ensure audio file format is supported (MP3, WAV, OGG)
- Verify file uploaded successfully to Firebase Storage
- Ensure all clients have stable internet
- Check Firestore read/write permissions
- Verify real-time listeners are working (check browser console)
rooms/{roomId}
{
name: string,
createdBy: string,
createdAt: number,
isPlaying: boolean,
currentSongIndex: number,
currentTime: number,
volume: number,
participants: string[],
playlist: [
{
id: string,
title: string,
artist: string,
duration: number,
url: string,
uploadedBy: string,
uploadedAt: number
}
],
lastUpdated: number
}Before deploying to production:
- Firestore Rules: Implement proper authentication and validation
- Storage Rules: Limit file sizes and types
- Rate Limiting: Add rate limits for uploads
- User Authentication: Implement Firebase Auth
- Environment Variables: Use secure environment variable management
npm install -g vercel
vercelAdd environment variables in Vercel dashboard.
Build the app:
npm run build
npm run startMIT License - Feel free to use this project for learning and development.
Contributions, issues, and feature requests are welcome!
Give a ⭐️ if you like this project!
Built with ❤️ using Next.js and Firebase