About me

Muse: Mopidy web client with Snapcast support

4 mins read time ❤️ Views

Mopidy is a versatile music server that can play music from different sources (TuneIn, SomaFM, Soundcloud, Youtube, Spotify, between others). It can be connected with Snapcast to provide a multiroom streaming service. It has a main core API and several extensions that can be added optionally by the user. Between there extension there are frontend web applications that can be connected to the core API and control Mopidy.

The historical web interface of Mopidy is MusicBox, which started being developed in 2013. It has the basic functionalities of Mopidy and it’s compatible with several web browsers but the problem is that it doesn’t include support to control Snapcast sources.

There are more recent web interfaces like Iris, with a gorgeous design and support for Snapcast control, but it collects utilization analytics for each client, which I prefer to avoid.

Then, I decided to develop my own web interface using the state of the art web technologies which are described in this post.

Muse webclient homepage

Web developpemnt technologies

I choose the Svelte framework as the core of the web interface. This framework is very light and I like the syntax because I feel that I’m only writing html, css and js but behind the scenes the code is completely optimized at build time.

I also chose Sapper (short for Svelte app maker) to be the glue between the different Svelte components. In the following section I’m explaining how I used those Sapper and Svelte together.


Svelte framework is relatively new (released in 2016) but it’s becoming very popular because it’s very light and fast. This is mainly due to the fact that it optimizes the code to manipulate the DOM directly using vanilla JavaScript, which is different from traditional frameworks like React or Vue, which need a framework in the browser to manipulate the DOM.

One of the advantage of Svelte is the reactivity system. This system is inspired in the philosophy of Excel cells, that are linked one to the other using formulas. When a value in a parent cell changes, all the linked cells are updated automatically. The same happens with reactive variables in Svelte, the variables are updated depending of a relation dependency graph.

I use reactive variables for saving information about the current track that it’s playing, the songs that are present in the tracklist, the songs that are present in a playlists and the results of a search action.

Another advantage of Svelte, is the fact that animation can be easily adapted to components. I add some drag and drop events to order the tracks playlists as you can see in the following video:

Your browser doesn't support HTML5 video.

The order of the items in the tracklists is synchronized with the values in the backend, so when the next song button is pressed, the song that has been dragged is played.


Svelte has a Read–Eval–Print Loop (REPL) page, where people can share their snippets. I think it’s a great way to test small things and share them with everybody. For instance, here is a REPL page for the algorithm that moves the items in theplaylists.

CSS framework

I used the Bulma CSS framework because of it’s simplicity. I added the SCSS version as a global variable using some components. Once the design is stabilized, I can use local CSS in each component to optimize the application.


Sapper has been developed by the Svelte team, so it follows the same principles and simplicity of the Svelte family. However, this framework is not still as popular as Svelte and also not as mature.

I use Sapper as a routing application for the different pages:

  • The homepage that shows the actual playing tracklist ;
  • The search page to explore tracks ;
  • The playlists page with the possibility of creating, deleting, playing and modifying the every playlist ;
  • A template for every playlist.

Sapper is also in charge of preparing the server side rendering mode, where part of the JavaScript code is executed on the server side, so that the browser can load faster the webpage.

Development environment

Mopidy core API runs on a backend which recently has reached version 3.0. This means that it needs to run on python 3.7, which is not installed by default in all desktop environments. To avoid version compatibility and break internal dependencies I also created a Dockerfile with support of Mopidy 3.0, which helps a lot for local development.

A Makefile is used to inject envrionement variables, like the location of the music and playlist folder. These can be easily tunned depending on the running environment.

Snapcast support

Snapcast is a multiroom client-server audio player, where all clients are time synchronized with the server to play perfectly synced audio. It’s not a standalone player, but an extension that turns your existing audio player into a Sonos-like multi room solution.

I have been using it in three Raspberry Pi, to have the same music in different house environments. I works well with Mopidy and initial configuration is very minimalistic. There is one master with the Mopidy instance and Snapcast server and there is a Snapcast client in every other.

Sanpcast also provides a JSON/RPC control API to communicate with the server using websockets. This protocol provides a continuous link between the client and the server. Messages are send when there are changes or events, which allows the client to have a notification when there are change in the server.

I implemented this communication in Muse, so that one can control the volume of the Snapcast clients. The following image shows the Snapcast control panel and the sound from three devices: raspi, raspimov and raspicam.

Snapcasts controls in Muse

Github Actions CI/CD worflow

There are two main components of Muse web client:

  • The html produced by Sapper
  • The python package that connects the web interface with Mopidy.

The final destination of the python extension is the PyPi repository, where python users can download and install Muse. The publishing action can be made manually using python wrappers like twine. However, there is always the risk of making a human mistake, like publishing the wrong version. This is why I prefer to delegate this action to Github.

Here is a description of the deploying pipeline of the Muse package using Github Actions:

  • At every push to the github repository a build of the html and the python package is made in order to check for errors.
  • At every push to master branch there is a publication of the html to github pages in order to validate the test the actual version in different devices using this url of the github pages.
  • The git tags control the version of the package, so when a tag is pushed, the following actions are run:
    • The package version is upgraded in setup.cfg and package.json
    • The python package is published to PyPi.org
    • A github release is published
    • The zip of the package is added to the release assets


There is still plenty of room for improvement for Muse. I would like to add features like integrating third party services like Discogs or Genius, improving user experience or improving the design. If you think about other nice features to add, you can open an issue on the github page of the project.

I’m also satisfied of having an automatic deploying workflow. I used different github actions developed by others and also contributed to improve one of them.

The code of Muse is available at this github repository and the python package is available at PyPi, you can install it with sudo python -m pip install Mopidy-Muse, use it without restriction.

In relation with 🏷️ mopidy, svelte, sapper, bulma, github-actions:

Algolia search engine in Jekyll static pages

This article shows how to implementent Algolia searching engine in a static website and update the entries using Github Actions.

Writing notes with Vimwiki and Hugo static generator

Vim is a simple and ubiquitous text editor that I use daily. In this article I show how to use Vim to take and publish diary notes using Vimwiki and Hugo.

Search posts functionality in AMP static website make with Jekyll

Good web reference is related with how fast your web page loads. Google works in a web format called AMP, which makes loading pages faster but also imposes some restrictions, like no external javascript. It's not simple to add API calls in AMP so this post shows how to implement a search fonctionality in a static page.

Interactive dashboard using Google Analytics, Github Actions and Dc.js

This article shows how to use google analytics api to build a customized dashboard using javascript library dc.js and deploy them using Github Actions

Subscribe to my newsletter 📰

and share it with your friends: