Please correct the errors below.

Using the Soundslice player in an offline app

Here’s technical documentation on how you can embed the Soundslice player in a native desktop or mobile app, with offline capability.

The intended audience for this document is: Soundslice licensing customers who have technical competency to do a non-trivial integration.


The Soundslice player is web-based and is embedded via an <iframe>, as described in this help section. For most purposes, this is fine — but if you want your customers to use the Soundslice player within your native desktop or mobile app without an Internet connection, you’ll need to cache the player on your users’ devices.

Because Soundslice is actively developed, with the player getting daily improvements and bug fixes, we have designed this to be “refreshable,” such that you have an easy way of invalidating the cache to get the latest-and-greatest Soundslice player.

The general approach is:

  • When the user installs or updates your app, your app does an initial download and caching of the Soundslice player, including assets such as fonts and images. This requires an Internet connection.
  • When the user views a particular slice for the first time, your app downloads the slice’s data and caches it. This requires an Internet connection.
  • When the user returns to that slice, your app uses the cached versions of (a) the Soundslice player and (b) the slice’s data. No Internet is required.
  • At strategic moments of your choosing, when Internet is available, your app checks for new versions of the player and/or slice data.

In order to do this, you’ll need to be able to intercept requests between your user and Soundslice — e.g., using a web proxy within your app. Are you familiar with service workers? Basically you’ll be mimicking the behavior of a service worker, within your app.

Implementation steps

The Soundslice player is complicated — it’s several files of JavaScript, images, fonts and JSON data. Because of that, and because of the fact that we actively develop the technology, we want your app to “hard-code” as little as possible.

Here are the steps you should take to implement this in your app:

  1. Download and cache the Soundslice “app shell.” It’s at the following URL, which is safe to hard-code because it will not change:

    (Note that viewing this in a web browser is meaningless and will likely produce error messages.)

  2. Using a “WebView” (or equivalent), embed a Soundslice slice using its normal embed URL. For example:

    Feel free to use URL parameters to customize, e.g., force_top_video=1.

  3. When you embed this slice, you’ll notice that the player makes many HTTP requests — for fonts, images, etc. Change your app to intercept all HTTP requests originating from the Soundslice player.

    Here’s specifically how to respond to the player’s requests:

    • Requests to URLs ending in /embed/ — e.g., the slices themselves — should always return the cached app shell HTML, without hitting the network. (Take care to account for possible query strings, like /embed/?notationless=1.)
    • Requests to URLs ending in /scoredata/ should hit the network if available, then cache the result. If the network is offline, use the cached version.
    • Requests to any other URL should first check the cache, then the network. If a cached version is used, return it; otherwise check the network and populate the cache. These files — CSS, JavaScript, images, fonts — all use versioned URLs in our system, so a file’s URL will change if its content changes.

Once you have this interception system working, you can merely embed Soundslice player URLs as needed, and your application will automatically work offline.

Managing player and data updates

Your app will need to consider two types of data updates: player updates and data updates.

Player updates

Whenever we deploy a new version of our player, the app shell’s contents will change on our server. This makes it very easy for you to determine whether you’re running the latest version of the player: just compare our server’s app shell to your cached version.

To ensure your users have the latest player, we recommend this approach:

  1. Each time your app loads a slice, use the currently cached app shell — but also start a background process in which you load the latest app shell from
  2. Compare the contents of the new app shell with your cached version. If it’s different, cache the new version and optionally encourage the user to reload the page.
  3. Optionally, also do all of this whenever the user updates your app to a new version.

Data updates

Whenever you update a slice’s data — notation, syncpoints, audio or video — the contents of the slice data file will change. This is a JSON file that contains data about the slice, along with references to other JSON files. Its URL always ends with /scoredata/.

As with the app shell, you can easily determine whether your app has the latest version of a slice. Just compare our server’s slice data file to your cached version.

If you followed the above instructions about URLs ending in /scoredata/, then your app will do the right thing — hitting the network first (if available), then caching the result.

Using old player versions

Even if we deploy a new version of our player, cached older versions of the player will still work — assuming the player and its slice data files were cached at the same time.

If you use an old version of our player with a newer slice data file, we cannot guarantee things will work. That’s because we occasionally change our internal data formats.

With this in mind, follow all of the tips above to make sure your users always have the latest player and data.


Finally, because all of this caching business is complicated and error-prone, we highly recommend giving your users a way of manually refreshing. This would download the latest app shell. Optionally, it could also download the latest slice data files for each slice that the user has saved offline.

Get help

We realize this integration requires a high amount of technical skill. Get in touch any time with questions!