diff --git a/src/traces/carpet/plot.js b/src/traces/carpet/plot.js
index e35963f492d..eb7b957985e 100644
--- a/src/traces/carpet/plot.js
+++ b/src/traces/carpet/plot.js
@@ -15,6 +15,8 @@ var map1dArray = require('./map_1d_array');
 var makepath = require('./makepath');
 var orientText = require('./orient_text');
 var svgTextUtils = require('../../lib/svg_text_utils');
+var Lib = require('../../lib');
+var alignmentConstants = require('../../constants/alignment');
 
 module.exports = function plot(gd, plotinfo, cdcarpet) {
     for(var i = 0; i < cdcarpet.length; i++) {
@@ -58,10 +60,10 @@ function plotOne(gd, plotinfo, cd) {
     drawGridLines(xa, ya, boundaryLayer, aax, 'a-boundary', aax._boundarylines);
     drawGridLines(xa, ya, boundaryLayer, bax, 'b-boundary', bax._boundarylines);
 
-    var maxAExtent = drawAxisLabels(gd, xa, ya, trace, t, labelLayer, aax._labels, 'a-label');
-    var maxBExtent = drawAxisLabels(gd, xa, ya, trace, t, labelLayer, bax._labels, 'b-label');
+    var labelOrientationA = drawAxisLabels(gd, xa, ya, trace, t, labelLayer, aax._labels, 'a-label');
+    var labelOrientationB = drawAxisLabels(gd, xa, ya, trace, t, labelLayer, bax._labels, 'b-label');
 
-    drawAxisTitles(gd, labelLayer, trace, t, xa, ya, maxAExtent, maxBExtent);
+    drawAxisTitles(gd, labelLayer, trace, t, xa, ya, labelOrientationA, labelOrientationB);
 
     drawClipPath(trace, t, clipLayer, xa, ya);
 }
@@ -131,8 +133,9 @@ function drawAxisLabels(gd, xaxis, yaxis, trace, t, layer, labels, labelClass) {
         .classed(labelClass, true);
 
     var maxExtent = 0;
+    var labelOrientation = {};
 
-    labelJoin.each(function(label) {
+    labelJoin.each(function(label, i) {
         // Most of the positioning is done in calc_labels. Only the parts that depend upon
         // the screen space representation of the x and y axes are here:
         var orientation;
@@ -142,6 +145,11 @@ function drawAxisLabels(gd, xaxis, yaxis, trace, t, layer, labels, labelClass) {
             var angle = (label.axis.tickangle + 180.0) * Math.PI / 180.0;
             orientation = orientText(trace, xaxis, yaxis, label.xy, [Math.cos(angle), Math.sin(angle)]);
         }
+
+        if(!i) {
+            // TODO: offsetMultiplier? Not currently used anywhere...
+            labelOrientation = {angle: orientation.angle, flip: orientation.flip};
+        }
         var direction = (label.endAnchor ? -1 : 1) * orientation.flip;
 
         var labelEl = d3.select(this)
@@ -169,29 +177,40 @@ function drawAxisLabels(gd, xaxis, yaxis, trace, t, layer, labels, labelClass) {
 
     labelJoin.exit().remove();
 
-    return maxExtent;
+    labelOrientation.maxExtent = maxExtent;
+    return labelOrientation;
 }
 
-function drawAxisTitles(gd, layer, trace, t, xa, ya, maxAExtent, maxBExtent) {
+function drawAxisTitles(gd, layer, trace, t, xa, ya, labelOrientationA, labelOrientationB) {
     var a, b, xy, dxy;
 
     a = 0.5 * (trace.a[0] + trace.a[trace.a.length - 1]);
     b = trace.b[0];
     xy = trace.ab2xy(a, b, true);
     dxy = trace.dxyda_rough(a, b);
-    drawAxisTitle(gd, layer, trace, t, xy, dxy, trace.aaxis, xa, ya, maxAExtent, 'a-title');
+    if(labelOrientationA.angle === undefined) {
+        Lib.extendFlat(labelOrientationA, orientText(trace, xa, ya, xy, trace.dxydb_rough(a, b)));
+    }
+    drawAxisTitle(gd, layer, trace, t, xy, dxy, trace.aaxis, xa, ya, labelOrientationA, 'a-title');
 
     a = trace.a[0];
     b = 0.5 * (trace.b[0] + trace.b[trace.b.length - 1]);
     xy = trace.ab2xy(a, b, true);
     dxy = trace.dxydb_rough(a, b);
-    drawAxisTitle(gd, layer, trace, t, xy, dxy, trace.baxis, xa, ya, maxBExtent, 'b-title');
+    if(labelOrientationB.angle === undefined) {
+        Lib.extendFlat(labelOrientationB, orientText(trace, xa, ya, xy, trace.dxyda_rough(a, b)));
+    }
+    drawAxisTitle(gd, layer, trace, t, xy, dxy, trace.baxis, xa, ya, labelOrientationB, 'b-title');
 }
 
-function drawAxisTitle(gd, layer, trace, t, xy, dxy, axis, xa, ya, offset, labelClass) {
+var lineSpacing = alignmentConstants.LINE_SPACING;
+var midShift = ((1 - alignmentConstants.MID_SHIFT) / lineSpacing) + 1;
+
+function drawAxisTitle(gd, layer, trace, t, xy, dxy, axis, xa, ya, labelOrientation, labelClass) {
     var data = [];
     if(axis.title) data.push(axis.title);
     var titleJoin = layer.selectAll('text.' + labelClass).data(data);
+    var offset = labelOrientation.maxExtent;
 
     titleJoin.enter().append('text')
         .classed(labelClass, true);
@@ -205,14 +224,23 @@ function drawAxisTitle(gd, layer, trace, t, xy, dxy, axis, xa, ya, offset, label
         }
 
         // In addition to the size of the labels, add on some extra padding:
-        offset += axis.titlefont.size + axis.titleoffset;
+        var titleSize = axis.titlefont.size;
+        offset += titleSize + axis.titleoffset;
 
+        var labelNorm = labelOrientation.angle + (labelOrientation.flip < 0 ? 180 : 0);
+        var angleDiff = (labelNorm - orientation.angle + 450) % 360;
+        var reverseTitle = angleDiff > 90 && angleDiff < 270;
 
         var el = d3.select(this);
 
         el.text(axis.title || '')
-            .call(svgTextUtils.convertToTspans, gd)
-            .attr('transform',
+            .call(svgTextUtils.convertToTspans, gd);
+
+        if(reverseTitle) {
+            offset = (-svgTextUtils.lineCount(el) + midShift) * lineSpacing * titleSize - offset;
+        }
+
+        el.attr('transform',
                 'translate(' + orientation.p[0] + ',' + orientation.p[1] + ') ' +
                 'rotate(' + orientation.angle + ') ' +
                 'translate(0,' + offset + ')'
diff --git a/test/image/baselines/cheaterslope.png b/test/image/baselines/cheaterslope.png
new file mode 100644
index 00000000000..5f71f40f1d8
Binary files /dev/null and b/test/image/baselines/cheaterslope.png differ
diff --git a/test/image/baselines/cheaterslope_noticklabels.png b/test/image/baselines/cheaterslope_noticklabels.png
new file mode 100644
index 00000000000..267ba49038d
Binary files /dev/null and b/test/image/baselines/cheaterslope_noticklabels.png differ
diff --git a/test/image/mocks/cheaterslope.json b/test/image/mocks/cheaterslope.json
new file mode 100644
index 00000000000..aff36f37929
--- /dev/null
+++ b/test/image/mocks/cheaterslope.json
@@ -0,0 +1,231 @@
+{
+  "data": [
+    {
+      "type": "carpet",
+      "carpet": "c1",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title"},
+      "xaxis": "x",
+      "yaxis": "y",
+      "cheaterslope": -10
+    },
+    {
+      "type": "carpet",
+      "carpet": "c2",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title"},
+      "xaxis": "x2",
+      "yaxis": "y",
+      "cheaterslope": -10
+    },
+    {
+      "type": "carpet",
+      "carpet": "c3",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title"},
+      "xaxis": "x",
+      "yaxis": "y2",
+      "cheaterslope": -0.3
+    },
+    {
+      "type": "carpet",
+      "carpet": "c4",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title"},
+      "xaxis": "x2",
+      "yaxis": "y2",
+      "cheaterslope": -0.3
+    },
+    {
+      "type": "carpet",
+      "carpet": "c5",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title"},
+      "xaxis": "x",
+      "yaxis": "y3",
+      "cheaterslope": 0
+    },
+    {
+      "type": "carpet",
+      "carpet": "c6",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title"},
+      "xaxis": "x2",
+      "yaxis": "y3",
+      "cheaterslope": 0
+    },
+    {
+      "type": "carpet",
+      "carpet": "c7",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title"},
+      "xaxis": "x",
+      "yaxis": "y4",
+      "cheaterslope": 0.3
+    },
+    {
+      "type": "carpet",
+      "carpet": "c8",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title"},
+      "xaxis": "x2",
+      "yaxis": "y4",
+      "cheaterslope": 0.3
+    },
+    {
+      "type": "carpet",
+      "carpet": "c9",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title"},
+      "xaxis": "x",
+      "yaxis": "y5",
+      "cheaterslope": 10
+    },
+    {
+      "type": "carpet",
+      "carpet": "c10",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title"},
+      "xaxis": "x2",
+      "yaxis": "y5",
+      "cheaterslope": 10
+    }
+  ],
+  "layout": {
+    "title": "Cheaterslope with titles",
+    "width": 600,
+    "height": 800,
+    "dragmode": "pan",
+    "margin": {
+      "t": 60,
+      "r": 30,
+      "b": 30,
+      "l": 30
+    },
+    "yaxis": {
+      "domain": [0, 0.19],
+      "zeroline": false,
+      "range": [-2, 5]
+    },
+    "yaxis2": {
+      "domain": [0.21, 0.39],
+      "zeroline": false,
+      "range": [-2, 5]
+    },
+    "yaxis3": {
+      "domain": [0.41, 0.59],
+      "zeroline": false,
+      "range": [-2, 5]
+    },
+    "yaxis4": {
+      "domain": [0.61, 0.79],
+      "zeroline": false,
+      "range": [-2, 5]
+    },
+    "yaxis5": {
+      "domain": [0.81, 1],
+      "zeroline": false,
+      "range": [-2, 5]
+    },
+    "xaxis": {
+      "domain": [0, 0.49],
+      "range": [-0.5, 1.2]
+    },
+    "xaxis2": {
+      "domain": [0.51, 1.0],
+      "range": [-0.5, 1.2]
+    },
+    "annotations": [
+      {
+        "x": 0,
+        "y": 4.5,
+        "text": "slope: -10",
+        "align": "left",
+        "xref": "x",
+        "yref": "y",
+        "showarrow": false,
+        "font": {
+          "size": 14
+        }
+      },
+      {
+        "x": 0,
+        "align": "left",
+        "y": 4.5,
+        "xref": "x",
+        "yref": "y2",
+        "text": "slope: -0.3",
+        "showarrow": false,
+        "font": {
+          "size": 14
+        }
+      },
+      {
+        "x": 0,
+        "y": 4.5,
+        "align": "left",
+        "xref": "x",
+        "yref": "y3",
+        "text": "slope: 0",
+        "showarrow": false,
+        "font": {
+          "size": 14
+        }
+      },
+      {
+        "x": 0,
+        "y": 4.5,
+        "align": "left",
+        "xref": "x",
+        "yref": "y4",
+        "text": "slope: 0.3",
+        "showarrow": false,
+        "font": {
+          "size": 14
+        }
+      },
+      {
+        "x": 0,
+        "y": 4.5,
+        "align": "left",
+        "xref": "x",
+        "yref": "y5",
+        "text": "slope: 10",
+        "showarrow": false,
+        "font": {
+          "size": 14
+        }
+      }
+    ]
+  }
+}
diff --git a/test/image/mocks/cheaterslope_noticklabels.json b/test/image/mocks/cheaterslope_noticklabels.json
new file mode 100644
index 00000000000..4311fc4ed29
--- /dev/null
+++ b/test/image/mocks/cheaterslope_noticklabels.json
@@ -0,0 +1,231 @@
+{
+  "data": [
+    {
+      "type": "carpet",
+      "carpet": "c1",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red", "showticklabels": "none"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title", "showticklabels": "none"},
+      "xaxis": "x",
+      "yaxis": "y",
+      "cheaterslope": -10
+    },
+    {
+      "type": "carpet",
+      "carpet": "c2",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red", "showticklabels": "none"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title", "showticklabels": "none"},
+      "xaxis": "x2",
+      "yaxis": "y",
+      "cheaterslope": -10
+    },
+    {
+      "type": "carpet",
+      "carpet": "c3",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red", "showticklabels": "none"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title", "showticklabels": "none"},
+      "xaxis": "x",
+      "yaxis": "y2",
+      "cheaterslope": -0.3
+    },
+    {
+      "type": "carpet",
+      "carpet": "c4",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red", "showticklabels": "none"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title", "showticklabels": "none"},
+      "xaxis": "x2",
+      "yaxis": "y2",
+      "cheaterslope": -0.3
+    },
+    {
+      "type": "carpet",
+      "carpet": "c5",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red", "showticklabels": "none"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title", "showticklabels": "none"},
+      "xaxis": "x",
+      "yaxis": "y3",
+      "cheaterslope": 0
+    },
+    {
+      "type": "carpet",
+      "carpet": "c6",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red", "showticklabels": "none"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title", "showticklabels": "none"},
+      "xaxis": "x2",
+      "yaxis": "y3",
+      "cheaterslope": 0
+    },
+    {
+      "type": "carpet",
+      "carpet": "c7",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red", "showticklabels": "none"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title", "showticklabels": "none"},
+      "xaxis": "x",
+      "yaxis": "y4",
+      "cheaterslope": 0.3
+    },
+    {
+      "type": "carpet",
+      "carpet": "c8",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red", "showticklabels": "none"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title", "showticklabels": "none"},
+      "xaxis": "x2",
+      "yaxis": "y4",
+      "cheaterslope": 0.3
+    },
+    {
+      "type": "carpet",
+      "carpet": "c9",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red", "showticklabels": "none"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title", "showticklabels": "none"},
+      "xaxis": "x",
+      "yaxis": "y5",
+      "cheaterslope": 10
+    },
+    {
+      "type": "carpet",
+      "carpet": "c10",
+      "a": [1, 2, 3],
+      "b": [1, 2, 3],
+      "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+      "aaxis": {"smoothing": 1, "title": "A axis<br>title", "color": "red", "showticklabels": "none"},
+      "baxis": {"smoothing": 1, "title": "B axis<br>title", "showticklabels": "none"},
+      "xaxis": "x2",
+      "yaxis": "y5",
+      "cheaterslope": 10
+    }
+  ],
+  "layout": {
+    "title": "Cheaterslope with titles and no tick labels",
+    "width": 600,
+    "height": 800,
+    "dragmode": "pan",
+    "margin": {
+      "t": 60,
+      "r": 30,
+      "b": 30,
+      "l": 30
+    },
+    "yaxis": {
+      "domain": [0, 0.19],
+      "zeroline": false,
+      "range": [-2, 5]
+    },
+    "yaxis2": {
+      "domain": [0.21, 0.39],
+      "zeroline": false,
+      "range": [-2, 5]
+    },
+    "yaxis3": {
+      "domain": [0.41, 0.59],
+      "zeroline": false,
+      "range": [-2, 5]
+    },
+    "yaxis4": {
+      "domain": [0.61, 0.79],
+      "zeroline": false,
+      "range": [-2, 5]
+    },
+    "yaxis5": {
+      "domain": [0.81, 1],
+      "zeroline": false,
+      "range": [-2, 5]
+    },
+    "xaxis": {
+      "domain": [0, 0.49],
+      "range": [-0.5, 1.2]
+    },
+    "xaxis2": {
+      "domain": [0.51, 1.0],
+      "range": [-0.5, 1.2]
+    },
+    "annotations": [
+      {
+        "x": 0,
+        "y": 4.5,
+        "text": "slope: -10",
+        "align": "left",
+        "xref": "x",
+        "yref": "y",
+        "showarrow": false,
+        "font": {
+          "size": 14
+        }
+      },
+      {
+        "x": 0,
+        "align": "left",
+        "y": 4.5,
+        "xref": "x",
+        "yref": "y2",
+        "text": "slope: -0.3",
+        "showarrow": false,
+        "font": {
+          "size": 14
+        }
+      },
+      {
+        "x": 0,
+        "y": 4.5,
+        "align": "left",
+        "xref": "x",
+        "yref": "y3",
+        "text": "slope: 0",
+        "showarrow": false,
+        "font": {
+          "size": 14
+        }
+      },
+      {
+        "x": 0,
+        "y": 4.5,
+        "align": "left",
+        "xref": "x",
+        "yref": "y4",
+        "text": "slope: 0.3",
+        "showarrow": false,
+        "font": {
+          "size": 14
+        }
+      },
+      {
+        "x": 0,
+        "y": 4.5,
+        "align": "left",
+        "xref": "x",
+        "yref": "y5",
+        "text": "slope: 10",
+        "showarrow": false,
+        "font": {
+          "size": 14
+        }
+      }
+    ]
+  }
+}