diff --git a/includes/ooyala.pages.inc b/includes/ooyala.pages.inc
index b6a4cde..3f0611d 100644
--- a/includes/ooyala.pages.inc
+++ b/includes/ooyala.pages.inc
@@ -32,6 +32,43 @@ function ooyala_settings_form($form, $form_state) {
     '#description' => t('Enter your Ooyala API Secret.'),
   );
 
+  $form['ooyala_player_version'] = array(
+    '#title' => t('Ooyala player version'),
+    '#type' => 'radios',
+    '#options' => array(
+      'v3' => 'V3 (HTML5 with Flash fallback, more mobile friendly)',
+      'v2' => 'V2 (Flash and iOS only)',
+    ),
+    '#default_value' => variable_get('ooyala_player_version', 'v3'),
+    '#description' => t('Ooyala introduced a new "V3" player in late 2012. The new player has superior event handling and better responsive design support (100% width players). If you have legacy code, you may be required to use the V2 player, but if unsure, use the V3 player. See <a href="http://support.ooyala.com/developers/documentation/reference/player_v3_dev_faq.html">the Ooyala V3 FAQ</a> for more information about the V3 player.'),
+  );
+
+  $form['ooyala_player_id'] = array(
+    '#type' => 'textfield',
+    '#default_value' => variable_get('ooyala_player_id', ''),
+    '#title' => t('Ooyala Default Player ID (V3 only)'),
+    '#max_length' => 40,
+    '#description' => t('Enter a default Ooyala Player ID. This will be used with the V3 player when a player ID is not otherwise specified.') . ' <em>' . t('Note this module is currently limited to one V3 player per site.') . '</em>',
+  );
+
+  $form['ooyala_video_width'] = array(
+    '#title' => t('Player width'),
+    '#type' => 'textfield', 
+    '#description' => t('The width of the video player when viewing content.'),
+    '#default_value' => variable_get('ooyala_video_width', 400),
+    '#size' => 6,
+    '#field_suffix' => ' ' . t('pixels'),
+  );
+
+  $form['ooyala_video_height'] = array(
+    '#title' => t('Player height'),
+    '#type' => 'textfield', 
+    '#description' => t('The height of the video player when viewing content.'),
+    '#default_value' => variable_get('ooyala_video_height', 300),
+    '#size' => 6,
+    '#field_suffix' => ' ' . t('pixels'),
+  );
+
   $form['ooyala_thumbnail_path'] = array(
     '#type' => 'textfield',
     '#default_value' => variable_get('ooyala_thumbnail_path', 'ooyalathumbs'),
@@ -52,21 +89,6 @@ function ooyala_settings_form($form, $form_state) {
     '#field_prefix' => file_create_url(file_build_uri('')),
   );
 
-  $form['ooyala_video_height'] = array(
-    '#type' => 'textfield', 
-    '#description' => t('The height of the video player when viewing content.'),
-    '#title' => t('Full video height'),
-    '#default_value' => variable_get('ooyala_video_height', 300),
-  );
-
-  $form['ooyala_video_width'] = array(
-    '#type' => 'textfield', 
-    '#description' => t('The width of the video player when viewing content.'),
-    '#title' => t('Full video width'),
-    '#default_value' => variable_get('ooyala_video_width', 400),
-  );
-  
-
   // Allow the user to determine what type of node this should be.
   $options = ooyala_type_names();
   $default = variable_get('ooyala_primary_content_type', 'video');
diff --git a/ooyala.module b/ooyala.module
index 995c33f..8b39a23 100755
--- a/ooyala.module
+++ b/ooyala.module
@@ -109,6 +109,7 @@ function ooyala_theme() {
     'ooyala_player' => array(
       'variables' => array(
         'embed_code' => NULL,
+        'container_id' => NULL,
         'params' => NULL,
       ),
     ),
@@ -850,36 +851,76 @@ function ooyala_player($embed_code, $params = array()) {
   }
   $container_ids[] = $container_id;
 
-  // Setup some default paramaters.
-  $params += array(
-    'embedCode' => $embed_code,
-    'height' => variable_get('ooyala_video_height', 300),
-    'playerId' => 'ooyala_player',
-    'width' => variable_get('ooyala_video_width', 400),
-    'playerContainerId' => $container_id,
-  );
+  // Set up some default parameters.
+  $params = array();
+
+  // Height and width are optional with V3 player.
+  if ($height = variable_get('ooyala_video_height', 300)) {
+    $params['height'] = (int) $height;
+  }
+  if ($width = variable_get('ooyala_video_width', 400)) {
+    $params['width'] = (int) $width;
+  }
+
+  // Set remaining parameters based on the version of the player being used.
+  if (variable_get('ooyala_player_version', 'v3') === 'v3') {
+    $params += array(
+      // Channels are not supported fully in the v3 player (Flash-only). Enable
+      // them for the players that support it.
+      'enableChannels' => TRUE,
+      // Add the required v3 player ID.
+      'ooyalaPlayerId' => variable_get('ooyala_player_id', ''),
+    );
+
+    if (isset($params['playerId'])) {
+      unset($params['playerId']);
+    }
+
+    // Provide a callback function to register the message event system for the
+    // v3 player. See ooyala_player.js for additional information about resonding to
+    // Player API events.
+    $params['onCreate'] = 'Drupal.ooyala.onCreate';
+  }
+  // V2 player parameters.
+  else {
+    $params += array(
+      'embedCode' => $embed_code,
+      'playerId' => 'ooyala_player',
+      'playerContainerId' => $container_id,
+    );
 
-  // The callback paramter determines the name of an optional javascript
-  // function that should recieve notification of Player API events. We do not
-  // allow this to be modified to ensure that multiple modules can play together
-  // nicely. See ooyala_player.js for additional information about resonding to
-  // Player API events.
-  $params['callback'] = 'receiveOoyalaEvent';
+    // The callback paramter determines the name of an optional javascript
+    // function that should receive notification of Player API events. We do not
+    // allow this to be modified to ensure that multiple modules can play together
+    // nicely. See ooyala_player.js for additional information about resonding to
+    // Player API events.
+    $params['callback'] = 'receiveOoyalaEvent';
+  }
 
-  return theme('ooyala_player', array('embed_code' => $embed_code, 'params' => $params));
+  return theme('ooyala_player', array('embed_code' => $embed_code, 'container_id' => $container_id, 'params' => $params));
 }
 
 /**
  * Theme function to output an Ooyala video player.
  */
-function theme_ooyala_player($vars) {
-  static $js_added;
+function theme_ooyala_player($variables) {
+  $player_version = variable_get('ooyala_player_version', 'v3');
+  drupal_add_js(drupal_get_path('module', 'ooyala') . '/ooyala_player.js');
+
+  if ($player_version === 'v3') {
+    drupal_add_js('http://player.ooyala.com/v3/' . $variables['params']['ooyalaPlayerId'], 'external');
 
-  if (!isset($js_added)) {
-    drupal_add_js(drupal_get_path('module', 'ooyala') . '/ooyala_player.js');
+    // Encode the list of parameters, and remove quotes from onCreate callbacks.
+    $params = json_encode($variables['params']);
+    $params = preg_replace('/"onCreate":"([0-9a-z._]+)"/i', '"onCreate":$1', $params);
+    $output = '<div id="' . $variables['container_id'] . '"></div><script type="text/javascript">var videoPlayer = OO.Player.create("' . $variables['container_id'] . '",' . '"' . $variables['embed_code'] . '", ' . $params . ');</script>';
   }
+  else {
+    $output = '<div id="' . $variables['container_id'] . '"></div><script type="text/javascript" src="http://player.ooyala.com/player.js?' . htmlspecialchars(drupal_http_build_query($variables['params'])) . '"></script>';
+  }
+
+  return $output;
 
-  return '<div id="' . $vars['params']['playerContainerId'] . '"></div><script type="text/javascript" src="http://player.ooyala.com/player.js?' . htmlspecialchars(drupal_http_build_query($vars['params'])) . '"></script>';
 }
 
 /**
diff --git a/ooyala_markers/ooyala_markers.js b/ooyala_markers/ooyala_markers.js
index 8805108..57ade9f 100644
--- a/ooyala_markers/ooyala_markers.js
+++ b/ooyala_markers/ooyala_markers.js
@@ -9,8 +9,9 @@
         var href = $(this).attr('href');
         var fragment = href.slice(href.indexOf('#') + 1).split(':');
 
+        // TODO: This code is still V2 player based, which stores the player
+        // in a DOM element.
         player = document.getElementById(fragment[0] + '_0_ooyala_player');
-
         if (!player) {
           player = document.getElementById('ooyala_player');
         }
@@ -35,10 +36,14 @@
    *   Time in milliseconds to jump to.
    */
   Drupal.ooyalaMarkersJumpTo = function(player, time) {
+    // Use the Ooyala V3 "videoPlayer" shortcut if a player is not defined.
+    player = (!player && window.videoPlayer) ? window.videoPlayer : player;
+
     // Jump the player to the appropriate time.
     // The time variable is converted to seconds here. Ooyala API claims that
     // it has millisecond accuracy so we denote times in milliseconds, however
     // the setPlayHeadTime method takes the number of seconds as an argument.
+    player.pauseMovie();
     player.setPlayheadTime(time/1000);
     player.playMovie();
   };
@@ -48,7 +53,7 @@
   Drupal.ooyala = Drupal.ooyala || {'listeners': {}};
 
   /**
-   * Register event listener for ooyala API callbacks.
+   * Register V2 event listener for Ooyala API callbacks.
    */
   Drupal.ooyala.listeners.ooyalaMarkers = function(player, eventName, p) {
     switch(eventName) {
@@ -64,4 +69,23 @@
     }
     return;
   };
+
+  /**
+   * Register V3 event listener for Ooyala API callbacks.
+   */
+  Drupal.ooyala.onCreateHandlers.ooyalaMarkers = function(player) {
+    player.mb.subscribe(OO.EVENTS.PLAYBACK_READY, 'ooyalaMarkers', function(eventName) {
+      // If there is a #time in the current href jump to that point in the
+      // video.
+      var href = window.location.href;
+      var fragment = href.slice(href.indexOf('#') + 1).split(':');
+      if (fragment[0] && parseInt(fragment[1])) {
+        // Apparently "PLAYBACK_READY" isn't quite ready yet, give it .5 second.
+        window.setTimeout(function() {
+          Drupal.ooyalaMarkersJumpTo(player, fragment[1]);
+        }, 500);
+      }
+    });
+  };
+
 })(jQuery);
\ No newline at end of file
diff --git a/ooyala_markers/ooyala_markers.module b/ooyala_markers/ooyala_markers.module
index 251d537..97c760c 100644
--- a/ooyala_markers/ooyala_markers.module
+++ b/ooyala_markers/ooyala_markers.module
@@ -245,7 +245,7 @@ function theme_ooyala_markers_formatter_default($vars) {
   $video_field = $vars['video_field'];
 
   if (!isset($ooyala_markers_js_added)) {
-    drupal_add_js(drupal_get_path('module', 'ooyala_markers') . '/ooyala_markers.js', array('scope' => 'footer'));
+    drupal_add_js(drupal_get_path('module', 'ooyala_markers') . '/ooyala_markers.js');
     $ooyala_markers_js_added = TRUE;
   }
 
diff --git a/ooyala_player.js b/ooyala_player.js
index e5cbec5..d6ff06b 100644
--- a/ooyala_player.js
+++ b/ooyala_player.js
@@ -1,14 +1,14 @@
 
 /**
  * @file
- * Ooyala Javascript API implementation.
+ * Ooyala JavaScript API implementation.
  *
- * The Ooyala javascript API allows you to register a callback function that
+ * The Ooyala JavaScript API allows you to register a callback function that
  * Ooyala will use to notify your script(s) of video player events. Since it is
  * only possible to specify a single callback function per player and we want
  * to make sure that any number of modules have access to the API we implement
  * a simple system that allows modules to register a listener which will be
- * notified whenever an event notification is recieved by the callback
+ * notified whenever an event notification is received by the callback
  * function.
  */
 
@@ -16,30 +16,33 @@
  * Intialize the Drupal.ooyala object if it hasn't already been initialized.
  *
  * Modules wishing to use the Drupal.ooyala API should include this line at the
- * top of their javascript files as it is possible for the module's javascript
+ * top of their JavaScript files as it is possible for the module's JavaScript
  * to be included before this file.
  */
-Drupal.ooyala = Drupal.ooyala || {'listeners': {}};
+Drupal.ooyala = Drupal.ooyala || {'listeners': {}, 'onCreateHandlers': {}, 'players': {} };
 
 /**
  * Dispatch Ooyala player callback events to all registered listeners.
  *
- * Listeners are registered in the Drupal.ooyala.listeners object as follows.
+ * This method is DEPRECATED. It only works with the v2 player. For the v3
+ * player API, see Drupal.ooyala.onCreate().
+ *
+ * Listeners are registered in the Drupal.ooyala.listeners object as follows:
  * @code
- *    Drupal.ooyala.listeners = function (player, eventName, p) {
+ *    Drupal.ooyala.listeners.myModule = function(player, eventName, p) {
  *      ...
  *    };
  * @endcode
  *
- * All listeners will recieve the following arguments.
- * - player: The DOM object representing the Ooyala player that is recieving
+ * All listeners will receive the following arguments.
+ * - player: The DOM object representing the Ooyala player that is receiving
  *   the event notification.
  * - eventName: The event name as sent by Ooyala.
  * - p: Extra notification parameters.
  *
- * Read the Ooyala javascript API documentation for a complete list of events
+ * Read the Ooyala JavaScript API documentation for a complete list of events
  * and their possible extra notification parameters.
- * @link http://www.ooyala.com/support/docs/player_api#javascript
+ * @see http://www.ooyala.com/support/docs/player_api#javascript
  */
 Drupal.ooyala.notifyListeners = function(playerId, eventName, p) {
   var player = document.getElementById(playerId);
@@ -48,6 +51,38 @@ Drupal.ooyala.notifyListeners = function(playerId, eventName, p) {
   });
 };
 
+/**
+ * Allow modules to respond to the creation of a new player.
+ *
+ * This callback is used on sites using the v3 player. Typically a module
+ * providing an onCreate handler will register a "message bus" using the Ooyala
+ * API.
+
+ * OnCreate handlers are registered in the Drupal.ooyala.onCreateHandlers object
+ * as follows:
+ * @code
+ *    Drupal.ooyala.onCreateHandlers.myModule = function(player) {
+ *      player.mb.subscribe(OO.EVENTS.PLAYING, 'example',
+ *        function(eventName) {
+ *          alert("Player is now playing!!!");
+ *        }
+ *      );
+ *    };
+ * @endcode
+ *
+ * All listeners will receive the following arguments.
+ * - player: The Ooyala player object. Events may be bound to the player.mb
+ *   (message bus) property.
+ *
+ * @see http://support.ooyala.com/developers/documentation/reference/player_v3_dev_listenevent.html
+ */
+Drupal.ooyala.onCreate = function(player) {
+  Drupal.ooyala.players[player.elementId] = player;
+  jQuery.each(Drupal.ooyala.onCreateHandlers, function() {
+    this(player);
+  });
+};
+
 (function ($) {
   Drupal.ooyala.listeners.ooyala = function(player, eventName, p) {
     var playerId = $(player).attr('id');
@@ -68,9 +103,13 @@ Drupal.ooyala.notifyListeners = function(playerId, eventName, p) {
 })(jQuery);
 
 /**
- * Callback function for Ooyala Javascript API. Recieves events for the player
+ * Callback function for Ooyala JavaScript API. Receives events for the player
  * and then dispatches them so that other modules can interact with the player.
  *
+ * Use of this callback function is DEPRECATED. It only works with the Ooyala
+ * V2 player. If your site is using the V3 player, see
+ * Drupal.ooyala.onCreate().
+ *
  * @see Drupal.ooyala.notifyListeners()
  */
 function receiveOoyalaEvent(playerId, eventName, p) {
diff --git a/ooyala_sharedplayer.js b/ooyala_sharedplayer.js
index 88ea6ce..02fe1ea 100644
--- a/ooyala_sharedplayer.js
+++ b/ooyala_sharedplayer.js
@@ -45,6 +45,8 @@
   /**
    * Automatically play the video when it has been selected from the list of
    * videos.
+   *
+   * @todo: This code is not compatible with the V3 player.
    */
   Drupal.ooyala.listeners.shared_player = function(player, eventName, p) {
     switch(eventName) {
