Three self-trained AI models read your mood from text, voice, or a selfie. A personalized Deezer playlist is generated, ranked by quality, and blended with your recent moods - in under two seconds, all open-source and privacy-first.
An intelligent music recommendation system that meets you where your mood is - typed, spoken, or photographed.
Make music discovery feel like being understood. Detect mood from three modalities (text, voice, face), then surface tracks that match the moment - not the demographic.
People express mood differently across contexts. A short text rarely lies; a voice clip captures tone you cannot type; a photo catches the micro-expression you would not admit to. Moodify reads all three.
Memory-snapshot inference container restores in seconds, not minutes. From input to playlist averages under two seconds end-to-end, even on first request after idle.
MIT license, public repo, public model weights on Hugging Face Hub. Every architectural decision is documented in the READMEs and condensed into the wiki below.
One React 18 codebase for web (Vercel), one Expo SDK 51 codebase for mobile (EAS Build). The mobile app talks directly to the Modal inference service - the Django API stays out of the data path.
Photos and voice clips are sent to the inference container for a single classification call and never persisted. Only the resulting mood label and saved track metadata land in your profile.
Every ๐, ๐ (and un-vote), plus open-in-Deezer, trains your playlist ranker - and your like/dislike state persists across reloads. A Thompson Sampling contextual bandit re-orders the list once you cross 20 events; a per-user calibration map even corrects mis-classified emotions. New & anonymous users see the rule-based pipeline untouched.
Code, decisions, and tradeoffs are all on GitHub. Browse the issues, copy a pattern, file a PR. Moodify is a working reference for shipping AI products, not a black box.
A complete emotion detection and music recommendation system, surfaced through a clean, accessible UI.
Fine-tuned BERT classifier reads tone, sentiment, and emotional vocabulary from typed input. Five labels: sadness, joy, love, anger, fear.
SVC classifier over librosa MFCC features. Eight labels: calm, happy, sad, angry, fearful, disgust, surprised, neutral. Sub-second inference, sub-second feature extract.
FER Keras model wrapped in MTCNN face detection. Seven labels (Ekman + neutral). Works on a single front-camera photo, no video stream required.
Keyless integration with Deezer's Search API. Returns track name, artist, album, 30s preview, cover art, popularity rank, and a deep link to the streamable web player.
Thompson Sampling contextual bandit over a Beta-Bernoulli posterior. Every ๐ / ๐ / un-vote (clear) / open-in-Deezer tap updates a 22-dim posterior (emotion × decade × duration × popularity-quintile) that re-ranks the next list once you cross 20 logged events. Votes use set-semantics - a like ↔ dislike switch or a clear reconciles cleanly with no double-count - and your like/dislike buttons restore their state after a reload via GET /api/feedback/tracks/.
A "Was this right?" widget captures every disagreement with the BERT detector. After three same-direction corrections (e.g. joy → love), the inference path rewrites the predicted label for that user only - immediate, no retrain, anonymous callers untouched.
Recency-weighted EWMA + first-order Markov chain + adaptive blend ratio produce the candidate list the bandit re-ranks. Recurring moods seep into the current playlist at a clamped, recency-decayed rate.
JWT auth (HS256, 7d access + 14d refresh) - sign in with username or email. Mongo-backed profile, append-only mood history, a listening_history of full track dicts (rich profile cards), and saved recommendations with rich track objects.
Sign in with Face ID, Touch ID, Windows Hello, or a hardware security key - no password. WebAuthn / FIDO2 verified by py_webauthn. Users enroll multiple passkeys and manage them (add / rename / delete) on a dedicated Account → Passkeys page; a verified assertion mints the same JWT pair as password login.
Expo SDK 51 + React Native 0.74 with Hermes engine. AsyncStorage for tokens, EAS Build for both stores, native camera + microphone for the three modalities.
Two-tier sliding-window rate limit (general + media) keyed on JWT sub, two-layer caches (text + Deezer + hash-of-bytes), and a hard MAX_CONTAINERS=5 ceiling at the Modal layer.
Per-request observability into MongoDB time-series collections. Admin-only /api/metrics/ aggregator returns p50/p95/p99 latency, error rates, and status-code breakdown for any rolling window.
Deezer down? Curated 14-track fallback list. Speech model fails to load? Endpoint returns degraded:true with neutral instead of 500. A failing model never takes down the others.
Theme switch toggles a full set of CSS variables. Honors prefers-color-scheme on first paint; persisted in localStorage across sessions.
17 country markets (Global + 16) and 19 genre filters bias the Deezer search phrase before it leaves the inference container. Results stay culturally relevant.
From "how do I feel" to a playlist on your phone in under two seconds.
Type a few words about your day, record a few seconds of your voice, or snap a quick selfie. Pick whichever feels easiest in the moment.
Three small AI models work behind the scenes - one for text, one for voice, one for faces. They turn your input into a mood label like "calm" or "excited."
Up to 60 tracks tuned to that mood appear instantly - with 30-second previews and one-tap deep links to Deezer. Save the ones you love to your history.
A side project that ended up being the music app I actually use every day.
No more endless scrolling for the right vibe. Three AI models read how you feel and skip straight to music that fits.
Selfies and voice clips are used once to detect mood, then thrown away. Only the mood label is saved. No tracking, no resale.
Under two seconds from input to a ranked, deduped 60-track playlist. No spinners, no waiting, no manual searching.
Every line of code is on GitHub. Read it, fork it, self-host it, contribute. Pull requests welcome.
Cloud-native, modular, and split along a clean service boundary. Pick a lens below.
auth.authenticate verifies either the JWT (end-user) or MODAL_SERVICE_TOKEN (Django proxy).mood_history and decides whether to fetch a second Deezer search for the recurring mood.rank_by_quality blends Deezer popularity with curated position; interleave mixes the two moods at the adaptive blend ratio.mood_history and recommendations on the Mongo profile. Then it routes to the Results page.DEPLOYMENT.md.k8s-addons/ directory layers in External Secrets, OPA Gatekeeper, Velero, and Chaos Mesh. See INFRASTRUCTURE_SETUP.md.JWT_SIGNING_KEY signs on Django and verifies on Modal. End-user tokens are 7d access + 14d refresh; silent refresh is wired into the React client. Login accepts a username or email, and a cold backend returns 503 "waking up" (the client auto-retries) instead of a misleading 401.MODAL_SERVICE_TOKEN is a shared constant for the Django→Modal proxy path; bypasses the Modal rate limiter (DRF throttles that side).CsrfViewMiddleware is intentionally removed.MAX_CONTAINERS=5 Modal ceiling guarantee a worst-case capacity ceiling.Modern, production-ready, and selected for long-term maintainability over novelty.
Multiple paths. Pick the one that matches your risk and infra budget.
Two identical production environments behind one Service. Promote by flipping the selector label - instant traffic switch with zero downtime, instant rollback by flipping back.
Argo Rollouts gradually shifts traffic 10% → 25% → 50% → 100% with automated health checks at each step. Rollback is automatic on a failed gate.
GitHub Actions on the canonical path, Jenkins on the self-host path. Lint, test, security scan, image build, push, and Argo trigger. Quality gates + approvals on protected branches.
HPA, PDB, NetworkPolicy, and an Ingress with cert-manager. Auto-scaling and self-healing. The same Helm charts deploy to Kind locally, then EKS / GKE / OKE in production.
The recommended path. Git push → preview URL on every PR. modal deploy atomically swaps the inference container. Rollback by re-deploying a previous SHA.
Cluster watches the repo. Changes under argocd/applications/ reconcile automatically. No kubectl apply from laptops, no drift between Git and cluster.
Terraform modules under terraform/ spin up VPC + cluster + databases + monitoring for AWS / GCP / Azure / Oracle. One terraform apply away from a fresh environment.
Drop-in operators for the self-host path. External Secrets pulls from Vault, OPA Gatekeeper enforces policy, Velero handles backups, Chaos Mesh proves the system can take a punch.
Per-request rows to MongoDB TS (canonical) or kube-prometheus-stack with custom dashboards (self-host).
Vercel + Modal logs natively, plus Loki + Promtail with label-based querying on the K8s path.
Grafana Tempo on the K8s path; OpenTelemetry hooks ready in the Django middleware.
SLO PrometheusRule ships in the monitoring chart: latency, error rate, container budget. Slack / PagerDuty receivers wired in values.yaml.
A snapshot of what Moodify ships on the wire today.
Numbers measured against the production deploy.
Experience Moodify in action - or read the live API contract.
The full SPA with all three emotion modes, mood history, and the personalized Deezer playlist.
Launch App →Live Django REST API with OpenAPI 3 contract. Interactive Swagger UI for testing, Redoc for reading.
Swagger UI → Redoc →Open-source on GitHub with full READMEs per subsystem, OpenAPI YAML, Terraform, Helm, and CI/CD.
View on GitHub →FastAPI on Modal. Scale-to-zero, CPU memory snapshots, sliding-window rate limiting. GET /health for liveness.
Quick answers to what people ask most.
Most music apps ask you to pick a genre or playlist. Moodify reads your mood three different ways (text, voice, face) and tunes the music to fit - no scrolling, no decision fatigue, no manual searching.
Yes - an account keeps your mood history and saved tracks with you across visits and devices. Sign-up takes about 10 seconds: username, email, password. That's it. When you sign back in, you can use either your username or your email in the sign-in field.
Yes - Moodify supports passkeys. Right after sign-up (or anytime from Account → Passkeys) you can add a passkey and then sign in with Face ID, your fingerprint, Windows Hello, or a hardware security key - no password to type. You can add several (phone, laptop, security key), rename or remove them, and your password keeps working as a fallback.
Three ways - pick whichever you feel like. Type a few words about how you feel. Record a few seconds of your voice. Snap a quick selfie. Moodify reads any of those and finds music that matches.
No. Your photo or voice clip is used once to figure out the mood, then thrown away. Only the mood label itself ("happy", "calm", etc.) is kept so your history works. Nothing about your face or voice is stored.
Deezer. Every recommendation links straight to a streamable track on Deezer with a 30-second preview built in. Tap any track to open it on Deezer and listen.
Yes. You can bias the recommendations toward 19 different genres (lo-fi, EDM, k-pop, classical, hip-hop, etc.) and 17 different country markets if you want the catalog to feel local.
Moodify's inference container hibernates when nobody is using it. The first request after a quiet stretch takes about 1-2 seconds to wake the server up. Every request after that is fast. If the backend is still warming when you sign in, it answers with a short "waking up" response and the app retries automatically - so a cold start never looks like a wrong password.
Around 84-92% across the three modes on our test data. It works best on clear input: a normal-length sentence, a few seconds of audible voice, or a well-lit selfie facing the camera.
Yes - Moodify runs in any modern mobile browser, and there's a native iOS + Android app built with Expo. The mobile app uses the phone's camera and microphone directly.
No. The recommendation and mood-detection happen on a server, so an internet connection is required. The app itself opens fast, but you need to be online to get a playlist.
Yes - any time. The profile page has a Delete Account button. Your account, mood history, and saved recommendations are removed immediately.
It happens occasionally. Try a different mode (text vs voice vs face), or use the genre filter to push the playlist in the direction you actually want. You can also ๐ / ๐ individual tracks (or un-vote to undo) - those ratings stick across reloads and quietly retrain your personal ranker. The more you use Moodify, the better your personal blend gets.
Yes. Liking or disliking a track is saved to your account, so when you reload or come back later the buttons show exactly how you voted. Tapping the same button again un-votes (clears it). Once you've logged about 20 ratings, Moodify starts re-ordering future playlists to match what you actually like.
Son Nguyen (@hoangsonww). Solo project, MIT-licensed, code is fully open on GitHub.
Open an issue on GitHub or email hoangson091104@gmail.com. Pull requests welcome.
Open the app, share how you feel, get a playlist tuned to the moment. Three modes, one ranked Deezer playlist, ready in seconds.