Why We Migrated to Compose Multiplatform?

The journey of Spotube was not a straight forward road. It was a maze. From React-Native (yes, you heard right!) to Flutter and now to Compose Multiplatform.
Backstory of frameworks #
NodeGUI/React-NodeGUI #
React-native was the primary framework Spotube used to use. We used React-NodeGUI which is basically QT with node-gyp bindings bonded with React to create a declarative UI framework for Nodejs. It was not a bad idea, was more efficient than Electron.js. It had only ~50MB-ish of RAM usage and ~40MB-ish of binary size, we guess.
But the problem was layout and the lack of base UI components. That’s when Spotube was pretty bare bones and we were not really aware of the features or stuff people wanted or knew about. We didn’t even know what scrobbling was. It was made only for our own use because Spotify’s Electron app couldn’t be run on a Core 2 Duo, 4GB RAM PC.
Transition to Flutter🐦 #
We knew about Flutter, but didn’t have any idea how it worked. So we started learning it and at a point we realized that we could probably migrate Spotube to Flutter. So we got our hands dirty, even had to make some audio playback libraries which were basically Dart (language Flutter is written in) bindings to MPV IPC protocol. Which to this day is actually used by the just_audio package through just_audio_mpv specifically for Linux although its unmaintained. And we literally copied the bindings from mpv-js bindings which was used in the react-nodegui version. we think we know a few people who hate us for this, so we’re sorry ig lol!
So, we actually decided to not use mpv and relied on just_audio and just_audio_libwinmedia and it worked good. And eventually it was not working well so we had to move on from that to a player called audioplayers and finally, we know nobody could guess it, but it’s media_kit which again uses MPV under the hood.
Why Not Flutter? #

Flutter served it’s purpose. It’s a great cross-platform framework. UIs were great, performance is near native. Though the platform integrations were a bit immature but Flutter team did overcome those problems. One thing they still haven’t improved or at least haven’t met our expectations was their Desktop support. Despite being THE QUICKEST (opens faster than native desktop/mobile apps literally) desktop app framework, their desktop support is still to this day terrible. And the only reason Flutter desktop is even usable is thanks to https://github.com/lijy91. And later Canonical (team behind Ubuntu) also did a great job for a Flutter desktop.
About that Elephant on the tree… #

There was also a big elephant on a tree. And the elephant finally fell from the tree on April 30, 2025. When we got a cease and desist letter from that Green Music Corp (u know who). So we had to remove anything related to that corp’s product. And the main complain was actually the use of their trademarked and registered logo and product name in our website (you are on it currently). Other ones were the abuse of their developer platform and APIs.
So, we had to make some changes. And legally we are now not allowed to do anything regarding that product. So, as any normal people, we rewrote part of the app to make it extensible, so it is no longer tied to that corp’s product and Spotube’s users/community can build the stuff they need. So basically, Spotube lost the thing that made Spotube, Spotube. Then the plan of making it a plugin based music streaming platform crossed our discord community’s (you’re not on it? JOIN OUR DISCORD NOW) mind and especially our discord mod Owen.
BUT! A HUGE BUT! #
Flutter is not extensible. It’s an embedded UI framework written in Dart. Although Dart can run on JIT/VM/Interpreter, but flutter compiles down to binary. So we tried everything. First was shipping a JS engine but the support was not there (we’ll come back to this), so we went for the greatest plugin language of all time, LUA. That also didn’t work, because we needed to make some kind of asynchronous bridge for Lua that would work with Dart’s event loop. We couldn’t, we are not cracked devs. But, somehow we found this gold https://hetu-script.github.io/ which is a custom script that runs on Dart specifically, and it’s written in Dart. It’s basically what Lua is to C. Amazing performance, like 2-3% performance difference with native Dart. It’s a great project. So we went with hetu_script, though it was unmaintained for a while and had lot of bugs and practically no ecosystem at all. We had to create the standard library, http client, crypto and pretty much everything.
The biggest advantage of hetu_script was it’s amazing interoperability with Dart. Even async code was totally, 100% interoperable with Dart which is so great, we can’t explain how happy we were the first time we learned about this.
Problems of the solution #
Tho hetu_script was great, it’s syntax was closer to javascript, but it lacked community support. It’s just not a good scripting language for plugin developers. It doesn’t have any LSP/autocomplete plugin, the syntax highlighting breaks every now and then. We had to pray and hope each time we built the plugin that it would work. It was not a good developer experience and we understand why it was not picked up well by the community. So recently, we realized that we had to look for alternatives.
We planned on using QuickJS so we tried doing our own dart binding to QuickJS, but failed miserably as we aforementioned our lack of intelligence and not being enough cracked (we just can’t write C/C++ code, idk why). We couldn’t even get a simple hello world to print. THERE WAS STACK OVERFLOW EVERYWHERE. And, we can confirm and declare our eternal hate for GCC, Clang, MSVC and all other C/C++ compilers out there and even the C/C++ languages itself. It broke us man, we gotta admit.
A Side Quest… #
We even tried rewriting the app in Tauri and Tauri-mobile for JavaScript (
eval()/service workers) based plugin system but it was just not good enough especially on platforms that don’t use Chromium (iOS, macOS, Linux). The performance was crap, absolute garbage. UI was fine and probably the easiest thanks to Tailwind and Shadcn but music playback was inconsistent across platforms, that it made us delete the project on spot when we were testing with Linux with WebKitGTK. We gave up on Web-technologies after that.
Zipline, opening the curtains of the dark room #

