A mobile app for discovering underground music through short, swipeable clips. Think TikTok, but built exclusively for music discovery.
Music discovery is broken:
- 90% of Spotify streams go to the top 1% of artists — independent musicians struggle to get heard with 100,000+ tracks uploaded daily
- Listeners are stuck in algorithmic loops — platforms keep serving the same recycled recommendations
- TikTok surfaces music but buries it — songs get lost under dance trends and memes
Nich fixes this by connecting listeners who want to discover something new with underground artists who deserve to be heard. Users swipe through 15-30 second clips, and the algorithm learns from their likes and skips to surface better recommendations over time.
The goal: help people find their next favorite artist before they blow up.
- Swipeable feed of 15-30 second music clips
- Search by popular songs you like to find similar underground artists
- Filter by genre, tempo, and mood
- Algorithm learns from likes/skips to improve recommendations
- Song history so you never lose a track you accidentally swiped past
- Follow friends and other curators
- In-app playlists with save functionality
- Activity feed showing what people you follow are discovering
- User profiles with discovery stats
- Host real-time listening sessions with friends
- Synchronized playback across all participants
- Queue songs collaboratively
- Live chat during sessions
- Early Supporter Badges — Earn recognition for discovering artists before they blow up
- Karma System — Build reputation based on your discovery track record
- Profile badges showcasing your music discovery credentials
- Direct upload portal
- Verified artist pages
- Analytics on engagement and who's discovering you
- Links to Spotify, Apple Music, SoundCloud for full tracks
| Technology | Purpose |
|---|---|
| React Native | Cross-platform iOS + Android from one codebase |
| TypeScript | Type safety and better developer experience |
| Expo | Simplified builds, OTA updates, native module management |
| Zustand | Lightweight state management |
| Technology | Purpose |
|---|---|
| Node.js + Express | REST API server |
| TypeScript | Consistent typing across the stack |
| Prisma | Type-safe ORM for database access |
| Socket.io | Real-time communication for listening parties |
| Technology | Purpose |
|---|---|
| PostgreSQL (Neon) | Primary database — serverless, auto-scaling |
| Redis | Session management, caching, real-time pub/sub |
| Technology | Purpose |
|---|---|
| Firebase Auth | Email/password and social login, JWT tokens |
| Technology | Purpose |
|---|---|
| Cloudflare R2 | Audio file and cover art storage (S3-compatible, zero egress fees) |
Why R2 over AWS S3? For a music streaming app, egress costs add up fast. R2 has no data transfer fees, which makes it significantly cheaper at scale while maintaining S3 API compatibility.
| Technology | Purpose |
|---|---|
| Railway | API hosting with auto-deploys from Git |
nich/
├── apps/
│ ├── api/ # Express backend
│ │ ├── src/
│ │ │ ├── routes/ # API endpoints
│ │ │ ├── lib/ # Utilities (Firebase, Redis, Storage)
│ │ │ ├── middleware/ # Auth, error handling
│ │ │ └── socket/ # Real-time listening party logic
│ │ └── prisma/ # Database schema and migrations
│ │
│ └── mobile/ # React Native app
│ └── src/
│ ├── screens/ # App screens
│ ├── components/ # Reusable UI components
│ ├── hooks/ # Custom React hooks
│ ├── stores/ # Zustand state stores
│ ├── lib/ # API client, Firebase config
│ └── navigation/ # React Navigation setup
│
└── packages/
└── shared/ # Shared types between API and mobile
- Node.js 18+
- npm 9+
- iOS Simulator (Mac) or Android Emulator
- PostgreSQL database (or Neon account)
- Firebase project
- Cloudflare R2 bucket
API (apps/api/.env)
DATABASE_URL=postgresql://...
FIREBASE_PROJECT_ID=your-project-id
FIREBASE_CLIENT_EMAIL=firebase-adminsdk-...@your-project.iam.gserviceaccount.com
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
R2_ACCOUNT_ID=your-account-id
R2_ACCESS_KEY_ID=your-access-key
R2_SECRET_ACCESS_KEY=your-secret-key
R2_BUCKET_NAME=nich-songs
REDIS_URL=redis://... # Optional, falls back to in-memory cacheMobile (apps/mobile/.env)
EXPO_PUBLIC_API_URL=http://localhost:3000
EXPO_PUBLIC_FIREBASE_API_KEY=...
EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN=...
EXPO_PUBLIC_FIREBASE_PROJECT_ID=...
EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET=...
EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=...
EXPO_PUBLIC_FIREBASE_APP_ID=...# Install dependencies
npm install
# Generate Prisma client
npm run db:generate --workspace=@nich/api
# Run database migrations
npx prisma migrate dev --schema=apps/api/prisma/schema.prisma
# Start the API
npm run api
# In another terminal, start the mobile app
npm run mobile| Command | Description |
|---|---|
npm run api |
Start API in development mode |
npm run mobile |
Start Expo development server |
npm run mobile:ios |
Run on iOS simulator |
npm run mobile:android |
Run on Android emulator |
npm run lint |
Run ESLint |
npm run format |
Format code with Prettier |
npm run typecheck |
Run TypeScript compiler |
The core matching works like this:
- User searches for a popular song (e.g., "I like Frank Ocean")
- System matches audio characteristics — tempo, mood, energy, instrumentation
- Collaborative filtering boosts results — songs liked by users who searched the same thing rank higher
- Feedback loop improves over time — likes and skips train the algorithm
Songs are stored with precomputed similarity scores for fast lookups. The SongSimilarity model tracks relationships between tracks with scores from 0.0 to 1.0.
Private project — not open source.