diff --git a/dfp.admin.inc b/dfp.admin.inc
index a5f1efa..f9f12da 100644
--- a/dfp.admin.inc
+++ b/dfp.admin.inc
@@ -422,6 +422,186 @@ function _dfp_targeting_remove_empty(&$target) {
 }
 
 /**
+ * Validation function used by an individual breakpoint in the breakpoints form.
+ */
+function dfp_breakpoint_form_validate($element, &$form_state) {
+  if (empty($element['browser_size']['#value']) && !empty($element['ad_sizes']['#value'])) {
+    form_error($element['browser_size'], t('The browser size cannot be empty if ad size(s) exists.'));
+  }
+  elseif (!empty($element['browser_size']['#value']) && empty($element['ad_sizes']['#value'])) {
+    form_error($element['ad_sizes'], t('The ad size(s) cannot be empty if a browser size exists.'));
+  }
+  if (!empty($element['browser_size']['#value']) && !empty($element['ad_sizes']['#value'])) {
+    if (preg_match('/[^x|0-9]/', $element['browser_size']['#value'])) {
+      form_error($element['browser_size'], t('The browser size can only contain numbers and the character x.'));
+    }
+    elseif (preg_match('/[^x|,|0-9]/', $element['ad_sizes']['#value'])) {
+      form_error($element['ad_sizes'], t('The ad size(s) can only contain numbers, the character x and commas.'));
+    }
+  }
+}
+
+/**
+ * Validation function used by the breakpoints form.
+ */
+function dfp_breakpoints_form_validate(&$element, &$form_state) {
+  if ($form_state['triggering_element']['#value'] != t('Add another breakpoint')) {
+    dfp_breakpoints_trim($form_state['values']);
+  }
+}
+
+/**
+ * Helper function that takes a form_state['values'] and removes
+ * empty breakpoints.
+ */
+function dfp_breakpoints_trim(&$values, $parent = 'breakpoints') {
+  foreach ($values as $key => &$val) {
+    if ($key === $parent) {
+      // We found the browser_size values.
+      foreach ($val as $k => $v) {
+        if (empty($val[$k]['browser_size']) && empty($val[$k]['ad_sizes'])) {
+          unset($val[$k]);
+        }
+      }
+      // Reset the array indexes to prevent wierd behavior caused by a
+      // breakpoint being removed in the middle of the array.
+      $val = array_values($val);
+      break;
+    }
+    elseif (is_array($val)) {
+      dfp_breakpoints_trim($val, $parent);
+    }
+  }
+}
+
+/**
+ * Submit handler to add more breakpoints to an ad tag.
+ */
+function dfp_more_breakpoints_submit($form, &$form_state) {
+  $form_state['breakpoints'] = $form_state['input']['breakpoints'];
+  $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * Ajax callback for adding breakpoints to the breakpoint form.
+ */
+function dfp_more_breakpoints_js($form, $form_state) {
+  return $form['breakpoint_settings']['breakpoints'];
+}
+
+/**
+ * Helper form builder for the breakpoints form.
+ */
+function _dfp_breakpoints_form(&$breakpoints_form, $existing_breakpoints = array()) {
+  // Display settings.
+  $breakpoints_form['breakpoints'] = array(
+    '#type' => 'markup',
+    '#tree' => FALSE,
+    '#prefix' => '<div id="dfp-breakpoints-wrapper">',
+    '#suffix' => '</div>',
+    '#theme' => 'dfp_breakpoint_settings',
+    '#element_validate' => array('dfp_breakpoints_form_validate'),
+  );
+
+  // Add existing breakpoints to the form unless they are empty.
+  foreach ($existing_breakpoints as $key => $data) {
+    _dfp_breakpoint_form($breakpoints_form, $key, $data);
+  }
+  // Add one blank set of breakpoint fields.
+  _dfp_breakpoint_form($breakpoints_form, count($existing_breakpoints));
+
+  $breakpoints_form['breakpoints']['dfp_more_breakpoints'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add another breakpoint'),
+    '#submit' => array('dfp_more_breakpoints_submit'),
+    '#limit_validation_errors' => array(),
+    '#ajax' => array(
+      'callback' => 'dfp_more_breakpoints_js',
+      'wrapper' => 'dfp-breakpoints-wrapper',
+      'effect' => 'fade',
+      'file' => 'plugins/export_ui/dfp_ctools_export_ui.inc',
+    ),
+  );
+}
+
+/**
+ * Helper form builder for an individual breakpoint.
+ */
+function _dfp_breakpoint_form(&$form, $key, $data = array()) {
+  $form['breakpoints'][$key] = array(
+    '#prefix' => '<div class="breakpoint" id="breakpoint-' . $key . '">',
+    '#suffix' => '</div>',
+    '#element_validate' => array('dfp_breakpoint_form_validate'),
+  );
+  $form['breakpoints'][$key]['browser_size'] = array(
+    '#type' => 'textfield',
+    '#title_display' => 'invisible',
+    '#title' => t('Minimum Browser Size'),
+    '#size' => 10,
+    '#default_value' => isset($data['browser_size']) ? $data['browser_size'] : '',
+    '#parents' => array('breakpoints', $key, 'browser_size'),
+    '#attributes' => array('class' => array('field-breakpoint-browser-size')),
+  );
+  $form['breakpoints'][$key]['ad_sizes'] = array(
+    '#type' => 'textfield',
+    '#title_display' => 'invisible',
+    '#title' => t('Ad Sizes'),
+    '#size' => 20,
+    '#default_value' => isset($data['ad_sizes']) ? $data['ad_sizes'] : '',
+    '#parents' => array('breakpoints', $key, 'ad_sizes'),
+    '#attributes' => array('class' => array('field-breakpoint-ad-sizes')),
+  );
+  $form['breakpoints'][$key]['delete_breakpoint'] = array(
+    '#type' => 'textfield',
+    '#title_display' => 'invisible',
+    '#title' => t('Ad Sizes'),
+    '#size' => 20,
+    '#default_value' => isset($data['ad_sizes']) ? $data['ad_sizes'] : '',
+    '#parents' => array('breakpoints', $key, 'ad_sizes'),
+    '#attributes' => array('class' => array('field-breakpoint-ad-sizes')),
+  );
+  if (empty($data)) {
+    $form['breakpoints'][$key]['browser_size']['#description'] = t('Example: 1024x768');
+    $form['breakpoints'][$key]['ad_sizes']['#description'] = t('Example: 300x600,300x250');
+  }
+}
+
+/**
+ * Returns the current breakpoints. The default value will be used unless an
+ * "input" exists in the form_state variable, in which case that will be used.
+ */
+function _dfp_breakpoints_get_existing($form_state, $default = array()) {
+  $breakpoints = array();
+  if (isset($form_state['input']['breakpoints'])) {
+    $breakpoints = $form_state['input']['breakpoints'];
+  }
+  elseif (!empty($default)) {
+    $breakpoints = $default;
+  }
+
+  return $breakpoints;
+}
+
+/**
+ * Theme function for the "breakpoint" form.
+ */
+function theme_dfp_breakpoint_settings($variables) {
+  $form = $variables['form'];
+
+  $more_button = drupal_render($form['dfp_more_breakpoints']);
+  unset($form['dfp_more_breakpoints']);
+
+  $headers = array(t('Browser Size'), t('Ad Size(s)'));
+  $rows = array();
+  foreach (element_children($form) as $key) {
+    $rows[] = array(drupal_render($form[$key]['browser_size']), drupal_render($form[$key]['ad_sizes']));
+  }
+  $rows[] = array(array('data' => 'These breakpoints are set to implement DFP responsive mappings. See <a href="https://support.google.com/dfp_premium/answer/3423562?hl=en">this support article</a> for more information.', 'colspan' => 2));
+  return theme('table', array('header' => $headers, 'rows' => $rows)) . $more_button;
+}
+
+
+/**
  * Helper function returns the a value from a givne multidimensional array given
  * a specific key. If the key exists in more than one place, the first occurance
  * is used.
diff --git a/dfp.admin.js b/dfp.admin.js
index 05f1ff8..b49d929 100644
--- a/dfp.admin.js
+++ b/dfp.admin.js
@@ -75,6 +75,11 @@ Drupal.behaviors.dfpVerticalTabs = {
       return summary;
     });
 
+    $('fieldset#edit-breakpoint-settings', context).drupalSetSummary(function (context) {
+      var summary = 'Configure DFP mappings.';
+      return summary;
+    });
+
     $('fieldset#edit-targeting-settings', context).drupalSetSummary(function (context) {
       var summary = '';
       $('.field-target-target', context).each(function (context) {
diff --git a/dfp.module b/dfp.module
index 607b6a3..f88cdc3 100644
--- a/dfp.module
+++ b/dfp.module
@@ -49,6 +49,10 @@ function dfp_theme($existing, $type, $theme, $path) {
       'render element' => 'form',
       'file' => 'dfp.admin.inc',
     ),
+    'dfp_breakpoint_settings' => array(
+      'render element' => 'form',
+      'file' => 'dfp.admin.inc',
+    ),
     'dfp_adsense_color_settings' => array(
       'render element' => 'form',
       'file' => 'dfp.admin.inc',
@@ -723,7 +727,18 @@ function _dfp_js_global_settings() {
  */
 function _dfp_js_slot_definition($tag) {
   // Add the js needed to define this adSlot to <head>.
-  $js = 'googletag.slots["' . $tag->machinename . '"] = googletag.defineSlot("' . $tag->adunit . '", ' . $tag->size . ', "' . $tag->placeholder_id . '")' . "\n";
+  $js = '';
+  // Start by defining breakpoints for this ad.
+  if (!empty($tag->breakpoints)) {
+    $breakpoints = $tag->breakpoints;
+    $js .= 'var mapping = googletag.sizeMapping()' . "\n";
+    foreach ($breakpoints as $breakpoint) {
+      $js .= '  .addSize(' . dfp_format_size($breakpoint['browser_size']) . ', ' . dfp_format_size($breakpoint['ad_sizes']) . ')' . "\n";
+    }
+    $js .= '  .build();' . "\n";
+  }
+  $js .= 'googletag.slots["' . $tag->machinename . '"] = googletag.defineSlot("' . $tag->adunit . '", ' . $tag->size . ', "' . $tag->placeholder_id . '")' . "\n";
+
   $click_url = variable_get('dfp_click_url', '');
   if (!empty($click_url)) {
     if (!preg_match("/^https?:\/\//", $click_url)) {
@@ -751,6 +766,10 @@ function _dfp_js_slot_definition($tag) {
   foreach ($targeting as $target) {
     $js .= '  .setTargeting(' . $target['target'] . ', ' . $target['value'] . ')' . "\n";
   }
+  // Apply size mapping when there are breakpoints.
+  if (!empty($tag->breakpoints)) {
+    $js .= '  .defineSizeMapping(mapping)' . "\n";
+  }
   $js = rtrim($js, "\n") . ';';
 
   $options = array(
diff --git a/plugins/export_ui/dfp_ctools_export_ui.inc b/plugins/export_ui/dfp_ctools_export_ui.inc
index 52b52df..43ed74a 100644
--- a/plugins/export_ui/dfp_ctools_export_ui.inc
+++ b/plugins/export_ui/dfp_ctools_export_ui.inc
@@ -144,6 +144,17 @@ function dfp_tag_form(&$form, &$form_state) {
     '#parents' => array('settings', 'short_tag'),
   );
 
+  // Responsive settings.
+  $form['breakpoint_settings'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Responsive Settings'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+    '#group' => 'settings',
+  );
+  $existing_breakpoints = _dfp_breakpoints_get_existing($form_state, isset($tag->settings['breakpoints']) ? $tag->settings['breakpoints'] : array());
+  _dfp_breakpoints_form($form['breakpoint_settings'], $existing_breakpoints);
+
   // Targeting options.
   $form['targeting_settings'] = array(
     '#type' => 'fieldset',
diff --git a/plugins/export_ui/dfp_tag_ui.class.php b/plugins/export_ui/dfp_tag_ui.class.php
index dfbcc52..e40b9a0 100644
--- a/plugins/export_ui/dfp_tag_ui.class.php
+++ b/plugins/export_ui/dfp_tag_ui.class.php
@@ -17,6 +17,7 @@ class dfp_tag_ui extends ctools_export_ui {
     // Since the targeting form is reusable it isn't already in the settings
     // array so we grab it here.
     $form_state['values']['settings']['targeting'] = $form_state['values']['targeting'];
+    $form_state['values']['settings']['breakpoints'] = $form_state['values']['breakpoints'];
 
     parent::edit_form_submit($form, $form_state);
   }