After failing to create simple binding to probably the simplest to bind JS engine, QuickJS, a depressed team decided to rewrite the entire app. We found Compose-Multiplatform quite amusing. And when we learned there’s a library called Zipline by cashapp that has Kotlin binding to QuickJS that is fully integrated with Kotlin Multiplatform (base of Compose Multiplatform), it clicked to us to rewrite the app for good this time.
Why Compose-Multiplatform? #
The benefits of Compose-Multiplatform just outweighs any hope we had left on Flutter and hetu_script. Here are some of them:
- Fully Native Android: Compose-Multiplatform uses the native APIs and everything as it’s originally based on jetpack-compose which is the official framework for building Android apps by Google. So yes, now Spotube is a native app on Android.
- Kotlin/JS based Plugins: Zipline library is used for this plugin support. And everyone can now write Spotube plugins in Kotlin (Kotlin/JS with no browser/nodejs API support) which has much bigger community and ecosystem. We already created docs, libraries, APIs that will solve 95% of the use cases. But you’re always welcome for new APIs in the GitHub issues.
- Better Performance: The architecture of Compose-Multiplatform and how it is written, is just better than Flutter most of the time.
- NewPipeExtractor support: NewPipeExtractor is written in Java which Kotlin is interoperable with out of the box. So we get 100% access to NewPipeExtractor’s APIs unlike the Flutter version which has to ship a dynamic library. That means a overall binary size reduction. And even iOS support for NewPipe. Yes, Spotube will now be 100% functional on iOS and same as other platforms. You still need to use AltStore/Sideloadly.
- Native Interop: Kotlin probably has the best interop support for different platform’s language, it literally supports Swift, Obj-C, C/C++ and Java. That literally means we can use any platform’s native APIs without touching those languages.
- Mental peace: Kotlin is 1000000x times better than Dart syntactically. It’s sugar syntax, DSL, and those juicy
{ }lambdas are so much better and time-saving and concise than Dart, is inexplicable. Kotlin makes Spotube development and maintaining a lot better. We mean the data class and not having to usebuild_runnerevery single time we make puny change in our serializable classes, is worth it for a full app migration.
We still can’t believe the Dart team implemented the compile time
macrosupport only to drop it. This alone could save millions of hours of wasted time that has gone into thedart run build_runner build --delete-conflicting-outputscommand.
(Mind you, we don’t hate build_runner (it’s improving and it’s good enough), or anyone for this. Dart team is doing great work and they’re limited with people so its understandable. But dropping a feature after putting so much time on, is something we hardly find amusing)
Hyping up Compose-Multiplatform #
In Compose Multiplatform, we also get prebuilt media playback libraries like Android’s androidx.Media3 , iOS’s AVAudioPlayer and VLC support on Desktop through vlcj . So we don’t have to hunt for libraries or make our own to handle playback, everything is just there and we only need to integrate well.
Compose Multiplatform is also highly composable (see what we did there?). Creating custom components (it’s called ✨Composables✨ in CMP) is just a function basically unlike Flutter.
The state management (androidx.viewmodel), navigation (androidx.navigation3), prebuilt material components are all great and really easy to use even for a beginner. We learned the most of the basic stuff in like 2 weeks so it’s pretty easy to pick up as well.
Though, we do miss Flutter’s Hot Reload. But Compose Multiplatform’s limited hot restart is not that bad and does the job basically.
On Linux, finally the WebView is better as Compose Multiplatform uses JVM on desktop which has Swing/AWT support. Which allows rendering WebView or any platform view (basically another app’s window) inside the Compose Multiplatform app’s window. We used ComposeNativeWebview which uses wry from tauri so basically we’re using System WebView even on desktop now! It’s pretty much same as before but, on Linux it doesn’t have to be a separate window now. And it also works better on Flatpak.
Gradlezilla #

Probably the biggest drawback to Compose Multiplatform is setting up Gradle. It’s a mess in the Kotlin/Java/Android ecosystem. Only C/C++’s CMake could make a bigger mess but ig both compete on the same level. But once we figured how it worked and the APIs of gradle, it was pretty straight forward for us to handle most of the issue. Though it took us a year to learn the quirks of gradle. Now we can finally create Compose Multiplatform libraries, YAY!
A few final words #
The journey of Spotube was long and full of surprises. And Spotube is a dynamic project. It adapts to it’s needs and will continue to do so. But this time we hope this rewrite will solve the major problems and flaws the old Spotube had. We used Flutter for almost 4 years. It hurts to say good bye to this great framework that helped shaping the app but we need to do this so our weak af minds can continue to maintain Spotube for coming years.
We have to say both Thanks and Sorry to our community and users of Spotube for supporting this project for so long!! Thanks for your unrequited loved and support which powers this project and fuels us!
And we apologize for the ads we show in this website😥. The ad revenue helps us to continue our work on Spotube and maintenance of it. Spotube doesn’t have any big sponsors or generating any profit. It relies solely on donations and ad-revenue from this website.
But the app is totally ad-free and will always be! 💖
Ready to experience Spotube?
Download the latest version and explore the new plugin ecosystem, improved playback, and more.