AI-powered music recommendations based on emotional profiles using valence-arousal modeling
- Overview
- Key Features
- Tech Stack
- Architecture
- Project Structure
- Getting Started
- API Documentation
- Services
- Database Schema
- Middleware
- Development
- Testing
- Deployment
- Contributing
- License
- Team
SoulSync is an intelligent music recommendation backend service that generates personalized playlists based on users' emotional states. Using the Valence-Arousal Model of emotion, the system matches songs to users' current mood by analyzing emotional dimensions:
- Valence: Positivity/negativity of emotion (0.0 to 1.0)
- Arousal: Energy/intensity of emotion (0.0 to 1.0)
The backend integrates AI services, user activity tracking, and sophisticated optimization algorithms to deliver highly personalized music experiences.
- Valence-arousal modeling for precise mood matching
- Dynamic playlist generation based on emotional profiles
- Multi-dimensional scoring algorithm (mood match + user history + language preference)
- User listening history analysis
- Language preference detection from activity patterns
- Personalized ranking using weighted scoring system:
- 40% mood match score
- 25% listening history score
- 20% language preference score
- 15% recency score
- Comprehensive activity logging (plays, skips, likes, etc.)
- Behavior pattern analysis for improved recommendations
- Privacy-focused data collection
- Firebase Authentication integration
- JWT token verification middleware
- Secure API endpoints with bearer token authorization
- Full-text search across song database
- Language-based filtering
- User playlist management
- Supabase: PostgreSQL database with real-time capabilities
- Firebase: Authentication and user management
- Cloudinary: Media asset management (ready for future features)
- Express.js for high-performance REST API
- CORS enabled for cross-origin requests
- Compression middleware for optimized responses
- Rate limiting support (via rate-limiter-flexible)
- Node.js (v16+) - JavaScript runtime
- Express.js (v4.22.1) - Web application framework
- Supabase (@supabase/supabase-js v2.98.0) - PostgreSQL database
- Mongoose (v8.0.3) - MongoDB ODM (for future features)
- Firebase Admin (v13.7.0) - Authentication & user management
- JWT (jsonwebtoken v9.0.2) - Token-based authentication
- Cloudinary (v2.9.0) - Media management
- Axios (v1.13.6) - HTTP client for AI service integration
- Helmet (v7.1.0) - HTTP header security
- CORS (v2.8.6) - Cross-origin resource sharing
- bcryptjs (v2.4.3) - Password hashing
- Compression (v1.7.4) - Response compression
- Rate Limiter Flexible (v4.0.1) - Rate limiting
- Joi (v17.11.0) - Schema validation
- Multer (v1.4.5) - File upload handling
- Morgan (v1.10.0) - HTTP request logger
- dotenv (v16.6.1) - Environment variable management
- Nodemon (v3.1.14) - Auto-restart during development
- ESLint (v8.55.0) - Code linting
- Jest (v29.7.0) - Testing framework
- Supertest (v6.3.3) - API testing
- Babel - JavaScript transpilation
βββββββββββββββββββ
β Client App β
β (Mobile/Web) β
ββββββββββ¬βββββββββ
β REST API
βΌ
βββββββββββββββββββββββββββββββββββββββ
β Express.js Backend β
β ββββββββββββββββββββββββββββββββ β
β β Authentication Middleware β β
β ββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββββββββββββββββ β
β β API Routes β β
β β β’ Recommendations β β
β β β’ Activity Tracking β β
β β β’ Playlists β β
β β β’ Search β β
β ββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββββββββββββββββ β
β β Business Services β β
β β β’ RecommendationService β β
β β β’ OptimizationService β β
β β β’ ActivityService β β
β β β’ AIService β β
β ββββββββββββββββββββββββββββββββ β
βββββββββββββ¬βββββββββββ¬βββββββββββββββ
β β
ββββββββΌβββ βββββΌβββββββββ
βSupabase β β Firebase β
β (DB) β β (Auth) β
βββββββββββ ββββββββββββββ
β
ββββββββΌβββββββββββ
β External AI β
β Engine β
βββββββββββββββββββ
backend_node/
β
βββ src/
β βββ config/ # Configuration files
β β βββ cloudinary.js # Cloudinary setup
β β βββ firebase.js # Firebase Admin SDK setup
β β βββ firebaseServiceAccount.json # Firebase credentials
β β βββ supabase.js # Supabase client configuration
β β
β βββ controllers/ # Route controllers (planned)
β β
β βββ middleware/ # Custom middleware
β β βββ authMiddleware.js # JWT authentication middleware
β β
β βββ routes/ # API route definitions
β β βββ activityRoutes.js # User activity endpoints
β β βββ playlistRoutes.js # Playlist management endpoints
β β βββ recommendationRoutes.js # Music recommendation endpoints
β β βββ searchRoutes.js # Search functionality endpoints
β β
β βββ services/ # Business logic layer
β β βββ activityService.js # User activity tracking
β β βββ aiService.js # AI engine communication
β β βββ optimizationService.js # Playlist optimization algorithms
β β βββ recommendationService.js # Recommendation generation
β β
β βββ index.js # Application entry point
β
βββ .env # Environment variables (not in VCS)
βββ .gitignore # Git ignore rules
βββ package.json # Project dependencies and scripts
βββ package-lock.json # Locked dependency versions
βββ README.md # Project documentation
Ensure you have the following installed:
- Node.js >= 16.0.0
- npm >= 8.0.0
- Git
- Supabase account (for database)
- Firebase project (for authentication)
- Cloudinary account (optional, for media features)
-
Clone the repository
git clone https://github.com/your-org/soulsync-backend.git cd soulsync-backend/backend_node -
Install dependencies
npm install
-
Set up Firebase
- Create a Firebase project at Firebase Console
- Generate a service account key
- Save the JSON file as
src/config/firebaseServiceAccount.json
-
Set up Supabase
- Create a Supabase project at Supabase
- Get your project URL and service key
- Create the required database tables (see Database Schema)
-
Configure environment variables
- Copy the example below and create a
.envfile - Fill in your actual credentials
- Copy the example below and create a
Create a .env file in the root directory with the following variables:
# Server Configuration
PORT=5000
NODE_ENV=development
# Supabase Configuration
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_KEY=your-supabase-service-role-key
# Firebase Configuration
# (Service account JSON file should be in src/config/firebaseServiceAccount.json)
# Cloudinary Configuration (Optional)
CLOUDINARY_CLOUD_NAME=your-cloud-name
CLOUDINARY_API_KEY=your-api-key
CLOUDINARY_API_SECRET=your-api-secret
# AI Engine Configuration
AI_ENGINE_URL=http://localhost:8000
# JWT Configuration (Optional - for custom JWT)
JWT_SECRET=your-secret-key-here
JWT_EXPIRES_IN=7d
# MongoDB Configuration (Optional - for future features)
MONGODB_URI=mongodb://localhost:27017/soulsync
# Rate Limiting
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100npm run devServer runs on http://localhost:5000 with auto-restart on file changes.
npm startcurl http://localhost:5000/healthExpected response:
{
"status": "healthy"
}http://localhost:5000/api
Most endpoints require Firebase authentication. Include the Firebase ID token in the Authorization header:
Authorization: Bearer <firebase-id-token>
Root endpoint - check if server is running
Response:
{
"message": "SoulSync Backend Running"
}Health check endpoint
Response:
{
"status": "healthy"
}Check AI engine connectivity
Response:
{
"status": "ok"
}Test Supabase connection
Response:
{
"data": [...],
"error": null
}Generate an emotion-based playlist
Request Body:
{
"userId": "user123",
"valence": 0.7,
"arousal": 0.6,
"language": "en"
}Parameters:
userId(string, required) - User identifiervalence(number, required) - Emotional valence (0.0 to 1.0)arousal(number, required) - Emotional arousal (0.0 to 1.0)language(string, optional) - Preferred language code
Response:
{
"playlist": [
{
"id": "song1",
"title": "Happy Song",
"artist": "Artist Name",
"valence": 0.72,
"arousal": 0.58,
"language": "en",
"finalScore": 0.85
},
...
]
}Error Response:
{
"error": "Valence and arousal required"
}Log user activity (play, skip, like, etc.)
Request Body:
{
"userId": "user123",
"songId": "song456",
"action": "play"
}Parameters:
userId(string, required) - User identifiersongId(string, required) - Song identifieraction(string, required) - Action type (play, skip, like, etc.)
Response:
{
"success": true
}Error Response:
{
"error": "Error message"
}Get user's playlists
Query Parameters:
userId(string, required) - User identifier
Example:
GET /api/playlists?userId=user123
Response:
{
"playlists": [
{
"id": "playlist1",
"user_id": "user123",
"name": "My Mood Mix",
"created_at": "2026-03-01T10:00:00Z"
},
...
]
}Search for songs
Query Parameters:
q(string, required) - Search query
Example:
GET /api/search?q=happy
Response:
{
"results": [
{
"id": "song1",
"title": "Happy Song",
"artist": "Artist Name",
"album": "Album Name",
"valence": 0.8,
"arousal": 0.7,
"language": "en"
},
...
]
}File: src/services/recommendationService.js
Purpose: Generates playlists based on emotional parameters
Key Function:
generatePlaylistFromEmotion(valence, arousal, language)- Queries songs within Β±0.15 range of target valence/arousal
- Filters by language if specified
- Returns up to 20 matching songs
File: src/services/optimizationService.js
Purpose: Optimizes and ranks playlist recommendations
Key Functions:
calculateOptimizedPlaylist(userId, songs, valence, arousal)- Calculates personalized scores for each song
- Combines multiple factors: mood match, history, language, recency
- Returns ranked playlist
Scoring Algorithm:
finalScore = (0.4 Γ moodMatch) + (0.25 Γ historyScore) +
(0.2 Γ languageScore) + (0.15 Γ recencyScore)
Helper Functions:
calculateMoodMatch()- Calculates similarity between song and target emotiongetUserLanguagePreference()- Determines user's preferred language from historygetListeningHistoryScore()- Scores songs based on past interactions
File: src/services/activityService.js
Purpose: Logs and manages user activity
Key Function:
logUserActivity(userId, songId, action)- Records user interactions with songs
- Supports various action types (play, skip, like, etc.)
- Stores in
user_activitytable
File: src/services/aiService.js
Purpose: Communicates with external AI engine
Key Function:
checkAI()- Health check for AI engine availability
- Enables future AI-powered features (emotion detection, advanced recommendations)
Stores song metadata and emotional attributes
CREATE TABLE songs (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
title TEXT NOT NULL,
artist TEXT NOT NULL,
album TEXT,
valence DECIMAL(3,2) NOT NULL, -- 0.00 to 1.00
arousal DECIMAL(3,2) NOT NULL, -- 0.00 to 1.00
language VARCHAR(10),
duration_ms INTEGER,
spotify_id VARCHAR(50),
created_at TIMESTAMP DEFAULT NOW()
);
-- Indexes for performance
CREATE INDEX idx_songs_valence_arousal ON songs(valence, arousal);
CREATE INDEX idx_songs_language ON songs(language);
CREATE INDEX idx_songs_title ON songs USING GIN(to_tsvector('english', title));Logs user interactions with songs
CREATE TABLE user_activity (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id VARCHAR(100) NOT NULL,
song_id UUID REFERENCES songs(id),
action VARCHAR(50) NOT NULL, -- 'play', 'skip', 'like', 'dislike', etc.
created_at TIMESTAMP DEFAULT NOW()
);
-- Indexes
CREATE INDEX idx_activity_user ON user_activity(user_id);
CREATE INDEX idx_activity_song ON user_activity(song_id);
CREATE INDEX idx_activity_user_song ON user_activity(user_id, song_id);Stores user-created playlists
CREATE TABLE playlists (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id VARCHAR(100) NOT NULL,
name TEXT NOT NULL,
description TEXT,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Index
CREATE INDEX idx_playlists_user ON playlists(user_id);Junction table for playlist-song relationships
CREATE TABLE playlist_songs (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
playlist_id UUID REFERENCES playlists(id) ON DELETE CASCADE,
song_id UUID REFERENCES songs(id) ON DELETE CASCADE,
position INTEGER NOT NULL,
added_at TIMESTAMP DEFAULT NOW()
);
-- Indexes
CREATE INDEX idx_playlist_songs_playlist ON playlist_songs(playlist_id);
CREATE UNIQUE INDEX idx_playlist_songs_unique ON playlist_songs(playlist_id, position);Basic user information (synced with Firebase)
CREATE TABLE users (
id VARCHAR(100) PRIMARY KEY, -- Firebase UID
email VARCHAR(255),
display_name VARCHAR(100),
photo_url TEXT,
created_at TIMESTAMP DEFAULT NOW(),
last_login TIMESTAMP DEFAULT NOW()
);File: src/middleware/authMiddleware.js
Function: verifyToken(req, res, next)
Purpose:
- Validates Firebase ID tokens
- Protects authenticated routes
- Attaches user information to request object
Usage:
const verifyToken = require('./middleware/authMiddleware');
router.post('/protected-route', verifyToken, (req, res) => {
// req.user contains decoded token data
const userId = req.user.uid;
// ... handle request
});| Command | Description |
|---|---|
npm start |
Start production server |
npm run dev |
Start development server with nodemon |
npm test |
Run test suite with Jest |
npm run test:watch |
Run tests in watch mode |
npm run lint |
Check code with ESLint |
npm run lint:fix |
Auto-fix linting issues |
This project follows JavaScript Standard Style. ESLint is configured to enforce consistent code quality.
Run linter:
npm run lintAuto-fix issues:
npm run lint:fix-
Create a feature branch
git checkout -b feature/your-feature-name
-
Make changes and test
npm run dev
-
Run linting and tests
npm run lint npm test -
Commit changes
git add . git commit -m "feat: add your feature description"
-
Push and create pull request
git push origin feature/your-feature-name
-
Create route file in
src/routes/// src/routes/exampleRoutes.js const express = require('express'); const router = express.Router(); router.get('/example', async (req, res) => { res.json({ message: 'Example route' }); }); module.exports = router;
-
Register route in
src/index.jsconst exampleRoutes = require('./routes/exampleRoutes'); app.use('/api', exampleRoutes);
-
Create service file in
src/services/// src/services/exampleService.js async function exampleFunction() { // Business logic here } module.exports = { exampleFunction };
-
Import and use in routes
const { exampleFunction } = require('../services/exampleService');
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm test -- --coveragetests/
βββ unit/
β βββ services/
β βββ middleware/
βββ integration/
β βββ routes/
βββ setup.js
const request = require('supertest');
const app = require('../src/index');
describe('POST /api/generate-playlist', () => {
it('should generate a playlist based on emotion', async () => {
const response = await request(app)
.post('/api/generate-playlist')
.send({
userId: 'test-user',
valence: 0.7,
arousal: 0.6
});
expect(response.status).toBe(200);
expect(response.body.playlist).toBeDefined();
});
});For production deployment, ensure all environment variables are properly configured:
- Set
NODE_ENV=production - Use production database credentials
- Configure proper CORS origins
- Enable rate limiting
- Set up proper logging
# Install Heroku CLI
heroku login
# Create app
heroku create soulsync-backend
# Set environment variables
heroku config:set SUPABASE_URL=your-url
heroku config:set SUPABASE_SERVICE_KEY=your-key
# Deploy
git push heroku main# Install Vercel CLI
npm i -g vercel
# Deploy
vercel --prodFROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 5000
CMD ["npm", "start"]# Build and run
docker build -t soulsync-backend .
docker run -p 5000:5000 --env-file .env soulsync-backend- Environment variables configured
- Database migrations run
- Firebase service account configured
- CORS origins properly set
- Rate limiting enabled
- Logging configured
- Error tracking set up (e.g., Sentry)
- Health check endpoint tested
- Performance monitoring enabled
- Backup strategy in place
We welcome contributions to SoulSync! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'feat: Add AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
We follow Conventional Commits:
feat:New featurefix:Bug fixdocs:Documentation changesstyle:Code style changes (formatting)refactor:Code refactoringtest:Adding or updating testschore:Maintenance tasks
- All submissions require review
- Ensure tests pass and code is linted
- Update documentation as needed
- Follow existing code style
Version 2.0 (Planned)
- Real-time playlist updates via WebSockets
- Advanced emotion detection via facial recognition
- Social features (playlist sharing, collaborative playlists)
- Music streaming integration (Spotify, Apple Music)
- Advanced analytics dashboard
- Machine learning model integration for better recommendations
- Multi-language support expansion
- GraphQL API option
If you encounter any issues or have questions:
- Check the documentation
- Search existing GitHub Issues
- Create a new issue with detailed information
- Contact the team at support@soulsync.app
Built with β€οΈ by the SoulSync Team
Connecting emotions with music, one playlist at a time