Skip to content


Subversion checkout URL

You can clone with
Download ZIP
play ► — your company's dj
Ruby JavaScript CSS CoffeeScript Shell HTML

Merge pull request #387 from nickborromeo/patch-1

Fix broken image like for Android
latest commit 4001175204
@holman holman authored


Play is an employee-powered iTunes-based client-driven distributed music server for your office. Also it can prepare your taxes.


Did you know that listening to music while you work produces better and faster code? It's true. It's in a README.

We listen to music constantly at GitHub. So I wrote Play. Initially it was a modest shell-oriented music server, but we've since grown it to quite the setup:

Play at GitHub

We have employees all over the world, but Play lets us all listen to the same music as if we were all in the office together. This has actually made a big impact on our culture.

The Setup


Play is built for the Mac. We use a Mac Mini.


Play is iTunes-based. That means we can let iTunes do what it's good at and manage the state of our music. We use iTunes DJ to handle the main Play queue, specifically because it emulates what Play does: provide a smart playlist neverending songs.

The Stream and Speakers

We use Nicecast to take the audio stream from iTunes and deliver it to our client apps so everyone can stream from their platform of choice.

We also use iTunes' built-in AirPlay support to stream to multiple speakers across our office network.


The heart of Play is the web app. This is effectively an API to access and control your iTunes library over the web. The app also handles music upload: just drag your files to the browser window, then they'll get uploaded and indexed in iTunes. No complex file sharing and office VPN setups necessary.


Play's realtime notification is powered by Pusher. Pusher allows Play to provide realtime updating to any client that cares. This includes the site as well as Play clients. Clients will be updated in realtime as Play cycles through songs as well as when new songs get queued.


We primarily drive Play through Campfire, the chat service we use internally at GitHub. Most Play interactions happen through the API rather than the web interface, and the API is actually a superset of the functionality available through the web.



Play has a lot of moving parts, but we've tried to simplify installation as much as we could.

First, clone down the repository:

git clone && cd play

Next, you need to run the bootstrap process, which will verify that we can talk to iTunes, that you have all of your settings set up correctly, and will guide you through the configuration setup:


During the bootstrap process you'll be asked to enter your Pusher credentials. This is optional, but it'll let you get realtime updates to your Play queue. It's like the future. Websockets and shit.

Starting It Up

Open up iTunes and start playing music from the iTunes DJ playlist. It's important that you do this first. Yes the DJ playlist is required.

iTunes Match is mostly NOT supported. See more details on this at the bottom of this README.

At this point, you should be ready to play:

rake start

That'll start the server up on localhost:5050.

Hubot Integration

We use Play primarily through Campfire, through Hubot. There's a file that you can drop into your Hubot installation for integration with Play.

API/Client Auth

Each user on Play has a unique auth token. They will give this token to each Play client for it to make requests on their behalf.

In addition to these unique tokens, each Play installation also has its own unique system wide auth token. This can be used to auth and masquerade as any user on the system. When using this system wide token, a login must be provided in the request so Play knows what user the request is masquerading as. This is essentially how Hubot will communicate with Play.

Both of these styles tokens can be included as a header or as a query param.

User Token

When using a user's token, only the token needs to be included. It can be added to the request in the header or as a query param.


"Authorization: 5422fd"

Query Param


System Token

When using a system token, a login needs to be added to the request in addition to the system token added by the means described above. It can be added to the request in the header or as a query param.


"X-PLAY-LOGIN: maddox"

Query Param



Part of the fun with Play is getting it everywhere: in your office, on your desktop, and on your phone. Once you get Play set up correctly, you'll need to install Nicecast.

The following clients exist for Play:









Now Playing on TV

We also have a TV at the office with the currently-playing song. This doesn't require any setup; just point your TV's browser at the main Play instance and the TV interface should show up as long as the screen ratio is 16:9 (ie, 720p or 1080p).

Technical Details


The entire Play + iTunes bridge is handled via AppleScript. Play talks to AppleScript, AppleScript talks to iTunes, and we make it dance.

The Web is the API

The web app is a simple, straightfoward Sinatra app. State that we can't stash in iTunes is persisted through Redis.

The entire web app is just a thin client over the API, which is delivered through JSON. We only really deliver one HTML page: the main root page. All data on that page is populated via JSON requests. This means we can focus on one API and use it for both the web and for every other client.

The frontend is built with SCSS, CoffeeScript, Mustache, Pusher, and jQuery. All assets are compiled and delivered via Sprockets.

Native Clients

Native clients use Pusher to be updated in realtime. They will show what is currently playing, and with some clients, what is queued. All clients are built to consume the Shoutcast stream.

iTunes Match

Like many people have experienced on iOS with apps that use your music library, iTunes Match royally screws this up. iTunes does nothing to differentiate songs that are actually available on disk, and those that would need to be pulled down first by Match in order to play them.

This screws up Play, just as it screws up iOS apps that naively (not their fault) attempt to play something out of the music library that is actually only available via Match.

This can hopefully be addressed in the future. For now, skip Match.


We'd love to see your contributions to Play. If you'd like to hack on Play, you'll likely want to run Play in development mode:


That will reload the code on each page request. You can hit the server on localhost:9393.

You can run the tests with:


Fork the project, make your commits, then send a Pull Request.


Play was written by Zach Holman and shaped by the fine ladies and gentlemen at GitHub. We've also benefited from a lot of hard work from our contributors.

Something went wrong with that request. Please try again.