Mac Media Keys for the Masses

Download .zip Download .tar.gz View on GitHub


Download the latest release, or find all released binaries here.

Mac OS X 10.7 or greater required.




BeardedSpice is a menubar application for Mac OSX that allows you to control web based media players with the media keys found on Mac keyboards. It is an extensible application that works with Chrome and Safari, and can control any tab with an applicable media player. BeardedSpice currently supports:

If you want another supported app supported, simply open an issue with the tag 'app support'. Or, if you are feeling extra feisty, implement the handler yourself!

BeardedSpice is built with SPMediaKeyTap and works well with other applications listening to media key events.


We use CocoaPods to manage all obj-c/cocoa dependences. Install them locally using:

sudo gem install cocoapods
pod install

Always use BeardedSpice.xcworkspace for development, not BeardedSpice.xcodeproject


Setting an active tab

Tell BeardedSpice to control a tab by either clicking the menubar icon and selecting a tab from the dropdown, or by pressing the 'Set Active Tab' shortcut when a browser window is active. The shortcut defaults to ⌘+F8, and is configurable in the preferences panel. Switching active tabs will pause the currently active tab (if there is one).

In Chrome you must reset your active tab if you move your tab to a new window. With Safari, reset your active tab when changing the order of your active tab or moving it to a new window.

Disabling certain handlers

From the preferences menu, uncheck any types of webpages that you don't want BeardedSpice to have control over. By default, all implemented handlers are enabled.

Writing a Handler

Media controllers are written as strategies. Each strategy defines a collection of Javascript functions to be excecuted on particular webpages.

@interface MediaStrategy : NSObject

-(BOOL) accepts:(id <Tab>) tab;
-(NSString *) displayName;

-(NSString *) toggle;
-(NSString *) previous;
-(NSString *) next;
-(NSString *) pause;


The accepts method takes a Tab object and returns YES if the strategy can control the given tab. displayName must return a unique string describing the controller and will be used as the name shown in the Preferences panel. All other functions return a Javascript function for the particular action. pause is a special case and is used when changing the active tab.

A sample strategy for GrooveShark:

@implementation GrooveSharkStrategy

-(id) init
    self = [super init];
    if (self) {
        predicate = [NSPredicate predicateWithFormat:@"SELF LIKE[c] '**'"];
    return self;

-(BOOL) accepts:(id <Tab>)tab
    return [predicate evaluateWithObject:[tab URL]];

-(NSString *) toggle
    return @"(function(){return window.Grooveshark.togglePlayPause()})()";

-(NSString *) previous
    return @"(function(){return window.Grooveshark.previous()})()";

-(NSString *) next
    return @"(function(){return})()";

-(NSString *) pause
    return @"(function(){return window.Grooveshark.pause()})()";

-(NSString *) displayName
    return @"Grooveshark";


Update the MediaStrategyRegistry to include an instance of your new strategy:

+(NSArray *) getDefaultMediaStrategies
        DefaultMediaStrategies = [NSArray arrayWithObjects:
                                  // ...
                                  [[GoogleMusicStrategy alloc] init],
                                  [[RdioStrategy alloc] init],
                                  // add your new strategy!
                                  [[GrooveSharkStrategy alloc] init],

Finally, update the default preferences plist to include your strategy.