Shuffle Algorithms for Music

Update, March 7, 2014: Improved shuffling has been rolled out on Spotify. More details at the Spotify Labs post, How to Shuffle Songs.

Lots of users complain that Spotify's shuffle algorithms aren't really random. This can best be explained by a cognitive bias called the clustering illusion.

Users aren't wrong to complain, though. What people want isn't true randomness, but an even distribution with separation of similar tracks.

A random sequence might put 3 Michael Jackson songs in a row, but most users don't want that to happen when shuffle is enabled. We should bear this in mind when designing a shuffle algorithm. A simple notion of similarity based on artist alone should get us pretty far. (We can revisit this when users begin complaining that our primitive shuffle algorithm played four cello sonatas in a row.)

Dithering provides a visual analogy for the problem. Dithering is like a halftone pattern, used to create the illusion of continuous tones in a reduced-color image. Two particular methods of dithering are random diffusion and error diffusion.


With random diffusion (top), pixels appear clustered together. Error diffusion algorithms (bottom) take neighboring pixels into account and avoid clusters. In shuffling music, we seek to avoid the clusters in the same way.

By the way, Apple got this right 9 years ago.

Subpixel Letter-Spacing in Webkit

As of sometime in August, Chrome Canary enables subpixel CSS letter-spacing attributes. It's a beautiful thing.


Avoid Faux-Bold with Web Fonts

Only the best web fonts have a full complement of bold, italic, and bold-italic faces. Typekit provides lots of great fonts with a fleet of variants, but if you're on a budget or enjoy the freedom of Google Fonts (as I do), you'll find a lot of great fonts that don't have true bold variants.

What you end up with is "faux bold," a glyph fattened procedurally by the OS. Here's Poly, with a synthetic bold word:

Screen Shot 2013-05-30 at 12.39.17 AM

It's showing the symptoms of a faux bold: the glyphs are puffed up and letter spacing is widened to accommodate the extra fatness. Yuck. A nice alternative is to find a stand-in that closely matches your ill-equipped font family. The bold word here is provided by Alegreya:

Screen Shot 2013-05-30 at 2.21.57 AM

For me, this is accomplished with a simple CSS rule that overrides font-family for bold elements: b, strong, th { font-family: Alegreya }

Converting to Retina

These are my personal recommendations for making the switch to a high-DPI website.

1. Step one: stop working at 72 DPI

This one is for the designers: get into the practice of doing everything at high-DPI. If you are working in Photoshop, get to know and love shape layers and the path tool, even for the single-pixel details. Any low-res PSD is a PSD you'll have to trash and redo when it gets converted to high-DPI, so don't create any more low-DPI art in the first place.

2. Avoid generating low-res assets when possible

Creating two sets of assets is not always necessary. Distinct low- and high-DPI assets, commonly applied using an '@2x' filename suffix and the min-device-pixel-ratio CSS media query, are only necessary for elements that need precise pixel rendering, such as icons or line art with pixel boundaries that need to be precisely controlled.  (Or when designers see fit to use completely different artwork for low- and high-DPI interface elements.)

It's often sufficient to render a single 2x resolution asset inside an <img> tag with height and width constrained to exactly 1/2 the actual image dimensions, and let the browser handle the scaling on low-DPI devices.  This also works for assets used as background images, if the background-size CSS property is used to constrain the image to the containing element. You'll be relying on the browser to resample the image for low-DPI screens, and this would normally be a deal-breaker in IE9, but results are acceptable when resizing to exactly half original size.

3. Image alternatives?

I don't use vector formats like SVG or web font dingbats because these assets are more difficult to generate and less maintainable than simple PNG or JPEG assets. The tools are not quite there yet. They also yield unpredictable pixel grid alignment at small sizes, which will be ugly on low-DPI displays.

Rendering icons with CSS is a parlor trick not suitable for "real-world" sites, in my opinion.  These hacks don't achieve pixel precision and don't handle browser zoom well.

4. Prioritize your assets

Asset classes should be prioritized for conversion to high-DPI, based on their prevalence in the user interface and their robustness to scaling. From most important to least important:

  1. Your logo
  2. Icons, pictographs, and other symbols
  3. Image-based interface elements: scrollbar caps, buttons, checkboxes, etc.
  4. Small photographic elements
  5. Large photographs

Some assets don't need to be converted to high DPI:

  1. Smooth gradient images
  2. Drop shadow images
  3. Any image that remains virtually unchanged by a 2-pixel Gaussian blur

5. File size is not a concern

Image size over the wire should generally not be a concern.  By decreasing JPEG compression quality, high-DPI images can be saved at the same size on disk as low-DPI images, and won't lose detail.  Check out these informative posts for examples:

Likewise, the compression scheme used in PNG images means file sizes don't scale linearly with number of pixels.

