Melo
A browser-first music player that became an Android app.
Why it exists
01I wanted a music player without ads, without an account, and without a catalogue that disappears behind a subscription. MElo was the first attempt: a web app that could search, stream and download.
It became the foundation for Swar. The web version is archived, but the lessons about background audio and mobile packaging carried over.
Recorded 2025
Kaushambi, India
The constraints
02- 01Browser autoplay and background-audio restrictions.
- 02CORS on third-party music endpoints.
- 03A small Node proxy with no budget for bandwidth.
The approach
03Progressive web app first
A service worker caches the shell and recent tracks. The app installs to the home screen and behaves like a native player until Android decides to pause it.
Capacitor for the native shell
The same interface is wrapped in a Capacitor Android project, which gives access to local storage and a proper WebView while keeping one codebase.
What it actually does
04- 01Search and stream from an online catalogue.
- 02Background playback with lockscreen controls.
- 03Download tracks as MP3.
- 04Queue management and drag-to-reorder.
- 05Installable PWA and Android build.
Technical notes
05Vite for the frontend, Express for the API proxy, Capacitor for the Android shell. Audio playback uses the HTML audio element with a custom queue manager.
The browser was never designed to be a music player. Android suspends background tabs, and the lockscreen integration is inconsistent. That is why Swar exists.
- Role
- Frontend, Node proxy, Capacitor packaging
- Year
- 2025
- Platform
- Web, Android
- Stack
- JavaScript, Vite, Node.js, Express, Capacitor, Service Workers
- Status
- Archived
Honest limits
06- 01Background playback is unreliable on some Android versions.
- 02The Node proxy is not deployed at scale.
- 03Catalogue availability depends on an unofficial endpoint.
- 04Archived in favour of the native Swar app.
What it taught me
07“A PWA can prototype a media app quickly, but it cannot promise native playback behaviour. Knowing when to leave the browser was the real lesson.”