Collaborative Spotify playlists with swipe reactions.
Create shared playlists (Swaplists), add tracks, and react to each other's picks.
- Swaplists — create or import collaborative playlists linked to your Spotify account
- Swipe reactions — swipe right to vibe, swipe left to skip; reactions drive playlist curation
- Vibe sort — auto-reorder tracks by collective reaction score and audio features
- Real-time sync — tracks stay in sync with the underlying Spotify playlist
- Activity feed — see what friends are adding, reacting to, and listening to
- Push notifications — get notified when friends add tracks or react to yours
- Email invites — invite members by email with secure verification links
- AI vibe names — auto-generated Daylist-style labels for playlists (Claude Haiku)
- Installable PWA — add to home screen with offline support and native feel
- Next.js 16 (App Router, Turbopack) + React 19
- TypeScript, Tailwind CSS v4, shadcn/ui
- Motion (Framer Motion v11+) for gestures and animations
- Drizzle ORM + PostgreSQL (PGlite local / node-postgres production)
- Spotify OAuth PKCE (no NextAuth) + iron-session
- Resend for transactional email
- Web Push (VAPID) for notifications
- Pino structured logging
- Deployed on Fly.io with Docker
- Node.js 20+
- A Spotify Developer app with redirect URI set to
http://127.0.0.1:3000/api/auth/callback
# Install dependencies
npm install
# Copy environment template and fill in your values
cp .env.example .env.local
# Run database migrations (creates local PGlite DB automatically)
npm run db:migrate
# Start the dev server
npm run devOpen http://127.0.0.1:3000 to log in with Spotify.
See .env.example for all variables. At minimum you need:
| Variable | Description |
|---|---|
SPOTIFY_CLIENT_ID |
From your Spotify Developer app |
SPOTIFY_REDIRECT_URI |
OAuth callback URL (http://127.0.0.1:3000/api/auth/callback for local) |
IRON_SESSION_PASSWORD |
Random string, min 32 characters |
POLL_SECRET |
Random string, min 16 characters (polling auth) |
NEXT_PUBLIC_APP_URL |
Full app URL (e.g., http://127.0.0.1:3000) |
Optional for full functionality:
| Variable | Description |
|---|---|
DATABASE_URL |
Production Postgres connection string (omit for local PGlite) |
RESEND_API_KEY |
Email invites via Resend |
NEXT_PUBLIC_VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY |
Web push notifications |
TOKEN_ENCRYPTION_KEY |
AES-256-GCM encryption for Spotify tokens at rest |
ANTHROPIC_API_KEY |
AI vibe name generation (Claude Haiku) |
SPOTIFY_DEV_MODE |
Set true for Spotify apps in development mode (5-user cap, conservative limits) |
Generate VAPID keys:
npx web-push generate-vapid-keys| Command | Description |
|---|---|
npm run dev |
Start dev server |
npm run build |
Production build |
npm run lint |
ESLint |
npm run lint:strict |
ESLint with zero warnings |
npm run format |
Prettier format |
npm run type-check |
TypeScript check |
npm test |
Run unit tests (Vitest) |
npm run test:watch |
Tests in watch mode |
npm run db:migrate |
Run database migrations |
npm run db:generate |
Generate migrations from schema changes |
npm run security |
Local security scans (CodeQL + npm audit + Gitleaks) |
npm run security:codeql |
CodeQL SAST only |
MIT