Google Analytics for Backbone Apps

When you build a single-page web app with something like Backbone driving routes and managing browser history, the page load event is only going to happen once at the beginning of the session.

The standard Google Analytics javascript snippet doesn't work so well for these single-page apps. You should trigger an Analytics event when the user changes routes or otherwise causes a meaningful content change in your app.

This can be accomplished with a simple call to _gaq.push(['_trackPageview']) with an optional second argument for the URL path/fragment/route of your current page. To tell Google that a user just searched for Nicki Minaj on my music site, I'd just call _gaq.push(['_trackPageview'], '/search/Nicki-Minaj') for example.

You can get fancy if you are using Backbone, and override the Backbone.history.loadURL function to do this for you:

Chat for the Workplace

If you have a big company with engineers in many time zones, you need to establish some rules for realtime communication. I will not address email or phone/video calls as a solution, because email is too asynchronous (you don't stare at your email waiting for responses) and phone calls are too synchronous (calls are either scheduled or disruptive, and always blocking).  Chat, however, performs well both ways.

To put it simply, all employees must run the same chat client. I suggest Skype or Hipchat. It's easier for an engineer to use Skype than it is for a designer to use IRC. Something is broken if these employees can't chat with each other. No matter the client, two features are essential: persistent group chats and offline messaging. Google Talk fails at persistent group chats, IRC fails at offline messaging.

When a company gets big enough, it's impossible to know everyone's name. Therefore,

Employees must be discoverable:

  • Every team in the company should have a corresponding group chat. The names of these chatrooms should match team names to maintain discoverability.
  • Every person must have profile details filled in so that their display name is simply their first and last name. Nicknames are useless to new employees.

One of the luxuries of having all employees under one roof is the ability to glance across the room to see if someone is at their desk. It makes communication decisions instantaneous. To preserve this, even across continents...

Availability must be predictable:

  • If you're at work, you're online.
  • Every person must enable "Set my status to Away after 10 minutes of inactivity" or a similar option. The 10 minutes is flexible as long as everyone in the company uses the same value. Employees must be able to reliably glance at the chat client to see who is active.

Open channels don't necessarily mean increased interruptions. This scheme is about reducing latency, not increasing the quantity of conversations taking place.


"Click-through" is a term for being able to interact with buttons and controls on a background application without clicking first to activate it.

Click-through is normally disabled in OS X. There are some exceptions where the first click is active, like iTunes player buttons, Safari UI, and Chrome UI (but not active webpage content area). John Gruber (Daring Fireball) has blogged about click-through quite a bit. Apple says:

In general, you want to allow click-through for nondestructive actions that users might want to perform when they’re focused on a task in a different window.

The Command+Click shortcut enables click-through without activating background apps.

So I noticed a little problem in OS X. In the typical situation where click-through is disabled, a double-click actually fires a double-click in the background application. I find this behavior to be inconsistent and annoying because, wait a minute, isn't the first click only supposed to activate the application? It's causing the kind of unintentional behaviors that blocking click-through was supposed to prevent in the first place.

One example is in the case of backgrounded Spotify. Single-click on the Next Track button, and the app will take focus without activating the button. No track advance. But, if you double-click the button - as you would if you expected the first click only to take focus - the button is pressed twice, advancing two tracks instead of one.

I believe there are two problems here; first, the Spotify buttons should be enabled for click-through, as the iTunes playback buttons are, but also, OS X should guard double-clicks from falling through to backgrounded applications that are protected from click-through. In effect, passing a double-click where click-through is disabled prevents a user from sending a quick single-click to the background app.

Music Streaming Snapshot

Monthly Users According to Facebook - February 2012

Service Launch Date Monthly Users % Change
Spotify Oct 2008 15,800,000 +20%
Pandora Jan 2000 9,200,000 +3%
SoundCloud Oct 2008 3,100,000 +24%
Bandcamp Sep 2008 1,200,000 +21%
Grooveshark Jan 2006 1,100,000 0%
Slacker Jun 2007 150,000 0%
MOG Dec 2009 140,000 -30%
Rdio Aug 2010 100,000 +43%
Rhapsody Dec 2001 40,000 -20%
iTunes 20,200,000 likes +6%
Amazon MP3 181,000 likes +1%

Monthly Users According to Facebook - January 2012

Service Launch Date Monthly Users
Spotify Oct 2008 13,200,000
Pandora Jan 2000 8,900,000
SoundCloud Oct 2008 2,500,000
Grooveshark Jan 2006 1,100,000
Bandcamp Sep 2008 990,000
MOG Dec 2009 200,000
Slacker Jun 2007 150,000
Rdio Aug 2010 70,000
Rhapsody Dec 2001 50,000
Google Music Nov 2011 1
iTunes 19,000,000 likes (typically 5x monthly users)
Amazon MP3 180,000 likes