diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js
index ca3ac9bca02..a52bc3537fd 100644
--- a/src/components/drawing/index.js
+++ b/src/components/drawing/index.js
@@ -221,6 +221,7 @@ var SYMBOLDEFS = require('./symbol_defs');
 
 drawing.symbolNames = [];
 drawing.symbolFuncs = [];
+drawing.symbolBackOffs = [];
 drawing.symbolNeedLines = {};
 drawing.symbolNoDot = {};
 drawing.symbolNoFill = {};
@@ -240,6 +241,7 @@ Object.keys(SYMBOLDEFS).forEach(function(k) {
     );
     drawing.symbolNames[n] = k;
     drawing.symbolFuncs[n] = symDef.f;
+    drawing.symbolBackOffs[n] = symDef.backoff || 0;
 
     if(symDef.needLine) {
         drawing.symbolNeedLines[n] = true;
@@ -287,9 +289,9 @@ drawing.symbolNumber = function(v) {
         0 : Math.floor(Math.max(v, 0));
 };
 
-function makePointPath(symbolNumber, r) {
+function makePointPath(symbolNumber, r, t, s) {
     var base = symbolNumber % 100;
-    return drawing.symbolFuncs[base](r) + (symbolNumber >= 200 ? DOTPATH : '');
+    return drawing.symbolFuncs[base](r, t, s) + (symbolNumber >= 200 ? DOTPATH : '');
 }
 
 var HORZGRADIENT = {x1: 1, x2: 0, y1: 0, y2: 0};
@@ -649,7 +651,10 @@ drawing.singlePointStyle = function(d, sel, trace, fns, gd) {
         // because that impacts how to handle colors
         d.om = x % 200 >= 100;
 
-        sel.attr('d', makePointPath(x, r));
+        var angle = getMarkerAngle(d, trace);
+        var standoff = getMarkerStandoff(d, trace);
+
+        sel.attr('d', makePointPath(x, r, angle, standoff));
     }
 
     var perPointGradient = false;
@@ -898,7 +903,7 @@ drawing.selectedPointStyle = function(s, trace) {
             var mx = d.mx || marker.symbol || 0;
             var mrc2 = fns.selectedSizeFn(d);
 
-            pt.attr('d', makePointPath(drawing.symbolNumber(mx), mrc2));
+            pt.attr('d', makePointPath(drawing.symbolNumber(mx), mrc2, getMarkerAngle(d, trace), getMarkerStandoff(d, trace)));
 
             // save for Drawing.selectedTextStyle
             d.mrc2 = mrc2;
@@ -1069,6 +1074,26 @@ drawing.smoothclosed = function(pts, smoothness) {
     return path;
 };
 
+var lastDrawnX, lastDrawnY;
+
+function roundEnd(pt, isY, isLastPoint) {
+    if(isLastPoint) pt = applyBackoff(pt);
+
+    return isY ? roundY(pt[1]) : roundX(pt[0]);
+}
+
+function roundX(p) {
+    var v = d3.round(p, 2);
+    lastDrawnX = v;
+    return v;
+}
+
+function roundY(p) {
+    var v = d3.round(p, 2);
+    lastDrawnY = v;
+    return v;
+}
+
 function makeTangent(prevpt, thispt, nextpt, smoothness) {
     var d1x = prevpt[0] - thispt[0];
     var d1y = prevpt[1] - thispt[1];
@@ -1082,11 +1107,11 @@ function makeTangent(prevpt, thispt, nextpt, smoothness) {
     var denom2 = 3 * d1a * (d1a + d2a);
     return [
         [
-            d3.round(thispt[0] + (denom1 && numx / denom1), 2),
-            d3.round(thispt[1] + (denom1 && numy / denom1), 2)
+            roundX(thispt[0] + (denom1 && numx / denom1)),
+            roundY(thispt[1] + (denom1 && numy / denom1))
         ], [
-            d3.round(thispt[0] - (denom2 && numx / denom2), 2),
-            d3.round(thispt[1] - (denom2 && numy / denom2), 2)
+            roundX(thispt[0] - (denom2 && numx / denom2)),
+            roundY(thispt[1] - (denom2 && numy / denom2))
         ]
     ];
 }
@@ -1094,35 +1119,99 @@ function makeTangent(prevpt, thispt, nextpt, smoothness) {
 // step paths - returns a generator function for paths
 // with the given step shape
 var STEPPATH = {
-    hv: function(p0, p1) {
-        return 'H' + d3.round(p1[0], 2) + 'V' + d3.round(p1[1], 2);
+    hv: function(p0, p1, isLastPoint) {
+        return 'H' +
+            roundX(p1[0]) + 'V' +
+            roundEnd(p1, 1, isLastPoint);
     },
-    vh: function(p0, p1) {
-        return 'V' + d3.round(p1[1], 2) + 'H' + d3.round(p1[0], 2);
+    vh: function(p0, p1, isLastPoint) {
+        return 'V' +
+            roundY(p1[1]) + 'H' +
+            roundEnd(p1, 0, isLastPoint);
     },
-    hvh: function(p0, p1) {
-        return 'H' + d3.round((p0[0] + p1[0]) / 2, 2) + 'V' +
-            d3.round(p1[1], 2) + 'H' + d3.round(p1[0], 2);
+    hvh: function(p0, p1, isLastPoint) {
+        return 'H' +
+            roundX((p0[0] + p1[0]) / 2) + 'V' +
+            roundY(p1[1]) + 'H' +
+            roundEnd(p1, 0, isLastPoint);
     },
-    vhv: function(p0, p1) {
-        return 'V' + d3.round((p0[1] + p1[1]) / 2, 2) + 'H' +
-            d3.round(p1[0], 2) + 'V' + d3.round(p1[1], 2);
+    vhv: function(p0, p1, isLastPoint) {
+        return 'V' +
+            roundY((p0[1] + p1[1]) / 2) + 'H' +
+            roundX(p1[0]) + 'V' +
+            roundEnd(p1, 1, isLastPoint);
     }
 };
-var STEPLINEAR = function(p0, p1) {
-    return 'L' + d3.round(p1[0], 2) + ',' + d3.round(p1[1], 2);
+var STEPLINEAR = function(p0, p1, isLastPoint) {
+    return 'L' +
+        roundEnd(p1, 0, isLastPoint) + ',' +
+        roundEnd(p1, 1, isLastPoint);
 };
 drawing.steps = function(shape) {
     var onestep = STEPPATH[shape] || STEPLINEAR;
     return function(pts) {
-        var path = 'M' + d3.round(pts[0][0], 2) + ',' + d3.round(pts[0][1], 2);
-        for(var i = 1; i < pts.length; i++) {
-            path += onestep(pts[i - 1], pts[i]);
+        var path = 'M' + roundX(pts[0][0]) + ',' + roundY(pts[0][1]);
+        var len = pts.length;
+        for(var i = 1; i < len; i++) {
+            path += onestep(pts[i - 1], pts[i], i === len - 1);
         }
         return path;
     };
 };
 
+function applyBackoff(pt, start) {
+    var backoff = pt.backoff;
+    var trace = pt.trace;
+    var d = pt.d;
+    var i = pt.i;
+
+    if(backoff && trace &&
+        trace.marker &&
+        trace.marker.angle % 360 === 0 &&
+        trace.line &&
+        trace.line.shape !== 'spline'
+    ) {
+        var arrayBackoff = Lib.isArrayOrTypedArray(backoff);
+        var end = pt;
+
+        var x1 = start ? start[0] : lastDrawnX || 0;
+        var y1 = start ? start[1] : lastDrawnY || 0;
+
+        var x2 = end[0];
+        var y2 = end[1];
+
+        var dx = x2 - x1;
+        var dy = y2 - y1;
+
+        var t = Math.atan2(dy, dx);
+
+        var b = arrayBackoff ? backoff[i] : backoff;
+
+        if(b === 'auto') {
+            var endI = end.i;
+            if(trace.type === 'scatter') endI--; // Why we need this hack?
+
+            var endMarker = end.marker;
+            b = endMarker ? drawing.symbolBackOffs[drawing.symbolNumber(endMarker.symbol)] * endMarker.size : 0;
+            b += drawing.getMarkerStandoff(d[endI], trace) || 0;
+        }
+
+        var x = x2 - b * Math.cos(t);
+        var y = y2 - b * Math.sin(t);
+
+        if(
+            ((x <= x2 && x >= x1) || (x >= x2 && x <= x1)) &&
+            ((y <= y2 && y >= y1) || (y >= y2 && y <= y1))
+        ) {
+            pt = [x, y];
+        }
+    }
+
+    return pt;
+}
+
+drawing.applyBackoff = applyBackoff;
+
 // off-screen svg render testing element, shared by the whole page
 // uses the id 'js-plotly-tester' and stores it in drawing.tester
 drawing.makeTester = function() {
@@ -1447,3 +1536,168 @@ drawing.setTextPointsScale = function(selection, xScale, yScale) {
         el.attr('transform', transforms.join(''));
     });
 };
+
+function getMarkerStandoff(d, trace) {
+    var standoff;
+
+    if(d) standoff = d.mf;
+
+    if(standoff === undefined) {
+        standoff = trace.marker ? trace.marker.standoff || 0 : 0;
+    }
+
+    if(!trace._geo && !trace._xA) {
+        // case of legends
+        return -standoff;
+    }
+
+    return standoff;
+}
+
+drawing.getMarkerStandoff = getMarkerStandoff;
+
+var atan2 = Math.atan2;
+var cos = Math.cos;
+var sin = Math.sin;
+
+function rotate(t, xy) {
+    var x = xy[0];
+    var y = xy[1];
+    return [
+        x * cos(t) - y * sin(t),
+        x * sin(t) + y * cos(t)
+    ];
+}
+
+var previousLon;
+var previousLat;
+var previousX;
+var previousY;
+var previousI;
+var previousTraceUid;
+
+function getMarkerAngle(d, trace) {
+    var angle = d.ma;
+
+    if(angle === undefined) {
+        angle = trace.marker.angle || 0;
+    }
+
+    var x, y;
+    var ref = trace.marker.angleref;
+    if(ref === 'previous' || ref === 'north') {
+        if(trace._geo) {
+            var p = trace._geo.project(d.lonlat);
+            x = p[0];
+            y = p[1];
+        } else {
+            var xa = trace._xA;
+            var ya = trace._yA;
+            if(xa && ya) {
+                x = xa.c2p(d.x);
+                y = ya.c2p(d.y);
+            } else {
+                // case of legends
+                return 90;
+            }
+        }
+
+        if(trace._geo) {
+            var lon = d.lonlat[0];
+            var lat = d.lonlat[1];
+
+            var north = trace._geo.project([
+                lon,
+                lat + 1e-5 // epsilon
+            ]);
+
+            var east = trace._geo.project([
+                lon + 1e-5, // epsilon
+                lat
+            ]);
+
+            var u = atan2(
+                east[1] - y,
+                east[0] - x
+            );
+
+            var v = atan2(
+                north[1] - y,
+                north[0] - x
+            );
+
+            var t;
+            if(ref === 'north') {
+                t = angle / 180 * Math.PI;
+                // To use counter-clockwise angles i.e.
+                // East: 90, West: -90
+                // to facilitate wind visualisations
+                // in future we should use t = -t here.
+            } else if(ref === 'previous') {
+                var lon1 = lon / 180 * Math.PI;
+                var lat1 = lat / 180 * Math.PI;
+                var lon2 = previousLon / 180 * Math.PI;
+                var lat2 = previousLat / 180 * Math.PI;
+
+                var dLon = lon2 - lon1;
+
+                var deltaY = cos(lat2) * sin(dLon);
+                var deltaX = sin(lat2) * cos(lat1) - cos(lat2) * sin(lat1) * cos(dLon);
+
+                t = -atan2(
+                    deltaY,
+                    deltaX
+                ) - Math.PI;
+
+                previousLon = lon;
+                previousLat = lat;
+            }
+
+            var A = rotate(u, [cos(t), 0]);
+            var B = rotate(v, [sin(t), 0]);
+
+            angle = atan2(
+                A[1] + B[1],
+                A[0] + B[0]
+            ) / Math.PI * 180;
+
+            if(ref === 'previous' && !(
+                previousTraceUid === trace.uid &&
+                d.i === previousI + 1
+            )) {
+                angle = null;
+            }
+        }
+
+        if(ref === 'previous' && !trace._geo) {
+            if(
+                previousTraceUid === trace.uid &&
+                d.i === previousI + 1 &&
+                isNumeric(x) &&
+                isNumeric(y)
+            ) {
+                var dX = x - previousX;
+                var dY = y - previousY;
+
+                var shape = trace.line ? trace.line.shape || '' : '';
+
+                var lastShapeChar = shape.slice(shape.length - 1);
+                if(lastShapeChar === 'h') dY = 0;
+                if(lastShapeChar === 'v') dX = 0;
+
+                angle += atan2(dY, dX) / Math.PI * 180 + 90;
+            } else {
+                angle = null;
+            }
+        }
+    }
+
+    previousX = x;
+    previousY = y;
+    previousI = d.i;
+    previousTraceUid = trace.uid;
+
+    return angle;
+}
+
+drawing.getMarkerAngle = getMarkerAngle;
diff --git a/src/components/drawing/symbol_defs.js b/src/components/drawing/symbol_defs.js
index 77b02946170..ce99d7c430d 100644
--- a/src/components/drawing/symbol_defs.js
+++ b/src/components/drawing/symbol_defs.js
@@ -1,6 +1,7 @@
 'use strict';
 
-var d3 = require('@plotly/d3');
+var parseSvgPath = require('parse-svg-path');
+var round = require('@plotly/d3').round;
 
 /** Marker symbol definitions
  * users can specify markers either by number or name
@@ -10,340 +11,416 @@ var d3 = require('@plotly/d3');
  * add both and you get both
  */
 
+
+var emptyPath = 'M0,0Z';
+var sqrt2 = Math.sqrt(2);
+var sqrt3 = Math.sqrt(3);
+var PI = Math.PI;
+var cos = Math.cos;
+var sin = Math.sin;
+
 module.exports = {
     circle: {
         n: 0,
-        f: function(r) {
-            var rs = d3.round(r, 2);
-            return 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs +
-                'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rs = round(r, 2);
+            var circle = 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs + 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z';
+            return standoff ? align(angle, standoff, circle) : circle;
         }
     },
     square: {
         n: 1,
-        f: function(r) {
-            var rs = d3.round(r, 2);
-            return 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rs = round(r, 2);
+            return align(angle, standoff, 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z');
         }
     },
     diamond: {
         n: 2,
-        f: function(r) {
-            var rd = d3.round(r * 1.3, 2);
-            return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rd = round(r * 1.3, 2);
+            return align(angle, standoff, 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z');
         }
     },
     cross: {
         n: 3,
-        f: function(r) {
-            var rc = d3.round(r * 0.4, 2);
-            var rc2 = d3.round(r * 1.2, 2);
-            return 'M' + rc2 + ',' + rc + 'H' + rc + 'V' + rc2 + 'H-' + rc +
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rc = round(r * 0.4, 2);
+            var rc2 = round(r * 1.2, 2);
+            return align(angle, standoff, 'M' + rc2 + ',' + rc + 'H' + rc + 'V' + rc2 + 'H-' + rc +
                 'V' + rc + 'H-' + rc2 + 'V-' + rc + 'H-' + rc + 'V-' + rc2 +
-                'H' + rc + 'V-' + rc + 'H' + rc2 + 'Z';
+                'H' + rc + 'V-' + rc + 'H' + rc2 + 'Z');
         }
     },
     x: {
         n: 4,
-        f: function(r) {
-            var rx = d3.round(r * 0.8 / Math.sqrt(2), 2);
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rx = round(r * 0.8 / sqrt2, 2);
             var ne = 'l' + rx + ',' + rx;
             var se = 'l' + rx + ',-' + rx;
             var sw = 'l-' + rx + ',-' + rx;
             var nw = 'l-' + rx + ',' + rx;
-            return 'M0,' + rx + ne + se + sw + se + sw + nw + sw + nw + ne + nw + ne + 'Z';
+            return align(angle, standoff, 'M0,' + rx + ne + se + sw + se + sw + nw + sw + nw + ne + nw + ne + 'Z');
         }
     },
     'triangle-up': {
         n: 5,
-        f: function(r) {
-            var rt = d3.round(r * 2 / Math.sqrt(3), 2);
-            var r2 = d3.round(r / 2, 2);
-            var rs = d3.round(r, 2);
-            return 'M-' + rt + ',' + r2 + 'H' + rt + 'L0,-' + rs + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rt = round(r * 2 / sqrt3, 2);
+            var r2 = round(r / 2, 2);
+            var rs = round(r, 2);
+            return align(angle, standoff, 'M-' + rt + ',' + r2 + 'H' + rt + 'L0,-' + rs + 'Z');
         }
     },
     'triangle-down': {
         n: 6,
-        f: function(r) {
-            var rt = d3.round(r * 2 / Math.sqrt(3), 2);
-            var r2 = d3.round(r / 2, 2);
-            var rs = d3.round(r, 2);
-            return 'M-' + rt + ',-' + r2 + 'H' + rt + 'L0,' + rs + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rt = round(r * 2 / sqrt3, 2);
+            var r2 = round(r / 2, 2);
+            var rs = round(r, 2);
+            return align(angle, standoff, 'M-' + rt + ',-' + r2 + 'H' + rt + 'L0,' + rs + 'Z');
         }
     },
     'triangle-left': {
         n: 7,
-        f: function(r) {
-            var rt = d3.round(r * 2 / Math.sqrt(3), 2);
-            var r2 = d3.round(r / 2, 2);
-            var rs = d3.round(r, 2);
-            return 'M' + r2 + ',-' + rt + 'V' + rt + 'L-' + rs + ',0Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rt = round(r * 2 / sqrt3, 2);
+            var r2 = round(r / 2, 2);
+            var rs = round(r, 2);
+            return align(angle, standoff, 'M' + r2 + ',-' + rt + 'V' + rt + 'L-' + rs + ',0Z');
         }
     },
     'triangle-right': {
         n: 8,
-        f: function(r) {
-            var rt = d3.round(r * 2 / Math.sqrt(3), 2);
-            var r2 = d3.round(r / 2, 2);
-            var rs = d3.round(r, 2);
-            return 'M-' + r2 + ',-' + rt + 'V' + rt + 'L' + rs + ',0Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rt = round(r * 2 / sqrt3, 2);
+            var r2 = round(r / 2, 2);
+            var rs = round(r, 2);
+            return align(angle, standoff, 'M-' + r2 + ',-' + rt + 'V' + rt + 'L' + rs + ',0Z');
         }
     },
     'triangle-ne': {
         n: 9,
-        f: function(r) {
-            var r1 = d3.round(r * 0.6, 2);
-            var r2 = d3.round(r * 1.2, 2);
-            return 'M-' + r2 + ',-' + r1 + 'H' + r1 + 'V' + r2 + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var r1 = round(r * 0.6, 2);
+            var r2 = round(r * 1.2, 2);
+            return align(angle, standoff, 'M-' + r2 + ',-' + r1 + 'H' + r1 + 'V' + r2 + 'Z');
         }
     },
     'triangle-se': {
         n: 10,
-        f: function(r) {
-            var r1 = d3.round(r * 0.6, 2);
-            var r2 = d3.round(r * 1.2, 2);
-            return 'M' + r1 + ',-' + r2 + 'V' + r1 + 'H-' + r2 + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var r1 = round(r * 0.6, 2);
+            var r2 = round(r * 1.2, 2);
+            return align(angle, standoff, 'M' + r1 + ',-' + r2 + 'V' + r1 + 'H-' + r2 + 'Z');
         }
     },
     'triangle-sw': {
         n: 11,
-        f: function(r) {
-            var r1 = d3.round(r * 0.6, 2);
-            var r2 = d3.round(r * 1.2, 2);
-            return 'M' + r2 + ',' + r1 + 'H-' + r1 + 'V-' + r2 + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var r1 = round(r * 0.6, 2);
+            var r2 = round(r * 1.2, 2);
+            return align(angle, standoff, 'M' + r2 + ',' + r1 + 'H-' + r1 + 'V-' + r2 + 'Z');
         }
     },
     'triangle-nw': {
         n: 12,
-        f: function(r) {
-            var r1 = d3.round(r * 0.6, 2);
-            var r2 = d3.round(r * 1.2, 2);
-            return 'M-' + r1 + ',' + r2 + 'V-' + r1 + 'H' + r2 + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var r1 = round(r * 0.6, 2);
+            var r2 = round(r * 1.2, 2);
+            return align(angle, standoff, 'M-' + r1 + ',' + r2 + 'V-' + r1 + 'H' + r2 + 'Z');
         }
     },
     pentagon: {
         n: 13,
-        f: function(r) {
-            var x1 = d3.round(r * 0.951, 2);
-            var x2 = d3.round(r * 0.588, 2);
-            var y0 = d3.round(-r, 2);
-            var y1 = d3.round(r * -0.309, 2);
-            var y2 = d3.round(r * 0.809, 2);
-            return 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2 + 'H-' + x2 +
-                'L-' + x1 + ',' + y1 + 'L0,' + y0 + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var x1 = round(r * 0.951, 2);
+            var x2 = round(r * 0.588, 2);
+            var y0 = round(-r, 2);
+            var y1 = round(r * -0.309, 2);
+            var y2 = round(r * 0.809, 2);
+            return align(angle, standoff, 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2 + 'H-' + x2 +
+                'L-' + x1 + ',' + y1 + 'L0,' + y0 + 'Z');
         }
     },
     hexagon: {
         n: 14,
-        f: function(r) {
-            var y0 = d3.round(r, 2);
-            var y1 = d3.round(r / 2, 2);
-            var x = d3.round(r * Math.sqrt(3) / 2, 2);
-            return 'M' + x + ',-' + y1 + 'V' + y1 + 'L0,' + y0 +
-                'L-' + x + ',' + y1 + 'V-' + y1 + 'L0,-' + y0 + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var y0 = round(r, 2);
+            var y1 = round(r / 2, 2);
+            var x = round(r * sqrt3 / 2, 2);
+            return align(angle, standoff, 'M' + x + ',-' + y1 + 'V' + y1 + 'L0,' + y0 +
+                'L-' + x + ',' + y1 + 'V-' + y1 + 'L0,-' + y0 + 'Z');
         }
     },
     hexagon2: {
         n: 15,
-        f: function(r) {
-            var x0 = d3.round(r, 2);
-            var x1 = d3.round(r / 2, 2);
-            var y = d3.round(r * Math.sqrt(3) / 2, 2);
-            return 'M-' + x1 + ',' + y + 'H' + x1 + 'L' + x0 +
-                ',0L' + x1 + ',-' + y + 'H-' + x1 + 'L-' + x0 + ',0Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var x0 = round(r, 2);
+            var x1 = round(r / 2, 2);
+            var y = round(r * sqrt3 / 2, 2);
+            return align(angle, standoff, 'M-' + x1 + ',' + y + 'H' + x1 + 'L' + x0 +
+                ',0L' + x1 + ',-' + y + 'H-' + x1 + 'L-' + x0 + ',0Z');
         }
     },
     octagon: {
         n: 16,
-        f: function(r) {
-            var a = d3.round(r * 0.924, 2);
-            var b = d3.round(r * 0.383, 2);
-            return 'M-' + b + ',-' + a + 'H' + b + 'L' + a + ',-' + b + 'V' + b +
-                'L' + b + ',' + a + 'H-' + b + 'L-' + a + ',' + b + 'V-' + b + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var a = round(r * 0.924, 2);
+            var b = round(r * 0.383, 2);
+            return align(angle, standoff, 'M-' + b + ',-' + a + 'H' + b + 'L' + a + ',-' + b + 'V' + b +
+                'L' + b + ',' + a + 'H-' + b + 'L-' + a + ',' + b + 'V-' + b + 'Z');
         }
     },
     star: {
         n: 17,
-        f: function(r) {
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
             var rs = r * 1.4;
-            var x1 = d3.round(rs * 0.225, 2);
-            var x2 = d3.round(rs * 0.951, 2);
-            var x3 = d3.round(rs * 0.363, 2);
-            var x4 = d3.round(rs * 0.588, 2);
-            var y0 = d3.round(-rs, 2);
-            var y1 = d3.round(rs * -0.309, 2);
-            var y3 = d3.round(rs * 0.118, 2);
-            var y4 = d3.round(rs * 0.809, 2);
-            var y5 = d3.round(rs * 0.382, 2);
-            return 'M' + x1 + ',' + y1 + 'H' + x2 + 'L' + x3 + ',' + y3 +
+            var x1 = round(rs * 0.225, 2);
+            var x2 = round(rs * 0.951, 2);
+            var x3 = round(rs * 0.363, 2);
+            var x4 = round(rs * 0.588, 2);
+            var y0 = round(-rs, 2);
+            var y1 = round(rs * -0.309, 2);
+            var y3 = round(rs * 0.118, 2);
+            var y4 = round(rs * 0.809, 2);
+            var y5 = round(rs * 0.382, 2);
+            return align(angle, standoff, 'M' + x1 + ',' + y1 + 'H' + x2 + 'L' + x3 + ',' + y3 +
                 'L' + x4 + ',' + y4 + 'L0,' + y5 + 'L-' + x4 + ',' + y4 +
                 'L-' + x3 + ',' + y3 + 'L-' + x2 + ',' + y1 + 'H-' + x1 +
-                'L0,' + y0 + 'Z';
+                'L0,' + y0 + 'Z');
         }
     },
     hexagram: {
         n: 18,
-        f: function(r) {
-            var y = d3.round(r * 0.66, 2);
-            var x1 = d3.round(r * 0.38, 2);
-            var x2 = d3.round(r * 0.76, 2);
-            return 'M-' + x2 + ',0l-' + x1 + ',-' + y + 'h' + x2 +
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var y = round(r * 0.66, 2);
+            var x1 = round(r * 0.38, 2);
+            var x2 = round(r * 0.76, 2);
+            return align(angle, standoff, 'M-' + x2 + ',0l-' + x1 + ',-' + y + 'h' + x2 +
                 'l' + x1 + ',-' + y + 'l' + x1 + ',' + y + 'h' + x2 +
                 'l-' + x1 + ',' + y + 'l' + x1 + ',' + y + 'h-' + x2 +
-                'l-' + x1 + ',' + y + 'l-' + x1 + ',-' + y + 'h-' + x2 + 'Z';
+                'l-' + x1 + ',' + y + 'l-' + x1 + ',-' + y + 'h-' + x2 + 'Z');
         }
     },
     'star-triangle-up': {
         n: 19,
-        f: function(r) {
-            var x = d3.round(r * Math.sqrt(3) * 0.8, 2);
-            var y1 = d3.round(r * 0.8, 2);
-            var y2 = d3.round(r * 1.6, 2);
-            var rc = d3.round(r * 4, 2);
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var x = round(r * sqrt3 * 0.8, 2);
+            var y1 = round(r * 0.8, 2);
+            var y2 = round(r * 1.6, 2);
+            var rc = round(r * 4, 2);
             var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
-            return 'M-' + x + ',' + y1 + aPart + x + ',' + y1 +
-                aPart + '0,-' + y2 + aPart + '-' + x + ',' + y1 + 'Z';
+            return align(angle, standoff, 'M-' + x + ',' + y1 + aPart + x + ',' + y1 +
+                aPart + '0,-' + y2 + aPart + '-' + x + ',' + y1 + 'Z');
         }
     },
     'star-triangle-down': {
         n: 20,
-        f: function(r) {
-            var x = d3.round(r * Math.sqrt(3) * 0.8, 2);
-            var y1 = d3.round(r * 0.8, 2);
-            var y2 = d3.round(r * 1.6, 2);
-            var rc = d3.round(r * 4, 2);
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var x = round(r * sqrt3 * 0.8, 2);
+            var y1 = round(r * 0.8, 2);
+            var y2 = round(r * 1.6, 2);
+            var rc = round(r * 4, 2);
             var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
-            return 'M' + x + ',-' + y1 + aPart + '-' + x + ',-' + y1 +
-                aPart + '0,' + y2 + aPart + x + ',-' + y1 + 'Z';
+            return align(angle, standoff, 'M' + x + ',-' + y1 + aPart + '-' + x + ',-' + y1 +
+                aPart + '0,' + y2 + aPart + x + ',-' + y1 + 'Z');
         }
     },
     'star-square': {
         n: 21,
-        f: function(r) {
-            var rp = d3.round(r * 1.1, 2);
-            var rc = d3.round(r * 2, 2);
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rp = round(r * 1.1, 2);
+            var rc = round(r * 2, 2);
             var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
-            return 'M-' + rp + ',-' + rp + aPart + '-' + rp + ',' + rp +
+            return align(angle, standoff, 'M-' + rp + ',-' + rp + aPart + '-' + rp + ',' + rp +
                 aPart + rp + ',' + rp + aPart + rp + ',-' + rp +
-                aPart + '-' + rp + ',-' + rp + 'Z';
+                aPart + '-' + rp + ',-' + rp + 'Z');
         }
     },
     'star-diamond': {
         n: 22,
-        f: function(r) {
-            var rp = d3.round(r * 1.4, 2);
-            var rc = d3.round(r * 1.9, 2);
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rp = round(r * 1.4, 2);
+            var rc = round(r * 1.9, 2);
             var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
-            return 'M-' + rp + ',0' + aPart + '0,' + rp +
+            return align(angle, standoff, 'M-' + rp + ',0' + aPart + '0,' + rp +
                 aPart + rp + ',0' + aPart + '0,-' + rp +
-                aPart + '-' + rp + ',0' + 'Z';
+                aPart + '-' + rp + ',0' + 'Z');
         }
     },
     'diamond-tall': {
         n: 23,
-        f: function(r) {
-            var x = d3.round(r * 0.7, 2);
-            var y = d3.round(r * 1.4, 2);
-            return 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var x = round(r * 0.7, 2);
+            var y = round(r * 1.4, 2);
+            return align(angle, standoff, 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z');
         }
     },
     'diamond-wide': {
         n: 24,
-        f: function(r) {
-            var x = d3.round(r * 1.4, 2);
-            var y = d3.round(r * 0.7, 2);
-            return 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var x = round(r * 1.4, 2);
+            var y = round(r * 0.7, 2);
+            return align(angle, standoff, 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z');
         }
     },
     hourglass: {
         n: 25,
-        f: function(r) {
-            var rs = d3.round(r, 2);
-            return 'M' + rs + ',' + rs + 'H-' + rs + 'L' + rs + ',-' + rs + 'H-' + rs + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rs = round(r, 2);
+            return align(angle, standoff, 'M' + rs + ',' + rs + 'H-' + rs + 'L' + rs + ',-' + rs + 'H-' + rs + 'Z');
         },
         noDot: true
     },
     bowtie: {
         n: 26,
-        f: function(r) {
-            var rs = d3.round(r, 2);
-            return 'M' + rs + ',' + rs + 'V-' + rs + 'L-' + rs + ',' + rs + 'V-' + rs + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rs = round(r, 2);
+            return align(angle, standoff, 'M' + rs + ',' + rs + 'V-' + rs + 'L-' + rs + ',' + rs + 'V-' + rs + 'Z');
         },
         noDot: true
     },
     'circle-cross': {
         n: 27,
-        f: function(r) {
-            var rs = d3.round(r, 2);
-            return 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs +
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rs = round(r, 2);
+            return align(angle, standoff, 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs +
                 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs +
-                'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z';
+                'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z');
         },
         needLine: true,
         noDot: true
     },
     'circle-x': {
         n: 28,
-        f: function(r) {
-            var rs = d3.round(r, 2);
-            var rc = d3.round(r / Math.sqrt(2), 2);
-            return 'M' + rc + ',' + rc + 'L-' + rc + ',-' + rc +
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rs = round(r, 2);
+            var rc = round(r / sqrt2, 2);
+            return align(angle, standoff, 'M' + rc + ',' + rc + 'L-' + rc + ',-' + rc +
                 'M' + rc + ',-' + rc + 'L-' + rc + ',' + rc +
                 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs +
-                'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z';
+                'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z');
         },
         needLine: true,
         noDot: true
     },
     'square-cross': {
         n: 29,
-        f: function(r) {
-            var rs = d3.round(r, 2);
-            return 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs +
-                'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rs = round(r, 2);
+            return align(angle, standoff, 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs +
+                'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z');
         },
         needLine: true,
         noDot: true
     },
     'square-x': {
         n: 30,
-        f: function(r) {
-            var rs = d3.round(r, 2);
-            return 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs +
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rs = round(r, 2);
+            return align(angle, standoff, 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs +
                 'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs +
-                'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z';
+                'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z');
         },
         needLine: true,
         noDot: true
     },
     'diamond-cross': {
         n: 31,
-        f: function(r) {
-            var rd = d3.round(r * 1.3, 2);
-            return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' +
-                'M0,-' + rd + 'V' + rd + 'M-' + rd + ',0H' + rd;
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rd = round(r * 1.3, 2);
+            return align(angle, standoff, 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' +
+                'M0,-' + rd + 'V' + rd + 'M-' + rd + ',0H' + rd);
         },
         needLine: true,
         noDot: true
     },
     'diamond-x': {
         n: 32,
-        f: function(r) {
-            var rd = d3.round(r * 1.3, 2);
-            var r2 = d3.round(r * 0.65, 2);
-            return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' +
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rd = round(r * 1.3, 2);
+            var r2 = round(r * 0.65, 2);
+            return align(angle, standoff, 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' +
                 'M-' + r2 + ',-' + r2 + 'L' + r2 + ',' + r2 +
-                'M-' + r2 + ',' + r2 + 'L' + r2 + ',-' + r2;
+                'M-' + r2 + ',' + r2 + 'L' + r2 + ',-' + r2);
         },
         needLine: true,
         noDot: true
     },
     'cross-thin': {
         n: 33,
-        f: function(r) {
-            var rc = d3.round(r * 1.4, 2);
-            return 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc;
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rc = round(r * 1.4, 2);
+            return align(angle, standoff, 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc);
         },
         needLine: true,
         noDot: true,
@@ -351,10 +428,12 @@ module.exports = {
     },
     'x-thin': {
         n: 34,
-        f: function(r) {
-            var rx = d3.round(r, 2);
-            return 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx +
-                'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx;
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rx = round(r, 2);
+            return align(angle, standoff, 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx +
+                'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx);
         },
         needLine: true,
         noDot: true,
@@ -362,12 +441,14 @@ module.exports = {
     },
     asterisk: {
         n: 35,
-        f: function(r) {
-            var rc = d3.round(r * 1.2, 2);
-            var rs = d3.round(r * 0.85, 2);
-            return 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc +
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rc = round(r * 1.2, 2);
+            var rs = round(r * 0.85, 2);
+            return align(angle, standoff, 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc +
                 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs +
-                'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs;
+                'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs);
         },
         needLine: true,
         noDot: true,
@@ -375,24 +456,29 @@ module.exports = {
     },
     hash: {
         n: 36,
-        f: function(r) {
-            var r1 = d3.round(r / 2, 2);
-            var r2 = d3.round(r, 2);
-            return 'M' + r1 + ',' + r2 + 'V-' + r2 +
-                'm-' + r2 + ',0V' + r2 +
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var r1 = round(r / 2, 2);
+            var r2 = round(r, 2);
+
+            return align(angle, standoff, 'M' + r1 + ',' + r2 + 'V-' + r2 +
+                'M' + (r1 - r2) + ',-' + r2 + 'V' + r2 +
                 'M' + r2 + ',' + r1 + 'H-' + r2 +
-                'm0,-' + r2 + 'H' + r2;
+                'M-' + r2 + ',' + (r1 - r2) + 'H' + r2);
         },
         needLine: true,
         noFill: true
     },
     'y-up': {
         n: 37,
-        f: function(r) {
-            var x = d3.round(r * 1.2, 2);
-            var y0 = d3.round(r * 1.6, 2);
-            var y1 = d3.round(r * 0.8, 2);
-            return 'M-' + x + ',' + y1 + 'L0,0M' + x + ',' + y1 + 'L0,0M0,-' + y0 + 'L0,0';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var x = round(r * 1.2, 2);
+            var y0 = round(r * 1.6, 2);
+            var y1 = round(r * 0.8, 2);
+            return align(angle, standoff, 'M-' + x + ',' + y1 + 'L0,0M' + x + ',' + y1 + 'L0,0M0,-' + y0 + 'L0,0');
         },
         needLine: true,
         noDot: true,
@@ -400,11 +486,13 @@ module.exports = {
     },
     'y-down': {
         n: 38,
-        f: function(r) {
-            var x = d3.round(r * 1.2, 2);
-            var y0 = d3.round(r * 1.6, 2);
-            var y1 = d3.round(r * 0.8, 2);
-            return 'M-' + x + ',-' + y1 + 'L0,0M' + x + ',-' + y1 + 'L0,0M0,' + y0 + 'L0,0';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var x = round(r * 1.2, 2);
+            var y0 = round(r * 1.6, 2);
+            var y1 = round(r * 0.8, 2);
+            return align(angle, standoff, 'M-' + x + ',-' + y1 + 'L0,0M' + x + ',-' + y1 + 'L0,0M0,' + y0 + 'L0,0');
         },
         needLine: true,
         noDot: true,
@@ -412,11 +500,13 @@ module.exports = {
     },
     'y-left': {
         n: 39,
-        f: function(r) {
-            var y = d3.round(r * 1.2, 2);
-            var x0 = d3.round(r * 1.6, 2);
-            var x1 = d3.round(r * 0.8, 2);
-            return 'M' + x1 + ',' + y + 'L0,0M' + x1 + ',-' + y + 'L0,0M-' + x0 + ',0L0,0';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var y = round(r * 1.2, 2);
+            var x0 = round(r * 1.6, 2);
+            var x1 = round(r * 0.8, 2);
+            return align(angle, standoff, 'M' + x1 + ',' + y + 'L0,0M' + x1 + ',-' + y + 'L0,0M-' + x0 + ',0L0,0');
         },
         needLine: true,
         noDot: true,
@@ -424,11 +514,13 @@ module.exports = {
     },
     'y-right': {
         n: 40,
-        f: function(r) {
-            var y = d3.round(r * 1.2, 2);
-            var x0 = d3.round(r * 1.6, 2);
-            var x1 = d3.round(r * 0.8, 2);
-            return 'M-' + x1 + ',' + y + 'L0,0M-' + x1 + ',-' + y + 'L0,0M' + x0 + ',0L0,0';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var y = round(r * 1.2, 2);
+            var x0 = round(r * 1.6, 2);
+            var x1 = round(r * 0.8, 2);
+            return align(angle, standoff, 'M-' + x1 + ',' + y + 'L0,0M-' + x1 + ',-' + y + 'L0,0M' + x0 + ',0L0,0');
         },
         needLine: true,
         noDot: true,
@@ -436,9 +528,11 @@ module.exports = {
     },
     'line-ew': {
         n: 41,
-        f: function(r) {
-            var rc = d3.round(r * 1.4, 2);
-            return 'M' + rc + ',0H-' + rc;
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rc = round(r * 1.4, 2);
+            return align(angle, standoff, 'M' + rc + ',0H-' + rc);
         },
         needLine: true,
         noDot: true,
@@ -446,9 +540,11 @@ module.exports = {
     },
     'line-ns': {
         n: 42,
-        f: function(r) {
-            var rc = d3.round(r * 1.4, 2);
-            return 'M0,' + rc + 'V-' + rc;
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rc = round(r * 1.4, 2);
+            return align(angle, standoff, 'M0,' + rc + 'V-' + rc);
         },
         needLine: true,
         noDot: true,
@@ -456,9 +552,11 @@ module.exports = {
     },
     'line-ne': {
         n: 43,
-        f: function(r) {
-            var rx = d3.round(r, 2);
-            return 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx;
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rx = round(r, 2);
+            return align(angle, standoff, 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx);
         },
         needLine: true,
         noDot: true,
@@ -466,9 +564,11 @@ module.exports = {
     },
     'line-nw': {
         n: 44,
-        f: function(r) {
-            var rx = d3.round(r, 2);
-            return 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx;
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rx = round(r, 2);
+            return align(angle, standoff, 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx);
         },
         needLine: true,
         noDot: true,
@@ -476,78 +576,235 @@ module.exports = {
     },
     'arrow-up': {
         n: 45,
-        f: function(r) {
-            var rx = d3.round(r, 2);
-            var ry = d3.round(r * 2, 2);
-            return 'M0,0L-' + rx + ',' + ry + 'H' + rx + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rx = round(r, 2);
+            var ry = round(r * 2, 2);
+            return align(angle, standoff, 'M0,0L-' + rx + ',' + ry + 'H' + rx + 'Z');
         },
+        backoff: 1,
         noDot: true
     },
     'arrow-down': {
         n: 46,
-        f: function(r) {
-            var rx = d3.round(r, 2);
-            var ry = d3.round(r * 2, 2);
-            return 'M0,0L-' + rx + ',-' + ry + 'H' + rx + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rx = round(r, 2);
+            var ry = round(r * 2, 2);
+            return align(angle, standoff, 'M0,0L-' + rx + ',-' + ry + 'H' + rx + 'Z');
         },
         noDot: true
     },
     'arrow-left': {
         n: 47,
-        f: function(r) {
-            var rx = d3.round(r * 2, 2);
-            var ry = d3.round(r, 2);
-            return 'M0,0L' + rx + ',-' + ry + 'V' + ry + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rx = round(r * 2, 2);
+            var ry = round(r, 2);
+            return align(angle, standoff, 'M0,0L' + rx + ',-' + ry + 'V' + ry + 'Z');
         },
         noDot: true
     },
     'arrow-right': {
         n: 48,
-        f: function(r) {
-            var rx = d3.round(r * 2, 2);
-            var ry = d3.round(r, 2);
-            return 'M0,0L-' + rx + ',-' + ry + 'V' + ry + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rx = round(r * 2, 2);
+            var ry = round(r, 2);
+            return align(angle, standoff, 'M0,0L-' + rx + ',-' + ry + 'V' + ry + 'Z');
         },
         noDot: true
     },
     'arrow-bar-up': {
         n: 49,
-        f: function(r) {
-            var rx = d3.round(r, 2);
-            var ry = d3.round(r * 2, 2);
-            return 'M-' + rx + ',0H' + rx + 'M0,0L-' + rx + ',' + ry + 'H' + rx + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rx = round(r, 2);
+            var ry = round(r * 2, 2);
+            return align(angle, standoff, 'M-' + rx + ',0H' + rx + 'M0,0L-' + rx + ',' + ry + 'H' + rx + 'Z');
         },
+        backoff: 1,
         needLine: true,
         noDot: true
     },
     'arrow-bar-down': {
         n: 50,
-        f: function(r) {
-            var rx = d3.round(r, 2);
-            var ry = d3.round(r * 2, 2);
-            return 'M-' + rx + ',0H' + rx + 'M0,0L-' + rx + ',-' + ry + 'H' + rx + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rx = round(r, 2);
+            var ry = round(r * 2, 2);
+            return align(angle, standoff, 'M-' + rx + ',0H' + rx + 'M0,0L-' + rx + ',-' + ry + 'H' + rx + 'Z');
         },
         needLine: true,
         noDot: true
     },
     'arrow-bar-left': {
         n: 51,
-        f: function(r) {
-            var rx = d3.round(r * 2, 2);
-            var ry = d3.round(r, 2);
-            return 'M0,-' + ry + 'V' + ry + 'M0,0L' + rx + ',-' + ry + 'V' + ry + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rx = round(r * 2, 2);
+            var ry = round(r, 2);
+            return align(angle, standoff, 'M0,-' + ry + 'V' + ry + 'M0,0L' + rx + ',-' + ry + 'V' + ry + 'Z');
         },
         needLine: true,
         noDot: true
     },
     'arrow-bar-right': {
         n: 52,
-        f: function(r) {
-            var rx = d3.round(r * 2, 2);
-            var ry = d3.round(r, 2);
-            return 'M0,-' + ry + 'V' + ry + 'M0,0L-' + rx + ',-' + ry + 'V' + ry + 'Z';
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var rx = round(r * 2, 2);
+            var ry = round(r, 2);
+            return align(angle, standoff, 'M0,-' + ry + 'V' + ry + 'M0,0L-' + rx + ',-' + ry + 'V' + ry + 'Z');
         },
         needLine: true,
         noDot: true
+    },
+    'arrow': {
+        n: 53,
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var headAngle = PI / 2.5; // 36 degrees - golden ratio
+            var x = 2 * r * cos(headAngle);
+            var y = 2 * r * sin(headAngle);
+
+            return align(angle, standoff,
+                'M0,0' +
+                'L' + -x + ',' + y +
+                'L' + x + ',' + y +
+                'Z'
+            );
+        },
+        backoff: 0.9,
+        noDot: true
+    },
+    'arrow-wide': {
+        n: 54,
+        f: function(r, angle, standoff) {
+            if(skipAngle(angle)) return emptyPath;
+
+            var headAngle = PI / 4; // 90 degrees
+            var x = 2 * r * cos(headAngle);
+            var y = 2 * r * sin(headAngle);
+
+            return align(angle, standoff,
+                'M0,0' +
+                'L' + -x + ',' + y +
+                'A ' + 2 * r + ',' + 2 * r + ' 0 0 1 ' + x + ',' + y +
+                'Z'
+            );
+        },
+        backoff: 0.4,
+        noDot: true
     }
 };
+
+function skipAngle(angle) {
+    return angle === null;
+}
+
+var lastPathIn, lastPathOut;
+var lastAngle, lastStandoff;
+
+function align(angle, standoff, path) {
+    if((!angle || angle % 360 === 0) && !standoff) return path;
+
+    if(
+        lastAngle === angle &&
+        lastStandoff === standoff &&
+        lastPathIn === path
+    ) return lastPathOut;
+
+    lastAngle = angle;
+    lastStandoff = standoff;
+    lastPathIn = path;
+
+    function rotate(t, xy) {
+        var cosT = cos(t);
+        var sinT = sin(t);
+
+        var x = xy[0];
+        var y = xy[1] + (standoff || 0);
+        return [
+            x * cosT - y * sinT,
+            x * sinT + y * cosT
+        ];
+    }
+
+    var t = angle / 180 * PI;
+
+    var x = 0;
+    var y = 0;
+    var cmd = parseSvgPath(path);
+    var str = '';
+
+    for(var i = 0; i < cmd.length; i++) {
+        var cmdI = cmd[i];
+        var op = cmdI[0];
+
+        var x0 = x;
+        var y0 = y;
+
+        if(op === 'M' || op === 'L') {
+            x = +cmdI[1];
+            y = +cmdI[2];
+        } else if(op === 'm' || op === 'l') {
+            x += +cmdI[1];
+            y += +cmdI[2];
+        } else if(op === 'H') {
+            x = +cmdI[1];
+        } else if(op === 'h') {
+            x += +cmdI[1];
+        } else if(op === 'V') {
+            y = +cmdI[1];
+        } else if(op === 'v') {
+            y += +cmdI[1];
+        } else if(op === 'A') {
+            x = +cmdI[1];
+            y = +cmdI[2];
+
+            var E = rotate(t, [+cmdI[6], +cmdI[7]]);
+            cmdI[6] = E[0];
+            cmdI[7] = E[1];
+            cmdI[3] = +cmdI[3] + angle;
+        }
+
+        // change from H, V, h, v to L or l
+        if(op === 'H' || op === 'V') op = 'L';
+        if(op === 'h' || op === 'v') op = 'l';
+
+        if(op === 'm' || op === 'l') {
+            x -= x0;
+            y -= y0;
+        }
+
+        var B = rotate(t, [x, y]);
+
+        if(op === 'H' || op === 'V') op = 'L';
+
+
+        if(
+            op === 'M' || op === 'L' ||
+            op === 'm' || op === 'l'
+        ) {
+            cmdI[1] = B[0];
+            cmdI[2] = B[1];
+        }
+        cmdI[0] = op;
+
+        str += cmdI[0] + cmdI.slice(1).join(',');
+    }
+
+    lastPathOut = str;
+
+    return str;
+}
diff --git a/src/components/selections/select.js b/src/components/selections/select.js
index 9155f5417cd..e2c94201fa7 100644
--- a/src/components/selections/select.js
+++ b/src/components/selections/select.js
@@ -1155,13 +1155,13 @@ function reselect(gd, mayEmitSelected, selectionTesters, searchTraces, dragOptio
         if(_selectionTesters) {
             var _searchTraces = searchTraces;
             if(!hadSearchTraces) {
-                var _xaxis = getFromId(gd, _xRef, 'x');
-                var _yaxis = getFromId(gd, _yRef, 'y');
+                var _xA = getFromId(gd, _xRef, 'x');
+                var _yA = getFromId(gd, _yRef, 'y');
 
                 _searchTraces = determineSearchTraces(
                     gd,
-                    [_xaxis],
-                    [_yaxis],
+                    [_xA],
+                    [_yA],
                     subplot
                 );
 
@@ -1178,8 +1178,8 @@ function reselect(gd, mayEmitSelected, selectionTesters, searchTraces, dragOptio
                         cd0.t.xpx = [];
                         cd0.t.ypx = [];
                         for(var j = 0; j < len; j++) {
-                            cd0.t.xpx[j] = _xaxis.c2p(x[j]);
-                            cd0.t.ypx[j] = _yaxis.c2p(y[j]);
+                            cd0.t.xpx[j] = _xA.c2p(x[j]);
+                            cd0.t.ypx[j] = _yA.c2p(y[j]);
                         }
                     }
 
diff --git a/src/lib/coerce.js b/src/lib/coerce.js
index 731b16bb631..44a56c2323e 100644
--- a/src/lib/coerce.js
+++ b/src/lib/coerce.js
@@ -171,7 +171,7 @@ exports.valObjectMeta = {
             'A number (in degree) between -180 and 180.'
         ].join(' '),
         requiredOpts: [],
-        otherOpts: ['dflt'],
+        otherOpts: ['dflt', 'arrayOk'],
         coerceFunction: function(v, propOut, dflt) {
             if(v === 'auto') propOut.set('auto');
             else if(!isNumeric(v)) propOut.set(dflt);
diff --git a/src/plots/geo/geo.js b/src/plots/geo/geo.js
index 8665e0162f4..3c057de94b2 100644
--- a/src/plots/geo/geo.js
+++ b/src/plots/geo/geo.js
@@ -67,8 +67,13 @@ module.exports = function createGeo(opts) {
     return new Geo(opts);
 };
 
-proto.plot = function(geoCalcData, fullLayout, promises) {
+proto.plot = function(geoCalcData, fullLayout, promises, replot) {
     var _this = this;
+    if(replot) return _this.update(geoCalcData, fullLayout, true);
+
+    _this._geoCalcData = geoCalcData;
+    _this._fullLayout = fullLayout;
+
     var geoLayout = fullLayout[this.id];
     var geoPromises = [];
 
@@ -79,12 +84,24 @@ proto.plot = function(geoCalcData, fullLayout, promises) {
             break;
         }
     }
+
+    var hasMarkerAngles = false;
     for(var i = 0; i < geoCalcData.length; i++) {
-        if(geoCalcData[0][0].trace.locationmode) {
+        var trace = geoCalcData[0][0].trace;
+        trace._geo = _this;
+
+        if(trace.locationmode) {
             needsTopojson = true;
-            break;
+        }
+
+        var marker = trace.marker;
+        if(marker) {
+            var angle = marker.angle;
+            var angleref = marker.angleref;
+            if(angle || angleref === 'north' || angleref === 'previous') hasMarkerAngles = true;
         }
     }
+    this._hasMarkerAngles = hasMarkerAngles;
 
     if(needsTopojson) {
         var topojsonNameNew = topojsonUtils.getTopojsonName(geoLayout);
@@ -137,7 +154,7 @@ proto.fetchTopojson = function() {
     });
 };
 
-proto.update = function(geoCalcData, fullLayout) {
+proto.update = function(geoCalcData, fullLayout, replot) {
     var geoLayout = fullLayout[this.id];
 
     // important: maps with choropleth traces have a different layer order
@@ -155,11 +172,13 @@ proto.update = function(geoCalcData, fullLayout) {
         }
     }
 
-    var hasInvalidBounds = this.updateProjection(geoCalcData, fullLayout);
-    if(hasInvalidBounds) return;
+    if(!replot) {
+        var hasInvalidBounds = this.updateProjection(geoCalcData, fullLayout);
+        if(hasInvalidBounds) return;
 
-    if(!this.viewInitial || this.scope !== geoLayout.scope) {
-        this.saveViewInitial(geoLayout);
+        if(!this.viewInitial || this.scope !== geoLayout.scope) {
+            this.saveViewInitial(geoLayout);
+        }
     }
     this.scope = geoLayout.scope;
 
@@ -177,7 +196,7 @@ proto.update = function(geoCalcData, fullLayout) {
     var choroplethLayer = this.layers.backplot.select('.choroplethlayer');
     this.dataPaths.choropleth = choroplethLayer.selectAll('path');
 
-    this.render();
+    this._render();
 };
 
 proto.updateProjection = function(geoCalcData, fullLayout) {
@@ -591,8 +610,16 @@ proto.saveViewInitial = function(geoLayout) {
     Lib.extendFlat(this.viewInitial, extra);
 };
 
+proto.render = function(mayRedrawOnUpdates) {
+    if(this._hasMarkerAngles && mayRedrawOnUpdates) {
+        this.plot(this._geoCalcData, this._fullLayout, [], true);
+    } else {
+        this._render();
+    }
+};
+
 // [hot code path] (re)draw all paths which depend on the projection
-proto.render = function() {
+proto._render = function() {
     var projection = this.projection;
     var pathFn = projection.getPath();
     var k;
@@ -619,7 +646,7 @@ proto.render = function() {
     for(k in this.dataPoints) {
         this.dataPoints[k]
             .attr('display', hideShowPoints)
-            .attr('transform', translatePoints);
+            .attr('transform', translatePoints); // TODO: need to redraw points with marker angle instead of calling translatePoints
     }
 };
 
diff --git a/src/plots/geo/zoom.js b/src/plots/geo/zoom.js
index d486122ddbc..2d79d69f581 100644
--- a/src/plots/geo/zoom.js
+++ b/src/plots/geo/zoom.js
@@ -77,7 +77,7 @@ function zoomScoped(geo, projection) {
         projection
             .scale(d3.event.scale)
             .translate(d3.event.translate);
-        geo.render();
+        geo.render(true);
 
         var center = projection.invert(geo.midPt);
         geo.graphDiv.emit('plotly_relayouting', {
@@ -162,7 +162,7 @@ function zoomNonClipped(geo, projection) {
         }
 
         didZoom = true;
-        geo.render();
+        geo.render(true);
 
         var rotate = projection.rotate();
         var center = projection.invert(geo.midPt);
@@ -268,7 +268,7 @@ function zoomClipped(geo, projection) {
         sync(geo, projection, syncCb);
     })
     .on('zoom.redraw', function() {
-        geo.render();
+        geo.render(true);
 
         var _rotate = projection.rotate();
         geo.graphDiv.emit('plotly_relayouting', {
diff --git a/src/traces/box/attributes.js b/src/traces/box/attributes.js
index 0d540a40a74..81fdce06524 100644
--- a/src/traces/box/attributes.js
+++ b/src/traces/box/attributes.js
@@ -312,6 +312,8 @@ module.exports = {
             {arrayOk: false, editType: 'plot'}),
         opacity: extendFlat({}, scatterMarkerAttrs.opacity,
             {arrayOk: false, dflt: 1, editType: 'style'}),
+        angle: extendFlat({}, scatterMarkerAttrs.angle,
+            {arrayOk: false, editType: 'calc'}),
         size: extendFlat({}, scatterMarkerAttrs.size,
             {arrayOk: false, editType: 'calc'}),
         color: extendFlat({}, scatterMarkerAttrs.color,
diff --git a/src/traces/box/defaults.js b/src/traces/box/defaults.js
index ad037009559..1a8980d0a64 100644
--- a/src/traces/box/defaults.js
+++ b/src/traces/box/defaults.js
@@ -251,6 +251,8 @@ function handlePointsDefaults(traceIn, traceOut, coerce, opts) {
         coerce('marker.symbol');
         coerce('marker.opacity');
         coerce('marker.size');
+        coerce('marker.angle');
+
         coerce('marker.color', traceOut.line.color);
         coerce('marker.line.color');
         coerce('marker.line.width');
diff --git a/src/traces/choropleth/style.js b/src/traces/choropleth/style.js
index 4ca8671a64d..cbf27d7a89c 100644
--- a/src/traces/choropleth/style.js
+++ b/src/traces/choropleth/style.js
@@ -26,7 +26,7 @@ function styleTrace(gd, calcTrace) {
             .style('opacity', marker.opacity);
     });
 
-    Drawing.selectedPointStyle(locs, trace, gd);
+    Drawing.selectedPointStyle(locs, trace);
 }
 
 function styleOnSelect(gd, calcTrace) {
@@ -34,7 +34,7 @@ function styleOnSelect(gd, calcTrace) {
     var trace = calcTrace[0].trace;
 
     if(trace.selectedpoints) {
-        Drawing.selectedPointStyle(s.selectAll('.choroplethlocation'), trace, gd);
+        Drawing.selectedPointStyle(s.selectAll('.choroplethlocation'), trace);
     } else {
         styleTrace(gd, calcTrace);
     }
diff --git a/src/traces/scatter/arrays_to_calcdata.js b/src/traces/scatter/arrays_to_calcdata.js
index 44f63d81609..94dfbd06813 100644
--- a/src/traces/scatter/arrays_to_calcdata.js
+++ b/src/traces/scatter/arrays_to_calcdata.js
@@ -24,6 +24,8 @@ module.exports = function arraysToCalcdata(cd, trace) {
         Lib.mergeArrayCastPositive(marker.size, cd, 'ms');
         Lib.mergeArrayCastPositive(marker.opacity, cd, 'mo');
         Lib.mergeArray(marker.symbol, cd, 'mx');
+        Lib.mergeArray(marker.angle, cd, 'ma');
+        Lib.mergeArray(marker.standoff, cd, 'mf');
         Lib.mergeArray(marker.color, cd, 'mc');
 
         var markerLine = marker.line;
diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js
index ad59a800fb1..cce2de0cd7d 100644
--- a/src/traces/scatter/attributes.js
+++ b/src/traces/scatter/attributes.js
@@ -292,6 +292,18 @@ module.exports = {
             ].join(' ')
         },
         dash: extendFlat({}, dash, {editType: 'style'}),
+        backoff: { // we want to have a similar option for the start of the line
+            valType: 'number',
+            min: 0,
+            dflt: 'auto',
+            arrayOk: true,
+            editType: 'plot',
+            description: [
+                'Sets the line back off from the end point of the nth line segment (in px).',
+                'This option is useful e.g. to avoid overlap with arrowhead markers.',
+                'With *auto* the lines would trim before markers if `marker.angleref` is set to *previous*.'
+            ].join(' ')
+        },
         simplify: {
             valType: 'boolean',
             dflt: true,
@@ -389,6 +401,41 @@ module.exports = {
             anim: true,
             description: 'Sets the marker opacity.'
         },
+        angle: {
+            valType: 'angle',
+            dflt: 0,
+            arrayOk: true,
+            editType: 'plot',
+            anim: false, // TODO: possibly set to true in future
+            description: [
+                'Sets the marker angle in respect to `angleref`.'
+            ].join(' ')
+        },
+        angleref: {
+            valType: 'enumerated',
+            values: ['previous', 'up'],
+            dflt: 'up',
+            editType: 'plot',
+            anim: false,
+            description: [
+                'Sets the reference for marker angle.',
+                'With *previous*, angle 0 points along the line from the previous point to this one.',
+                'With *up*, angle 0 points toward the top of the screen.'
+            ].join(' ')
+        },
+        standoff: {
+            valType: 'number',
+            min: 0,
+            dflt: 0,
+            arrayOk: true,
+            editType: 'plot',
+            anim: true,
+            description: [
+                'Moves the marker away from the data point in the direction of `angle` (in px).',
+                'This can be useful for example if you have another marker at this',
+                'location and you want to point an arrowhead marker at it.'
+            ].join(' ')
+        },
         size: {
             valType: 'number',
             min: 0,
diff --git a/src/traces/scatter/calc.js b/src/traces/scatter/calc.js
index 2c2ac558a84..a7471c0d85b 100644
--- a/src/traces/scatter/calc.js
+++ b/src/traces/scatter/calc.js
@@ -14,8 +14,8 @@ var calcSelection = require('./calc_selection');
 
 function calc(gd, trace) {
     var fullLayout = gd._fullLayout;
-    var xa = Axes.getFromId(gd, trace.xaxis || 'x');
-    var ya = Axes.getFromId(gd, trace.yaxis || 'y');
+    var xa = trace._xA = Axes.getFromId(gd, trace.xaxis || 'x', 'x');
+    var ya = trace._yA = Axes.getFromId(gd, trace.yaxis || 'y', 'y');
     var origX = xa.makeCalcdata(trace, 'x');
     var origY = ya.makeCalcdata(trace, 'y');
     var xObj = alignPeriod(trace, xa, 'x', origX);
diff --git a/src/traces/scatter/defaults.js b/src/traces/scatter/defaults.js
index 0d47c52c216..33ac037c920 100644
--- a/src/traces/scatter/defaults.js
+++ b/src/traces/scatter/defaults.js
@@ -39,7 +39,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
     coerce('mode', defaultMode);
 
     if(subTypes.hasLines(traceOut)) {
-        handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
+        handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {backoff: true});
         handleLineShapeDefaults(traceIn, traceOut, coerce);
         coerce('connectgaps');
         coerce('line.simplify');
diff --git a/src/traces/scatter/line_defaults.js b/src/traces/scatter/line_defaults.js
index 1812112a077..fe70e8f847a 100644
--- a/src/traces/scatter/line_defaults.js
+++ b/src/traces/scatter/line_defaults.js
@@ -5,6 +5,8 @@ var hasColorscale = require('../../components/colorscale/helpers').hasColorscale
 var colorscaleDefaults = require('../../components/colorscale/defaults');
 
 module.exports = function lineDefaults(traceIn, traceOut, defaultColor, layout, coerce, opts) {
+    if(!opts) opts = {};
+
     var markerColor = (traceIn.marker || {}).color;
 
     coerce('line.color', defaultColor);
@@ -17,5 +19,7 @@ module.exports = function lineDefaults(traceIn, traceOut, defaultColor, layout,
     }
 
     coerce('line.width');
-    if(!(opts || {}).noDash) coerce('line.dash');
+
+    if(!opts.noDash) coerce('line.dash');
+    if(opts.backoff) coerce('line.backoff');
 };
diff --git a/src/traces/scatter/line_points.js b/src/traces/scatter/line_points.js
index 58c9436dc59..3ae36f952c3 100644
--- a/src/traces/scatter/line_points.js
+++ b/src/traces/scatter/line_points.js
@@ -1,5 +1,6 @@
 'use strict';
 
+var Drawing = require('../../components/drawing');
 var numConstants = require('../../constants/numerical');
 var BADNUM = numConstants.BADNUM;
 var LOG_CLIP = numConstants.LOG_CLIP;
@@ -12,17 +13,20 @@ var constants = require('./constants');
 
 
 module.exports = function linePoints(d, opts) {
+    var trace = opts.trace || {};
     var xa = opts.xaxis;
     var ya = opts.yaxis;
     var xLog = xa.type === 'log';
     var yLog = ya.type === 'log';
     var xLen = xa._length;
     var yLen = ya._length;
+    var backoff = opts.backoff;
+    var marker = trace.marker;
     var connectGaps = opts.connectGaps;
     var baseTolerance = opts.baseTolerance;
     var shape = opts.shape;
     var linear = shape === 'linear';
-    var fill = opts.fill && opts.fill !== 'none';
+    var fill = trace.fill && trace.fill !== 'none';
     var segments = [];
     var minTolerance = constants.minTolerance;
     var len = d.length;
@@ -275,7 +279,17 @@ module.exports = function linePoints(d, opts) {
         lastXEdge = lastYEdge = 0;
     }
 
+    var arrayMarker = Lib.isArrayOrTypedArray(marker);
+
     function addPt(pt) {
+        if(pt && backoff) {
+            pt.i = i;
+            pt.d = d;
+            pt.trace = trace;
+            pt.marker = arrayMarker ? marker[pt.i] : marker;
+            pt.backoff = backoff;
+        }
+
         latestXFrac = pt[0] / xLen;
         latestYFrac = pt[1] / yLen;
         // Are we more than maxScreensAway off-screen any direction?
@@ -446,5 +460,36 @@ module.exports = function linePoints(d, opts) {
         segments.push(pts.slice(0, pti));
     }
 
+
+    var lastShapeChar = shape.slice(shape.length - 1);
+    if(backoff && lastShapeChar !== 'h' && lastShapeChar !== 'v') {
+        var trimmed = false;
+        var n = -1;
+        var newSegments = [];
+
+        for(var j = 0; j < segments.length; j++) {
+            for(var k = 0; k < segments[j].length - 1; k++) {
+                var start = segments[j][k];
+                var end = segments[j][k + 1];
+
+                var xy = Drawing.applyBackoff(end, start);
+                if(
+                    xy[0] !== end[0] ||
+                    xy[1] !== end[1]
+                ) {
+                    trimmed = true;
+                }
+                if(!newSegments[n + 1]) {
+                    n++;
+                    newSegments[n] = [
+                        start, [xy[0], xy[1]]
+                    ];
+                }
+            }
+        }
+
+        return trimmed ? newSegments : segments;
+    }
+
     return segments;
 };
diff --git a/src/traces/scatter/marker_defaults.js b/src/traces/scatter/marker_defaults.js
index a881cc7cf3c..c4358730534 100644
--- a/src/traces/scatter/marker_defaults.js
+++ b/src/traces/scatter/marker_defaults.js
@@ -25,6 +25,16 @@ module.exports = function markerDefaults(traceIn, traceOut, defaultColor, layout
     coerce('marker.symbol');
     coerce('marker.opacity', isBubble ? 0.7 : 1);
     coerce('marker.size');
+    if(!opts.noAngle) {
+        coerce('marker.angle');
+        if(!opts.noAngleRef) {
+            coerce('marker.angleref');
+        }
+
+        if(!opts.noStandOff) {
+            coerce('marker.standoff');
+        }
+    }
 
     coerce('marker.color', defaultColor);
     if(hasColorscale(traceIn, 'marker')) {
diff --git a/src/traces/scatter/plot.js b/src/traces/scatter/plot.js
index 7fb3e6c3cc9..3a63470687e 100644
--- a/src/traces/scatter/plot.js
+++ b/src/traces/scatter/plot.js
@@ -209,9 +209,11 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
         segments = linePoints(cdscatter, {
             xaxis: xa,
             yaxis: ya,
+            trace: trace,
             connectGaps: trace.connectgaps,
             baseTolerance: Math.max(line.width || 1, 3) / 4,
             shape: line.shape,
+            backoff: line.backoff,
             simplify: line.simplify,
             fill: trace.fill
         });
diff --git a/src/traces/scatter3d/defaults.js b/src/traces/scatter3d/defaults.js
index eb9e130565b..2cb922e5be6 100644
--- a/src/traces/scatter3d/defaults.js
+++ b/src/traces/scatter3d/defaults.js
@@ -36,7 +36,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
     }
 
     if(subTypes.hasMarkers(traceOut)) {
-        handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noSelect: true});
+        handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noSelect: true, noAngle: true});
     }
 
     if(subTypes.hasText(traceOut)) {
diff --git a/src/traces/scattercarpet/attributes.js b/src/traces/scattercarpet/attributes.js
index 54a81e80b37..6022878cc9a 100644
--- a/src/traces/scattercarpet/attributes.js
+++ b/src/traces/scattercarpet/attributes.js
@@ -61,6 +61,7 @@ module.exports = {
         color: scatterLineAttrs.color,
         width: scatterLineAttrs.width,
         dash: scatterLineAttrs.dash,
+        backoff: scatterLineAttrs.backoff,
         shape: extendFlat({}, scatterLineAttrs.shape,
             {values: ['linear', 'spline']}),
         smoothing: scatterLineAttrs.smoothing,
@@ -87,6 +88,9 @@ module.exports = {
         symbol: scatterMarkerAttrs.symbol,
         opacity: scatterMarkerAttrs.opacity,
         maxdisplayed: scatterMarkerAttrs.maxdisplayed,
+        angle: scatterMarkerAttrs.angle,
+        angleref: scatterMarkerAttrs.angleref,
+        standoff: scatterMarkerAttrs.standoff,
         size: scatterMarkerAttrs.size,
         sizeref: scatterMarkerAttrs.sizeref,
         sizemin: scatterMarkerAttrs.sizemin,
diff --git a/src/traces/scattercarpet/defaults.js b/src/traces/scattercarpet/defaults.js
index 29986064fde..b21f4e956df 100644
--- a/src/traces/scattercarpet/defaults.js
+++ b/src/traces/scattercarpet/defaults.js
@@ -42,7 +42,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
     coerce('mode', defaultMode);
 
     if(subTypes.hasLines(traceOut)) {
-        handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
+        handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {backoff: true});
         handleLineShapeDefaults(traceIn, traceOut, coerce);
         coerce('connectgaps');
     }
diff --git a/src/traces/scattercarpet/plot.js b/src/traces/scattercarpet/plot.js
index d94ae5d6c51..6d7de9cacd1 100644
--- a/src/traces/scattercarpet/plot.js
+++ b/src/traces/scattercarpet/plot.js
@@ -8,13 +8,24 @@ module.exports = function plot(gd, plotinfoproxy, data, layer) {
     var i, trace, node;
 
     var carpet = data[0][0].carpet;
+
+    var xaxis = Axes.getFromId(gd, carpet.xaxis || 'x');
+    var yaxis = Axes.getFromId(gd, carpet.yaxis || 'y');
+
     // mimic cartesian plotinfo
     var plotinfo = {
-        xaxis: Axes.getFromId(gd, carpet.xaxis || 'x'),
-        yaxis: Axes.getFromId(gd, carpet.yaxis || 'y'),
+        xaxis: xaxis,
+        yaxis: yaxis,
         plot: plotinfoproxy.plot,
     };
 
+    for(i = 0; i < data.length; i++) {
+        trace = data[i][0].trace;
+
+        trace._xA = xaxis;
+        trace._yA = yaxis;
+    }
+
     scatterPlot(gd, plotinfo, data, layer);
 
     for(i = 0; i < data.length; i++) {
diff --git a/src/traces/scattergeo/attributes.js b/src/traces/scattergeo/attributes.js
index 7a9b7509d21..d80b62c7735 100644
--- a/src/traces/scattergeo/attributes.js
+++ b/src/traces/scattergeo/attributes.js
@@ -114,6 +114,17 @@ module.exports = overrideAll({
     marker: extendFlat({
         symbol: scatterMarkerAttrs.symbol,
         opacity: scatterMarkerAttrs.opacity,
+        angle: scatterMarkerAttrs.angle,
+        angleref: extendFlat({}, scatterMarkerAttrs.angleref, {
+            values: ['previous', 'up', 'north'],
+            description: [
+                'Sets the reference for marker angle.',
+                'With *previous*, angle 0 points along the line from the previous point to this one.',
+                'With *up*, angle 0 points toward the top of the screen.',
+                'With *north*, angle 0 points north based on the current map projection.',
+            ].join(' ')
+        }),
+        standoff: scatterMarkerAttrs.standoff,
         size: scatterMarkerAttrs.size,
         sizeref: scatterMarkerAttrs.sizeref,
         sizemin: scatterMarkerAttrs.sizemin,
diff --git a/src/traces/scattergl/attributes.js b/src/traces/scattergl/attributes.js
index cf49750ed4f..f458b27086b 100644
--- a/src/traces/scattergl/attributes.js
+++ b/src/traces/scattergl/attributes.js
@@ -67,6 +67,7 @@ var attrs = module.exports = overrideAll({
     },
     marker: extendFlat({}, colorScaleAttrs('marker'), {
         symbol: scatterMarkerAttrs.symbol,
+        angle: scatterMarkerAttrs.angle,
         size: scatterMarkerAttrs.size,
         sizeref: scatterMarkerAttrs.sizeref,
         sizemin: scatterMarkerAttrs.sizemin,
diff --git a/src/traces/scattergl/calc.js b/src/traces/scattergl/calc.js
index d27f12e5fc4..240418b07af 100644
--- a/src/traces/scattergl/calc.js
+++ b/src/traces/scattergl/calc.js
@@ -20,8 +20,9 @@ var TOO_MANY_POINTS = require('./constants').TOO_MANY_POINTS;
 
 module.exports = function calc(gd, trace) {
     var fullLayout = gd._fullLayout;
-    var xa = AxisIDs.getFromId(gd, trace.xaxis);
-    var ya = AxisIDs.getFromId(gd, trace.yaxis);
+    var xa = trace._xA = AxisIDs.getFromId(gd, trace.xaxis, 'x');
+    var ya = trace._yA = AxisIDs.getFromId(gd, trace.yaxis, 'y');
+
     var subplot = fullLayout._plots[trace.xaxis + trace.yaxis];
     var len = trace._length;
     var hasTooManyPoints = len >= TOO_MANY_POINTS;
diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js
index 3a42dc4050c..66c43d753cb 100644
--- a/src/traces/scattergl/convert.js
+++ b/src/traces/scattergl/convert.js
@@ -50,9 +50,9 @@ function convertStyle(gd, trace) {
     }
 
     if(subTypes.hasMarkers(trace)) {
-        opts.marker = convertMarkerStyle(trace);
-        opts.markerSel = convertMarkerSelection(trace, trace.selected);
-        opts.markerUnsel = convertMarkerSelection(trace, trace.unselected);
+        opts.marker = convertMarkerStyle(gd, trace);
+        opts.markerSel = convertMarkerSelection(gd, trace, trace.selected);
+        opts.markerUnsel = convertMarkerSelection(gd, trace, trace.unselected);
 
         if(!trace.unselected && Lib.isArrayOrTypedArray(trace.marker.opacity)) {
             var mo = trace.marker.opacity;
@@ -207,13 +207,14 @@ function convertTextStyle(gd, trace) {
 }
 
 
-function convertMarkerStyle(trace) {
+function convertMarkerStyle(gd, trace) {
     var count = trace._length;
     var optsIn = trace.marker;
     var optsOut = {};
     var i;
 
     var multiSymbol = Lib.isArrayOrTypedArray(optsIn.symbol);
+    var multiAngle = Lib.isArrayOrTypedArray(optsIn.angle);
     var multiColor = Lib.isArrayOrTypedArray(optsIn.color);
     var multiLineColor = Lib.isArrayOrTypedArray(optsIn.line.color);
     var multiOpacity = Lib.isArrayOrTypedArray(optsIn.opacity);
@@ -224,10 +225,14 @@ function convertMarkerStyle(trace) {
     if(!multiSymbol) isOpen = helpers.isOpenSymbol(optsIn.symbol);
 
     // prepare colors
-    if(multiSymbol || multiColor || multiLineColor || multiOpacity) {
+    if(multiSymbol || multiColor || multiLineColor || multiOpacity || multiAngle) {
+        optsOut.symbols = new Array(count);
+        optsOut.angles = new Array(count);
         optsOut.colors = new Array(count);
         optsOut.borderColors = new Array(count);
 
+        var symbols = optsIn.symbol;
+        var angles = optsIn.angle;
         var colors = formatColor(optsIn, optsIn.opacity, count);
         var borderColors = formatColor(optsIn.line, optsIn.opacity, count);
 
@@ -245,14 +250,29 @@ function convertMarkerStyle(trace) {
                 colors[i] = color;
             }
         }
+        if(!Array.isArray(symbols)) {
+            var symbol = symbols;
+            symbols = Array(count);
+            for(i = 0; i < count; i++) {
+                symbols[i] = symbol;
+            }
+        }
+        if(!Array.isArray(angles)) {
+            var angle = angles;
+            angles = Array(count);
+            for(i = 0; i < count; i++) {
+                angles[i] = angle;
+            }
+        }
 
+        optsOut.symbols = symbols;
+        optsOut.angles = angles;
         optsOut.colors = colors;
         optsOut.borderColors = borderColors;
 
         for(i = 0; i < count; i++) {
             if(multiSymbol) {
-                var symbol = optsIn.symbol[i];
-                isOpen = helpers.isOpenSymbol(symbol);
+                isOpen = helpers.isOpenSymbol(optsIn.symbol[i]);
             }
             if(isOpen) {
                 borderColors[i] = colors[i].slice();
@@ -262,6 +282,14 @@ function convertMarkerStyle(trace) {
         }
 
         optsOut.opacity = trace.opacity;
+
+        optsOut.markers = new Array(count);
+        for(i = 0; i < count; i++) {
+            optsOut.markers[i] = getSymbolSdf({
+                mx: optsOut.symbols[i],
+                ma: optsOut.angles[i]
+            }, trace);
+        }
     } else {
         if(isOpen) {
             optsOut.color = rgba(optsIn.color, 'uint8');
@@ -273,16 +301,11 @@ function convertMarkerStyle(trace) {
         }
 
         optsOut.opacity = trace.opacity * optsIn.opacity;
-    }
 
-    // prepare symbols
-    if(multiSymbol) {
-        optsOut.markers = new Array(count);
-        for(i = 0; i < count; i++) {
-            optsOut.markers[i] = getSymbolSdf(optsIn.symbol[i]);
-        }
-    } else {
-        optsOut.marker = getSymbolSdf(optsIn.symbol);
+        optsOut.marker = getSymbolSdf({
+            mx: optsIn.symbol,
+            ma: optsIn.angle
+        }, trace);
     }
 
     // prepare sizes
@@ -330,14 +353,14 @@ function convertMarkerStyle(trace) {
     return optsOut;
 }
 
-function convertMarkerSelection(trace, target) {
+function convertMarkerSelection(gd, trace, target) {
     var optsIn = trace.marker;
     var optsOut = {};
 
     if(!target) return optsOut;
 
     if(target.marker && target.marker.symbol) {
-        optsOut = convertMarkerStyle(Lib.extendFlat({}, optsIn, target.marker));
+        optsOut = convertMarkerStyle(gd, Lib.extendFlat({}, optsIn, target.marker));
     } else if(target.marker) {
         if(target.marker.size) optsOut.size = target.marker.size;
         if(target.marker.color) optsOut.colors = target.marker.color;
@@ -389,7 +412,8 @@ var SYMBOL_STROKE = constants.SYMBOL_STROKE;
 var SYMBOL_SDF = {};
 var SYMBOL_SVG_CIRCLE = Drawing.symbolFuncs[0](SYMBOL_SIZE * 0.05);
 
-function getSymbolSdf(symbol) {
+function getSymbolSdf(d, trace) {
+    var symbol = d.mx;
     if(symbol === 'circle') return null;
 
     var symbolPath, symbolSdf;
@@ -400,13 +424,17 @@ function getSymbolSdf(symbol) {
 
     var isDot = helpers.isDotSymbol(symbol);
 
+    // until we may handle angles in shader?
+    if(d.ma) symbol += '_' + d.ma;
+
     // get symbol sdf from cache or generate it
     if(SYMBOL_SDF[symbol]) return SYMBOL_SDF[symbol];
 
+    var angle = Drawing.getMarkerAngle(d, trace);
     if(isDot && !symbolNoDot) {
-        symbolPath = symbolFunc(SYMBOL_SIZE * 1.1) + SYMBOL_SVG_CIRCLE;
+        symbolPath = symbolFunc(SYMBOL_SIZE * 1.1, angle) + SYMBOL_SVG_CIRCLE;
     } else {
-        symbolPath = symbolFunc(SYMBOL_SIZE);
+        symbolPath = symbolFunc(SYMBOL_SIZE, angle);
     }
 
     symbolSdf = svgSdf(symbolPath, {
@@ -415,6 +443,7 @@ function getSymbolSdf(symbol) {
         viewBox: [-SYMBOL_SIZE, -SYMBOL_SIZE, SYMBOL_SIZE, SYMBOL_SIZE],
         stroke: symbolNoFill ? SYMBOL_STROKE : -SYMBOL_STROKE
     });
+
     SYMBOL_SDF[symbol] = symbolSdf;
 
     return symbolSdf || null;
@@ -545,8 +574,8 @@ function convertLinePositions(gd, trace, positions) {
 
 function convertErrorBarPositions(gd, trace, positions, x, y) {
     var makeComputeError = Registry.getComponentMethod('errorbars', 'makeComputeError');
-    var xa = AxisIDs.getFromId(gd, trace.xaxis);
-    var ya = AxisIDs.getFromId(gd, trace.yaxis);
+    var xa = AxisIDs.getFromId(gd, trace.xaxis, 'x');
+    var ya = AxisIDs.getFromId(gd, trace.yaxis, 'y');
     var count = positions.length / 2;
     var out = {};
 
diff --git a/src/traces/scattergl/defaults.js b/src/traces/scattergl/defaults.js
index b4cce07a5d7..a15ff09e254 100644
--- a/src/traces/scattergl/defaults.js
+++ b/src/traces/scattergl/defaults.js
@@ -46,7 +46,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
     }
 
     if(subTypes.hasMarkers(traceOut)) {
-        handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce);
+        handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noAngleRef: true, noStandOff: true});
         coerce('marker.line.width', isOpen || isBubble ? 1 : 0);
     }
 
diff --git a/src/traces/scattergl/hover.js b/src/traces/scattergl/hover.js
index 41e25458f6f..74efc4776b3 100644
--- a/src/traces/scattergl/hover.js
+++ b/src/traces/scattergl/hover.js
@@ -139,6 +139,7 @@ function calcHover(pointData, x, y, trace) {
         di.ms = Lib.isArrayOrTypedArray(marker.size) ? marker.size[id] : marker.size;
         di.mo = Lib.isArrayOrTypedArray(marker.opacity) ? marker.opacity[id] : marker.opacity;
         di.mx = Lib.isArrayOrTypedArray(marker.symbol) ? marker.symbol[id] : marker.symbol;
+        di.ma = Lib.isArrayOrTypedArray(marker.angle) ? marker.angle[id] : marker.angle;
         di.mc = Lib.isArrayOrTypedArray(marker.color) ? marker.color[id] : marker.color;
     }
 
diff --git a/src/traces/scattermapbox/defaults.js b/src/traces/scattermapbox/defaults.js
index 7d6747783ad..e3b87adc3df 100644
--- a/src/traces/scattermapbox/defaults.js
+++ b/src/traces/scattermapbox/defaults.js
@@ -33,7 +33,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
     }
 
     if(subTypes.hasMarkers(traceOut)) {
-        handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noLine: true});
+        handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noLine: true, noAngle: true});
 
         coerce('marker.allowoverlap');
         coerce('marker.angle');
diff --git a/src/traces/scatterpolar/attributes.js b/src/traces/scatterpolar/attributes.js
index 81b6ae72482..f5d0321c519 100644
--- a/src/traces/scatterpolar/attributes.js
+++ b/src/traces/scatterpolar/attributes.js
@@ -82,6 +82,7 @@ module.exports = {
         color: lineAttrs.color,
         width: lineAttrs.width,
         dash: lineAttrs.dash,
+        backoff: lineAttrs.backoff,
         shape: extendFlat({}, lineAttrs.shape, {
             values: ['linear', 'spline']
         }),
diff --git a/src/traces/scatterpolar/defaults.js b/src/traces/scatterpolar/defaults.js
index 0d18056ba38..5bf2c5e97b9 100644
--- a/src/traces/scatterpolar/defaults.js
+++ b/src/traces/scatterpolar/defaults.js
@@ -30,7 +30,7 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
     if(traceOut.hoveron !== 'fills') coerce('hovertemplate');
 
     if(subTypes.hasLines(traceOut)) {
-        handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
+        handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {backoff: true});
         handleLineShapeDefaults(traceIn, traceOut, coerce);
         coerce('connectgaps');
     }
diff --git a/src/traces/scatterpolar/plot.js b/src/traces/scatterpolar/plot.js
index a62ff1bdd48..a456701c160 100644
--- a/src/traces/scatterpolar/plot.js
+++ b/src/traces/scatterpolar/plot.js
@@ -6,9 +6,12 @@ var BADNUM = require('../../constants/numerical').BADNUM;
 module.exports = function plot(gd, subplot, moduleCalcData) {
     var mlayer = subplot.layers.frontplot.select('g.scatterlayer');
 
+    var xa = subplot.xaxis;
+    var ya = subplot.yaxis;
+
     var plotinfo = {
-        xaxis: subplot.xaxis,
-        yaxis: subplot.yaxis,
+        xaxis: xa,
+        yaxis: ya,
         plot: subplot.framework,
         layerClipId: subplot._hasClipOnAxisFalse ? subplot.clipIds.forTraces : null
     };
@@ -22,6 +25,11 @@ module.exports = function plot(gd, subplot, moduleCalcData) {
         var cdi = moduleCalcData[i];
 
         for(var j = 0; j < cdi.length; j++) {
+            if(j === 0) {
+                cdi[0].trace._xA = xa;
+                cdi[0].trace._yA = ya;
+            }
+
             var cd = cdi[j];
             var r = cd.r;
 
diff --git a/src/traces/scatterpolargl/defaults.js b/src/traces/scatterpolargl/defaults.js
index 65c25f26a06..6b64bcb3179 100644
--- a/src/traces/scatterpolargl/defaults.js
+++ b/src/traces/scatterpolargl/defaults.js
@@ -35,7 +35,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
     }
 
     if(subTypes.hasMarkers(traceOut)) {
-        handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce);
+        handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noAngleRef: true, noStandOff: true});
     }
 
     if(subTypes.hasText(traceOut)) {
diff --git a/src/traces/scattersmith/attributes.js b/src/traces/scattersmith/attributes.js
index 90311398341..2344a5fc2a5 100644
--- a/src/traces/scattersmith/attributes.js
+++ b/src/traces/scattersmith/attributes.js
@@ -38,6 +38,7 @@ module.exports = {
         color: lineAttrs.color,
         width: lineAttrs.width,
         dash: lineAttrs.dash,
+        backoff: lineAttrs.backoff,
         shape: extendFlat({}, lineAttrs.shape, {
             values: ['linear', 'spline']
         }),
diff --git a/src/traces/scattersmith/defaults.js b/src/traces/scattersmith/defaults.js
index ad00da49fd4..d1001487803 100644
--- a/src/traces/scattersmith/defaults.js
+++ b/src/traces/scattersmith/defaults.js
@@ -29,7 +29,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
     if(traceOut.hoveron !== 'fills') coerce('hovertemplate');
 
     if(subTypes.hasLines(traceOut)) {
-        handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
+        handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {backoff: true});
         handleLineShapeDefaults(traceIn, traceOut, coerce);
         coerce('connectgaps');
     }
diff --git a/src/traces/scattersmith/plot.js b/src/traces/scattersmith/plot.js
index 5a9c12a3bcf..7bd853abc98 100644
--- a/src/traces/scattersmith/plot.js
+++ b/src/traces/scattersmith/plot.js
@@ -8,9 +8,12 @@ var smith = helpers.smith;
 module.exports = function plot(gd, subplot, moduleCalcData) {
     var mlayer = subplot.layers.frontplot.select('g.scatterlayer');
 
+    var xa = subplot.xaxis;
+    var ya = subplot.yaxis;
+
     var plotinfo = {
-        xaxis: subplot.xaxis,
-        yaxis: subplot.yaxis,
+        xaxis: xa,
+        yaxis: ya,
         plot: subplot.framework,
         layerClipId: subplot._hasClipOnAxisFalse ? subplot.clipIds.forTraces : null
     };
@@ -21,6 +24,11 @@ module.exports = function plot(gd, subplot, moduleCalcData) {
         var cdi = moduleCalcData[i];
 
         for(var j = 0; j < cdi.length; j++) {
+            if(j === 0) {
+                cdi[0].trace._xA = xa;
+                cdi[0].trace._yA = ya;
+            }
+
             var cd = cdi[j];
             var real = cd.real;
 
diff --git a/src/traces/scatterternary/attributes.js b/src/traces/scatterternary/attributes.js
index 1b87b52ff01..23c0bba0621 100644
--- a/src/traces/scatterternary/attributes.js
+++ b/src/traces/scatterternary/attributes.js
@@ -89,6 +89,7 @@ module.exports = {
         color: scatterLineAttrs.color,
         width: scatterLineAttrs.width,
         dash: dash,
+        backoff: scatterLineAttrs.backoff,
         shape: extendFlat({}, scatterLineAttrs.shape,
             {values: ['linear', 'spline']}),
         smoothing: scatterLineAttrs.smoothing,
@@ -115,6 +116,9 @@ module.exports = {
     marker: extendFlat({
         symbol: scatterMarkerAttrs.symbol,
         opacity: scatterMarkerAttrs.opacity,
+        angle: scatterMarkerAttrs.angle,
+        angleref: scatterMarkerAttrs.angleref,
+        standoff: scatterMarkerAttrs.standoff,
         maxdisplayed: scatterMarkerAttrs.maxdisplayed,
         size: scatterMarkerAttrs.size,
         sizeref: scatterMarkerAttrs.sizeref,
diff --git a/src/traces/scatterternary/defaults.js b/src/traces/scatterternary/defaults.js
index 3e1569f0482..cff521b24b9 100644
--- a/src/traces/scatterternary/defaults.js
+++ b/src/traces/scatterternary/defaults.js
@@ -56,7 +56,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
     coerce('mode', defaultMode);
 
     if(subTypes.hasLines(traceOut)) {
-        handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
+        handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {backoff: true});
         handleLineShapeDefaults(traceIn, traceOut, coerce);
         coerce('connectgaps');
     }
diff --git a/src/traces/scatterternary/plot.js b/src/traces/scatterternary/plot.js
index a3d0e4172b5..cac38c11635 100644
--- a/src/traces/scatterternary/plot.js
+++ b/src/traces/scatterternary/plot.js
@@ -9,14 +9,25 @@ module.exports = function plot(gd, ternary, moduleCalcData) {
     plotContainer.select('.scatterlayer').selectAll('*').remove();
 
     // mimic cartesian plotinfo
+    var xa = ternary.xaxis;
+    var ya = ternary.yaxis;
+
     var plotinfo = {
-        xaxis: ternary.xaxis,
-        yaxis: ternary.yaxis,
+        xaxis: xa,
+        yaxis: ya,
         plot: plotContainer,
         layerClipId: ternary._hasClipOnAxisFalse ? ternary.clipIdRelative : null
     };
 
     var scatterLayer = ternary.layers.frontplot.select('g.scatterlayer');
 
+    for(var i = 0; i < moduleCalcData.length; i++) {
+        var cdi = moduleCalcData[i];
+        if(cdi.length) {
+            cdi[0].trace._xA = xa;
+            cdi[0].trace._yA = ya;
+        }
+    }
+
     scatterPlot(gd, plotinfo, moduleCalcData, scatterLayer);
 };
diff --git a/src/traces/splom/attributes.js b/src/traces/splom/attributes.js
index dfbb585255a..0c1c09b8457 100644
--- a/src/traces/splom/attributes.js
+++ b/src/traces/splom/attributes.js
@@ -19,6 +19,7 @@ var markerLineAttrs = extendFlat(colorScaleAttrs('marker.line', {editTypeOverrid
 
 var markerAttrs = extendFlat(colorScaleAttrs('marker'), {
     symbol: scatterMarkerAttrs.symbol,
+    angle: scatterMarkerAttrs.angle,
     size: extendFlat({}, scatterMarkerAttrs.size, {editType: 'markerSize'}),
     sizeref: scatterMarkerAttrs.sizeref,
     sizemin: scatterMarkerAttrs.sizemin,
diff --git a/src/traces/splom/calc.js b/src/traces/splom/calc.js
index c7544278866..5cdb35bcad1 100644
--- a/src/traces/splom/calc.js
+++ b/src/traces/splom/calc.js
@@ -67,7 +67,7 @@ module.exports = function calc(gd, trace) {
     }
 
     calcColorscale(gd, trace);
-    Lib.extendFlat(opts, convertMarkerStyle(trace));
+    Lib.extendFlat(opts, convertMarkerStyle(gd, trace));
 
     var visibleLength = cdata.length;
     var hasTooManyPoints = (visibleLength * commonLength) > TOO_MANY_POINTS;
@@ -94,8 +94,8 @@ module.exports = function calc(gd, trace) {
     if(!scene.matrix) scene.matrix = true;
     scene.matrixOptions = opts;
 
-    scene.selectedOptions = convertMarkerSelection(trace, trace.selected);
-    scene.unselectedOptions = convertMarkerSelection(trace, trace.unselected);
+    scene.selectedOptions = convertMarkerSelection(gd, trace, trace.selected);
+    scene.unselectedOptions = convertMarkerSelection(gd, trace, trace.unselected);
 
     return [{x: false, y: false, t: {}, trace: trace}];
 };
diff --git a/src/traces/splom/defaults.js b/src/traces/splom/defaults.js
index 8e450badf07..72cb247733a 100644
--- a/src/traces/splom/defaults.js
+++ b/src/traces/splom/defaults.js
@@ -36,7 +36,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
     coerce('xhoverformat');
     coerce('yhoverformat');
 
-    handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce);
+    handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noAngleRef: true, noStandOff: true});
 
     var isOpen = isOpenSymbol(traceOut.marker.symbol);
     var isBubble = subTypes.isBubble(traceOut);
diff --git a/src/traces/splom/edit_style.js b/src/traces/splom/edit_style.js
index f53071295a9..c3abca799ea 100644
--- a/src/traces/splom/edit_style.js
+++ b/src/traces/splom/edit_style.js
@@ -11,7 +11,7 @@ module.exports = function editStyle(gd, cd0) {
     if(scene) {
         calcColorscale(gd, trace);
 
-        Lib.extendFlat(scene.matrixOptions, convertMarkerStyle(trace));
+        Lib.extendFlat(scene.matrixOptions, convertMarkerStyle(gd, trace));
         // TODO [un]selected styles?
 
         var opts = Lib.extendFlat({}, scene.matrixOptions, scene.viewOpts);
diff --git a/src/traces/violin/plot.js b/src/traces/violin/plot.js
index 62f46650266..b46292d319d 100644
--- a/src/traces/violin/plot.js
+++ b/src/traces/violin/plot.js
@@ -13,10 +13,11 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) {
     var xa = plotinfo.xaxis;
     var ya = plotinfo.yaxis;
 
-    function makePath(pts) {
+    function makePath(pts, trace) {
         var segments = linePoints(pts, {
             xaxis: xa,
             yaxis: ya,
+            trace: trace,
             connectGaps: true,
             baseTolerance: 0.75,
             shape: 'spline',
@@ -80,7 +81,7 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) {
                     pt[t.posLetter] = posCenter + (density[i].v / scale);
                     pt[t.valLetter] = valAxis.c2l(density[i].t, true);
                 }
-                pathPos = makePath(pts);
+                pathPos = makePath(pts, trace);
             }
 
             if(hasNegativeSide) {
@@ -90,7 +91,7 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) {
                     pt[t.posLetter] = posCenter - (density[i].v / scale);
                     pt[t.valLetter] = valAxis.c2l(density[i].t, true);
                 }
-                pathNeg = makePath(pts);
+                pathNeg = makePath(pts, trace);
             }
 
             if(hasBothSides) {
diff --git a/test/image/baselines/19.png b/test/image/baselines/19.png
index e98f113080f..1baf427a06c 100644
Binary files a/test/image/baselines/19.png and b/test/image/baselines/19.png differ
diff --git a/test/image/baselines/box_grouped_horz.png b/test/image/baselines/box_grouped_horz.png
index d3c07ef0049..f904a3757cb 100644
Binary files a/test/image/baselines/box_grouped_horz.png and b/test/image/baselines/box_grouped_horz.png differ
diff --git a/test/image/baselines/geo_canadian-cities.png b/test/image/baselines/geo_canadian-cities.png
index 2415bf9248e..aeedf21e9fb 100644
Binary files a/test/image/baselines/geo_canadian-cities.png and b/test/image/baselines/geo_canadian-cities.png differ
diff --git a/test/image/baselines/geo_conic-conformal.png b/test/image/baselines/geo_conic-conformal.png
index b6a6ad20a2d..b4900e61c1c 100644
Binary files a/test/image/baselines/geo_conic-conformal.png and b/test/image/baselines/geo_conic-conformal.png differ
diff --git a/test/image/baselines/geo_scattergeo-out-of-usa.png b/test/image/baselines/geo_scattergeo-out-of-usa.png
index a5679ab2c38..623f7d08cf0 100644
Binary files a/test/image/baselines/geo_scattergeo-out-of-usa.png and b/test/image/baselines/geo_scattergeo-out-of-usa.png differ
diff --git a/test/image/baselines/geo_text_chart_arrays.png b/test/image/baselines/geo_text_chart_arrays.png
index 36d3459582c..0f4a3b5e05a 100644
Binary files a/test/image/baselines/geo_text_chart_arrays.png and b/test/image/baselines/geo_text_chart_arrays.png differ
diff --git a/test/image/baselines/gl2d_marker_symbols.png b/test/image/baselines/gl2d_marker_symbols.png
index 56246eea2ac..c25f6b7fe13 100644
Binary files a/test/image/baselines/gl2d_marker_symbols.png and b/test/image/baselines/gl2d_marker_symbols.png differ
diff --git a/test/image/baselines/glpolar_subplots.png b/test/image/baselines/glpolar_subplots.png
index e603cdaa040..e25d4eb6913 100644
Binary files a/test/image/baselines/glpolar_subplots.png and b/test/image/baselines/glpolar_subplots.png differ
diff --git a/test/image/baselines/marker_symbols.png b/test/image/baselines/marker_symbols.png
index 8a902aabd21..f7ceb938ac2 100644
Binary files a/test/image/baselines/marker_symbols.png and b/test/image/baselines/marker_symbols.png differ
diff --git a/test/image/baselines/polar_direction.png b/test/image/baselines/polar_direction.png
index 17620d34c69..abb99d44401 100644
Binary files a/test/image/baselines/polar_direction.png and b/test/image/baselines/polar_direction.png differ
diff --git a/test/image/baselines/scattercarpet-on-two-carpets.png b/test/image/baselines/scattercarpet-on-two-carpets.png
index 1bca750da1e..c86c3857cb9 100644
Binary files a/test/image/baselines/scattercarpet-on-two-carpets.png and b/test/image/baselines/scattercarpet-on-two-carpets.png differ
diff --git a/test/image/baselines/smith_gaps.png b/test/image/baselines/smith_gaps.png
index 0b5efb1a5d5..25eaa1f3f80 100644
Binary files a/test/image/baselines/smith_gaps.png and b/test/image/baselines/smith_gaps.png differ
diff --git a/test/image/baselines/splom_log.png b/test/image/baselines/splom_log.png
index 39c842cc11d..d12325b9700 100644
Binary files a/test/image/baselines/splom_log.png and b/test/image/baselines/splom_log.png differ
diff --git a/test/image/baselines/ternary_markers.png b/test/image/baselines/ternary_markers.png
index 41740a11fb2..aeed01798be 100644
Binary files a/test/image/baselines/ternary_markers.png and b/test/image/baselines/ternary_markers.png differ
diff --git a/test/image/baselines/violin_ridgeplot.png b/test/image/baselines/violin_ridgeplot.png
index 66d3edbeb37..cb1de0b284c 100644
Binary files a/test/image/baselines/violin_ridgeplot.png and b/test/image/baselines/violin_ridgeplot.png differ
diff --git a/test/image/baselines/z-gl2d_marker_symbols-angle.png b/test/image/baselines/z-gl2d_marker_symbols-angle.png
new file mode 100644
index 00000000000..001d27f695f
Binary files /dev/null and b/test/image/baselines/z-gl2d_marker_symbols-angle.png differ
diff --git a/test/image/baselines/z-line-shape-arrow.png b/test/image/baselines/z-line-shape-arrow.png
new file mode 100644
index 00000000000..8037c4d4625
Binary files /dev/null and b/test/image/baselines/z-line-shape-arrow.png differ
diff --git a/test/image/baselines/z-line-shape-arrow_backoff-auto.png b/test/image/baselines/z-line-shape-arrow_backoff-auto.png
new file mode 100644
index 00000000000..ac2811e4d84
Binary files /dev/null and b/test/image/baselines/z-line-shape-arrow_backoff-auto.png differ
diff --git a/test/image/baselines/z-line-shape-arrow_backoff.png b/test/image/baselines/z-line-shape-arrow_backoff.png
new file mode 100644
index 00000000000..bcab37977e9
Binary files /dev/null and b/test/image/baselines/z-line-shape-arrow_backoff.png differ
diff --git a/test/image/baselines/z-marker-standoff.png b/test/image/baselines/z-marker-standoff.png
new file mode 100644
index 00000000000..54db57a75a5
Binary files /dev/null and b/test/image/baselines/z-marker-standoff.png differ
diff --git a/test/image/baselines/z-marker-standoff_auto-backoff.png b/test/image/baselines/z-marker-standoff_auto-backoff.png
new file mode 100644
index 00000000000..34abdddae0c
Binary files /dev/null and b/test/image/baselines/z-marker-standoff_auto-backoff.png differ
diff --git a/test/image/baselines/z-marker_symbols-angle.png b/test/image/baselines/z-marker_symbols-angle.png
new file mode 100644
index 00000000000..cac234aabbb
Binary files /dev/null and b/test/image/baselines/z-marker_symbols-angle.png differ
diff --git a/test/image/mocks/19.json b/test/image/mocks/19.json
index 007d414dc63..8d645078d05 100644
--- a/test/image/mocks/19.json
+++ b/test/image/mocks/19.json
@@ -1,6 +1,11 @@
 {
     "data": [
         {
+            "marker": {
+                "size": 10,
+                "symbol": "arrow-wide",
+                "angleref": "previous"
+            },
             "x": [
                 1,
                 2,
@@ -15,6 +20,12 @@
             "type": "scatter"
         },
         {
+            "marker": {
+                "size": 10,
+                "symbol": "arrow",
+                "angleref": "up",
+                "angle": [180, 0, 180]
+            },
             "x": [
                 20,
                 30,
diff --git a/test/image/mocks/box_grouped_horz.json b/test/image/mocks/box_grouped_horz.json
index 0f9dedb0d9d..fdc0c85eb02 100644
--- a/test/image/mocks/box_grouped_horz.json
+++ b/test/image/mocks/box_grouped_horz.json
@@ -3,7 +3,7 @@
   "y": ["day 1", "day 1", "day 1", "day 1", "day 1", "day 1",
          "day 2", "day 2", "day 2", "day 2", "day 2", "day 2"],
   "name": "kale",
-  "marker": {"color": "#3D9970"},
+  "marker": {"color": "#3D9970", "symbol": "arrow-wide", "angle": 0, "size": 10},
   "type": "box",
   "orientation": "h",
   "pointpos": -1.5,
@@ -16,7 +16,7 @@
   "y": ["day 1", "day 1", "day 1", "day 1", "day 1", "day 1",
          "day 2", "day 2", "day 2", "day 2", "day 2", "day 2"],
   "name": "radishes",
-  "marker": {"color": "#FF4136"},
+  "marker": {"color": "#FF4136", "symbol": "arrow", "angle": -90, "size": 10},
   "type": "box",
   "orientation": "h",
   "pointpos": -1.5,
@@ -29,7 +29,7 @@
   "y": ["day 1", "day 1", "day 1", "day 1", "day 1", "day 1",
          "day 2", "day 2", "day 2", "day 2", "day 2", "day 2"],
   "name": "carrots",
-  "marker": {"color": "#FF851B"},
+  "marker": {"color": "#FF851B", "symbol": "arrow", "angle": 90, "size": 10},
   "type": "box",
   "orientation": "h",
   "pointpos": -1.5,
diff --git a/test/image/mocks/geo_canadian-cities.json b/test/image/mocks/geo_canadian-cities.json
index 11058db5e39..75db415dfdd 100644
--- a/test/image/mocks/geo_canadian-cities.json
+++ b/test/image/mocks/geo_canadian-cities.json
@@ -2,7 +2,7 @@
     "data": [
         {
             "type": "scattergeo",
-            "mode": "markers+text",
+            "mode": "markers+lines+text",
             "lon": [
                 -73.57,
                 -79.24,
@@ -39,7 +39,13 @@
                 "Winnepeg",
                 "Regina"
             ],
+            "line": {
+                "color": "green",
+                "dash": "dot"
+            },
             "marker": {
+                "symbol": "arrow-wide",
+                "angleref": "previous",
                 "size": 10,
                 "color": [
                     "#bebada",
diff --git a/test/image/mocks/geo_conic-conformal.json b/test/image/mocks/geo_conic-conformal.json
index e5da3ed1933..60d74a42b56 100644
--- a/test/image/mocks/geo_conic-conformal.json
+++ b/test/image/mocks/geo_conic-conformal.json
@@ -10,6 +10,9 @@
                 "FRA"
             ],
             "marker": {
+                "symbol": "arrow",
+                "angleref": "north",
+                "angle": [0, -90, 180, 90, 0],
                 "color": [
                     80,
                     20,
diff --git a/test/image/mocks/geo_scattergeo-out-of-usa.json b/test/image/mocks/geo_scattergeo-out-of-usa.json
index 6ed41dc33ec..37d407649ec 100644
--- a/test/image/mocks/geo_scattergeo-out-of-usa.json
+++ b/test/image/mocks/geo_scattergeo-out-of-usa.json
@@ -20009,7 +20009,12 @@
             ],
             "mode": "markers",
             "marker": {
-                "color": "#19d3f3"
+                "symbol": "arrow",
+                "angleref": "north",
+                "angle": -90,
+                "size": 12,
+                "opacity": 0.1,
+                "color": "blue"
             },
             "type": "scattergeo"
         }
diff --git a/test/image/mocks/geo_text_chart_arrays.json b/test/image/mocks/geo_text_chart_arrays.json
index b6b5a763827..a12e4b2efd3 100644
--- a/test/image/mocks/geo_text_chart_arrays.json
+++ b/test/image/mocks/geo_text_chart_arrays.json
@@ -40,7 +40,9 @@
                 "Regina"
             ],
             "marker": {
-                "size": 10,
+                "symbol": "arrow",
+                "angleref": "north",
+                "size": 20,
                 "color": [
                     "#bebada",
                     "#fdb462",
diff --git a/test/image/mocks/gl2d_marker_symbols.json b/test/image/mocks/gl2d_marker_symbols.json
index 8460619f1b4..a805c471405 100644
--- a/test/image/mocks/gl2d_marker_symbols.json
+++ b/test/image/mocks/gl2d_marker_symbols.json
@@ -8,14 +8,18 @@
         1, 1, 1, 1, 1, 1, 1, 1, 1,
         2, 2, 2, 2, 2, 2, 2, 2, 2,
         3, 3, 3, 3, 3, 3, 3, 3, 3,
-        4, 4, 4, 4, 4, 4, 4, 4, 4
+        4, 4, 4, 4, 4, 4, 4, 4, 4,
+        5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5,
+        7
       ],
       "y": [
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
-        0, 1, 2, 3, 4, 5, 6, 7, 8
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0
       ],
       "marker": {
         "symbol": [
@@ -63,7 +67,17 @@
           "line-ew",
           "line-ns",
           "line-ne",
-          "line-nw"
+          "line-nw",
+          "arrow-up",
+          "arrow-down",
+          "arrow-left",
+          "arrow-right",
+          "arrow-bar-up",
+          "arrow-bar-down",
+          "arrow-bar-left",
+          "arrow-bar-right",
+          "arrow",
+          "arrow-wide"
         ],
         "color": "blue",
         "size": 20,
@@ -117,7 +131,17 @@
         "marker symbol: line-ew
number: 41",
         "marker symbol: line-ns
number: 42",
         "marker symbol: line-ne
number: 43",
-        "marker symbol: line-nw
number: 44"
+        "marker symbol: line-nw
number: 44",
+        "marker symbol: arrow-up
number: 45",
+        "marker symbol: arrow-down
number: 46",
+        "marker symbol: arrow-left
number: 47",
+        "marker symbol: arrow-right
number: 48",
+        "marker symbol: arrow-bar-up
number: 49",
+        "marker symbol: arrow-bar-down
number: 50",
+        "marker symbol: arrow-bar-left
number: 51",
+        "marker symbol: arrow-bar-right
number: 52",
+        "marker symbol: arrow
number: 53",
+        "marker symbol: arrow-wide
number: 54"
       ],
       "hoverinfo": "text"
     },
@@ -125,18 +149,22 @@
       "type": "scattergl",
       "mode": "markers",
       "x": [
-        5, 5, 5, 5, 5, 5, 5, 5, 5,
-        6, 6, 6, 6, 6, 6, 6, 6, 6,
-        7, 7, 7, 7, 7, 7, 7, 7, 7,
-        8, 8, 8, 8, 8, 8, 8, 8, 8,
-        9, 9, 9, 9, 9, 9, 9, 9, 9
+        9, 9, 9, 9, 9, 9, 9, 9, 9,
+        10, 10, 10, 10, 10, 10, 10, 10, 10,
+        11, 11, 11, 11, 11, 11, 11, 11, 11,
+        12, 12, 12, 12, 12, 12, 12, 12, 12,
+        13, 13, 13, 13, 13, 13, 13, 13, 13,
+        14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5,
+        16
       ],
       "y": [
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
-        0, 1, 2, 3, 4, 5, 6, 7, 8
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0
       ],
       "marker": {
         "symbol": [
@@ -184,7 +212,17 @@
           "line-ew-open",
           "line-ns-open",
           "line-ne-open",
-          "line-nw-open"
+          "line-nw-open",
+          "arrow-up-open",
+          "arrow-down-open",
+          "arrow-left-open",
+          "arrow-right-open",
+          "arrow-bar-up-open",
+          "arrow-bar-down-open",
+          "arrow-bar-left-open",
+          "arrow-bar-right-open",
+          "arrow-open",
+          "arrow-wide-open"
         ],
         "color": "blue",
         "size": 20,
@@ -238,7 +276,17 @@
         "marker symbol: line-ew-open
number: 141",
         "marker symbol: line-ns-open
number: 142",
         "marker symbol: line-ne-open
number: 143",
-        "marker symbol: line-nw-open
number: 144"
+        "marker symbol: line-nw-open
number: 144",
+        "marker symbol: arrow-up-open
number: 145",
+        "marker symbol: arrow-down-open
number: 146",
+        "marker symbol: arrow-left-open
number: 147",
+        "marker symbol: arrow-right-open
number: 148",
+        "marker symbol: arrow-bar-up-open
number: 149",
+        "marker symbol: arrow-bar-down-open
number: 150",
+        "marker symbol: arrow-bar-left-open
number: 151",
+        "marker symbol: arrow-bar-right-open
number: 152",
+        "marker symbol: arrow-open
number: 153",
+        "marker symbol: arrow-wide-open
number: 154"
       ],
       "hoverinfo": "text"
     },
@@ -246,18 +294,22 @@
       "type": "scattergl",
       "mode": "markers",
       "x": [
-        10, 10, 10, 10, 10, 10, 10, 10, 10,
-        11, 11, 11, 11, 11, 11, 11, 11, 11,
-        12, 12, 12, 12, 12, 12, 12, 12, 12,
-        13, 13, 13, 13, 13, 13, 13, 13, 13,
-        14, 14, 14, 14, 14, 14, 14, 14, 14
+        18, 18, 18, 18, 18, 18, 18, 18, 18,
+        19, 19, 19, 19, 19, 19, 19, 19, 19,
+        20, 20, 20, 20, 20, 20, 20, 20, 20,
+        21, 21, 21, 21, 21, 21, 21, 21, 21,
+        22, 22, 22, 22, 22, 22, 22, 22, 22,
+        23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5,
+        25
       ],
       "y": [
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
-        0, 1, 2, 3, 4, 5, 6, 7, 8
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0
       ],
       "marker": {
         "symbol": [
@@ -305,7 +357,17 @@
           "line-ew-dot",
           "line-ns-dot",
           "line-ne-dot",
-          "line-nw-dot"
+          "line-nw-dot",
+          "arrow-up-dot",
+          "arrow-down-dot",
+          "arrow-left-dot",
+          "arrow-right-dot",
+          "arrow-bar-up-dot",
+          "arrow-bar-down-dot",
+          "arrow-bar-left-dot",
+          "arrow-bar-right-dot",
+          "arrow-dot",
+          "arrow-wide-dot"
         ],
         "color": "blue",
         "size": 20,
@@ -359,7 +421,17 @@
         "marker symbol: line-ew-dot
number: 241",
         "marker symbol: line-ns-dot
number: 242",
         "marker symbol: line-ne-dot
number: 243",
-        "marker symbol: line-nw-dot
number: 244"
+        "marker symbol: line-nw-dot
number: 244",
+        "marker symbol: arrow-up-dot
number: 245",
+        "marker symbol: arrow-down-dot
number: 246",
+        "marker symbol: arrow-left-dot
number: 247",
+        "marker symbol: arrow-right-dot
number: 248",
+        "marker symbol: arrow-bar-up-dot
number: 249",
+        "marker symbol: arrow-bar-down-dot
number: 250",
+        "marker symbol: arrow-bar-left-dot
number: 251",
+        "marker symbol: arrow-bar-right-dot
number: 252",
+        "marker symbol: arrow-dot
number: 253",
+        "marker symbol: arrow-wide-dot
number: 254"
       ],
       "hoverinfo": "text"
     },
@@ -367,18 +439,22 @@
       "type": "scattergl",
       "mode": "markers",
       "x": [
-        15, 15, 15, 15, 15, 15, 15, 15, 15,
-        16, 16, 16, 16, 16, 16, 16, 16, 16,
-        17, 17, 17, 17, 17, 17, 17, 17, 17,
-        18, 18, 18, 18, 18, 18, 18, 18, 18,
-        19, 19, 19, 19, 19, 19, 19, 19, 19
+        27, 27, 27, 27, 27, 27, 27, 27, 27,
+        28, 28, 28, 28, 28, 28, 28, 28, 28,
+        29, 29, 29, 29, 29, 29, 29, 29, 29,
+        30, 30, 30, 30, 30, 30, 30, 30, 30,
+        31, 31, 31, 31, 31, 31, 31, 31, 31,
+        32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5,
+        34
       ],
       "y": [
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
-        0, 1, 2, 3, 4, 5, 6, 7, 8
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0
       ],
       "marker": {
         "symbol": [
@@ -426,7 +502,17 @@
           "line-ew-open-dot",
           "line-ns-open-dot",
           "line-ne-open-dot",
-          "line-nw-open-dot"
+          "line-nw-open-dot",
+          "arrow-up-open-dot",
+          "arrow-down-open-dot",
+          "arrow-left-open-dot",
+          "arrow-right-open-dot",
+          "arrow-bar-up-open-dot",
+          "arrow-bar-down-open-dot",
+          "arrow-bar-left-open-dot",
+          "arrow-bar-right-open-dot",
+          "arrow-open-dot",
+          "arrow-wide-open-dot"
         ],
         "color": "blue",
         "size": 20,
@@ -480,7 +566,18 @@
         "marker symbol: line-ew-open-dot
number: 341",
         "marker symbol: line-ns-open-dot
number: 342",
         "marker symbol: line-ne-open-dot
number: 343",
-        "marker symbol: line-nw-open-dot
number: 344"
+        "marker symbol: line-nw-open-dot
number: 344",
+        "marker symbol: line-nw-open-dot
number: 344",
+        "marker symbol: arrow-up-open-dot
number: 345",
+        "marker symbol: arrow-down-open-dot
number: 346",
+        "marker symbol: arrow-left-open-dot
number: 347",
+        "marker symbol: arrow-right-open-dot
number: 348",
+        "marker symbol: arrow-bar-up-open-dot
number: 349",
+        "marker symbol: arrow-bar-down-open-dot
number: 350",
+        "marker symbol: arrow-bar-left-open-dot
number: 351",
+        "marker symbol: arrow-bar-right-open-dot
number: 352",
+        "marker symbol: arrow-open-dot
number: 353",
+        "marker symbol: arrow-wide-open-dot
number: 354"
       ],
       "hoverinfo": "text"
     }
@@ -492,7 +589,7 @@
       "b": 0,
       "t": 0
     },
-    "width": 800,
+    "width": 1500,
     "height": 500,
     "xaxis": {
       "showgrid": false,
diff --git a/test/image/mocks/glpolar_subplots.json b/test/image/mocks/glpolar_subplots.json
index 78a58cbda8d..888d36ae69f 100644
--- a/test/image/mocks/glpolar_subplots.json
+++ b/test/image/mocks/glpolar_subplots.json
@@ -6,7 +6,7 @@
     "theta": [0, 45, 180],
     "text": ["A0", "B0", "C0"],
     "hovertext": ["hover A0", "hover B0", "hover C0"],
-    "marker": {"symbol": "square", "size": 15},
+    "marker": {"symbol": "arrow", "angle": [0, 135, 180], "size": 15},
     "textfont": {
       "color": ["red", "green", "blue"],
       "size": 20
diff --git a/test/image/mocks/marker_symbols.json b/test/image/mocks/marker_symbols.json
index 7509fa7547f..197af9d1f9b 100644
--- a/test/image/mocks/marker_symbols.json
+++ b/test/image/mocks/marker_symbols.json
@@ -7,14 +7,18 @@
         1, 1, 1, 1, 1, 1, 1, 1, 1,
         2, 2, 2, 2, 2, 2, 2, 2, 2,
         3, 3, 3, 3, 3, 3, 3, 3, 3,
-        4, 4, 4, 4, 4, 4, 4, 4, 4
+        4, 4, 4, 4, 4, 4, 4, 4, 4,
+        5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5,
+        7
       ],
       "y": [
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
-        0, 1, 2, 3, 4, 5, 6, 7, 8
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0
       ],
       "marker": {
         "symbol": [
@@ -62,7 +66,17 @@
           "line-ew",
           "line-ns",
           "line-ne",
-          "line-nw"
+          "line-nw",
+          "arrow-up",
+          "arrow-down",
+          "arrow-left",
+          "arrow-right",
+          "arrow-bar-up",
+          "arrow-bar-down",
+          "arrow-bar-left",
+          "arrow-bar-right",
+          "arrow",
+          "arrow-wide"
         ],
         "color": "blue",
         "size": 20,
@@ -116,25 +130,39 @@
         "marker symbol: line-ew
number: 41",
         "marker symbol: line-ns
number: 42",
         "marker symbol: line-ne
number: 43",
-        "marker symbol: line-nw
number: 44"
+        "marker symbol: line-nw
number: 44",
+        "marker symbol: arrow-up
number: 45",
+        "marker symbol: arrow-down
number: 46",
+        "marker symbol: arrow-left
number: 47",
+        "marker symbol: arrow-right
number: 48",
+        "marker symbol: arrow-bar-up
number: 49",
+        "marker symbol: arrow-bar-down
number: 50",
+        "marker symbol: arrow-bar-left
number: 51",
+        "marker symbol: arrow-bar-right
number: 52",
+        "marker symbol: arrow
number: 53",
+        "marker symbol: arrow-wide
number: 54"
       ],
       "hoverinfo": "text"
     },
     {
       "mode": "markers",
       "x": [
-        5, 5, 5, 5, 5, 5, 5, 5, 5,
-        6, 6, 6, 6, 6, 6, 6, 6, 6,
-        7, 7, 7, 7, 7, 7, 7, 7, 7,
-        8, 8, 8, 8, 8, 8, 8, 8, 8,
-        9, 9, 9, 9, 9, 9, 9, 9, 9
+        9, 9, 9, 9, 9, 9, 9, 9, 9,
+        10, 10, 10, 10, 10, 10, 10, 10, 10,
+        11, 11, 11, 11, 11, 11, 11, 11, 11,
+        12, 12, 12, 12, 12, 12, 12, 12, 12,
+        13, 13, 13, 13, 13, 13, 13, 13, 13,
+        14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5,
+        16
       ],
       "y": [
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
-        0, 1, 2, 3, 4, 5, 6, 7, 8
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0
       ],
       "marker": {
         "symbol": [
@@ -182,7 +210,17 @@
           "line-ew-open",
           "line-ns-open",
           "line-ne-open",
-          "line-nw-open"
+          "line-nw-open",
+          "arrow-up-open",
+          "arrow-down-open",
+          "arrow-left-open",
+          "arrow-right-open",
+          "arrow-bar-up-open",
+          "arrow-bar-down-open",
+          "arrow-bar-left-open",
+          "arrow-bar-right-open",
+          "arrow-open",
+          "arrow-wide-open"
         ],
         "color": "blue",
         "size": 20,
@@ -236,25 +274,39 @@
         "marker symbol: line-ew-open
number: 141",
         "marker symbol: line-ns-open
number: 142",
         "marker symbol: line-ne-open
number: 143",
-        "marker symbol: line-nw-open
number: 144"
+        "marker symbol: line-nw-open
number: 144",
+        "marker symbol: arrow-up-open
number: 145",
+        "marker symbol: arrow-down-open
number: 146",
+        "marker symbol: arrow-left-open
number: 147",
+        "marker symbol: arrow-right-open
number: 148",
+        "marker symbol: arrow-bar-up-open
number: 149",
+        "marker symbol: arrow-bar-down-open
number: 150",
+        "marker symbol: arrow-bar-left-open
number: 151",
+        "marker symbol: arrow-bar-right-open
number: 152",
+        "marker symbol: arrow-open
number: 153",
+        "marker symbol: arrow-wide-open
number: 154"
       ],
       "hoverinfo": "text"
     },
     {
       "mode": "markers",
       "x": [
-        10, 10, 10, 10, 10, 10, 10, 10, 10,
-        11, 11, 11, 11, 11, 11, 11, 11, 11,
-        12, 12, 12, 12, 12, 12, 12, 12, 12,
-        13, 13, 13, 13, 13, 13, 13, 13, 13,
-        14, 14, 14, 14, 14, 14, 14, 14, 14
+        18, 18, 18, 18, 18, 18, 18, 18, 18,
+        19, 19, 19, 19, 19, 19, 19, 19, 19,
+        20, 20, 20, 20, 20, 20, 20, 20, 20,
+        21, 21, 21, 21, 21, 21, 21, 21, 21,
+        22, 22, 22, 22, 22, 22, 22, 22, 22,
+        23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5,
+        25
       ],
       "y": [
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
-        0, 1, 2, 3, 4, 5, 6, 7, 8
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0
       ],
       "marker": {
         "symbol": [
@@ -302,7 +354,17 @@
           "line-ew-dot",
           "line-ns-dot",
           "line-ne-dot",
-          "line-nw-dot"
+          "line-nw-dot",
+          "arrow-up-dot",
+          "arrow-down-dot",
+          "arrow-left-dot",
+          "arrow-right-dot",
+          "arrow-bar-up-dot",
+          "arrow-bar-down-dot",
+          "arrow-bar-left-dot",
+          "arrow-bar-right-dot",
+          "arrow-dot",
+          "arrow-wide-dot"
         ],
         "color": "blue",
         "size": 20,
@@ -356,25 +418,39 @@
         "marker symbol: line-ew-dot
number: 241",
         "marker symbol: line-ns-dot
number: 242",
         "marker symbol: line-ne-dot
number: 243",
-        "marker symbol: line-nw-dot
number: 244"
+        "marker symbol: line-nw-dot
number: 244",
+        "marker symbol: arrow-up-dot
number: 245",
+        "marker symbol: arrow-down-dot
number: 246",
+        "marker symbol: arrow-left-dot
number: 247",
+        "marker symbol: arrow-right-dot
number: 248",
+        "marker symbol: arrow-bar-up-dot
number: 249",
+        "marker symbol: arrow-bar-down-dot
number: 250",
+        "marker symbol: arrow-bar-left-dot
number: 251",
+        "marker symbol: arrow-bar-right-dot
number: 252",
+        "marker symbol: arrow-dot
number: 253",
+        "marker symbol: arrow-wide-dot
number: 254"
       ],
       "hoverinfo": "text"
     },
     {
       "mode": "markers",
       "x": [
-        15, 15, 15, 15, 15, 15, 15, 15, 15,
-        16, 16, 16, 16, 16, 16, 16, 16, 16,
-        17, 17, 17, 17, 17, 17, 17, 17, 17,
-        18, 18, 18, 18, 18, 18, 18, 18, 18,
-        19, 19, 19, 19, 19, 19, 19, 19, 19
+        27, 27, 27, 27, 27, 27, 27, 27, 27,
+        28, 28, 28, 28, 28, 28, 28, 28, 28,
+        29, 29, 29, 29, 29, 29, 29, 29, 29,
+        30, 30, 30, 30, 30, 30, 30, 30, 30,
+        31, 31, 31, 31, 31, 31, 31, 31, 31,
+        32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5,
+        34
       ],
       "y": [
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
         0, 1, 2, 3, 4, 5, 6, 7, 8,
-        0, 1, 2, 3, 4, 5, 6, 7, 8
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0
       ],
       "marker": {
         "symbol": [
@@ -422,7 +498,17 @@
           "line-ew-open-dot",
           "line-ns-open-dot",
           "line-ne-open-dot",
-          "line-nw-open-dot"
+          "line-nw-open-dot",
+          "arrow-up-open-dot",
+          "arrow-down-open-dot",
+          "arrow-left-open-dot",
+          "arrow-right-open-dot",
+          "arrow-bar-up-open-dot",
+          "arrow-bar-down-open-dot",
+          "arrow-bar-left-open-dot",
+          "arrow-bar-right-open-dot",
+          "arrow-open-dot",
+          "arrow-wide-open-dot"
         ],
         "color": "blue",
         "size": 20,
@@ -476,7 +562,18 @@
         "marker symbol: line-ew-open-dot
number: 341",
         "marker symbol: line-ns-open-dot
number: 342",
         "marker symbol: line-ne-open-dot
number: 343",
-        "marker symbol: line-nw-open-dot
number: 344"
+        "marker symbol: line-nw-open-dot
number: 344",
+        "marker symbol: line-nw-open-dot
number: 344",
+        "marker symbol: arrow-up-open-dot
number: 345",
+        "marker symbol: arrow-down-open-dot
number: 346",
+        "marker symbol: arrow-left-open-dot
number: 347",
+        "marker symbol: arrow-right-open-dot
number: 348",
+        "marker symbol: arrow-bar-up-open-dot
number: 349",
+        "marker symbol: arrow-bar-down-open-dot
number: 350",
+        "marker symbol: arrow-bar-left-open-dot
number: 351",
+        "marker symbol: arrow-bar-right-open-dot
number: 352",
+        "marker symbol: arrow-open-dot
number: 353",
+        "marker symbol: arrow-wide-open-dot
number: 354"
       ],
       "hoverinfo": "text"
     }
@@ -488,7 +585,7 @@
       "b": 0,
       "t": 0
     },
-    "width": 800,
+    "width": 1500,
     "height": 500,
     "xaxis": {
       "showgrid": false,
diff --git a/test/image/mocks/polar_direction.json b/test/image/mocks/polar_direction.json
index bf044ab7011..6c017abba78 100644
--- a/test/image/mocks/polar_direction.json
+++ b/test/image/mocks/polar_direction.json
@@ -21,8 +21,9 @@
       },
       "marker": {
         "color": "#8090c7",
-        "symbol": "square",
-        "size": 8
+        "symbol": "arrow",
+        "angleref": "previous",
+        "size": 12
       },
       "subplot": "polar",
       "text": "sector: 0->360
rotation: -90
direction: clockwise"
@@ -48,8 +49,9 @@
       },
       "marker": {
         "color": "#8090c7",
-        "symbol": "square",
-        "size": 8
+        "symbol": "arrow",
+        "angleref": "previous",
+        "size": 12
       },
       "subplot": "polar2",
       "text": "sector: 0->360
rotation: 90
direction: clockwise"
@@ -75,8 +77,9 @@
       },
       "marker": {
         "color": "#8090c7",
-        "symbol": "square",
-        "size": 8
+        "symbol": "arrow",
+        "angleref": "previous",
+        "size": 12
       },
       "subplot": "polar3",
       "text": "sector: 45->135
rotation: 90
direction: clockwise"
@@ -102,8 +105,9 @@
       },
       "marker": {
         "color": "#8090c7",
-        "symbol": "square",
-        "size": 8
+        "symbol": "arrow",
+        "angleref": "previous",
+        "size": 12
       },
       "subplot": "polar4",
       "text": "sector: 135->225
rotation: 90
direction: clockwise"
@@ -129,8 +133,9 @@
       },
       "marker": {
         "color": "#8090c7",
-        "symbol": "square",
-        "size": 8
+        "symbol": "arrow",
+        "angleref": "previous",
+        "size": 12
       },
       "subplot": "polar5",
       "text": "sector: 135->225
rotation: 90
direction: counterclockwise"
@@ -156,8 +161,9 @@
       },
       "marker": {
         "color": "#8090c7",
-        "symbol": "square",
-        "size": 8
+        "symbol": "arrow",
+        "angleref": "previous",
+        "size": 12
       },
       "subplot": "polar6",
       "text": "sector: 45->135
rotation: 90
direction: counterclockwise"
@@ -183,8 +189,9 @@
       },
       "marker": {
         "color": "#8090c7",
-        "symbol": "square",
-        "size": 8
+        "symbol": "arrow",
+        "angleref": "previous",
+        "size": 12
       },
       "subplot": "polar7",
       "text": "sector: 0->360
rotation: 90
direction: counterclockwise"
@@ -210,8 +217,9 @@
       },
       "marker": {
         "color": "#8090c7",
-        "symbol": "square",
-        "size": 8
+        "symbol": "arrow",
+        "angleref": "previous",
+        "size": 12
       },
       "subplot": "polar8",
       "text": "sector: 0->360
rotation: 0
direction: clockwise"
@@ -237,8 +245,9 @@
       },
       "marker": {
         "color": "#8090c7",
-        "symbol": "square",
-        "size": 8
+        "symbol": "arrow",
+        "angleref": "previous",
+        "size": 12
       },
       "subplot": "polar9",
       "text": "sector: 0->360
rotation: 0
direction: counterclockwise"
@@ -271,12 +280,12 @@
       },
       "radialaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         }
       },
       "angularaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         },
         "rotation": -90,
         "direction": "clockwise"
@@ -299,12 +308,12 @@
       },
       "radialaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         }
       },
       "angularaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         },
         "rotation": 90,
         "direction": "clockwise"
@@ -327,12 +336,12 @@
       },
       "radialaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         }
       },
       "angularaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         },
         "rotation": 90,
         "direction": "clockwise"
@@ -355,12 +364,12 @@
       },
       "radialaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         }
       },
       "angularaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         },
         "rotation": 90,
         "direction": "clockwise"
@@ -383,12 +392,12 @@
       },
       "radialaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         }
       },
       "angularaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         },
         "rotation": 90,
         "direction": "counterclockwise"
@@ -411,12 +420,12 @@
       },
       "radialaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         }
       },
       "angularaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         },
         "rotation": 90,
         "direction": "counterclockwise"
@@ -439,12 +448,12 @@
       },
       "radialaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         }
       },
       "angularaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         },
         "rotation": 90,
         "direction": "counterclockwise"
@@ -467,12 +476,12 @@
       },
       "radialaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         }
       },
       "angularaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         },
         "rotation": 0,
         "direction": "clockwise"
@@ -495,12 +504,12 @@
       },
       "radialaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         }
       },
       "angularaxis": {
         "tickfont": {
-          "size": 8
+          "size": 12
         },
         "rotation": 0,
         "direction": "counterclockwise"
diff --git a/test/image/mocks/scattercarpet-on-two-carpets.json b/test/image/mocks/scattercarpet-on-two-carpets.json
index dbac1ca27b8..2e52c1d5e90 100644
--- a/test/image/mocks/scattercarpet-on-two-carpets.json
+++ b/test/image/mocks/scattercarpet-on-two-carpets.json
@@ -47,7 +47,12 @@
         "smoothing": 1,
         "color": "blue"
       },
-      "mode": "lines",
+      "mode": "lines+markers",
+      "marker": {
+        "size": 15,
+        "symbol": "arrow",
+        "angleref": "previous"
+      },
       "carpet": "c0"
     },
     {
@@ -59,7 +64,13 @@
         "smoothing": 1,
         "color": "red"
       },
-      "mode": "lines",
+      "mode": "lines+markers",
+      "marker": {
+        "size": 15,
+        "symbol": "arrow",
+        "angle": 90,
+        "angleref": "previous"
+      },
       "carpet": "c1"
     }
   ]
diff --git a/test/image/mocks/smith_gaps.json b/test/image/mocks/smith_gaps.json
index f1c64de8cd1..11b5136f919 100644
--- a/test/image/mocks/smith_gaps.json
+++ b/test/image/mocks/smith_gaps.json
@@ -10,6 +10,7 @@
         0, 0.2, 0.4, null, 0.8, 1, null, 2, 3, null, 5
       ],
       "mode": "markers+lines",
+      "marker": { "size": 10, "symbol": "arrow-wide", "angleref": "previous" },
       "line": { "shape": "spline" }
     },
     {
@@ -23,6 +24,7 @@
         0, 0.2, 0.4, null, 0.8, 1, null, 2, 3, null, 5
       ],
       "mode": "markers+lines",
+      "marker": { "size": 20, "symbol": "arrow", "angleref": "previous", "angle": -90 },
       "line": { "shape": "spline" },
       "subplot": "smith2"
     }
diff --git a/test/image/mocks/splom_log.json b/test/image/mocks/splom_log.json
index 8ec446f0c26..75618cd4fe2 100644
--- a/test/image/mocks/splom_log.json
+++ b/test/image/mocks/splom_log.json
@@ -9,6 +9,8 @@
       "label": "B"
     }],
     "marker": {
+      "symbol": "arrow",
+      "angle": 180,
       "size": 20,
       "line": {"width": 2, "color": "#444"},
       "color": [10, 40, 100],
diff --git a/test/image/mocks/ternary_markers.json b/test/image/mocks/ternary_markers.json
index 6283daa5d29..c7cd6f851a9 100644
--- a/test/image/mocks/ternary_markers.json
+++ b/test/image/mocks/ternary_markers.json
@@ -72,9 +72,10 @@
                 "color": "#DB7365"
             },
             "marker": {
-                "symbol": 100,
+                "symbol": "arrow",
+                "angleref": "previous",
                 "color": "#DB7365",
-                "size": 14,
+                "size": 10,
                 "line": {
                     "width": 2
                 }
diff --git a/test/image/mocks/violin_ridgeplot.json b/test/image/mocks/violin_ridgeplot.json
index 1cccf678e1b..988fe62eb70 100644
--- a/test/image/mocks/violin_ridgeplot.json
+++ b/test/image/mocks/violin_ridgeplot.json
@@ -11,7 +11,7 @@
       "hovertemplate": "day=%{y}
total_bill=%{x}",
       "legendgroup": "day=Fri",
       "marker": {
-        "color": "#636efa"
+        "color": "#636efa", "symbol": "arrow", "angle": 180, "size": 20
       },
       "name": "day=Fri",
       "offsetgroup": "day=Fri",
@@ -80,7 +80,7 @@
       "hovertemplate": "day=%{y}
total_bill=%{x}",
       "legendgroup": "day=Sat",
       "marker": {
-        "color": "#EF553B"
+        "color": "#EF553B", "symbol": "arrow", "angle": 45, "size": 20
       },
       "name": "day=Sat",
       "offsetgroup": "day=Sat",
@@ -285,7 +285,7 @@
       "hovertemplate": "day=%{y}
total_bill=%{x}",
       "legendgroup": "day=Sun",
       "marker": {
-        "color": "#00cc96"
+        "color": "#00cc96", "symbol": "arrow", "angle": 0, "size": 20
       },
       "name": "day=Sun",
       "offsetgroup": "day=Sun",
@@ -468,7 +468,7 @@
       "hovertemplate": "day=%{y}
total_bill=%{x}",
       "legendgroup": "day=Thur",
       "marker": {
-        "color": "#ab63fa"
+        "color": "#ab63fa", "symbol": "arrow", "angle": -45, "size": 20
       },
       "name": "day=Thur",
       "offsetgroup": "day=Thur",
diff --git a/test/image/mocks/z-gl2d_marker_symbols-angle.json b/test/image/mocks/z-gl2d_marker_symbols-angle.json
new file mode 100644
index 00000000000..2b24b4d1eb7
--- /dev/null
+++ b/test/image/mocks/z-gl2d_marker_symbols-angle.json
@@ -0,0 +1,607 @@
+{
+  "data": [
+    {
+      "type": "scattergl",
+      "mode": "markers",
+      "x": [
+        0, 0, 0, 0, 0, 0, 0, 0, 0,
+        1, 1, 1, 1, 1, 1, 1, 1, 1,
+        2, 2, 2, 2, 2, 2, 2, 2, 2,
+        3, 3, 3, 3, 3, 3, 3, 3, 3,
+        4, 4, 4, 4, 4, 4, 4, 4, 4,
+        5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5,
+        7
+      ],
+      "y": [
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0
+      ],
+      "marker": { "angle": 15,
+        "symbol": [
+          "circle",
+          "square",
+          "diamond",
+          "cross",
+          "x",
+          "triangle-up",
+          "triangle-down",
+          "triangle-left",
+          "triangle-right",
+          "triangle-ne",
+          "triangle-se",
+          "triangle-sw",
+          "triangle-nw",
+          "pentagon",
+          "hexagon",
+          "hexagon2",
+          "octagon",
+          "star",
+          "hexagram",
+          "star-triangle-up",
+          "star-triangle-down",
+          "star-square",
+          "star-diamond",
+          "diamond-tall",
+          "diamond-wide",
+          "hourglass",
+          "bowtie",
+          "circle-cross",
+          "circle-x",
+          "square-cross",
+          "square-x",
+          "diamond-cross",
+          "diamond-x",
+          "cross-thin",
+          "x-thin",
+          "asterisk",
+          "hash",
+          "y-up",
+          "y-down",
+          "y-left",
+          "y-right",
+          "line-ew",
+          "line-ns",
+          "line-ne",
+          "line-nw",
+          "arrow-up",
+          "arrow-down",
+          "arrow-left",
+          "arrow-right",
+          "arrow-bar-up",
+          "arrow-bar-down",
+          "arrow-bar-left",
+          "arrow-bar-right",
+          "arrow",
+          "arrow-wide"
+        ],
+        "color": "blue",
+        "size": 20,
+        "line": {
+          "color": "orange",
+          "width": 1.5
+        }
+      },
+      "text": [
+        "marker symbol: circle
number: 0",
+        "marker symbol: square
number: 1",
+        "marker symbol: diamond
number: 2",
+        "marker symbol: cross
number: 3",
+        "marker symbol: x
number: 4",
+        "marker symbol: triangle-up
number: 5",
+        "marker symbol: triangle-down
number: 6",
+        "marker symbol: triangle-left
number: 7",
+        "marker symbol: triangle-right
number: 8",
+        "marker symbol: triangle-ne
number: 9",
+        "marker symbol: triangle-se
number: 10",
+        "marker symbol: triangle-sw
number: 11",
+        "marker symbol: triangle-nw
number: 12",
+        "marker symbol: pentagon
number: 13",
+        "marker symbol: hexagon
number: 14",
+        "marker symbol: hexagon2
number: 15",
+        "marker symbol: octagon
number: 16",
+        "marker symbol: star
number: 17",
+        "marker symbol: hexagram
number: 18",
+        "marker symbol: star-triangle-up
number: 19",
+        "marker symbol: star-triangle-down
number: 20",
+        "marker symbol: star-square
number: 21",
+        "marker symbol: star-diamond
number: 22",
+        "marker symbol: diamond-tall
number: 23",
+        "marker symbol: diamond-wide
number: 24",
+        "marker symbol: hourglass
number: 25",
+        "marker symbol: bowtie
number: 26",
+        "marker symbol: circle-cross
number: 27",
+        "marker symbol: circle-x
number: 28",
+        "marker symbol: square-cross
number: 29",
+        "marker symbol: square-x
number: 30",
+        "marker symbol: diamond-cross
number: 31",
+        "marker symbol: diamond-x
number: 32",
+        "marker symbol: cross-thin
number: 33",
+        "marker symbol: x-thin
number: 34",
+        "marker symbol: asterisk
number: 35",
+        "marker symbol: hash
number: 36",
+        "marker symbol: y-up
number: 37",
+        "marker symbol: y-down
number: 38",
+        "marker symbol: y-left
number: 39",
+        "marker symbol: y-right
number: 40",
+        "marker symbol: line-ew
number: 41",
+        "marker symbol: line-ns
number: 42",
+        "marker symbol: line-ne
number: 43",
+        "marker symbol: line-nw
number: 44",
+        "marker symbol: arrow-up
number: 45",
+        "marker symbol: arrow-down
number: 46",
+        "marker symbol: arrow-left
number: 47",
+        "marker symbol: arrow-right
number: 48",
+        "marker symbol: arrow-bar-up
number: 49",
+        "marker symbol: arrow-bar-down
number: 50",
+        "marker symbol: arrow-bar-left
number: 51",
+        "marker symbol: arrow-bar-right
number: 52",
+        "marker symbol: arrow
number: 53",
+        "marker symbol: arrow-wide
number: 54"
+      ],
+      "hoverinfo": "text"
+    },
+    {
+      "type": "scattergl",
+      "mode": "markers",
+      "x": [
+        9, 9, 9, 9, 9, 9, 9, 9, 9,
+        10, 10, 10, 10, 10, 10, 10, 10, 10,
+        11, 11, 11, 11, 11, 11, 11, 11, 11,
+        12, 12, 12, 12, 12, 12, 12, 12, 12,
+        13, 13, 13, 13, 13, 13, 13, 13, 13,
+        14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5,
+        16
+      ],
+      "y": [
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0
+      ],
+      "marker": { "angle": 15,
+        "symbol": [
+          "circle-open",
+          "square-open",
+          "diamond-open",
+          "cross-open",
+          "x-open",
+          "triangle-up-open",
+          "triangle-down-open",
+          "triangle-left-open",
+          "triangle-right-open",
+          "triangle-ne-open",
+          "triangle-se-open",
+          "triangle-sw-open",
+          "triangle-nw-open",
+          "pentagon-open",
+          "hexagon-open",
+          "hexagon2-open",
+          "octagon-open",
+          "star-open",
+          "hexagram-open",
+          "star-triangle-up-open",
+          "star-triangle-down-open",
+          "star-square-open",
+          "star-diamond-open",
+          "diamond-tall-open",
+          "diamond-wide-open",
+          "hourglass-open",
+          "bowtie-open",
+          "circle-cross-open",
+          "circle-x-open",
+          "square-cross-open",
+          "square-x-open",
+          "diamond-cross-open",
+          "diamond-x-open",
+          "cross-thin-open",
+          "x-thin-open",
+          "asterisk-open",
+          "hash-open",
+          "y-up-open",
+          "y-down-open",
+          "y-left-open",
+          "y-right-open",
+          "line-ew-open",
+          "line-ns-open",
+          "line-ne-open",
+          "line-nw-open",
+          "arrow-up-open",
+          "arrow-down-open",
+          "arrow-left-open",
+          "arrow-right-open",
+          "arrow-bar-up-open",
+          "arrow-bar-down-open",
+          "arrow-bar-left-open",
+          "arrow-bar-right-open",
+          "arrow-open",
+          "arrow-wide-open"
+        ],
+        "color": "blue",
+        "size": 20,
+        "line": {
+          "color": "orange",
+          "width": 1.5
+        }
+      },
+      "text": [
+        "marker symbol: circle-open
number: 100",
+        "marker symbol: square-open
number: 101",
+        "marker symbol: diamond-open
number: 102",
+        "marker symbol: cross-open
number: 103",
+        "marker symbol: x-open
number: 104",
+        "marker symbol: triangle-up-open
number: 105",
+        "marker symbol: triangle-down-open
number: 106",
+        "marker symbol: triangle-left-open
number: 107",
+        "marker symbol: triangle-right-open
number: 108",
+        "marker symbol: triangle-ne-open
number: 109",
+        "marker symbol: triangle-se-open
number: 110",
+        "marker symbol: triangle-sw-open
number: 111",
+        "marker symbol: triangle-nw-open
number: 112",
+        "marker symbol: pentagon-open
number: 113",
+        "marker symbol: hexagon-open
number: 114",
+        "marker symbol: hexagon2-open
number: 115",
+        "marker symbol: octagon-open
number: 116",
+        "marker symbol: star-open
number: 117",
+        "marker symbol: hexagram-open
number: 118",
+        "marker symbol: star-triangle-up-open
number: 119",
+        "marker symbol: star-triangle-down-open
number: 120",
+        "marker symbol: star-square-open
number: 121",
+        "marker symbol: star-diamond-open
number: 122",
+        "marker symbol: diamond-tall-open
number: 123",
+        "marker symbol: diamond-wide-open
number: 124",
+        "marker symbol: hourglass-open
number: 125",
+        "marker symbol: bowtie-open
number: 126",
+        "marker symbol: circle-cross-open
number: 127",
+        "marker symbol: circle-x-open
number: 128",
+        "marker symbol: square-cross-open
number: 129",
+        "marker symbol: square-x-open
number: 130",
+        "marker symbol: diamond-cross-open
number: 131",
+        "marker symbol: diamond-x-open
number: 132",
+        "marker symbol: cross-thin-open
number: 133",
+        "marker symbol: x-thin-open
number: 134",
+        "marker symbol: asterisk-open
number: 135",
+        "marker symbol: hash-open
number: 136",
+        "marker symbol: y-up-open
number: 137",
+        "marker symbol: y-down-open
number: 138",
+        "marker symbol: y-left-open
number: 139",
+        "marker symbol: y-right-open
number: 140",
+        "marker symbol: line-ew-open
number: 141",
+        "marker symbol: line-ns-open
number: 142",
+        "marker symbol: line-ne-open
number: 143",
+        "marker symbol: line-nw-open
number: 144",
+        "marker symbol: arrow-up-open
number: 145",
+        "marker symbol: arrow-down-open
number: 146",
+        "marker symbol: arrow-left-open
number: 147",
+        "marker symbol: arrow-right-open
number: 148",
+        "marker symbol: arrow-bar-up-open
number: 149",
+        "marker symbol: arrow-bar-down-open
number: 150",
+        "marker symbol: arrow-bar-left-open
number: 151",
+        "marker symbol: arrow-bar-right-open
number: 152",
+        "marker symbol: arrow-open
number: 153",
+        "marker symbol: arrow-wide-open
number: 154"
+      ],
+      "hoverinfo": "text"
+    },
+    {
+      "type": "scattergl",
+      "mode": "markers",
+      "x": [
+        18, 18, 18, 18, 18, 18, 18, 18, 18,
+        19, 19, 19, 19, 19, 19, 19, 19, 19,
+        20, 20, 20, 20, 20, 20, 20, 20, 20,
+        21, 21, 21, 21, 21, 21, 21, 21, 21,
+        22, 22, 22, 22, 22, 22, 22, 22, 22,
+        23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5,
+        25
+      ],
+      "y": [
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0
+      ],
+      "marker": { "angle": 15,
+        "symbol": [
+          "circle-dot",
+          "square-dot",
+          "diamond-dot",
+          "cross-dot",
+          "x-dot",
+          "triangle-up-dot",
+          "triangle-down-dot",
+          "triangle-left-dot",
+          "triangle-right-dot",
+          "triangle-ne-dot",
+          "triangle-se-dot",
+          "triangle-sw-dot",
+          "triangle-nw-dot",
+          "pentagon-dot",
+          "hexagon-dot",
+          "hexagon2-dot",
+          "octagon-dot",
+          "star-dot",
+          "hexagram-dot",
+          "star-triangle-up-dot",
+          "star-triangle-down-dot",
+          "star-square-dot",
+          "star-diamond-dot",
+          "diamond-tall-dot",
+          "diamond-wide-dot",
+          "hourglass-dot",
+          "bowtie-dot",
+          "circle-cross-dot",
+          "circle-x-dot",
+          "square-cross-dot",
+          "square-x-dot",
+          "diamond-cross-dot",
+          "diamond-x-dot",
+          "cross-thin-dot",
+          "x-thin-dot",
+          "asterisk-dot",
+          "hash-dot",
+          "y-up-dot",
+          "y-down-dot",
+          "y-left-dot",
+          "y-right-dot",
+          "line-ew-dot",
+          "line-ns-dot",
+          "line-ne-dot",
+          "line-nw-dot",
+          "arrow-up-dot",
+          "arrow-down-dot",
+          "arrow-left-dot",
+          "arrow-right-dot",
+          "arrow-bar-up-dot",
+          "arrow-bar-down-dot",
+          "arrow-bar-left-dot",
+          "arrow-bar-right-dot",
+          "arrow-dot",
+          "arrow-wide-dot"
+        ],
+        "color": "blue",
+        "size": 20,
+        "line": {
+          "color": "orange",
+          "width": 1.5
+        }
+      },
+      "text": [
+        "marker symbol: circle-dot
number: 200",
+        "marker symbol: square-dot
number: 201",
+        "marker symbol: diamond-dot
number: 202",
+        "marker symbol: cross-dot
number: 203",
+        "marker symbol: x-dot
number: 204",
+        "marker symbol: triangle-up-dot
number: 205",
+        "marker symbol: triangle-down-dot
number: 206",
+        "marker symbol: triangle-left-dot
number: 207",
+        "marker symbol: triangle-right-dot
number: 208",
+        "marker symbol: triangle-ne-dot
number: 209",
+        "marker symbol: triangle-se-dot
number: 210",
+        "marker symbol: triangle-sw-dot
number: 211",
+        "marker symbol: triangle-nw-dot
number: 212",
+        "marker symbol: pentagon-dot
number: 213",
+        "marker symbol: hexagon-dot
number: 214",
+        "marker symbol: hexagon2-dot
number: 215",
+        "marker symbol: octagon-dot
number: 216",
+        "marker symbol: star-dot
number: 217",
+        "marker symbol: hexagram-dot
number: 218",
+        "marker symbol: star-triangle-up-dot
number: 219",
+        "marker symbol: star-triangle-down-dot
number: 220",
+        "marker symbol: star-square-dot
number: 221",
+        "marker symbol: star-diamond-dot
number: 222",
+        "marker symbol: diamond-tall-dot
number: 223",
+        "marker symbol: diamond-wide-dot
number: 224",
+        "marker symbol: hourglass-dot
number: 225",
+        "marker symbol: bowtie-dot
number: 226",
+        "marker symbol: circle-cross-dot
number: 227",
+        "marker symbol: circle-x-dot
number: 228",
+        "marker symbol: square-cross-dot
number: 229",
+        "marker symbol: square-x-dot
number: 230",
+        "marker symbol: diamond-cross-dot
number: 231",
+        "marker symbol: diamond-x-dot
number: 232",
+        "marker symbol: cross-thin-dot
number: 233",
+        "marker symbol: x-thin-dot
number: 234",
+        "marker symbol: asterisk-dot
number: 235",
+        "marker symbol: hash-dot
number: 236",
+        "marker symbol: y-up-dot
number: 237",
+        "marker symbol: y-down-dot
number: 238",
+        "marker symbol: y-left-dot
number: 239",
+        "marker symbol: y-right-dot
number: 240",
+        "marker symbol: line-ew-dot
number: 241",
+        "marker symbol: line-ns-dot
number: 242",
+        "marker symbol: line-ne-dot
number: 243",
+        "marker symbol: line-nw-dot
number: 244",
+        "marker symbol: arrow-up-dot
number: 245",
+        "marker symbol: arrow-down-dot
number: 246",
+        "marker symbol: arrow-left-dot
number: 247",
+        "marker symbol: arrow-right-dot
number: 248",
+        "marker symbol: arrow-bar-up-dot
number: 249",
+        "marker symbol: arrow-bar-down-dot
number: 250",
+        "marker symbol: arrow-bar-left-dot
number: 251",
+        "marker symbol: arrow-bar-right-dot
number: 252",
+        "marker symbol: arrow-dot
number: 253",
+        "marker symbol: arrow-wide-dot
number: 254"
+      ],
+      "hoverinfo": "text"
+    },
+    {
+      "type": "scattergl",
+      "mode": "markers",
+      "x": [
+        27, 27, 27, 27, 27, 27, 27, 27, 27,
+        28, 28, 28, 28, 28, 28, 28, 28, 28,
+        29, 29, 29, 29, 29, 29, 29, 29, 29,
+        30, 30, 30, 30, 30, 30, 30, 30, 30,
+        31, 31, 31, 31, 31, 31, 31, 31, 31,
+        32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5,
+        34
+      ],
+      "y": [
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0
+      ],
+      "marker": { "angle": 15,
+        "symbol": [
+          "circle-open-dot",
+          "square-open-dot",
+          "diamond-open-dot",
+          "cross-open-dot",
+          "x-open-dot",
+          "triangle-up-open-dot",
+          "triangle-down-open-dot",
+          "triangle-left-open-dot",
+          "triangle-right-open-dot",
+          "triangle-ne-open-dot",
+          "triangle-se-open-dot",
+          "triangle-sw-open-dot",
+          "triangle-nw-open-dot",
+          "pentagon-open-dot",
+          "hexagon-open-dot",
+          "hexagon2-open-dot",
+          "octagon-open-dot",
+          "star-open-dot",
+          "hexagram-open-dot",
+          "star-triangle-up-open-dot",
+          "star-triangle-down-open-dot",
+          "star-square-open-dot",
+          "star-diamond-open-dot",
+          "diamond-tall-open-dot",
+          "diamond-wide-open-dot",
+          "hourglass-open-dot",
+          "bowtie-open-dot",
+          "circle-cross-open-dot",
+          "circle-x-open-dot",
+          "square-cross-open-dot",
+          "square-x-open-dot",
+          "diamond-cross-open-dot",
+          "diamond-x-open-dot",
+          "cross-thin-open-dot",
+          "x-thin-open-dot",
+          "asterisk-open-dot",
+          "hash-open-dot",
+          "y-up-open-dot",
+          "y-down-open-dot",
+          "y-left-open-dot",
+          "y-right-open-dot",
+          "line-ew-open-dot",
+          "line-ns-open-dot",
+          "line-ne-open-dot",
+          "line-nw-open-dot",
+          "arrow-up-open-dot",
+          "arrow-down-open-dot",
+          "arrow-left-open-dot",
+          "arrow-right-open-dot",
+          "arrow-bar-up-open-dot",
+          "arrow-bar-down-open-dot",
+          "arrow-bar-left-open-dot",
+          "arrow-bar-right-open-dot",
+          "arrow-open-dot",
+          "arrow-wide-open-dot"
+        ],
+        "color": "blue",
+        "size": 20,
+        "line": {
+          "color": "orange",
+          "width": 1.5
+        }
+      },
+      "text": [
+        "marker symbol: circle-open-dot
number: 300",
+        "marker symbol: square-open-dot
number: 301",
+        "marker symbol: diamond-open-dot
number: 302",
+        "marker symbol: cross-open-dot
number: 303",
+        "marker symbol: x-open-dot
number: 304",
+        "marker symbol: triangle-up-open-dot
number: 305",
+        "marker symbol: triangle-down-open-dot
number: 306",
+        "marker symbol: triangle-left-open-dot
number: 307",
+        "marker symbol: triangle-right-open-dot
number: 308",
+        "marker symbol: triangle-ne-open-dot
number: 309",
+        "marker symbol: triangle-se-open-dot
number: 310",
+        "marker symbol: triangle-sw-open-dot
number: 311",
+        "marker symbol: triangle-nw-open-dot
number: 312",
+        "marker symbol: pentagon-open-dot
number: 313",
+        "marker symbol: hexagon-open-dot
number: 314",
+        "marker symbol: hexagon2-open-dot
number: 315",
+        "marker symbol: octagon-open-dot
number: 316",
+        "marker symbol: star-open-dot
number: 317",
+        "marker symbol: hexagram-open-dot
number: 318",
+        "marker symbol: star-triangle-up-open-dot
number: 319",
+        "marker symbol: star-triangle-down-open-dot
number: 320",
+        "marker symbol: star-square-open-dot
number: 321",
+        "marker symbol: star-diamond-open-dot
number: 322",
+        "marker symbol: diamond-tall-open-dot
number: 323",
+        "marker symbol: diamond-wide-open-dot
number: 324",
+        "marker symbol: hourglass-open-dot
number: 325",
+        "marker symbol: bowtie-open-dot
number: 326",
+        "marker symbol: circle-cross-open-dot
number: 327",
+        "marker symbol: circle-x-open-dot
number: 328",
+        "marker symbol: square-cross-open-dot
number: 329",
+        "marker symbol: square-x-open-dot
number: 330",
+        "marker symbol: diamond-cross-open-dot
number: 331",
+        "marker symbol: diamond-x-open-dot
number: 332",
+        "marker symbol: cross-thin-open-dot
number: 333",
+        "marker symbol: x-thin-open-dot
number: 334",
+        "marker symbol: asterisk-open-dot
number: 335",
+        "marker symbol: hash-open-dot
number: 336",
+        "marker symbol: y-up-open-dot
number: 337",
+        "marker symbol: y-down-open-dot
number: 338",
+        "marker symbol: y-left-open-dot
number: 339",
+        "marker symbol: y-right-open-dot
number: 340",
+        "marker symbol: line-ew-open-dot
number: 341",
+        "marker symbol: line-ns-open-dot
number: 342",
+        "marker symbol: line-ne-open-dot
number: 343",
+        "marker symbol: line-nw-open-dot
number: 344",
+        "marker symbol: line-nw-open-dot
number: 344",
+        "marker symbol: arrow-up-open-dot
number: 345",
+        "marker symbol: arrow-down-open-dot
number: 346",
+        "marker symbol: arrow-left-open-dot
number: 347",
+        "marker symbol: arrow-right-open-dot
number: 348",
+        "marker symbol: arrow-bar-up-open-dot
number: 349",
+        "marker symbol: arrow-bar-down-open-dot
number: 350",
+        "marker symbol: arrow-bar-left-open-dot
number: 351",
+        "marker symbol: arrow-bar-right-open-dot
number: 352",
+        "marker symbol: arrow-open-dot
number: 353",
+        "marker symbol: arrow-wide-open-dot
number: 354"
+      ],
+      "hoverinfo": "text"
+    }
+  ],
+  "layout": {
+    "margin": {
+      "l": 0,
+      "r": 0,
+      "b": 0,
+      "t": 0
+    },
+    "width": 1500,
+    "height": 500,
+    "xaxis": {
+      "showgrid": false,
+      "zeroline": false
+    },
+    "yaxis": {
+      "showgrid": false,
+      "zeroline": false,
+      "autorange": "reversed"
+    },
+    "showlegend": false,
+    "plot_bgcolor": "#d3d3d3",
+    "hovermode": "closest"
+  }
+}
diff --git a/test/image/mocks/z-line-shape-arrow.json b/test/image/mocks/z-line-shape-arrow.json
new file mode 100644
index 00000000000..d4951d6b0f8
--- /dev/null
+++ b/test/image/mocks/z-line-shape-arrow.json
@@ -0,0 +1,176 @@
+{
+    "data": [
+        {
+            "line": {
+                "shape": "linear"
+            },
+            "mode": "lines+markers",
+            "name": "'linear'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 10
+            },
+            "x": [
+                1,
+                2,
+                3,
+                4,
+                5
+            ],
+            "y": [
+                1,
+                3,
+                2,
+                3,
+                1
+            ]
+        },
+        {
+            "line": {
+                "shape": "spline"
+            },
+            "mode": "lines+markers",
+            "name": "'spline'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 10
+            },
+            "x": [
+                1,
+                2,
+                3,
+                4,
+                5
+            ],
+            "y": [
+                6,
+                8,
+                7,
+                8,
+                6
+            ]
+        },
+        {
+            "line": {
+                "shape": "vhv"
+            },
+            "mode": "lines+markers",
+            "name": "'vhv'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 10
+            },
+            "x": [
+                1,
+                2,
+                3,
+                4,
+                5
+            ],
+            "y": [
+                11,
+                13,
+                12,
+                13,
+                11
+            ]
+        },
+        {
+            "line": {
+                "shape": "hvh"
+            },
+            "mode": "lines+markers",
+            "name": "'hvh'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 10
+            },
+            "x": [
+                1,
+                2,
+                3,
+                4,
+                5
+            ],
+            "y": [
+                16,
+                18,
+                17,
+                18,
+                16
+            ]
+        },
+        {
+            "line": {
+                "shape": "vh"
+            },
+            "mode": "lines+markers",
+            "name": "'vh'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 10
+            },
+            "x": [
+                1,
+                2,
+                3,
+                4,
+                5
+            ],
+            "y": [
+                21,
+                23,
+                22,
+                23,
+                21
+            ]
+        },
+        {
+            "line": {
+                "shape": "hv"
+            },
+            "mode": "lines+markers",
+            "name": "'hv'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 10
+            },
+            "x": [
+                1,
+                2,
+                3,
+                4,
+                5
+            ],
+            "y": [
+                26,
+                28,
+                27,
+                28,
+                26
+            ]
+        }
+    ],
+    "layout": {
+        "title": {
+            "text" : "line shape and arrows"
+        },
+        "legend": {
+            "orientation": "h"
+        },
+        "height": 700,
+        "width": 700
+    }
+}
diff --git a/test/image/mocks/z-line-shape-arrow_backoff-auto.json b/test/image/mocks/z-line-shape-arrow_backoff-auto.json
new file mode 100644
index 00000000000..977cd481758
--- /dev/null
+++ b/test/image/mocks/z-line-shape-arrow_backoff-auto.json
@@ -0,0 +1,242 @@
+{
+    "data": [
+        {
+            "line": {
+                "width": 5,
+                "shape": "linear"
+            },
+            "mode": "lines+markers",
+            "name": "'linear'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "x": [
+                1,
+                2,
+                null,
+                2,
+                3,
+                null,
+                3,
+                4,
+                null,
+                4,
+                5
+            ],
+            "y": [
+                1,
+                3,
+                null,
+                3,
+                2,
+                null,
+                2,
+                3,
+                null,
+                3,
+                1
+            ]
+        },
+        {
+            "line": {
+                "width": 5,
+                "shape": "spline"
+            },
+            "mode": "lines+markers",
+            "name": "'spline'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "x": [
+                1,
+                2,
+                3,
+                4,
+                5
+            ],
+            "y": [
+                6,
+                8,
+                7,
+                8,
+                6
+            ]
+        },
+        {
+            "line": {
+                "width": 5,
+                "shape": "vhv"
+            },
+            "mode": "lines+markers",
+            "name": "'vhv'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "x": [
+                1,
+                2,
+                null,
+                2,
+                3,
+                null,
+                3,
+                4,
+                null,
+                4,
+                5
+            ],
+            "y": [
+                11,
+                13,
+                null,
+                13,
+                12,
+                null,
+                12,
+                13,
+                null,
+                13,
+                11
+            ]
+        },
+        {
+            "line": {
+                "width": 5,
+                "shape": "hvh"
+            },
+            "mode": "lines+markers",
+            "name": "'hvh'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "x": [
+                1,
+                2,
+                null,
+                2,
+                3,
+                null,
+                3,
+                4,
+                null,
+                4,
+                5
+            ],
+            "y": [
+                16,
+                18,
+                null,
+                18,
+                17,
+                null,
+                17,
+                18,
+                null,
+                18,
+                16
+            ]
+        },
+        {
+            "line": {
+                "width": 5,
+                "shape": "vh"
+            },
+            "mode": "lines+markers",
+            "name": "'vh'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "x": [
+                1,
+                2,
+                null,
+                2,
+                3,
+                null,
+                3,
+                4,
+                null,
+                4,
+                5
+            ],
+            "y": [
+                21,
+                23,
+                null,
+                23,
+                22,
+                null,
+                22,
+                23,
+                null,
+                23,
+                21
+            ]
+        },
+        {
+            "line": {
+                "width": 5,
+                "shape": "hv"
+            },
+            "mode": "lines+markers",
+            "name": "'hv'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "x": [
+                1,
+                2,
+                null,
+                2,
+                3,
+                null,
+                3,
+                4,
+                null,
+                4,
+                5
+            ],
+            "y": [
+                26,
+                28,
+                null,
+                28,
+                27,
+                null,
+                27,
+                28,
+                null,
+                28,
+                26
+            ]
+        }
+    ],
+    "layout": {
+        "title": {
+            "text" : "line shape and arrows with auto backoff"
+        },
+        "legend": {
+            "orientation": "h"
+        },
+        "height": 700,
+        "width": 700
+    }
+}
diff --git a/test/image/mocks/z-line-shape-arrow_backoff.json b/test/image/mocks/z-line-shape-arrow_backoff.json
new file mode 100644
index 00000000000..b10bd07187a
--- /dev/null
+++ b/test/image/mocks/z-line-shape-arrow_backoff.json
@@ -0,0 +1,188 @@
+{
+    "data": [
+        {
+            "line": {
+                "backoff": 10,
+                "width": 5,
+                "shape": "linear"
+            },
+            "mode": "lines+markers",
+            "name": "'linear'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "x": [
+                1,
+                2,
+                3,
+                4,
+                5
+            ],
+            "y": [
+                1,
+                3,
+                2,
+                3,
+                1
+            ]
+        },
+        {
+            "line": {
+                "backoff": 10,
+                "width": 5,
+                "shape": "spline"
+            },
+            "mode": "lines+markers",
+            "name": "'spline'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "x": [
+                1,
+                2,
+                3,
+                4,
+                5
+            ],
+            "y": [
+                6,
+                8,
+                7,
+                8,
+                6
+            ]
+        },
+        {
+            "line": {
+                "backoff": 10,
+                "width": 5,
+                "shape": "vhv"
+            },
+            "mode": "lines+markers",
+            "name": "'vhv'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "x": [
+                1,
+                2,
+                3,
+                4,
+                5
+            ],
+            "y": [
+                11,
+                13,
+                12,
+                13,
+                11
+            ]
+        },
+        {
+            "line": {
+                "backoff": 10,
+                "width": 5,
+                "shape": "hvh"
+            },
+            "mode": "lines+markers",
+            "name": "'hvh'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "x": [
+                1,
+                2,
+                3,
+                4,
+                5
+            ],
+            "y": [
+                16,
+                18,
+                17,
+                18,
+                16
+            ]
+        },
+        {
+            "line": {
+                "backoff": 10,
+                "width": 5,
+                "shape": "vh"
+            },
+            "mode": "lines+markers",
+            "name": "'vh'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "x": [
+                1,
+                2,
+                3,
+                4,
+                5
+            ],
+            "y": [
+                21,
+                23,
+                22,
+                23,
+                21
+            ]
+        },
+        {
+            "line": {
+                "backoff": 10,
+                "width": 5,
+                "shape": "hv"
+            },
+            "mode": "lines+markers",
+            "name": "'hv'",
+            "type": "scatter",
+            "marker": {
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "x": [
+                1,
+                2,
+                3,
+                4,
+                5
+            ],
+            "y": [
+                26,
+                28,
+                27,
+                28,
+                26
+            ]
+        }
+    ],
+    "layout": {
+        "title": {
+            "text" : "line shape and arrows with backoff"
+        },
+        "legend": {
+            "orientation": "h"
+        },
+        "height": 700,
+        "width": 700
+    }
+}
diff --git a/test/image/mocks/z-marker-standoff.json b/test/image/mocks/z-marker-standoff.json
new file mode 100644
index 00000000000..cd4925f7c9e
--- /dev/null
+++ b/test/image/mocks/z-marker-standoff.json
@@ -0,0 +1,403 @@
+{
+    "data": [
+       {
+            "mode": "markers+lines",
+            "line": {
+                "width": 5,
+                "backoff": [30, 20]
+            },
+            "marker": {
+                "standoff": [5, 20, null, 20, 10],
+                "angleref": "previous",
+                "symbol": "arrow",
+                "size": 20
+            },
+            "x": [
+                1,
+                2,
+                null,
+                2,
+                3
+            ],
+            "y": [
+                3,
+                1,
+                null,
+                1,
+                2
+            ]
+        },
+        {
+            "mode": "markers+text",
+            "marker": {
+                "opacity": 1,
+                "size": [10, 40, 20]
+            },
+            "text": [10, 40, 20],
+            "textposition": ["right", "center", "top"],
+            "x": [
+                1,
+                2,
+                3
+            ],
+            "y": [
+                3,
+                1,
+                2
+            ]
+        },
+        {
+            "type": "scattergeo",
+            "mode": "markers+lines",
+            "line": { "width": 5 },
+            "marker": {
+                "standoff": [5, 20, 10, 2.5],
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "lon": [
+                -30,
+                0,
+                30,
+                15
+            ],
+            "lat": [
+                20,
+                -5,
+                10,
+                -20
+            ]
+        },
+        {
+            "type": "scattergeo",
+            "mode": "markers+text",
+            "marker": {
+                "opacity": 1,
+                "size": [10, 40, 20, 5]
+            },
+            "text": [10, 40, 20, 5],
+            "textposition": ["right", "center", "top", "bottom"],
+            "lon": [
+                -30,
+                0,
+                30,
+                15
+            ],
+            "lat": [
+                20,
+                -5,
+                10,
+                -20
+            ]
+        },
+        {
+            "type": "scatterternary",
+            "mode": "markers+lines",
+            "line": {
+                "width": 5,
+                "backoff": [30, 20]
+            },
+            "marker": {
+                "standoff": [5, 20, null, 20, 10],
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "a": [
+                2,
+                1,
+                null,
+                1,
+                1
+            ],
+            "b": [
+                1,
+                2,
+                null,
+                2,
+                1
+            ],
+            "c": [
+                1,
+                1,
+                null,
+                1,
+                2
+            ]
+        },
+        {
+            "type": "scatterternary",
+            "mode": "markers+text",
+            "marker": {
+                "opacity": 1,
+                "size": [10, 40, 20]
+            },
+            "text": [10, 40, 20],
+            "textposition": ["right", "center", "top"],
+            "a": [
+                2,
+                1,
+                1
+            ],
+            "b": [
+                1,
+                2,
+                1
+            ],
+            "c": [
+                1,
+                1,
+                2
+            ]
+        },
+        {
+            "xaxis": "x2",
+            "yaxis": "y2",
+            "type": "carpet",
+            "a": [ 4, 4, 4, 4.5, 4.5, 4.5, 5, 5, 5, 6, 6, 6 ],
+            "b": [ 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3 ],
+            "y": [ 2, 3.5, 4, 3, 4.5, 5, 5.5, 6.5, 7.5, 8, 8.5, 10 ],
+            "aaxis": {
+              "tickprefix": "a = ",
+              "ticksuffix": "m",
+              "smoothing": 1,
+              "minorgridcount": 9
+            },
+            "baxis": {
+              "tickprefix": "b = ",
+              "ticksuffix": "Pa",
+              "smoothing": 1,
+              "minorgridcount": 9
+            },
+            "carpet": "c0"
+        },
+        {
+            "xaxis": "x2",
+            "yaxis": "y2",
+            "type": "scattercarpet",
+            "mode": "markers+lines",
+            "line": {
+                "width": 5,
+                "backoff": [30, 20]
+            },
+            "marker": {
+                "standoff": [5, 20, null, 20, 10],
+                "angleref": "previous",
+                "symbol": "arrow",
+                "size": 20
+            },
+            "a": [ 4, 5, null, 5, 6 ],
+            "b": [ 2.5, 2.5, null, 2.5, 2.5 ],
+            "carpet": "c0"
+        },
+        {
+            "xaxis": "x2",
+            "yaxis": "y2",
+            "type": "scattercarpet",
+            "mode": "markers+text",
+            "marker": {
+                "opacity": 1,
+                "size": [10, 40, 20]
+            },
+            "text": [10, 40, 20],
+            "textposition": ["right", "center", "top"],
+            "a": [ 4, 5, 6 ],
+            "b": [ 2.5, 2.5, 2.5 ],
+            "carpet": "c0"
+        },
+        {
+            "subplot": "polar",
+            "type": "scatterpolar",
+            "mode": "markers+lines",
+            "line": {
+                "width": 5,
+                "backoff": [30, 20]
+            },
+            "marker": {
+                "standoff": [5, 20, null, 20, 10],
+                "angleref": "previous",
+                "symbol": "arrow",
+                "size": 20
+            },
+            "r": [
+                1,
+                2,
+                null,
+                2,
+                3
+            ],
+            "theta": [
+                270,
+                90,
+                null,
+                90,
+                180
+            ]
+        },
+        {
+            "subplot": "polar",
+            "type": "scatterpolar",
+            "mode": "markers+text",
+            "marker": {
+                "opacity": 1,
+                "size": [10, 40, 20]
+            },
+            "text": [10, 40, 20],
+            "textposition": ["right", "center", "top"],
+            "r": [
+                1,
+                2,
+                3
+            ],
+            "theta": [
+                270,
+                90,
+                180
+            ]
+        },
+        {
+            "subplot": "smith",
+            "type": "scattersmith",
+            "mode": "markers+lines",
+            "line": {
+                "width": 5,
+                "backoff": [30, 20]
+            },
+            "marker": {
+                "standoff": [5, 20, null, 20, 10],
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "real": [
+                1,
+                2,
+                null,
+                2,
+                0.5
+            ],
+            "imag": [
+                -0.5,
+                1,
+                null,
+                1,
+                1
+            ]
+        },
+        {
+            "subplot": "smith",
+            "type": "scattersmith",
+            "mode": "markers+text",
+            "marker": {
+                "opacity": 1,
+                "size": [10, 40, 20]
+            },
+            "text": [10, 40, 20],
+            "textposition": ["right", "center", "top"],
+            "real": [
+                1,
+                2,
+                0.5
+            ],
+            "imag": [
+                -0.5,
+                1,
+                1
+            ]
+        }
+    ],
+    "layout": {
+        "xaxis": {
+            "domain": [
+                0,
+                0.3
+            ]
+        },
+        "yaxis": {
+            "domain": [
+                0,
+                0.45
+            ]
+        },
+        "xaxis2": {
+            "anchor": "y2",
+            "domain": [
+                0.35,
+                0.65
+            ]
+        },
+        "yaxis2": {
+            "anchor": "x2",
+            "domain": [
+                0.55,
+                1
+            ]
+        },
+        "geo": {
+            "projection": {
+                "type": "satellite"
+			},
+            "domain": {
+                "x": [
+                    0.35,
+                    0.65
+                ],
+                "y": [
+                    0,
+                    0.45
+                ]
+            }
+        },
+        "ternary": {
+            "domain": {
+                "x": [
+                    0,
+                    0.3
+                ],
+                "y": [
+                    0.55,
+                    1
+                ]
+            },
+            "aaxis": {
+                "title": {"text": "A"}
+            },
+            "baxis": {
+                "title": {"text": "B"}
+            },
+            "caxis": {
+                "title": {"text": "C"}
+            }
+        },
+        "polar": {
+            "domain": {
+                "x": [
+                    0.7,
+                    1
+                ],
+                "y": [
+                    0,
+                    0.45
+                ]
+            }
+        },
+        "smith": {
+            "domain": {
+                "x": [
+                    0.7,
+                    1
+                ],
+                "y": [
+                    0.55,
+                    1
+                ]
+            }
+        },
+        "height": 900,
+        "width": 1200,
+        "legend": {
+            "orientation": "h"
+        }
+    }
+}
diff --git a/test/image/mocks/z-marker-standoff_auto-backoff.json b/test/image/mocks/z-marker-standoff_auto-backoff.json
new file mode 100644
index 00000000000..fad2bed639b
--- /dev/null
+++ b/test/image/mocks/z-marker-standoff_auto-backoff.json
@@ -0,0 +1,402 @@
+{
+    "data": [
+       {
+            "mode": "markers+lines",
+            "line": {
+                "width": 5
+            },
+            "marker": {
+                "line": {
+                    "width": 2,
+                    "color": "darkblue"
+                },
+                "standoff": [5, 20, null, 20, 10],
+                "angleref": "previous",
+                "symbol": "arrow-bar-up",
+                "size": 20
+            },
+            "x": [
+                1,
+                2,
+                null,
+                2,
+                3
+            ],
+            "y": [
+                3,
+                1,
+                null,
+                1,
+                2
+            ]
+        },
+        {
+            "mode": "markers+text",
+            "marker": {
+                "opacity": 1,
+                "size": [10, 40, 20]
+            },
+            "text": [10, 40, 20],
+            "textposition": ["right", "center", "top"],
+            "x": [
+                1,
+                2,
+                3
+            ],
+            "y": [
+                3,
+                1,
+                2
+            ]
+        },
+        {
+            "type": "scattergeo",
+            "mode": "markers+lines",
+            "line": { "width": 5 },
+            "marker": {
+                "standoff": [5, 20, null, 20, 10],
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "lon": [
+                -30,
+                0,
+                null,
+                0,
+                30
+            ],
+            "lat": [
+                20,
+                -5,
+                null,
+                -5,
+                10
+            ]
+        },
+        {
+            "type": "scattergeo",
+            "mode": "markers+text",
+            "marker": {
+                "opacity": 1,
+                "size": [10, 40, 20]
+            },
+            "text": [10, 40, 20],
+            "textposition": ["right", "center", "top"],
+            "lon": [
+                -30,
+                0,
+                30
+            ],
+            "lat": [
+                20,
+                -5,
+                10
+            ]
+        },
+        {
+            "type": "scatterternary",
+            "mode": "markers+lines",
+            "line": {
+                "width": 5
+            },
+            "marker": {
+                "standoff": [5, 20, null, 20, 10],
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "a": [
+                2,
+                1,
+                null,
+                1,
+                1
+            ],
+            "b": [
+                1,
+                2,
+                null,
+                2,
+                1
+            ],
+            "c": [
+                1,
+                1,
+                null,
+                1,
+                2
+            ]
+        },
+        {
+            "type": "scatterternary",
+            "mode": "markers+text",
+            "marker": {
+                "opacity": 1,
+                "size": [10, 40, 20]
+            },
+            "text": [10, 40, 20],
+            "textposition": ["right", "center", "top"],
+            "a": [
+                2,
+                1,
+                1
+            ],
+            "b": [
+                1,
+                2,
+                1
+            ],
+            "c": [
+                1,
+                1,
+                2
+            ]
+        },
+        {
+            "xaxis": "x2",
+            "yaxis": "y2",
+            "type": "carpet",
+            "a": [ 4, 4, 4, 4.5, 4.5, 4.5, 5, 5, 5, 6, 6, 6 ],
+            "b": [ 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3 ],
+            "y": [ 2, 3.5, 4, 3, 4.5, 5, 5.5, 6.5, 7.5, 8, 8.5, 10 ],
+            "aaxis": {
+              "tickprefix": "a = ",
+              "ticksuffix": "m",
+              "smoothing": 1,
+              "minorgridcount": 9
+            },
+            "baxis": {
+              "tickprefix": "b = ",
+              "ticksuffix": "Pa",
+              "smoothing": 1,
+              "minorgridcount": 9
+            },
+            "carpet": "c0"
+        },
+        {
+            "xaxis": "x2",
+            "yaxis": "y2",
+            "type": "scattercarpet",
+            "mode": "markers+lines",
+            "line": {
+                "width": 5
+            },
+            "marker": {
+                "standoff": [5, 20, null, 20, 10],
+                "angleref": "previous",
+                "symbol": "arrow",
+                "size": 20
+            },
+            "a": [ 4, 5, null, 5, 6 ],
+            "b": [ 2.5, 2.5, null, 2.5, 2.5 ],
+            "carpet": "c0"
+        },
+        {
+            "xaxis": "x2",
+            "yaxis": "y2",
+            "type": "scattercarpet",
+            "mode": "markers+text",
+            "marker": {
+                "opacity": 1,
+                "size": [10, 40, 20]
+            },
+            "text": [10, 40, 20],
+            "textposition": ["right", "center", "top"],
+            "a": [ 4, 5, 6 ],
+            "b": [ 2.5, 2.5, 2.5 ],
+            "carpet": "c0"
+        },
+        {
+            "subplot": "polar",
+            "type": "scatterpolar",
+            "mode": "markers+lines",
+            "line": {
+                "width": 5
+            },
+            "marker": {
+                "standoff": [5, 20, null, 20, 10],
+                "angleref": "previous",
+                "symbol": "arrow-up",
+                "size": 20
+            },
+            "r": [
+                1,
+                2,
+                null,
+                2,
+                3
+            ],
+            "theta": [
+                270,
+                90,
+                null,
+                90,
+                180
+            ]
+        },
+        {
+            "subplot": "polar",
+            "type": "scatterpolar",
+            "mode": "markers+text",
+            "marker": {
+                "opacity": 1,
+                "size": [10, 40, 20]
+            },
+            "text": [10, 40, 20],
+            "textposition": ["right", "center", "top"],
+            "r": [
+                1,
+                2,
+                3
+            ],
+            "theta": [
+                270,
+                90,
+                180
+            ]
+        },
+        {
+            "subplot": "smith",
+            "type": "scattersmith",
+            "mode": "markers+lines",
+            "line": {
+                "width": 5
+            },
+            "marker": {
+                "standoff": [5, 20, null, 20, 10],
+                "angleref": "previous",
+                "symbol": "arrow-wide",
+                "size": 20
+            },
+            "real": [
+                1,
+                2,
+                null,
+                2,
+                0.5
+            ],
+            "imag": [
+                -0.5,
+                1,
+                null,
+                1,
+                1
+            ]
+        },
+        {
+            "subplot": "smith",
+            "type": "scattersmith",
+            "mode": "markers+text",
+            "marker": {
+                "opacity": 1,
+                "size": [10, 40, 20]
+            },
+            "text": [10, 40, 20],
+            "textposition": ["right", "center", "top"],
+            "real": [
+                1,
+                2,
+                0.5
+            ],
+            "imag": [
+                -0.5,
+                1,
+                1
+            ]
+        }
+    ],
+    "layout": {
+        "xaxis": {
+            "domain": [
+                0,
+                0.3
+            ]
+        },
+        "yaxis": {
+            "domain": [
+                0,
+                0.45
+            ]
+        },
+        "xaxis2": {
+            "anchor": "y2",
+            "domain": [
+                0.35,
+                0.65
+            ]
+        },
+        "yaxis2": {
+            "anchor": "x2",
+            "domain": [
+                0.55,
+                1
+            ]
+        },
+        "geo": {
+            "projection": {
+                "type": "satellite"
+			},
+            "domain": {
+                "x": [
+                    0.35,
+                    0.65
+                ],
+                "y": [
+                    0,
+                    0.45
+                ]
+            }
+        },
+        "ternary": {
+            "domain": {
+                "x": [
+                    0,
+                    0.3
+                ],
+                "y": [
+                    0.55,
+                    1
+                ]
+            },
+            "aaxis": {
+                "title": {"text": "A"}
+            },
+            "baxis": {
+                "title": {"text": "B"}
+            },
+            "caxis": {
+                "title": {"text": "C"}
+            }
+        },
+        "polar": {
+            "domain": {
+                "x": [
+                    0.7,
+                    1
+                ],
+                "y": [
+                    0,
+                    0.45
+                ]
+            }
+        },
+        "smith": {
+            "domain": {
+                "x": [
+                    0.7,
+                    1
+                ],
+                "y": [
+                    0.55,
+                    1
+                ]
+            }
+        },
+        "height": 900,
+        "width": 1200,
+        "legend": {
+            "orientation": "h"
+        }
+    }
+}
diff --git a/test/image/mocks/z-marker_symbols-angle.json b/test/image/mocks/z-marker_symbols-angle.json
new file mode 100644
index 00000000000..57485c39cfa
--- /dev/null
+++ b/test/image/mocks/z-marker_symbols-angle.json
@@ -0,0 +1,603 @@
+{
+  "data": [
+    {
+      "mode": "markers",
+      "x": [
+        0, 0, 0, 0, 0, 0, 0, 0, 0,
+        1, 1, 1, 1, 1, 1, 1, 1, 1,
+        2, 2, 2, 2, 2, 2, 2, 2, 2,
+        3, 3, 3, 3, 3, 3, 3, 3, 3,
+        4, 4, 4, 4, 4, 4, 4, 4, 4,
+        5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5,
+        7
+      ],
+      "y": [
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0
+      ],
+      "marker": { "angle": 15,
+        "symbol": [
+          "circle",
+          "square",
+          "diamond",
+          "cross",
+          "x",
+          "triangle-up",
+          "triangle-down",
+          "triangle-left",
+          "triangle-right",
+          "triangle-ne",
+          "triangle-se",
+          "triangle-sw",
+          "triangle-nw",
+          "pentagon",
+          "hexagon",
+          "hexagon2",
+          "octagon",
+          "star",
+          "hexagram",
+          "star-triangle-up",
+          "star-triangle-down",
+          "star-square",
+          "star-diamond",
+          "diamond-tall",
+          "diamond-wide",
+          "hourglass",
+          "bowtie",
+          "circle-cross",
+          "circle-x",
+          "square-cross",
+          "square-x",
+          "diamond-cross",
+          "diamond-x",
+          "cross-thin",
+          "x-thin",
+          "asterisk",
+          "hash",
+          "y-up",
+          "y-down",
+          "y-left",
+          "y-right",
+          "line-ew",
+          "line-ns",
+          "line-ne",
+          "line-nw",
+          "arrow-up",
+          "arrow-down",
+          "arrow-left",
+          "arrow-right",
+          "arrow-bar-up",
+          "arrow-bar-down",
+          "arrow-bar-left",
+          "arrow-bar-right",
+          "arrow",
+          "arrow-wide"
+        ],
+        "color": "blue",
+        "size": 20,
+        "line": {
+          "color": "orange",
+          "width": 1.5
+        }
+      },
+      "text": [
+        "marker symbol: circle
number: 0",
+        "marker symbol: square
number: 1",
+        "marker symbol: diamond
number: 2",
+        "marker symbol: cross
number: 3",
+        "marker symbol: x
number: 4",
+        "marker symbol: triangle-up
number: 5",
+        "marker symbol: triangle-down
number: 6",
+        "marker symbol: triangle-left
number: 7",
+        "marker symbol: triangle-right
number: 8",
+        "marker symbol: triangle-ne
number: 9",
+        "marker symbol: triangle-se
number: 10",
+        "marker symbol: triangle-sw
number: 11",
+        "marker symbol: triangle-nw
number: 12",
+        "marker symbol: pentagon
number: 13",
+        "marker symbol: hexagon
number: 14",
+        "marker symbol: hexagon2
number: 15",
+        "marker symbol: octagon
number: 16",
+        "marker symbol: star
number: 17",
+        "marker symbol: hexagram
number: 18",
+        "marker symbol: star-triangle-up
number: 19",
+        "marker symbol: star-triangle-down
number: 20",
+        "marker symbol: star-square
number: 21",
+        "marker symbol: star-diamond
number: 22",
+        "marker symbol: diamond-tall
number: 23",
+        "marker symbol: diamond-wide
number: 24",
+        "marker symbol: hourglass
number: 25",
+        "marker symbol: bowtie
number: 26",
+        "marker symbol: circle-cross
number: 27",
+        "marker symbol: circle-x
number: 28",
+        "marker symbol: square-cross
number: 29",
+        "marker symbol: square-x
number: 30",
+        "marker symbol: diamond-cross
number: 31",
+        "marker symbol: diamond-x
number: 32",
+        "marker symbol: cross-thin
number: 33",
+        "marker symbol: x-thin
number: 34",
+        "marker symbol: asterisk
number: 35",
+        "marker symbol: hash
number: 36",
+        "marker symbol: y-up
number: 37",
+        "marker symbol: y-down
number: 38",
+        "marker symbol: y-left
number: 39",
+        "marker symbol: y-right
number: 40",
+        "marker symbol: line-ew
number: 41",
+        "marker symbol: line-ns
number: 42",
+        "marker symbol: line-ne
number: 43",
+        "marker symbol: line-nw
number: 44",
+        "marker symbol: arrow-up
number: 45",
+        "marker symbol: arrow-down
number: 46",
+        "marker symbol: arrow-left
number: 47",
+        "marker symbol: arrow-right
number: 48",
+        "marker symbol: arrow-bar-up
number: 49",
+        "marker symbol: arrow-bar-down
number: 50",
+        "marker symbol: arrow-bar-left
number: 51",
+        "marker symbol: arrow-bar-right
number: 52",
+        "marker symbol: arrow
number: 53",
+        "marker symbol: arrow-wide
number: 54"
+      ],
+      "hoverinfo": "text"
+    },
+    {
+      "mode": "markers",
+      "x": [
+        9, 9, 9, 9, 9, 9, 9, 9, 9,
+        10, 10, 10, 10, 10, 10, 10, 10, 10,
+        11, 11, 11, 11, 11, 11, 11, 11, 11,
+        12, 12, 12, 12, 12, 12, 12, 12, 12,
+        13, 13, 13, 13, 13, 13, 13, 13, 13,
+        14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5,
+        16
+      ],
+      "y": [
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0
+      ],
+      "marker": { "angle": 15,
+        "symbol": [
+          "circle-open",
+          "square-open",
+          "diamond-open",
+          "cross-open",
+          "x-open",
+          "triangle-up-open",
+          "triangle-down-open",
+          "triangle-left-open",
+          "triangle-right-open",
+          "triangle-ne-open",
+          "triangle-se-open",
+          "triangle-sw-open",
+          "triangle-nw-open",
+          "pentagon-open",
+          "hexagon-open",
+          "hexagon2-open",
+          "octagon-open",
+          "star-open",
+          "hexagram-open",
+          "star-triangle-up-open",
+          "star-triangle-down-open",
+          "star-square-open",
+          "star-diamond-open",
+          "diamond-tall-open",
+          "diamond-wide-open",
+          "hourglass-open",
+          "bowtie-open",
+          "circle-cross-open",
+          "circle-x-open",
+          "square-cross-open",
+          "square-x-open",
+          "diamond-cross-open",
+          "diamond-x-open",
+          "cross-thin-open",
+          "x-thin-open",
+          "asterisk-open",
+          "hash-open",
+          "y-up-open",
+          "y-down-open",
+          "y-left-open",
+          "y-right-open",
+          "line-ew-open",
+          "line-ns-open",
+          "line-ne-open",
+          "line-nw-open",
+          "arrow-up-open",
+          "arrow-down-open",
+          "arrow-left-open",
+          "arrow-right-open",
+          "arrow-bar-up-open",
+          "arrow-bar-down-open",
+          "arrow-bar-left-open",
+          "arrow-bar-right-open",
+          "arrow-open",
+          "arrow-wide-open"
+        ],
+        "color": "blue",
+        "size": 20,
+        "line": {
+          "color": "orange",
+          "width": 1.5
+        }
+      },
+      "text": [
+        "marker symbol: circle-open
number: 100",
+        "marker symbol: square-open
number: 101",
+        "marker symbol: diamond-open
number: 102",
+        "marker symbol: cross-open
number: 103",
+        "marker symbol: x-open
number: 104",
+        "marker symbol: triangle-up-open
number: 105",
+        "marker symbol: triangle-down-open
number: 106",
+        "marker symbol: triangle-left-open
number: 107",
+        "marker symbol: triangle-right-open
number: 108",
+        "marker symbol: triangle-ne-open
number: 109",
+        "marker symbol: triangle-se-open
number: 110",
+        "marker symbol: triangle-sw-open
number: 111",
+        "marker symbol: triangle-nw-open
number: 112",
+        "marker symbol: pentagon-open
number: 113",
+        "marker symbol: hexagon-open
number: 114",
+        "marker symbol: hexagon2-open
number: 115",
+        "marker symbol: octagon-open
number: 116",
+        "marker symbol: star-open
number: 117",
+        "marker symbol: hexagram-open
number: 118",
+        "marker symbol: star-triangle-up-open
number: 119",
+        "marker symbol: star-triangle-down-open
number: 120",
+        "marker symbol: star-square-open
number: 121",
+        "marker symbol: star-diamond-open
number: 122",
+        "marker symbol: diamond-tall-open
number: 123",
+        "marker symbol: diamond-wide-open
number: 124",
+        "marker symbol: hourglass-open
number: 125",
+        "marker symbol: bowtie-open
number: 126",
+        "marker symbol: circle-cross-open
number: 127",
+        "marker symbol: circle-x-open
number: 128",
+        "marker symbol: square-cross-open
number: 129",
+        "marker symbol: square-x-open
number: 130",
+        "marker symbol: diamond-cross-open
number: 131",
+        "marker symbol: diamond-x-open
number: 132",
+        "marker symbol: cross-thin-open
number: 133",
+        "marker symbol: x-thin-open
number: 134",
+        "marker symbol: asterisk-open
number: 135",
+        "marker symbol: hash-open
number: 136",
+        "marker symbol: y-up-open
number: 137",
+        "marker symbol: y-down-open
number: 138",
+        "marker symbol: y-left-open
number: 139",
+        "marker symbol: y-right-open
number: 140",
+        "marker symbol: line-ew-open
number: 141",
+        "marker symbol: line-ns-open
number: 142",
+        "marker symbol: line-ne-open
number: 143",
+        "marker symbol: line-nw-open
number: 144",
+        "marker symbol: arrow-up-open
number: 145",
+        "marker symbol: arrow-down-open
number: 146",
+        "marker symbol: arrow-left-open
number: 147",
+        "marker symbol: arrow-right-open
number: 148",
+        "marker symbol: arrow-bar-up-open
number: 149",
+        "marker symbol: arrow-bar-down-open
number: 150",
+        "marker symbol: arrow-bar-left-open
number: 151",
+        "marker symbol: arrow-bar-right-open
number: 152",
+        "marker symbol: arrow-open
number: 153",
+        "marker symbol: arrow-wide-open
number: 154"
+      ],
+      "hoverinfo": "text"
+    },
+    {
+      "mode": "markers",
+      "x": [
+        18, 18, 18, 18, 18, 18, 18, 18, 18,
+        19, 19, 19, 19, 19, 19, 19, 19, 19,
+        20, 20, 20, 20, 20, 20, 20, 20, 20,
+        21, 21, 21, 21, 21, 21, 21, 21, 21,
+        22, 22, 22, 22, 22, 22, 22, 22, 22,
+        23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5,
+        25
+      ],
+      "y": [
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0
+      ],
+      "marker": { "angle": 15,
+        "symbol": [
+          "circle-dot",
+          "square-dot",
+          "diamond-dot",
+          "cross-dot",
+          "x-dot",
+          "triangle-up-dot",
+          "triangle-down-dot",
+          "triangle-left-dot",
+          "triangle-right-dot",
+          "triangle-ne-dot",
+          "triangle-se-dot",
+          "triangle-sw-dot",
+          "triangle-nw-dot",
+          "pentagon-dot",
+          "hexagon-dot",
+          "hexagon2-dot",
+          "octagon-dot",
+          "star-dot",
+          "hexagram-dot",
+          "star-triangle-up-dot",
+          "star-triangle-down-dot",
+          "star-square-dot",
+          "star-diamond-dot",
+          "diamond-tall-dot",
+          "diamond-wide-dot",
+          "hourglass-dot",
+          "bowtie-dot",
+          "circle-cross-dot",
+          "circle-x-dot",
+          "square-cross-dot",
+          "square-x-dot",
+          "diamond-cross-dot",
+          "diamond-x-dot",
+          "cross-thin-dot",
+          "x-thin-dot",
+          "asterisk-dot",
+          "hash-dot",
+          "y-up-dot",
+          "y-down-dot",
+          "y-left-dot",
+          "y-right-dot",
+          "line-ew-dot",
+          "line-ns-dot",
+          "line-ne-dot",
+          "line-nw-dot",
+          "arrow-up-dot",
+          "arrow-down-dot",
+          "arrow-left-dot",
+          "arrow-right-dot",
+          "arrow-bar-up-dot",
+          "arrow-bar-down-dot",
+          "arrow-bar-left-dot",
+          "arrow-bar-right-dot",
+          "arrow-dot",
+          "arrow-wide-dot"
+        ],
+        "color": "blue",
+        "size": 20,
+        "line": {
+          "color": "orange",
+          "width": 1.5
+        }
+      },
+      "text": [
+        "marker symbol: circle-dot
number: 200",
+        "marker symbol: square-dot
number: 201",
+        "marker symbol: diamond-dot
number: 202",
+        "marker symbol: cross-dot
number: 203",
+        "marker symbol: x-dot
number: 204",
+        "marker symbol: triangle-up-dot
number: 205",
+        "marker symbol: triangle-down-dot
number: 206",
+        "marker symbol: triangle-left-dot
number: 207",
+        "marker symbol: triangle-right-dot
number: 208",
+        "marker symbol: triangle-ne-dot
number: 209",
+        "marker symbol: triangle-se-dot
number: 210",
+        "marker symbol: triangle-sw-dot
number: 211",
+        "marker symbol: triangle-nw-dot
number: 212",
+        "marker symbol: pentagon-dot
number: 213",
+        "marker symbol: hexagon-dot
number: 214",
+        "marker symbol: hexagon2-dot
number: 215",
+        "marker symbol: octagon-dot
number: 216",
+        "marker symbol: star-dot
number: 217",
+        "marker symbol: hexagram-dot
number: 218",
+        "marker symbol: star-triangle-up-dot
number: 219",
+        "marker symbol: star-triangle-down-dot
number: 220",
+        "marker symbol: star-square-dot
number: 221",
+        "marker symbol: star-diamond-dot
number: 222",
+        "marker symbol: diamond-tall-dot
number: 223",
+        "marker symbol: diamond-wide-dot
number: 224",
+        "marker symbol: hourglass-dot
number: 225",
+        "marker symbol: bowtie-dot
number: 226",
+        "marker symbol: circle-cross-dot
number: 227",
+        "marker symbol: circle-x-dot
number: 228",
+        "marker symbol: square-cross-dot
number: 229",
+        "marker symbol: square-x-dot
number: 230",
+        "marker symbol: diamond-cross-dot
number: 231",
+        "marker symbol: diamond-x-dot
number: 232",
+        "marker symbol: cross-thin-dot
number: 233",
+        "marker symbol: x-thin-dot
number: 234",
+        "marker symbol: asterisk-dot
number: 235",
+        "marker symbol: hash-dot
number: 236",
+        "marker symbol: y-up-dot
number: 237",
+        "marker symbol: y-down-dot
number: 238",
+        "marker symbol: y-left-dot
number: 239",
+        "marker symbol: y-right-dot
number: 240",
+        "marker symbol: line-ew-dot
number: 241",
+        "marker symbol: line-ns-dot
number: 242",
+        "marker symbol: line-ne-dot
number: 243",
+        "marker symbol: line-nw-dot
number: 244",
+        "marker symbol: arrow-up-dot
number: 245",
+        "marker symbol: arrow-down-dot
number: 246",
+        "marker symbol: arrow-left-dot
number: 247",
+        "marker symbol: arrow-right-dot
number: 248",
+        "marker symbol: arrow-bar-up-dot
number: 249",
+        "marker symbol: arrow-bar-down-dot
number: 250",
+        "marker symbol: arrow-bar-left-dot
number: 251",
+        "marker symbol: arrow-bar-right-dot
number: 252",
+        "marker symbol: arrow-dot
number: 253",
+        "marker symbol: arrow-wide-dot
number: 254"
+      ],
+      "hoverinfo": "text"
+    },
+    {
+      "mode": "markers",
+      "x": [
+        27, 27, 27, 27, 27, 27, 27, 27, 27,
+        28, 28, 28, 28, 28, 28, 28, 28, 28,
+        29, 29, 29, 29, 29, 29, 29, 29, 29,
+        30, 30, 30, 30, 30, 30, 30, 30, 30,
+        31, 31, 31, 31, 31, 31, 31, 31, 31,
+        32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5,
+        34
+      ],
+      "y": [
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0, 1, 2, 3, 4, 5, 6, 7, 8,
+        0
+      ],
+      "marker": { "angle": 15,
+        "symbol": [
+          "circle-open-dot",
+          "square-open-dot",
+          "diamond-open-dot",
+          "cross-open-dot",
+          "x-open-dot",
+          "triangle-up-open-dot",
+          "triangle-down-open-dot",
+          "triangle-left-open-dot",
+          "triangle-right-open-dot",
+          "triangle-ne-open-dot",
+          "triangle-se-open-dot",
+          "triangle-sw-open-dot",
+          "triangle-nw-open-dot",
+          "pentagon-open-dot",
+          "hexagon-open-dot",
+          "hexagon2-open-dot",
+          "octagon-open-dot",
+          "star-open-dot",
+          "hexagram-open-dot",
+          "star-triangle-up-open-dot",
+          "star-triangle-down-open-dot",
+          "star-square-open-dot",
+          "star-diamond-open-dot",
+          "diamond-tall-open-dot",
+          "diamond-wide-open-dot",
+          "hourglass-open-dot",
+          "bowtie-open-dot",
+          "circle-cross-open-dot",
+          "circle-x-open-dot",
+          "square-cross-open-dot",
+          "square-x-open-dot",
+          "diamond-cross-open-dot",
+          "diamond-x-open-dot",
+          "cross-thin-open-dot",
+          "x-thin-open-dot",
+          "asterisk-open-dot",
+          "hash-open-dot",
+          "y-up-open-dot",
+          "y-down-open-dot",
+          "y-left-open-dot",
+          "y-right-open-dot",
+          "line-ew-open-dot",
+          "line-ns-open-dot",
+          "line-ne-open-dot",
+          "line-nw-open-dot",
+          "arrow-up-open-dot",
+          "arrow-down-open-dot",
+          "arrow-left-open-dot",
+          "arrow-right-open-dot",
+          "arrow-bar-up-open-dot",
+          "arrow-bar-down-open-dot",
+          "arrow-bar-left-open-dot",
+          "arrow-bar-right-open-dot",
+          "arrow-open-dot",
+          "arrow-wide-open-dot"
+        ],
+        "color": "blue",
+        "size": 20,
+        "line": {
+          "color": "orange",
+          "width": 1.5
+        }
+      },
+      "text": [
+        "marker symbol: circle-open-dot
number: 300",
+        "marker symbol: square-open-dot
number: 301",
+        "marker symbol: diamond-open-dot
number: 302",
+        "marker symbol: cross-open-dot
number: 303",
+        "marker symbol: x-open-dot
number: 304",
+        "marker symbol: triangle-up-open-dot
number: 305",
+        "marker symbol: triangle-down-open-dot
number: 306",
+        "marker symbol: triangle-left-open-dot
number: 307",
+        "marker symbol: triangle-right-open-dot
number: 308",
+        "marker symbol: triangle-ne-open-dot
number: 309",
+        "marker symbol: triangle-se-open-dot
number: 310",
+        "marker symbol: triangle-sw-open-dot
number: 311",
+        "marker symbol: triangle-nw-open-dot
number: 312",
+        "marker symbol: pentagon-open-dot
number: 313",
+        "marker symbol: hexagon-open-dot
number: 314",
+        "marker symbol: hexagon2-open-dot
number: 315",
+        "marker symbol: octagon-open-dot
number: 316",
+        "marker symbol: star-open-dot
number: 317",
+        "marker symbol: hexagram-open-dot
number: 318",
+        "marker symbol: star-triangle-up-open-dot
number: 319",
+        "marker symbol: star-triangle-down-open-dot
number: 320",
+        "marker symbol: star-square-open-dot
number: 321",
+        "marker symbol: star-diamond-open-dot
number: 322",
+        "marker symbol: diamond-tall-open-dot
number: 323",
+        "marker symbol: diamond-wide-open-dot
number: 324",
+        "marker symbol: hourglass-open-dot
number: 325",
+        "marker symbol: bowtie-open-dot
number: 326",
+        "marker symbol: circle-cross-open-dot
number: 327",
+        "marker symbol: circle-x-open-dot
number: 328",
+        "marker symbol: square-cross-open-dot
number: 329",
+        "marker symbol: square-x-open-dot
number: 330",
+        "marker symbol: diamond-cross-open-dot
number: 331",
+        "marker symbol: diamond-x-open-dot
number: 332",
+        "marker symbol: cross-thin-open-dot
number: 333",
+        "marker symbol: x-thin-open-dot
number: 334",
+        "marker symbol: asterisk-open-dot
number: 335",
+        "marker symbol: hash-open-dot
number: 336",
+        "marker symbol: y-up-open-dot
number: 337",
+        "marker symbol: y-down-open-dot
number: 338",
+        "marker symbol: y-left-open-dot
number: 339",
+        "marker symbol: y-right-open-dot
number: 340",
+        "marker symbol: line-ew-open-dot
number: 341",
+        "marker symbol: line-ns-open-dot
number: 342",
+        "marker symbol: line-ne-open-dot
number: 343",
+        "marker symbol: line-nw-open-dot
number: 344",
+        "marker symbol: line-nw-open-dot
number: 344",
+        "marker symbol: arrow-up-open-dot
number: 345",
+        "marker symbol: arrow-down-open-dot
number: 346",
+        "marker symbol: arrow-left-open-dot
number: 347",
+        "marker symbol: arrow-right-open-dot
number: 348",
+        "marker symbol: arrow-bar-up-open-dot
number: 349",
+        "marker symbol: arrow-bar-down-open-dot
number: 350",
+        "marker symbol: arrow-bar-left-open-dot
number: 351",
+        "marker symbol: arrow-bar-right-open-dot
number: 352",
+        "marker symbol: arrow-open-dot
number: 353",
+        "marker symbol: arrow-wide-open-dot
number: 354"
+      ],
+      "hoverinfo": "text"
+    }
+  ],
+  "layout": {
+    "margin": {
+      "l": 0,
+      "r": 0,
+      "b": 0,
+      "t": 0
+    },
+    "width": 1500,
+    "height": 500,
+    "xaxis": {
+      "showgrid": false,
+      "zeroline": false
+    },
+    "yaxis": {
+      "showgrid": false,
+      "zeroline": false,
+      "autorange": "reversed"
+    },
+    "showlegend": false,
+    "plot_bgcolor": "#d3d3d3",
+    "hovermode": "closest"
+  }
+}
diff --git a/test/jasmine/tests/geo_test.js b/test/jasmine/tests/geo_test.js
index f28ff0e7d70..11d0f7d9eab 100644
--- a/test/jasmine/tests/geo_test.js
+++ b/test/jasmine/tests/geo_test.js
@@ -2732,6 +2732,43 @@ describe('Test geo zoom/pan/drag interactions:', function() {
     });
 });
 
+describe('Test geo interactions update marker angles:', function() {
+    var gd;
+
+    beforeEach(function() { gd = createGraphDiv(); });
+
+    afterEach(destroyGraphDiv);
+
+    function getPath() {
+        return d3Select('.scattergeo .point').node().getAttribute('d');
+    }
+
+    it('update angles when panning', function(done) {
+        var fig = Lib.extendDeep({}, require('@mocks/geo_conic-conformal'));
+        fig.layout.width = 700;
+        fig.layout.height = 500;
+        fig.layout.dragmode = 'pan';
+
+        var initialPath, newPath;
+
+        Plotly.newPlot(gd, fig)
+        .then(function() {
+            initialPath = getPath();
+
+            return drag({path: [[300, 200], [350, 250], [400, 300]], noCover: true});
+        })
+        .then(function() {
+            newPath = getPath();
+            expect(newPath).toEqual('M0,0L18.238949513733473,8.206139204003389L19.579067718529352,-4.081679467234269Z');
+
+            expect(newPath).not.toEqual(initialPath);
+            expect(newPath).toEqual('M0,0L18.238949513733473,8.206139204003389L19.579067718529352,-4.081679467234269Z');
+            expect(initialPath).toEqual('M0,0L-1.5033314641545745,19.94341982983066L10.506227353572104,17.01820163222463Z');
+        })
+        .then(done, done.fail);
+    });
+});
+
 describe('plotly_relayouting', function() {
     var gd;
     var events;
diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js
index 66ea5a85b58..a2064d33db1 100644
--- a/test/jasmine/tests/hover_label_test.js
+++ b/test/jasmine/tests/hover_label_test.js
@@ -3999,6 +3999,9 @@ describe('hover distance', function() {
     describe('closest hovermode', function() {
         var mockCopy = Lib.extendDeep({}, mock);
         mockCopy.layout.hovermode = 'closest';
+        // use simple markers here
+        delete mockCopy.data[0].marker;
+        delete mockCopy.data[1].marker;
 
         beforeEach(function(done) {
             Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done);
diff --git a/test/jasmine/tests/scatterternary_test.js b/test/jasmine/tests/scatterternary_test.js
index 3d947a02b8b..1b762f981ed 100644
--- a/test/jasmine/tests/scatterternary_test.js
+++ b/test/jasmine/tests/scatterternary_test.js
@@ -455,6 +455,8 @@ describe('Test scatterternary *cliponaxis*', function() {
     it('should show/hide point/text/errorbars in clipped and non-clipped layers', function(done) {
         var gd = createGraphDiv();
         var fig = Lib.extendDeep({}, require('@mocks/ternary_markers.json'));
+        // use simple markers here
+        delete fig.data[0].marker;
 
         function _assert(layerClips, nodeDisplays, lineClips) {
             var frontLayer = d3Select('.frontplot');
diff --git a/test/plot-schema.json b/test/plot-schema.json
index 353097973cb..de21dc77c47 100644
--- a/test/plot-schema.json
+++ b/test/plot-schema.json
@@ -435,7 +435,8 @@
    "angle": {
     "description": "A number (in degree) between -180 and 180.",
     "otherOpts": [
-     "dflt"
+     "dflt",
+     "arrayOk"
     ],
     "requiredOpts": []
    },
@@ -15451,6 +15452,13 @@
      "valType": "string"
     },
     "marker": {
+     "angle": {
+      "arrayOk": false,
+      "description": "Sets the marker angle in respect to `angleref`.",
+      "dflt": 0,
+      "editType": "calc",
+      "valType": "angle"
+     },
      "color": {
       "arrayOk": false,
       "description": "Sets the marker color. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `marker.cmin` and `marker.cmax` if set.",
@@ -15993,7 +16001,19 @@
        "arrow-bar-right",
        152,
        "152",
-       "arrow-bar-right-open"
+       "arrow-bar-right-open",
+       53,
+       "53",
+       "arrow",
+       153,
+       "153",
+       "arrow-open",
+       54,
+       "54",
+       "arrow-wide",
+       154,
+       "154",
+       "arrow-wide-open"
       ]
      }
     },
@@ -43252,6 +43272,19 @@
      "valType": "number"
     },
     "line": {
+     "backoff": {
+      "arrayOk": true,
+      "description": "Sets the line back off from the end point of the nth line segment (in px). This option is useful e.g. to avoid overlap with arrowhead markers. With *auto* the lines would trim before markers if `marker.angleref` is set to *previous*.",
+      "dflt": "auto",
+      "editType": "plot",
+      "min": 0,
+      "valType": "number"
+     },
+     "backoffsrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `backoff`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "color": {
       "anim": true,
       "description": "Sets the line color.",
@@ -43312,6 +43345,30 @@
      }
     },
     "marker": {
+     "angle": {
+      "anim": false,
+      "arrayOk": true,
+      "description": "Sets the marker angle in respect to `angleref`.",
+      "dflt": 0,
+      "editType": "plot",
+      "valType": "angle"
+     },
+     "angleref": {
+      "anim": false,
+      "description": "Sets the reference for marker angle. With *previous*, angle 0 points along the line from the previous point to this one. With *up*, angle 0 points toward the top of the screen.",
+      "dflt": "up",
+      "editType": "plot",
+      "valType": "enumerated",
+      "values": [
+       "previous",
+       "up"
+      ]
+     },
+     "anglesrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `angle`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "autocolorscale": {
       "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.",
       "dflt": true,
@@ -44060,6 +44117,20 @@
       "editType": "none",
       "valType": "string"
      },
+     "standoff": {
+      "anim": true,
+      "arrayOk": true,
+      "description": "Moves the marker away from the data point in the direction of `angle` (in px). This can be useful for example if you have another marker at this location and you want to point an arrowhead marker at it.",
+      "dflt": 0,
+      "editType": "plot",
+      "min": 0,
+      "valType": "number"
+     },
+     "standoffsrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `standoff`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "symbol": {
       "arrayOk": true,
       "description": "Sets the marker symbol type. Adding 100 is equivalent to appending *-open* to a symbol name. Adding 200 is equivalent to appending *-dot* to a symbol name. Adding 300 is equivalent to appending *-open-dot* or *dot-open* to a symbol name.",
@@ -44540,7 +44611,19 @@
        "arrow-bar-right",
        152,
        "152",
-       "arrow-bar-right-open"
+       "arrow-bar-right-open",
+       53,
+       "53",
+       "arrow",
+       153,
+       "153",
+       "arrow-open",
+       54,
+       "54",
+       "arrow-wide",
+       154,
+       "154",
+       "arrow-wide-open"
       ]
      },
      "symbolsrc": {
@@ -47492,6 +47575,19 @@
      "valType": "number"
     },
     "line": {
+     "backoff": {
+      "arrayOk": true,
+      "description": "Sets the line back off from the end point of the nth line segment (in px). This option is useful e.g. to avoid overlap with arrowhead markers. With *auto* the lines would trim before markers if `marker.angleref` is set to *previous*.",
+      "dflt": "auto",
+      "editType": "plot",
+      "min": 0,
+      "valType": "number"
+     },
+     "backoffsrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `backoff`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "color": {
       "description": "Sets the line color.",
       "editType": "style",
@@ -47540,6 +47636,28 @@
      }
     },
     "marker": {
+     "angle": {
+      "arrayOk": true,
+      "description": "Sets the marker angle in respect to `angleref`.",
+      "dflt": 0,
+      "editType": "plot",
+      "valType": "angle"
+     },
+     "angleref": {
+      "description": "Sets the reference for marker angle. With *previous*, angle 0 points along the line from the previous point to this one. With *up*, angle 0 points toward the top of the screen.",
+      "dflt": "up",
+      "editType": "plot",
+      "valType": "enumerated",
+      "values": [
+       "previous",
+       "up"
+      ]
+     },
+     "anglesrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `angle`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "autocolorscale": {
       "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.",
       "dflt": true,
@@ -48283,6 +48401,19 @@
       "editType": "none",
       "valType": "string"
      },
+     "standoff": {
+      "arrayOk": true,
+      "description": "Moves the marker away from the data point in the direction of `angle` (in px). This can be useful for example if you have another marker at this location and you want to point an arrowhead marker at it.",
+      "dflt": 0,
+      "editType": "plot",
+      "min": 0,
+      "valType": "number"
+     },
+     "standoffsrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `standoff`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "symbol": {
       "arrayOk": true,
       "description": "Sets the marker symbol type. Adding 100 is equivalent to appending *-open* to a symbol name. Adding 200 is equivalent to appending *-dot* to a symbol name. Adding 300 is equivalent to appending *-open-dot* or *dot-open* to a symbol name.",
@@ -48763,7 +48894,19 @@
        "arrow-bar-right",
        152,
        "152",
-       "arrow-bar-right-open"
+       "arrow-bar-right-open",
+       53,
+       "53",
+       "arrow",
+       153,
+       "153",
+       "arrow-open",
+       54,
+       "54",
+       "arrow-wide",
+       154,
+       "154",
+       "arrow-wide-open"
       ]
      },
      "symbolsrc": {
@@ -49375,6 +49518,29 @@
      "valType": "string"
     },
     "marker": {
+     "angle": {
+      "arrayOk": true,
+      "description": "Sets the marker angle in respect to `angleref`.",
+      "dflt": 0,
+      "editType": "calc",
+      "valType": "angle"
+     },
+     "angleref": {
+      "description": "Sets the reference for marker angle. With *previous*, angle 0 points along the line from the previous point to this one. With *up*, angle 0 points toward the top of the screen. With *north*, angle 0 points north based on the current map projection.",
+      "dflt": "up",
+      "editType": "calc",
+      "valType": "enumerated",
+      "values": [
+       "previous",
+       "up",
+       "north"
+      ]
+     },
+     "anglesrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `angle`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "autocolorscale": {
       "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.",
       "dflt": true,
@@ -50111,6 +50277,19 @@
       "editType": "none",
       "valType": "string"
      },
+     "standoff": {
+      "arrayOk": true,
+      "description": "Moves the marker away from the data point in the direction of `angle` (in px). This can be useful for example if you have another marker at this location and you want to point an arrowhead marker at it.",
+      "dflt": 0,
+      "editType": "calc",
+      "min": 0,
+      "valType": "number"
+     },
+     "standoffsrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `standoff`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "symbol": {
       "arrayOk": true,
       "description": "Sets the marker symbol type. Adding 100 is equivalent to appending *-open* to a symbol name. Adding 200 is equivalent to appending *-dot* to a symbol name. Adding 300 is equivalent to appending *-open-dot* or *dot-open* to a symbol name.",
@@ -50591,7 +50770,19 @@
        "arrow-bar-right",
        152,
        "152",
-       "arrow-bar-right-open"
+       "arrow-bar-right-open",
+       53,
+       "53",
+       "arrow",
+       153,
+       "153",
+       "arrow-open",
+       54,
+       "54",
+       "arrow-wide",
+       154,
+       "154",
+       "arrow-wide-open"
       ]
      },
      "symbolsrc": {
@@ -51356,6 +51547,18 @@
      }
     },
     "marker": {
+     "angle": {
+      "arrayOk": true,
+      "description": "Sets the marker angle in respect to `angleref`.",
+      "dflt": 0,
+      "editType": "calc",
+      "valType": "angle"
+     },
+     "anglesrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `angle`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "autocolorscale": {
       "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.",
       "dflt": true,
@@ -52539,7 +52742,19 @@
        "arrow-bar-right",
        152,
        "152",
-       "arrow-bar-right-open"
+       "arrow-bar-right-open",
+       53,
+       "53",
+       "arrow",
+       153,
+       "153",
+       "arrow-open",
+       54,
+       "54",
+       "arrow-wide",
+       154,
+       "154",
+       "arrow-wide-open"
       ]
      },
      "symbolsrc": {
@@ -54377,6 +54592,19 @@
      "valType": "number"
     },
     "line": {
+     "backoff": {
+      "arrayOk": true,
+      "description": "Sets the line back off from the end point of the nth line segment (in px). This option is useful e.g. to avoid overlap with arrowhead markers. With *auto* the lines would trim before markers if `marker.angleref` is set to *previous*.",
+      "dflt": "auto",
+      "editType": "plot",
+      "min": 0,
+      "valType": "number"
+     },
+     "backoffsrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `backoff`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "color": {
       "description": "Sets the line color.",
       "editType": "style",
@@ -54425,6 +54653,28 @@
      }
     },
     "marker": {
+     "angle": {
+      "arrayOk": true,
+      "description": "Sets the marker angle in respect to `angleref`.",
+      "dflt": 0,
+      "editType": "plot",
+      "valType": "angle"
+     },
+     "angleref": {
+      "description": "Sets the reference for marker angle. With *previous*, angle 0 points along the line from the previous point to this one. With *up*, angle 0 points toward the top of the screen.",
+      "dflt": "up",
+      "editType": "plot",
+      "valType": "enumerated",
+      "values": [
+       "previous",
+       "up"
+      ]
+     },
+     "anglesrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `angle`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "autocolorscale": {
       "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.",
       "dflt": true,
@@ -55168,6 +55418,19 @@
       "editType": "none",
       "valType": "string"
      },
+     "standoff": {
+      "arrayOk": true,
+      "description": "Moves the marker away from the data point in the direction of `angle` (in px). This can be useful for example if you have another marker at this location and you want to point an arrowhead marker at it.",
+      "dflt": 0,
+      "editType": "plot",
+      "min": 0,
+      "valType": "number"
+     },
+     "standoffsrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `standoff`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "symbol": {
       "arrayOk": true,
       "description": "Sets the marker symbol type. Adding 100 is equivalent to appending *-open* to a symbol name. Adding 200 is equivalent to appending *-dot* to a symbol name. Adding 300 is equivalent to appending *-open-dot* or *dot-open* to a symbol name.",
@@ -55648,7 +55911,19 @@
        "arrow-bar-right",
        152,
        "152",
-       "arrow-bar-right-open"
+       "arrow-bar-right-open",
+       53,
+       "53",
+       "arrow",
+       153,
+       "153",
+       "arrow-open",
+       54,
+       "54",
+       "arrow-wide",
+       154,
+       "154",
+       "arrow-wide-open"
       ]
      },
      "symbolsrc": {
@@ -56263,6 +56538,18 @@
      }
     },
     "marker": {
+     "angle": {
+      "arrayOk": true,
+      "description": "Sets the marker angle in respect to `angleref`.",
+      "dflt": 0,
+      "editType": "calc",
+      "valType": "angle"
+     },
+     "anglesrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `angle`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "autocolorscale": {
       "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.",
       "dflt": true,
@@ -57446,7 +57733,19 @@
        "arrow-bar-right",
        152,
        "152",
-       "arrow-bar-right-open"
+       "arrow-bar-right-open",
+       53,
+       "53",
+       "arrow",
+       153,
+       "153",
+       "arrow-open",
+       54,
+       "54",
+       "arrow-wide",
+       154,
+       "154",
+       "arrow-wide-open"
       ]
      },
      "symbolsrc": {
@@ -58030,6 +58329,19 @@
      "valType": "number"
     },
     "line": {
+     "backoff": {
+      "arrayOk": true,
+      "description": "Sets the line back off from the end point of the nth line segment (in px). This option is useful e.g. to avoid overlap with arrowhead markers. With *auto* the lines would trim before markers if `marker.angleref` is set to *previous*.",
+      "dflt": "auto",
+      "editType": "plot",
+      "min": 0,
+      "valType": "number"
+     },
+     "backoffsrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `backoff`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "color": {
       "description": "Sets the line color.",
       "editType": "style",
@@ -58078,6 +58390,28 @@
      }
     },
     "marker": {
+     "angle": {
+      "arrayOk": true,
+      "description": "Sets the marker angle in respect to `angleref`.",
+      "dflt": 0,
+      "editType": "plot",
+      "valType": "angle"
+     },
+     "angleref": {
+      "description": "Sets the reference for marker angle. With *previous*, angle 0 points along the line from the previous point to this one. With *up*, angle 0 points toward the top of the screen.",
+      "dflt": "up",
+      "editType": "plot",
+      "valType": "enumerated",
+      "values": [
+       "previous",
+       "up"
+      ]
+     },
+     "anglesrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `angle`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "autocolorscale": {
       "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.",
       "dflt": true,
@@ -58821,6 +59155,19 @@
       "editType": "none",
       "valType": "string"
      },
+     "standoff": {
+      "arrayOk": true,
+      "description": "Moves the marker away from the data point in the direction of `angle` (in px). This can be useful for example if you have another marker at this location and you want to point an arrowhead marker at it.",
+      "dflt": 0,
+      "editType": "plot",
+      "min": 0,
+      "valType": "number"
+     },
+     "standoffsrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `standoff`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "symbol": {
       "arrayOk": true,
       "description": "Sets the marker symbol type. Adding 100 is equivalent to appending *-open* to a symbol name. Adding 200 is equivalent to appending *-dot* to a symbol name. Adding 300 is equivalent to appending *-open-dot* or *dot-open* to a symbol name.",
@@ -59301,7 +59648,19 @@
        "arrow-bar-right",
        152,
        "152",
-       "arrow-bar-right-open"
+       "arrow-bar-right-open",
+       53,
+       "53",
+       "arrow",
+       153,
+       "153",
+       "arrow-open",
+       54,
+       "54",
+       "arrow-wide",
+       154,
+       "154",
+       "arrow-wide-open"
       ]
      },
      "symbolsrc": {
@@ -59871,6 +60230,19 @@
      "valType": "number"
     },
     "line": {
+     "backoff": {
+      "arrayOk": true,
+      "description": "Sets the line back off from the end point of the nth line segment (in px). This option is useful e.g. to avoid overlap with arrowhead markers. With *auto* the lines would trim before markers if `marker.angleref` is set to *previous*.",
+      "dflt": "auto",
+      "editType": "plot",
+      "min": 0,
+      "valType": "number"
+     },
+     "backoffsrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `backoff`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "color": {
       "description": "Sets the line color.",
       "editType": "style",
@@ -59919,6 +60291,28 @@
      }
     },
     "marker": {
+     "angle": {
+      "arrayOk": true,
+      "description": "Sets the marker angle in respect to `angleref`.",
+      "dflt": 0,
+      "editType": "plot",
+      "valType": "angle"
+     },
+     "angleref": {
+      "description": "Sets the reference for marker angle. With *previous*, angle 0 points along the line from the previous point to this one. With *up*, angle 0 points toward the top of the screen.",
+      "dflt": "up",
+      "editType": "plot",
+      "valType": "enumerated",
+      "values": [
+       "previous",
+       "up"
+      ]
+     },
+     "anglesrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `angle`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "autocolorscale": {
       "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.",
       "dflt": true,
@@ -60662,6 +61056,19 @@
       "editType": "none",
       "valType": "string"
      },
+     "standoff": {
+      "arrayOk": true,
+      "description": "Moves the marker away from the data point in the direction of `angle` (in px). This can be useful for example if you have another marker at this location and you want to point an arrowhead marker at it.",
+      "dflt": 0,
+      "editType": "plot",
+      "min": 0,
+      "valType": "number"
+     },
+     "standoffsrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `standoff`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "symbol": {
       "arrayOk": true,
       "description": "Sets the marker symbol type. Adding 100 is equivalent to appending *-open* to a symbol name. Adding 200 is equivalent to appending *-dot* to a symbol name. Adding 300 is equivalent to appending *-open-dot* or *dot-open* to a symbol name.",
@@ -61142,7 +61549,19 @@
        "arrow-bar-right",
        152,
        "152",
-       "arrow-bar-right-open"
+       "arrow-bar-right-open",
+       53,
+       "53",
+       "arrow",
+       153,
+       "153",
+       "arrow-open",
+       54,
+       "54",
+       "arrow-wide",
+       154,
+       "154",
+       "arrow-wide-open"
       ]
      },
      "symbolsrc": {
@@ -61714,6 +62133,18 @@
      "valType": "number"
     },
     "marker": {
+     "angle": {
+      "arrayOk": true,
+      "description": "Sets the marker angle in respect to `angleref`.",
+      "dflt": 0,
+      "editType": "plot",
+      "valType": "angle"
+     },
+     "anglesrc": {
+      "description": "Sets the source reference on Chart Studio Cloud for `angle`.",
+      "editType": "none",
+      "valType": "string"
+     },
      "autocolorscale": {
       "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.",
       "dflt": true,
@@ -62897,7 +63328,19 @@
        "arrow-bar-right",
        152,
        "152",
-       "arrow-bar-right-open"
+       "arrow-bar-right-open",
+       53,
+       "53",
+       "arrow",
+       153,
+       "153",
+       "arrow-open",
+       54,
+       "54",
+       "arrow-wide",
+       154,
+       "154",
+       "arrow-wide-open"
       ]
      },
      "symbolsrc": {
@@ -69114,6 +69557,13 @@
      }
     },
     "marker": {
+     "angle": {
+      "arrayOk": false,
+      "description": "Sets the marker angle in respect to `angleref`.",
+      "dflt": 0,
+      "editType": "calc",
+      "valType": "angle"
+     },
      "color": {
       "arrayOk": false,
       "description": "Sets the marker color. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `marker.cmin` and `marker.cmax` if set.",
@@ -69656,7 +70106,19 @@
        "arrow-bar-right",
        152,
        "152",
-       "arrow-bar-right-open"
+       "arrow-bar-right-open",
+       53,
+       "53",
+       "arrow",
+       153,
+       "153",
+       "arrow-open",
+       54,
+       "54",
+       "arrow-wide",
+       154,
+       "154",
+       "arrow-wide-open"
       ]
      }
     },