diff --git a/lib/index.js b/lib/index.js
index 39a357bac96..57e33be6c60 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -48,7 +48,8 @@ Plotly.register([
     require('./ohlc'),
     require('./candlestick'),
 
-    require('./scatterpolar')
+    require('./scatterpolar'),
+    require('./scatterpolargl')
 ]);
 
 // transforms
diff --git a/lib/scatterpolargl.js b/lib/scatterpolargl.js
new file mode 100644
index 00000000000..33f35606ca7
--- /dev/null
+++ b/lib/scatterpolargl.js
@@ -0,0 +1,11 @@
+/**
+* Copyright 2012-2018, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+module.exports = require('../src/traces/scatterpolargl');
diff --git a/package.json b/package.json
index 9b20549885d..a531c1ce0e8 100644
--- a/package.json
+++ b/package.json
@@ -57,7 +57,10 @@
     "3d-view": "^2.0.0",
     "@plotly/d3-sankey": "^0.5.0",
     "alpha-shape": "^1.0.0",
-    "color-rgba": "^1.1.1",
+    "bubleify": "^1.0.0",
+    "canvas-fit": "^1.5.0",
+    "color-normalize": "^1.0.3",
+    "color-rgba": "^2.0.0",
     "convex-hull": "^1.0.3",
     "country-regex": "^1.1.0",
     "d3": "^3.5.12",
@@ -67,24 +70,21 @@
     "fast-isnumeric": "^1.1.1",
     "font-atlas-sdf": "^1.3.3",
     "gl-contour2d": "^1.1.2",
-    "gl-error2d": "^1.2.1",
     "gl-error3d": "^1.0.6",
     "gl-heatmap2d": "^1.0.3",
-    "gl-line2d": "^1.4.1",
     "gl-line3d": "^1.1.0",
     "gl-mat4": "^1.1.2",
     "gl-mesh3d": "^1.3.0",
     "gl-plot2d": "^1.3.0",
     "gl-plot3d": "^1.5.4",
     "gl-pointcloud2d": "^1.0.0",
-    "gl-scatter2d": "^1.3.2",
-    "gl-scatter2d-sdf": "^1.3.11",
     "gl-scatter3d": "^1.0.4",
     "gl-select-box": "^1.0.1",
     "gl-shader": "4.2.0",
     "gl-spikes2d": "^1.0.1",
     "gl-surface3d": "^1.3.1",
     "has-hover": "^1.0.1",
+    "kdgrass": "^1.0.1",
     "mapbox-gl": "^0.22.0",
     "matrix-camera-controller": "^2.1.3",
     "minify-stream": "^1.1.0",
@@ -96,12 +96,16 @@
     "ndarray-homography": "^1.0.0",
     "ndarray-ops": "^1.2.2",
     "polybooljs": "^1.2.0",
-    "regl": "^1.3.0",
+    "regl": "^1.3.1",
+    "regl-error2d": "^2.0.3",
+    "regl-line2d": "^2.1.0",
+    "regl-scatter2d": "^2.1.9",
     "right-now": "^1.0.0",
     "robust-orientation": "^1.1.3",
     "sane-topojson": "^2.0.0",
     "strongly-connected-components": "^1.0.1",
     "superscript-text": "^1.0.0",
+    "svg-path-sdf": "^1.1.1",
     "tinycolor2": "^1.3.0",
     "topojson-client": "^2.1.0",
     "webgl-context": "^2.2.0",
diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js
index 0aea2fc36f0..98a64b9e334 100644
--- a/src/components/drawing/index.js
+++ b/src/components/drawing/index.js
@@ -213,6 +213,7 @@ drawing.symbolNames = [];
 drawing.symbolFuncs = [];
 drawing.symbolNeedLines = {};
 drawing.symbolNoDot = {};
+drawing.symbolNoFill = {};
 drawing.symbolList = [];
 
 Object.keys(SYMBOLDEFS).forEach(function(k) {
@@ -231,6 +232,9 @@ Object.keys(SYMBOLDEFS).forEach(function(k) {
         drawing.symbolList = drawing.symbolList.concat(
             [symDef.n + 200, k + '-dot', symDef.n + 300, k + '-open-dot']);
     }
+    if(symDef.noFill) {
+        drawing.symbolNoFill[symDef.n] = true;
+    }
 });
 var MAXSYMBOL = drawing.symbolNames.length,
     // add a dot in the middle of the symbol
diff --git a/src/components/drawing/symbol_defs.js b/src/components/drawing/symbol_defs.js
index 86883414b08..45c7e3401d9 100644
--- a/src/components/drawing/symbol_defs.js
+++ b/src/components/drawing/symbol_defs.js
@@ -355,7 +355,8 @@ module.exports = {
             return 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc;
         },
         needLine: true,
-        noDot: true
+        noDot: true,
+        noFill: true
     },
     'x-thin': {
         n: 34,
@@ -365,7 +366,8 @@ module.exports = {
                 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx;
         },
         needLine: true,
-        noDot: true
+        noDot: true,
+        noFill: true
     },
     asterisk: {
         n: 35,
@@ -377,7 +379,8 @@ module.exports = {
                 'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs;
         },
         needLine: true,
-        noDot: true
+        noDot: true,
+        noFill: true
     },
     hash: {
         n: 36,
@@ -389,7 +392,8 @@ module.exports = {
                 'M' + r2 + ',' + r1 + 'H-' + r2 +
                 'm0,-' + r2 + 'H' + r2;
         },
-        needLine: true
+        needLine: true,
+        noFill: true
     },
     'y-up': {
         n: 37,
@@ -400,7 +404,8 @@ module.exports = {
             return 'M-' + x + ',' + y1 + 'L0,0M' + x + ',' + y1 + 'L0,0M0,-' + y0 + 'L0,0';
         },
         needLine: true,
-        noDot: true
+        noDot: true,
+        noFill: true
     },
     'y-down': {
         n: 38,
@@ -411,7 +416,8 @@ module.exports = {
             return 'M-' + x + ',-' + y1 + 'L0,0M' + x + ',-' + y1 + 'L0,0M0,' + y0 + 'L0,0';
         },
         needLine: true,
-        noDot: true
+        noDot: true,
+        noFill: true
     },
     'y-left': {
         n: 39,
@@ -422,7 +428,8 @@ module.exports = {
             return 'M' + x1 + ',' + y + 'L0,0M' + x1 + ',-' + y + 'L0,0M-' + x0 + ',0L0,0';
         },
         needLine: true,
-        noDot: true
+        noDot: true,
+        noFill: true
     },
     'y-right': {
         n: 40,
@@ -433,7 +440,8 @@ module.exports = {
             return 'M-' + x1 + ',' + y + 'L0,0M-' + x1 + ',-' + y + 'L0,0M' + x0 + ',0L0,0';
         },
         needLine: true,
-        noDot: true
+        noDot: true,
+        noFill: true
     },
     'line-ew': {
         n: 41,
@@ -442,7 +450,8 @@ module.exports = {
             return 'M' + rc + ',0H-' + rc;
         },
         needLine: true,
-        noDot: true
+        noDot: true,
+        noFill: true
     },
     'line-ns': {
         n: 42,
@@ -451,7 +460,8 @@ module.exports = {
             return 'M0,' + rc + 'V-' + rc;
         },
         needLine: true,
-        noDot: true
+        noDot: true,
+        noFill: true
     },
     'line-ne': {
         n: 43,
@@ -460,7 +470,8 @@ module.exports = {
             return 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx;
         },
         needLine: true,
-        noDot: true
+        noDot: true,
+        noFill: true
     },
     'line-nw': {
         n: 44,
@@ -469,6 +480,7 @@ module.exports = {
             return 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx;
         },
         needLine: true,
-        noDot: true
+        noDot: true,
+        noFill: true
     }
 };
diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js
index 4c606c3bcba..efbaf07e306 100644
--- a/src/components/fx/hover.js
+++ b/src/components/fx/hover.js
@@ -519,7 +519,7 @@ function createHoverText(hoverData, opts, gd) {
     var i, traceHoverinfo;
     for(i = 0; i < hoverData.length; i++) {
         traceHoverinfo = hoverData[i].hoverinfo || hoverData[i].trace.hoverinfo;
-        var parts = traceHoverinfo.split('+');
+        var parts = Array.isArray(traceHoverinfo) ? traceHoverinfo : traceHoverinfo.split('+');
         if(parts.indexOf('all') === -1 &&
             parts.indexOf(hovermode) === -1) {
             showCommonLabel = false;
@@ -1077,8 +1077,9 @@ function cleanPoint(d, hovermode) {
     }
 
     var infomode = d.hoverinfo || d.trace.hoverinfo;
+
     if(infomode !== 'all') {
-        infomode = infomode.split('+');
+        infomode = Array.isArray(infomode) ? infomode : infomode.split('+');
         if(infomode.indexOf('x') === -1) d.xLabel = undefined;
         if(infomode.indexOf('y') === -1) d.yLabel = undefined;
         if(infomode.indexOf('z') === -1) d.zLabel = undefined;
diff --git a/src/constants/gl2d_markers.js b/src/constants/gl2d_markers.js
deleted file mode 100644
index dc0720cf091..00000000000
--- a/src/constants/gl2d_markers.js
+++ /dev/null
@@ -1,132 +0,0 @@
-/**
-* Copyright 2012-2018, Plotly, Inc.
-* All rights reserved.
-*
-* This source code is licensed under the MIT license found in the
-* LICENSE file in the root directory of this source tree.
-*/
-
-
-'use strict';
-
-var extendFlat = require('../lib/extend').extendFlat;
-
-var symbolsWithOpenSupport = {
-    'circle': {
-        unicode: '●'
-    },
-    'square': {
-        unicode: '■'
-    },
-    'diamond': {
-        unicode: '◆'
-    },
-    'cross': {
-        unicode: '✚'
-    },
-    'x': {
-        unicode: '❌'
-    },
-    'triangle-up': {
-        unicode: '▲'
-    },
-    'triangle-down': {
-        unicode: '▼'
-    },
-    'triangle-left': {
-        unicode: '◄'
-    },
-    'triangle-right': {
-        unicode: '►'
-    },
-    'triangle-ne': {
-        unicode: '◥'
-    },
-    'triangle-nw': {
-        unicode: '◤'
-    },
-    'triangle-se': {
-        unicode: '◢'
-    },
-    'triangle-sw': {
-        unicode: '◣'
-    },
-    'pentagon': {
-        unicode: '⬟'
-    },
-    'hexagon': {
-        unicode: '⬢'
-    },
-    'hexagon2': {
-        unicode: '⬣'
-    },
-    'star': {
-        unicode: '★'
-    },
-    'diamond-tall': {
-        unicode: '♦'
-    },
-    'bowtie': {
-        unicode: '⧓'
-    },
-    'diamond-x': {
-        unicode: '❖'
-    },
-    'cross-thin': {
-        unicode: '+',
-        noBorder: true
-    },
-    'asterisk': {
-        unicode: '✳',
-        noBorder: true
-    },
-    'y-up': {
-        unicode: '⅄',
-        noBorder: true
-    },
-    'y-down': {
-        unicode: 'Y',
-        noBorder: true
-    },
-    'line-ew': {
-        unicode: '─',
-        noBorder: true
-    },
-    'line-ns': {
-        unicode: '│',
-        noBorder: true
-    }
-};
-
-var openSymbols = {};
-var keys = Object.keys(symbolsWithOpenSupport);
-
-for(var i = 0; i < keys.length; i++) {
-    var k = keys[i];
-    openSymbols[k + '-open'] = extendFlat({}, symbolsWithOpenSupport[k]);
-}
-
-var otherSymbols = {
-    'circle-cross-open': {
-        unicode: '⨁',
-        noFill: true
-    },
-    'circle-x-open': {
-        unicode: '⨂',
-        noFill: true
-    },
-    'square-cross-open': {
-        unicode: '⊞',
-        noFill: true
-    },
-    'square-x-open': {
-        unicode: '⊠',
-        noFill: true
-    }
-};
-
-module.exports = extendFlat({},
-    symbolsWithOpenSupport,
-    openSymbols,
-    otherSymbols
-);
diff --git a/src/fonts/ploticon/config.json b/src/fonts/ploticon/config.json
index 851669be315..6bdb659f75d 100644
--- a/src/fonts/ploticon/config.json
+++ b/src/fonts/ploticon/config.json
@@ -87,7 +87,7 @@
         "width": 1500
       },
       "search": [
-        "tooltip_basic"
+          "tooltip_basic"
       ]
     },
     {
diff --git a/src/lib/gl_format_color.js b/src/lib/gl_format_color.js
index 2607b99ea07..e725613e2d3 100644
--- a/src/lib/gl_format_color.js
+++ b/src/lib/gl_format_color.js
@@ -10,7 +10,7 @@
 'use strict';
 
 var isNumeric = require('fast-isnumeric');
-var rgba = require('color-rgba');
+var rgba = require('color-normalize');
 
 var Colorscale = require('../components/colorscale');
 var colorDflt = require('../components/color/attributes').defaultLine;
@@ -59,6 +59,7 @@ function formatColor(containerIn, opacityIn, len) {
 
     if(isArrayColorIn) {
         getColor = function(c, i) {
+            // FIXME: there is double work, considering that sclFunc does the opposite
             return c[i] === undefined ? colorDfltRgba : rgba(sclFunc(c[i]));
         };
     }
diff --git a/src/lib/str2rgbarray.js b/src/lib/str2rgbarray.js
index 9b9f2590447..ed7be8c1df5 100644
--- a/src/lib/str2rgbarray.js
+++ b/src/lib/str2rgbarray.js
@@ -9,11 +9,11 @@
 
 'use strict';
 
-var rgba = require('color-rgba');
+var rgba = require('color-normalize');
 
 function str2RgbaArray(color) {
-    var colorOut = rgba(color);
-    return colorOut.length ? colorOut : [0, 0, 0, 1];
+    if(!color) return [0, 0, 0, 1];
+    return rgba(color);
 }
 
 module.exports = str2RgbaArray;
diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js
index 356ebd484aa..9296a02db31 100644
--- a/src/plot_api/plot_api.js
+++ b/src/plot_api/plot_api.js
@@ -223,16 +223,11 @@ Plotly.plot = function(gd, data, layout, config) {
                     'left': 0,
                     'width': '100%',
                     'height': '100%',
-                    'overflow': 'visible'
+                    'overflow': 'visible',
+                    'pointer-events': 'none'
                 })
                 .attr('width', fullLayout.width)
                 .attr('height', fullLayout.height);
-
-            fullLayout._glcanvas.filter(function(d) {
-                return !d.pick;
-            }).style({
-                'pointer-events': 'none'
-            });
         }
 
         return Lib.syncOrAsync([
@@ -2817,6 +2812,7 @@ function makePlotFramework(gd) {
     // FIXME: parcoords reuses this object, not the best pattern
     fullLayout._glcontainer = fullLayout._paperdiv.selectAll('.gl-container')
         .data([{}]);
+
     fullLayout._glcontainer.enter().append('div')
         .classed('gl-container', true);
 
diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js
index b82041d6f8b..18a5a807959 100644
--- a/src/plot_api/subroutines.js
+++ b/src/plot_api/subroutines.js
@@ -485,7 +485,6 @@ exports.doModeBar = function(gd) {
         if(updateFx) updateFx(fullLayout);
     }
 
-
     return Plots.previousPromises(gd);
 };
 
diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js
index 044e45be39e..6693e57b205 100644
--- a/src/plots/cartesian/dragbox.js
+++ b/src/plots/cartesian/dragbox.js
@@ -746,7 +746,9 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
             }
 
             // don't scale at all if neither axis is scalable here
-            if(!xScaleFactor2 && !yScaleFactor2) continue;
+            if(!xScaleFactor2 && !yScaleFactor2) {
+                continue;
+            }
 
             // but if only one is, reset the other axis scaling
             if(!xScaleFactor2) xScaleFactor2 = 1;
diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js
index 7b7c065281f..5b1fcf4dd6a 100644
--- a/src/plots/cartesian/index.js
+++ b/src/plots/cartesian/index.js
@@ -16,6 +16,7 @@ var getModuleCalcData = require('../get_data').getModuleCalcData;
 
 var axisIds = require('./axis_ids');
 var constants = require('./constants');
+var xmlnsNamespaces = require('../../constants/xmlns_namespaces');
 
 exports.name = 'cartesian';
 
@@ -209,7 +210,7 @@ function plotOne(gd, plotinfo, cdSubplot, transitionOpts, makeOnCompleteCallback
         // plot all traces of this type on this subplot at once
         var cdModule = getModuleCalcData(cdSubplot, _module);
 
-        _module.plot(gd, plotinfo, cdModule, transitionOpts, makeOnCompleteCallback);
+        if(_module.plot) _module.plot(gd, plotinfo, cdModule, transitionOpts, makeOnCompleteCallback);
     }
 }
 
@@ -217,13 +218,14 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout)
     var oldModules = oldFullLayout._modules || [],
         newModules = newFullLayout._modules || [];
 
-    var hadScatter, hasScatter, i;
+    var hadScatter, hasScatter, hadGl, hasGl, i, oldPlots, ids, subplotInfo;
+
 
     for(i = 0; i < oldModules.length; i++) {
         if(oldModules[i].name === 'scatter') {
             hadScatter = true;
-            break;
         }
+        break;
     }
 
     for(i = 0; i < newModules.length; i++) {
@@ -233,12 +235,26 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout)
         }
     }
 
+    for(i = 0; i < oldModules.length; i++) {
+        if(oldModules[i].name === 'scattergl') {
+            hadGl = true;
+        }
+        break;
+    }
+
+    for(i = 0; i < newModules.length; i++) {
+        if(newModules[i].name === 'scattergl') {
+            hasGl = true;
+            break;
+        }
+    }
+
     if(hadScatter && !hasScatter) {
-        var oldPlots = oldFullLayout._plots,
-            ids = Object.keys(oldPlots || {});
+        oldPlots = oldFullLayout._plots;
+        ids = Object.keys(oldPlots || {});
 
         for(i = 0; i < ids.length; i++) {
-            var subplotInfo = oldPlots[ids[i]];
+            subplotInfo = oldPlots[ids[i]];
 
             if(subplotInfo.plot) {
                 subplotInfo.plot.select('g.scatterlayer')
@@ -253,6 +269,19 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout)
             .remove();
     }
 
+    if(hadGl && !hasGl) {
+        oldPlots = oldFullLayout._plots;
+        ids = Object.keys(oldPlots || {});
+
+        for(i = 0; i < ids.length; i++) {
+            subplotInfo = oldPlots[ids[i]];
+
+            if(subplotInfo._scene) {
+                subplotInfo._scene.destroy();
+            }
+        }
+    }
+
     var oldSubplotList = oldFullLayout._subplots || {};
     var newSubplotList = newFullLayout._subplots || {xaxis: [], yaxis: []};
 
@@ -304,7 +333,6 @@ exports.drawFramework = function(gd) {
         plotinfo.overlays = [];
 
         makeSubplotLayer(plotinfo);
-
         // fill in list of overlay subplots
         if(plotinfo.mainplot) {
             var mainplot = fullLayout._plots[plotinfo.mainplot];
@@ -496,3 +524,28 @@ function joinLayer(parent, nodeType, className, dataVal) {
 
     return layer;
 }
+
+exports.toSVG = function(gd) {
+    var imageRoot = gd._fullLayout._glimages;
+    var root = d3.select(gd).selectAll('.svg-container');
+    var canvases = root.filter(function(d, i) {return i === root.size() - 1;})
+        .selectAll('.gl-canvas-context, .gl-canvas-focus');
+
+    function canvasToImage() {
+        var canvas = this;
+        var imageData = canvas.toDataURL('image/png');
+        var image = imageRoot.append('svg:image');
+
+        image.attr({
+            xmlns: xmlnsNamespaces.svg,
+            'xlink:href': imageData,
+            preserveAspectRatio: 'none',
+            x: 0,
+            y: 0,
+            width: canvas.width,
+            height: canvas.height
+        });
+    }
+
+    canvases.each(canvasToImage);
+};
diff --git a/src/plots/cartesian/select.js b/src/plots/cartesian/select.js
index 55bcd1a958a..92837250622 100644
--- a/src/plots/cartesian/select.js
+++ b/src/plots/cartesian/select.js
@@ -14,6 +14,7 @@ var polygon = require('../../lib/polygon');
 var throttle = require('../../lib/throttle');
 var color = require('../../components/color');
 var makeEventData = require('../../components/fx/helpers').makeEventData;
+var Fx = require('../../components/fx');
 
 var axes = require('./axes');
 var constants = require('./constants');
@@ -62,12 +63,11 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
     if(mode === 'lasso') {
         filterPoly = filteredPolygon([[x0, y0]], constants.BENDPX);
     }
-
-    var outlines = zoomLayer.selectAll('path.select-outline').data([1, 2]);
+    var outlines = zoomLayer.selectAll('path.select-outline-' + plotinfo.id).data([1, 2]);
 
     outlines.enter()
         .append('path')
-        .attr('class', function(d) { return 'select-outline select-outline-' + d; })
+        .attr('class', function(d) { return 'select-outline select-outline-' + d + ' select-outline-' + plotinfo.id; })
         .attr('transform', 'translate(' + xs + ', ' + ys + ')')
         .attr('d', path0 + 'Z');
 
@@ -148,7 +148,7 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
                 }
             };
         } else {
-            fillRangeItems = function(eventData, currentPolygon, filterPoly) {
+            fillRangeItems = function(eventData, poly, filterPoly) {
                 var dataPts = eventData.lassoPoints = {};
 
                 for(i = 0; i < allAxes.length; i++) {
@@ -225,7 +225,8 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
             var ppts = mergedPolygons[i];
             paths.push(ppts.join('L') + 'L' + ppts[0]);
         }
-        outlines.attr('d', 'M' + paths.join('M') + 'Z');
+        outlines
+            .attr('d', 'M' + paths.join('M') + 'Z');
 
         throttle.throttle(
             throttleID,
@@ -233,14 +234,15 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
             function() {
                 selection = [];
 
-                var traceSelections = [], traceSelection;
+                var thisSelection, traceSelections = [], traceSelection;
                 for(i = 0; i < searchTraces.length; i++) {
                     searchInfo = searchTraces[i];
 
                     traceSelection = searchInfo.selectPoints(searchInfo, testPoly);
                     traceSelections.push(traceSelection);
 
-                    var thisSelection = fillSelectionItem(traceSelection, searchInfo);
+                    thisSelection = fillSelectionItem(traceSelection, searchInfo);
+
                     if(selection.length) {
                         for(var j = 0; j < thisSelection.length; j++) {
                             selection.push(thisSelection[j]);
@@ -257,7 +259,7 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
         );
     };
 
-    dragOptions.clickFn = function(numClicks) {
+    dragOptions.clickFn = function(numClicks, evt) {
         corners.remove();
 
         throttle.done(throttleID).then(function() {
@@ -278,6 +280,8 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
                 // but in case anyone depends on it we don't want to break it now.
                 gd.emit('plotly_selected', undefined);
             }
+
+            Fx.click(gd, evt);
         });
     };
 
@@ -319,8 +323,8 @@ function updateSelectedState(gd, searchTraces, eventData) {
             var fullData = pt.fullData;
 
             if(pt.pointIndices) {
-                data.selectedpoints = data.selectedpoints.concat(pt.pointIndices);
-                fullData.selectedpoints = fullData.selectedpoints.concat(pt.pointIndices);
+                [].push.apply(data.selectedpoints, pt.pointIndices);
+                [].push.apply(fullData.selectedpoints, pt.pointIndices);
             } else {
                 data.selectedpoints.push(pt.pointIndex);
                 fullData.selectedpoints.push(pt.pointIndex);
@@ -332,6 +336,11 @@ function updateSelectedState(gd, searchTraces, eventData) {
             trace = searchTraces[i].cd[0].trace;
             delete trace.selectedpoints;
             delete trace._input.selectedpoints;
+
+            // delete scattergl selection
+            if(searchTraces[i].cd[0].t && searchTraces[i].cd[0].t.scene) {
+                searchTraces[i].cd[0].t.scene.clearSelect();
+            }
         }
     }
 
diff --git a/src/plots/geo/geo.js b/src/plots/geo/geo.js
index c0362781854..6aabf11a77f 100644
--- a/src/plots/geo/geo.js
+++ b/src/plots/geo/geo.js
@@ -140,7 +140,7 @@ proto.update = function(geoCalcData, fullLayout) {
     this.updateDims(fullLayout, geoLayout);
     this.updateFx(fullLayout, geoLayout);
 
-    Plots.generalUpdatePerTraceModule(this, geoCalcData, geoLayout);
+    Plots.generalUpdatePerTraceModule(this.graphDiv, this, geoCalcData, geoLayout);
 
     var scatterLayer = this.layers.frontplot.select('.scatterlayer');
     this.dataPoints.point = scatterLayer.selectAll('.point');
diff --git a/src/plots/plots.js b/src/plots/plots.js
index a54052cf16e..a1736502e59 100644
--- a/src/plots/plots.js
+++ b/src/plots/plots.js
@@ -49,7 +49,6 @@ plots.computeAPICommandBindings = commandModule.computeAPICommandBindings;
 plots.manageCommandObserver = commandModule.manageCommandObserver;
 plots.hasSimpleAPICommandBindings = commandModule.hasSimpleAPICommandBindings;
 
-
 // in some cases the browser doesn't seem to know how big
 // the text is at first, so it needs to draw it,
 // then wait a little, then draw it again
@@ -586,7 +585,6 @@ plots.createTransitionData = function(gd) {
 // or trace has a category
 plots._hasPlotType = function(category) {
     // check plot
-
     var basePlotModules = this._basePlotModules || [];
     var i;
 
@@ -701,10 +699,6 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa
         if(oldSubplot) {
             plotinfo = newSubplots[id] = oldSubplot;
 
-            if(plotinfo._scene2d) {
-                plotinfo._scene2d.updateRefs(newFullLayout);
-            }
-
             if(plotinfo.xaxis.layer !== xaxis.layer) {
                 plotinfo.xlines.attr('d', null);
                 plotinfo.xaxislayer.selectAll('*').remove();
@@ -928,7 +922,6 @@ plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) {
             }
         }
         else {
-
             // add identify refs for consistency with transformed traces
             fullTrace._fullInput = fullTrace;
             fullTrace._expandedInput = fullTrace;
@@ -1122,7 +1115,7 @@ plots.supplyTraceDefaults = function(traceIn, colorIndex, layout, traceInIndex)
             traceOut.visible = !!traceOut.visible;
         }
 
-        if(_module && _module.selectPoints && traceOut.type !== 'scattergl') {
+        if(_module && _module.selectPoints) {
             coerce('selectedpoints');
         }
 
@@ -1417,7 +1410,6 @@ plots.supplyLayoutModuleDefaults = function(layoutIn, layoutOut, fullData, trans
 // Remove all plotly attributes from a div so it can be replotted fresh
 // TODO: these really need to be encapsulated into a much smaller set...
 plots.purge = function(gd) {
-
     // note: we DO NOT remove _context because it doesn't change when we insert
     // a new plot, and may have been set outside of our scope.
 
@@ -2436,10 +2428,10 @@ plots.rehover = function(gd) {
     }
 };
 
-plots.generalUpdatePerTraceModule = function(subplot, subplotCalcData, subplotLayout) {
-    var traceHashOld = subplot.traceHash,
-        traceHash = {},
-        i;
+plots.generalUpdatePerTraceModule = function(gd, subplot, subplotCalcData, subplotLayout) {
+    var traceHashOld = subplot.traceHash;
+    var traceHash = {};
+    var i;
 
     // build up moduleName -> calcData hash
     for(i = 0; i < subplotCalcData.length; i++) {
@@ -2458,7 +2450,6 @@ plots.generalUpdatePerTraceModule = function(subplot, subplotCalcData, subplotLa
     // plot method is called so that it is properly
     // removed from the DOM.
     for(var moduleNameOld in traceHashOld) {
-
         if(!traceHash[moduleNameOld]) {
             var fakeCalcTrace = traceHashOld[moduleNameOld][0],
                 fakeTrace = fakeCalcTrace[0].trace;
@@ -2473,7 +2464,7 @@ plots.generalUpdatePerTraceModule = function(subplot, subplotCalcData, subplotLa
         var moduleCalcData = traceHash[moduleName];
         var _module = moduleCalcData[0][0].trace._module;
 
-        _module.plot(subplot, Lib.filterVisible(moduleCalcData), subplotLayout);
+        _module.plot(gd, subplot, Lib.filterVisible(moduleCalcData), subplotLayout);
     }
 
     // update moduleName -> calcData hash
diff --git a/src/plots/polar/index.js b/src/plots/polar/index.js
index c0f134f52fe..9c8695245f6 100644
--- a/src/plots/polar/index.js
+++ b/src/plots/polar/index.js
@@ -79,5 +79,6 @@ module.exports = {
     layoutAttributes: require('./layout_attributes'),
     supplyLayoutDefaults: require('./layout_defaults'),
     plot: plot,
-    clean: clean
+    clean: clean,
+    toSVG: require('../cartesian').toSVG
 };
diff --git a/src/plots/polar/polar.js b/src/plots/polar/polar.js
index d1cad6e1f62..7db125ce6bc 100644
--- a/src/plots/polar/polar.js
+++ b/src/plots/polar/polar.js
@@ -85,7 +85,7 @@ proto.plot = function(polarCalcData, fullLayout) {
 
     _this.updateLayers(fullLayout, polarLayout);
     _this.updateLayout(fullLayout, polarLayout);
-    Plots.generalUpdatePerTraceModule(_this, polarCalcData, polarLayout);
+    Plots.generalUpdatePerTraceModule(_this.gd, _this, polarCalcData, polarLayout);
     _this.updateFx(fullLayout, polarLayout);
 };
 
@@ -837,7 +837,7 @@ proto.updateRadialDrag = function(fullLayout, polarLayout) {
             var _module = moduleCalcData[0][0].trace._module;
             var polarLayoutNow = gd._fullLayout[_this.id];
 
-            _module.plot(_this, moduleCalcDataVisible, polarLayoutNow);
+            _module.plot(gd, _this, moduleCalcDataVisible, polarLayoutNow);
 
             if(!Registry.traceIs(k, 'gl')) {
                 for(var i = 0; i < moduleCalcDataVisible.length; i++) {
@@ -962,7 +962,7 @@ proto.updateAngularDrag = function(fullLayout, polarLayout) {
                 var _module = moduleCalcData[0][0].trace._module;
                 var polarLayoutNow = gd._fullLayout[_this.id];
 
-                _module.plot(_this, moduleCalcDataVisible, polarLayoutNow);
+                _module.plot(gd, _this, moduleCalcDataVisible, polarLayoutNow);
             }
         }
     }
diff --git a/src/plots/ternary/ternary.js b/src/plots/ternary/ternary.js
index 36b83231edf..cf2b607e824 100644
--- a/src/plots/ternary/ternary.js
+++ b/src/plots/ternary/ternary.js
@@ -64,7 +64,7 @@ proto.plot = function(ternaryCalcData, fullLayout) {
 
     _this.updateLayers(ternaryLayout);
     _this.adjustLayout(ternaryLayout, graphSize);
-    Plots.generalUpdatePerTraceModule(_this, ternaryCalcData, ternaryLayout);
+    Plots.generalUpdatePerTraceModule(_this.graphDiv, _this, ternaryCalcData, ternaryLayout);
     _this.layers.plotbg.select('path').call(Color.fill, ternaryLayout.bgcolor);
 };
 
diff --git a/src/traces/choropleth/plot.js b/src/traces/choropleth/plot.js
index c88df083ac2..e0579accf3a 100644
--- a/src/traces/choropleth/plot.js
+++ b/src/traces/choropleth/plot.js
@@ -17,7 +17,7 @@ var getTopojsonFeatures = require('../../lib/topojson_utils').getTopojsonFeature
 var locationToFeature = require('../../lib/geo_location_utils').locationToFeature;
 var style = require('./style');
 
-module.exports = function plot(geo, calcData) {
+module.exports = function plot(gd, geo, calcData) {
     for(var i = 0; i < calcData.length; i++) {
         calcGeoJSON(calcData[i], geo.topojson);
     }
@@ -45,7 +45,7 @@ module.exports = function plot(geo, calcData) {
         paths.exit().remove();
 
         // call style here within topojson request callback
-        style(geo.graphDiv, calcTrace);
+        style(gd, calcTrace);
     });
 };
 
diff --git a/src/traces/parcoords/attributes.js b/src/traces/parcoords/attributes.js
index b9968d029b1..1713b722d86 100644
--- a/src/traces/parcoords/attributes.js
+++ b/src/traces/parcoords/attributes.js
@@ -106,6 +106,7 @@ module.exports = {
 
     line: extendFlat(
         // the default autocolorscale isn't quite usable for parcoords due to context ambiguity around 0 (grey, off-white)
+
         // autocolorscale therefore defaults to false too, to avoid being overridden by the  blue-white-red autocolor palette
         extendDeepAll(
             colorAttributes('line', 'calc'),
@@ -123,7 +124,6 @@ module.exports = {
                         'The default value is false, so that `parcoords` colorscale can default to `Viridis`.'
                     ].join(' ')
                 }
-
             }
         ),
 
diff --git a/src/traces/parcoords/base_plot.js b/src/traces/parcoords/base_plot.js
index 4b8ab7379d0..25fbfd48b29 100644
--- a/src/traces/parcoords/base_plot.js
+++ b/src/traces/parcoords/base_plot.js
@@ -33,7 +33,6 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout)
 };
 
 exports.toSVG = function(gd) {
-
     var imageRoot = gd._fullLayout._glimages;
     var root = d3.select(gd).selectAll('.svg-container');
     var canvases = root.filter(function(d, i) {return i === root.size() - 1;})
@@ -47,11 +46,11 @@ exports.toSVG = function(gd) {
         image.attr({
             xmlns: xmlnsNamespaces.svg,
             'xlink:href': imageData,
+            preserveAspectRatio: 'none',
             x: 0,
             y: 0,
             width: canvas.width,
-            height: canvas.height,
-            preserveAspectRatio: 'none'
+            height: canvas.height
         });
     }
 
diff --git a/src/traces/parcoords/lines.js b/src/traces/parcoords/lines.js
index 62b47591c4d..fb1072780d8 100644
--- a/src/traces/parcoords/lines.js
+++ b/src/traces/parcoords/lines.js
@@ -55,7 +55,8 @@ function renderBlock(regl, glAes, renderState, blockLineCount, sampleCount, item
         item.offset = sectionVertexCount * blockNumber * blockLineCount;
         item.count = sectionVertexCount * count;
         if(blockNumber === 0) {
-            window.cancelAnimationFrame(renderState.currentRafs[rafKey]); // stop drawing possibly stale glyphs before clearing
+            // stop drawing possibly stale glyphs before clearing
+            window.cancelAnimationFrame(renderState.currentRafs[rafKey]);
             delete renderState.currentRafs[rafKey];
             clear(regl, item.scissorX, item.scissorY, item.scissorWidth, item.viewBoxSize[1]);
         }
@@ -353,6 +354,7 @@ module.exports = function(canvasGL, d, scatter) {
 
             colorClamp: colorClamp,
             scatter: scatter || 0,
+
             scissorX: (I === leftmost ? 0 : x + overdrag) + (model.pad.l - overdrag) + model.layoutWidth * domain.x[0],
             scissorWidth: (I === rightmost ? canvasWidth - x + overdrag : panelSizeX + 0.5) + (I === leftmost ? x + overdrag : 0),
             scissorY: y + model.pad.b + model.layoutHeight * domain.y[0],
@@ -431,6 +433,7 @@ module.exports = function(canvasGL, d, scatter) {
     }
 
     function destroy() {
+        canvasGL.style['pointer-events'] = 'none';
         paletteTexture.destroy();
     }
 
diff --git a/src/traces/parcoords/parcoords.js b/src/traces/parcoords/parcoords.js
index 317ada1e08f..74f1a31f111 100644
--- a/src/traces/parcoords/parcoords.js
+++ b/src/traces/parcoords/parcoords.js
@@ -305,6 +305,7 @@ module.exports = function(root, svg, parcoordsLineLayers, styledData, layout, ca
         .filter(function(d) {
             return d.pick;
         })
+        .style('pointer-events', 'auto')
         .on('mousemove', function(d) {
             if(linePickActive && d.lineLayer && callbacks && callbacks.hover) {
                 var event = d3.event;
diff --git a/src/traces/parcoords/plot.js b/src/traces/parcoords/plot.js
index 71b3e7860c2..6d18d227bfc 100644
--- a/src/traces/parcoords/plot.js
+++ b/src/traces/parcoords/plot.js
@@ -12,7 +12,6 @@ var parcoords = require('./parcoords');
 var createRegl = require('regl');
 
 module.exports = function plot(gd, cdparcoords) {
-
     var fullLayout = gd._fullLayout;
     var svg = fullLayout._toppaper;
     var root = fullLayout._paperdiv;
diff --git a/src/traces/pointcloud/attributes.js b/src/traces/pointcloud/attributes.js
index 30570bb4cdc..df1e43900ba 100644
--- a/src/traces/pointcloud/attributes.js
+++ b/src/traces/pointcloud/attributes.js
@@ -8,7 +8,7 @@
 
 'use strict';
 
-var scatterglAttrs = require('../scattergl/attributes');
+var scatterglAttrs = require('../scatter/attributes');
 
 module.exports = {
     x: scatterglAttrs.x,
diff --git a/src/traces/scattergeo/plot.js b/src/traces/scattergeo/plot.js
index f4fbacb15eb..9bba530371e 100644
--- a/src/traces/scattergeo/plot.js
+++ b/src/traces/scattergeo/plot.js
@@ -19,7 +19,7 @@ var geoJsonUtils = require('../../lib/geojson_utils');
 var subTypes = require('../scatter/subtypes');
 var style = require('./style');
 
-module.exports = function plot(geo, calcData) {
+module.exports = function plot(gd, geo, calcData) {
     for(var i = 0; i < calcData.length; i++) {
         calcGeoJSON(calcData[i], geo.topojson);
     }
@@ -79,7 +79,7 @@ module.exports = function plot(geo, calcData) {
         }
 
         // call style here within topojson request callback
-        style(geo.graphDiv, calcTrace);
+        style(gd, calcTrace);
     });
 };
 
diff --git a/src/traces/scattergl/attributes.js b/src/traces/scattergl/attributes.js
index 8e2b18e8a4c..0c69b843c0a 100644
--- a/src/traces/scattergl/attributes.js
+++ b/src/traces/scattergl/attributes.js
@@ -12,7 +12,6 @@ var scatterAttrs = require('../scatter/attributes');
 var colorAttributes = require('../../components/colorscale/color_attributes');
 
 var DASHES = require('../../constants/gl2d_dashes');
-var MARKERS = require('../../constants/gl2d_markers');
 var extendFlat = require('../../lib/extend').extendFlat;
 var overrideAll = require('../../plot_api/edit_types').overrideAll;
 
@@ -58,14 +57,7 @@ var attrs = module.exports = overrideAll({
         }
     },
     marker: extendFlat({}, colorAttributes('marker'), {
-        symbol: {
-            valType: 'enumerated',
-            values: Object.keys(MARKERS),
-            dflt: 'circle',
-            arrayOk: true,
-            role: 'style',
-            description: 'Sets the marker symbol type.'
-        },
+        symbol: scatterMarkerAttrs.symbol,
         size: scatterMarkerAttrs.size,
         sizeref: scatterMarkerAttrs.sizeref,
         sizemin: scatterMarkerAttrs.sizemin,
@@ -78,11 +70,18 @@ var attrs = module.exports = overrideAll({
         })
     }),
     connectgaps: scatterAttrs.connectgaps,
-    fill: extendFlat({}, scatterAttrs.fill, {
-        values: ['none', 'tozeroy', 'tozerox']
-    }),
+    fill: scatterAttrs.fill,
     fillcolor: scatterAttrs.fillcolor,
 
+    hoveron: scatterAttrs.hoveron,
+
+    selected: {
+        marker: scatterAttrs.selected.marker
+    },
+    unselected: {
+        marker: scatterAttrs.unselected.marker
+    },
+
     error_y: scatterAttrs.error_y,
     error_x: scatterAttrs.error_x
 }, 'calc', 'nested');
diff --git a/src/traces/scattergl/calc.js b/src/traces/scattergl/calc.js
deleted file mode 100644
index 2cc5238b8d1..00000000000
--- a/src/traces/scattergl/calc.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
-* Copyright 2012-2018, Plotly, Inc.
-* All rights reserved.
-*
-* This source code is licensed under the MIT license found in the
-* LICENSE file in the root directory of this source tree.
-*/
-
-
-'use strict';
-
-var Axes = require('../../plots/cartesian/axes');
-var arraysToCalcdata = require('../scatter/arrays_to_calcdata');
-var calcColorscales = require('../scatter/colorscale_calc');
-
-module.exports = function calc(gd, trace) {
-    var dragmode = gd._fullLayout.dragmode;
-    var cd;
-
-    if(dragmode === 'lasso' || dragmode === 'select') {
-        var xa = Axes.getFromId(gd, trace.xaxis || 'x');
-        var ya = Axes.getFromId(gd, trace.yaxis || 'y');
-
-        var x = xa.makeCalcdata(trace, 'x');
-        var y = ya.makeCalcdata(trace, 'y');
-
-        var serieslen = Math.min(x.length, y.length), i;
-
-        // create the "calculated data" to plot
-        cd = new Array(serieslen);
-
-        for(i = 0; i < serieslen; i++) {
-            cd[i] = {x: x[i], y: y[i]};
-        }
-    } else {
-        cd = [{x: false, y: false, trace: trace, t: {}}];
-        arraysToCalcdata(cd, trace);
-    }
-
-    calcColorscales(trace);
-
-    return cd;
-};
diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js
deleted file mode 100644
index 4796a95145a..00000000000
--- a/src/traces/scattergl/convert.js
+++ /dev/null
@@ -1,768 +0,0 @@
-/**
-* Copyright 2012-2018, Plotly, Inc.
-* All rights reserved.
-*
-* This source code is licensed under the MIT license found in the
-* LICENSE file in the root directory of this source tree.
-*/
-
-
-'use strict';
-
-var createScatter = require('gl-scatter2d');
-var createFancyScatter = require('gl-scatter2d-sdf');
-var createLine = require('gl-line2d');
-var createError = require('gl-error2d');
-var isNumeric = require('fast-isnumeric');
-
-var Lib = require('../../lib');
-var Axes = require('../../plots/cartesian/axes');
-var autoType = require('../../plots/cartesian/axis_autotype');
-var ErrorBars = require('../../components/errorbars');
-var str2RGBArray = require('../../lib/str2rgbarray');
-var truncate = require('../../lib/typed_array_truncate');
-var formatColor = require('../../lib/gl_format_color');
-var subTypes = require('../scatter/subtypes');
-var makeBubbleSizeFn = require('../scatter/make_bubble_size_func');
-var getTraceColor = require('../scatter/get_trace_color');
-var MARKER_SYMBOLS = require('../../constants/gl2d_markers');
-var DASHES = require('../../constants/gl2d_dashes');
-var DESELECTDIM = require('../../constants/interactions').DESELECTDIM;
-
-var AXES = ['xaxis', 'yaxis'];
-var TRANSPARENT = [0, 0, 0, 0];
-
-function LineWithMarkers(scene, uid) {
-    this.scene = scene;
-    this.uid = uid;
-    this.type = 'scattergl';
-
-    this.pickXData = [];
-    this.pickYData = [];
-    this.xData = [];
-    this.yData = [];
-    this.textLabels = [];
-    this.color = 'rgb(0, 0, 0)';
-    this.name = '';
-    this.hoverinfo = 'all';
-    this.connectgaps = true;
-
-    this.index = null;
-    this.idToIndex = [];
-    this.bounds = [0, 0, 0, 0];
-
-    this.isVisible = false;
-    this.hasLines = false;
-    this.hasErrorX = false;
-    this.hasErrorY = false;
-    this.hasMarkers = false;
-
-    this.line = this.initObject(createLine, {
-        positions: new Float64Array(0),
-        color: [0, 0, 0, 1],
-        width: 1,
-        fill: [false, false, false, false],
-        fillColor: [
-            [0, 0, 0, 1],
-            [0, 0, 0, 1],
-            [0, 0, 0, 1],
-            [0, 0, 0, 1]],
-        dashes: [1],
-    }, 0);
-
-    this.errorX = this.initObject(createError, {
-        positions: new Float64Array(0),
-        errors: new Float64Array(0),
-        lineWidth: 1,
-        capSize: 0,
-        color: [0, 0, 0, 1]
-    }, 1);
-
-    this.errorY = this.initObject(createError, {
-        positions: new Float64Array(0),
-        errors: new Float64Array(0),
-        lineWidth: 1,
-        capSize: 0,
-        color: [0, 0, 0, 1]
-    }, 2);
-
-    var scatterOptions0 = {
-        positions: new Float64Array(0),
-        sizes: [],
-        colors: [],
-        glyphs: [],
-        borderWidths: [],
-        borderColors: [],
-        size: 12,
-        color: [0, 0, 0, 1],
-        borderSize: 1,
-        borderColor: [0, 0, 0, 1],
-        snapPoints: true
-    };
-    var scatterOptions1 = Lib.extendFlat({}, scatterOptions0, {snapPoints: false});
-
-    this.scatter = this.initObject(createScatter, scatterOptions0, 3);
-    this.fancyScatter = this.initObject(createFancyScatter, scatterOptions0, 4);
-    this.selectScatter = this.initObject(createScatter, scatterOptions1, 5);
-}
-
-var proto = LineWithMarkers.prototype;
-
-proto.initObject = function(createFn, options, objIndex) {
-    var _this = this;
-    var glplot = _this.scene.glplot;
-    var options0 = Lib.extendFlat({}, options);
-    var obj = null;
-
-    function update() {
-        if(!obj) {
-            obj = createFn(glplot, options);
-            obj._trace = _this;
-            obj._index = objIndex;
-        }
-        obj.update(options);
-    }
-
-    function clear() {
-        if(obj) obj.update(options0);
-    }
-
-    function dispose() {
-        if(obj) obj.dispose();
-    }
-
-    return {
-        options: options,
-        update: update,
-        clear: clear,
-        dispose: dispose
-    };
-};
-
-proto.handlePick = function(pickResult) {
-    var index = pickResult.pointId;
-
-    if(pickResult.object !== this.line || this.connectgaps) {
-        index = this.idToIndex[pickResult.pointId];
-    }
-
-    var x = this.pickXData[index];
-
-    return {
-        trace: this,
-        dataCoord: pickResult.dataCoord,
-        traceCoord: [
-            isNumeric(x) || !Lib.isDateTime(x) ? x : Lib.dateTime2ms(x),
-            this.pickYData[index]
-        ],
-        textLabel: Array.isArray(this.textLabels) ?
-            this.textLabels[index] :
-            this.textLabels,
-        color: Array.isArray(this.color) ?
-            this.color[index] :
-            this.color,
-        name: this.name,
-        pointIndex: index,
-        hoverinfo: this.hoverinfo
-    };
-};
-
-// check if trace is fancy
-proto.isFancy = function(options) {
-    if(this.scene.xaxis.type !== 'linear' && this.scene.xaxis.type !== 'date') return true;
-    if(this.scene.yaxis.type !== 'linear') return true;
-
-    if(!options.x || !options.y) return true;
-
-    if(this.hasMarkers) {
-        var marker = options.marker || {};
-
-        if(Array.isArray(marker.symbol) ||
-             marker.symbol !== 'circle' ||
-             Array.isArray(marker.size) ||
-             Array.isArray(marker.color) ||
-             Array.isArray(marker.line.width) ||
-             Array.isArray(marker.line.color) ||
-             Array.isArray(marker.opacity)
-        ) return true;
-    }
-
-    if(this.hasLines && !this.connectgaps) return true;
-
-    if(this.hasErrorX) return true;
-    if(this.hasErrorY) return true;
-
-    return false;
-};
-
-// handle the situation where values can be array-like or not array like
-function convertArray(convert, data, count) {
-    if(!Array.isArray(data)) data = [data];
-
-    return _convertArray(convert, data, count);
-}
-
-function _convertArray(convert, data, count) {
-    var result = new Array(count),
-        data0 = data[0];
-
-    for(var i = 0; i < count; ++i) {
-        result[i] = (i >= data.length) ?
-            convert(data0) :
-            convert(data[i]);
-    }
-
-    return result;
-}
-
-var convertNumber = convertArray.bind(null, function(x) { return +x; });
-var convertColorBase = convertArray.bind(null, str2RGBArray);
-var convertSymbol = convertArray.bind(null, function(x) {
-    return MARKER_SYMBOLS[x] ? x : 'circle';
-});
-
-function convertColor(color, opacity, count) {
-    return _convertColor(
-        convertColorBase(color, count),
-        convertNumber(opacity, count),
-        count
-    );
-}
-
-function convertColorScale(containerIn, markerOpacity, traceOpacity, count) {
-    var colors = formatColor(containerIn, markerOpacity, count);
-
-    colors = Array.isArray(colors[0]) ?
-        colors :
-        _convertArray(Lib.identity, [colors], count);
-
-    return _convertColor(
-        colors,
-        convertNumber(traceOpacity, count),
-        count
-    );
-}
-
-function _convertColor(colors, opacities, count) {
-    var result = new Array(4 * count);
-
-    for(var i = 0; i < count; ++i) {
-        for(var j = 0; j < 3; ++j) result[4 * i + j] = colors[i][j];
-
-        result[4 * i + 3] = colors[i][3] * opacities[i];
-    }
-
-    return result;
-}
-
-function isSymbolOpen(symbol) {
-    return symbol.split('-open')[1] === '';
-}
-
-function fillColor(colorIn, colorOut, offsetIn, offsetOut, isDimmed) {
-    var dim = isDimmed ? DESELECTDIM : 1;
-    var j;
-
-    for(j = 0; j < 3; j++) {
-        colorIn[4 * offsetIn + j] = colorOut[4 * offsetOut + j];
-    }
-    colorIn[4 * offsetIn + j] = dim * colorOut[4 * offsetOut + j];
-}
-
-proto.update = function(options, cdscatter) {
-    if(options.visible !== true) {
-        this.isVisible = false;
-        this.hasLines = false;
-        this.hasErrorX = false;
-        this.hasErrorY = false;
-        this.hasMarkers = false;
-    }
-    else {
-        this.isVisible = true;
-        this.hasLines = subTypes.hasLines(options);
-        this.hasErrorX = options.error_x.visible === true;
-        this.hasErrorY = options.error_y.visible === true;
-        this.hasMarkers = subTypes.hasMarkers(options);
-    }
-
-    this.textLabels = options.text;
-    this.name = options.name;
-    this.hoverinfo = options.hoverinfo;
-    this.bounds = [Infinity, Infinity, -Infinity, -Infinity];
-    this.connectgaps = !!options.connectgaps;
-
-    if(!this.isVisible) {
-        this.line.clear();
-        this.errorX.clear();
-        this.errorY.clear();
-        this.scatter.clear();
-        this.fancyScatter.clear();
-    }
-    else if(this.isFancy(options)) {
-        this.updateFancy(options);
-    }
-    else {
-        this.updateFast(options);
-    }
-
-    // sort objects so that order is preserve on updates:
-    // - lines
-    // - errorX
-    // - errorY
-    // - markers
-    this.scene.glplot.objects.sort(function(a, b) {
-        return a._index - b._index;
-    });
-
-    // set trace index so that scene2d can sort object per traces
-    this.index = options.index;
-
-    // not quite on-par with 'scatter', but close enough for now
-    // does not handle the colorscale case
-    this.color = getTraceColor(options, {});
-
-    // provide reference for selecting points
-    if(cdscatter && cdscatter[0] && !cdscatter[0]._glTrace) {
-        cdscatter[0]._glTrace = this;
-    }
-};
-
-// We'd ideally know that all values are of fast types; sampling gives no certainty but faster
-//     (for the future, typed arrays can guarantee it, and Date values can be done with
-//      representing the epoch milliseconds in a typed array;
-//      also, perhaps the Python / R interfaces take care of String->Date conversions
-//      such that there's no need to check for string dates in plotly.js)
-// Patterned from axis_autotype.js:moreDates
-// Code DRYing is not done to preserve the most direct compilation possible for speed;
-// also, there are quite a few differences
-function allFastTypesLikely(a) {
-    var len = a.length,
-        inc = Math.max(1, (len - 1) / Math.min(Math.max(len, 1), 1000)),
-        ai;
-
-    for(var i = 0; i < len; i += inc) {
-        ai = a[Math.floor(i)];
-        if(!isNumeric(ai) && !(ai instanceof Date)) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-proto.updateFast = function(options) {
-    var x = this.xData = this.pickXData = options.x;
-    var y = this.yData = this.pickYData = options.y;
-
-    var len = x.length,
-        idToIndex = new Array(len),
-        positions = new Float64Array(2 * len),
-        bounds = this.bounds,
-        pId = 0,
-        ptr = 0,
-        selection = options.selection,
-        i, selPositions, l;
-
-    var xx, yy;
-
-    var xcalendar = options.xcalendar;
-
-    var fastType = allFastTypesLikely(x);
-    var isDateTime = !fastType && autoType(x, xcalendar) === 'date';
-
-    // TODO add 'very fast' mode that bypasses this loop
-    // TODO bypass this on modebar +/- zoom
-    if(fastType || isDateTime) {
-
-        for(i = 0; i < len; ++i) {
-            xx = x[i];
-            yy = y[i];
-
-            if(isNumeric(yy)) {
-
-                if(!fastType) {
-                    xx = Lib.dateTime2ms(xx, xcalendar);
-                }
-
-                positions[ptr++] = xx;
-                positions[ptr++] = yy;
-
-                idToIndex[pId++] = i;
-
-                bounds[0] = Math.min(bounds[0], xx);
-                bounds[1] = Math.min(bounds[1], yy);
-                bounds[2] = Math.max(bounds[2], xx);
-                bounds[3] = Math.max(bounds[3], yy);
-            }
-        }
-    }
-
-    positions = truncate(positions, ptr);
-    this.idToIndex = idToIndex;
-
-    // form selected set
-    if(selection && selection.length) {
-        selPositions = new Float64Array(2 * selection.length);
-
-        for(i = 0, l = selection.length; i < l; i++) {
-            selPositions[i * 2 + 0] = selection[i].x;
-            selPositions[i * 2 + 1] = selection[i].y;
-        }
-    }
-
-    this.updateLines(options, positions);
-    this.updateError('X', options);
-    this.updateError('Y', options);
-
-    var markerSize;
-
-    if(this.hasMarkers) {
-        var markerColor, borderColor, opacity;
-
-        // if we have selPositions array - means we have to render all points transparent, and selected points opaque
-        if(selPositions) {
-            this.scatter.options.positions = null;
-
-            markerColor = str2RGBArray(options.marker.color);
-            borderColor = str2RGBArray(options.marker.line.color);
-            opacity = (options.opacity) * (options.marker.opacity) * DESELECTDIM;
-
-            markerColor[3] *= opacity;
-            this.scatter.options.color = markerColor;
-
-            borderColor[3] *= opacity;
-            this.scatter.options.borderColor = borderColor;
-
-            markerSize = options.marker.size;
-            this.scatter.options.size = markerSize;
-            this.scatter.options.borderSize = options.marker.line.width;
-
-            this.scatter.update();
-            this.scatter.options.positions = positions;
-
-
-            this.selectScatter.options.positions = selPositions;
-
-            markerColor = str2RGBArray(options.marker.color);
-            borderColor = str2RGBArray(options.marker.line.color);
-            opacity = (options.opacity) * (options.marker.opacity);
-
-            markerColor[3] *= opacity;
-            this.selectScatter.options.color = markerColor;
-
-            borderColor[3] *= opacity;
-            this.selectScatter.options.borderColor = borderColor;
-
-            markerSize = options.marker.size;
-            this.selectScatter.options.size = markerSize;
-            this.selectScatter.options.borderSize = options.marker.line.width;
-
-            this.selectScatter.update();
-        }
-
-        else {
-            this.scatter.options.positions = positions;
-
-            markerColor = str2RGBArray(options.marker.color);
-            borderColor = str2RGBArray(options.marker.line.color);
-            opacity = (options.opacity) * (options.marker.opacity);
-            markerColor[3] *= opacity;
-            this.scatter.options.color = markerColor;
-
-            borderColor[3] *= opacity;
-            this.scatter.options.borderColor = borderColor;
-
-            markerSize = options.marker.size;
-            this.scatter.options.size = markerSize;
-            this.scatter.options.borderSize = options.marker.line.width;
-
-            this.scatter.update();
-        }
-
-    }
-    else {
-        this.scatter.clear();
-    }
-
-    // turn off fancy scatter plot
-    this.fancyScatter.clear();
-
-    // add item for autorange routine
-    this.expandAxesFast(bounds, markerSize);
-};
-
-proto.updateFancy = function(options) {
-    var scene = this.scene,
-        xaxis = scene.xaxis,
-        yaxis = scene.yaxis,
-        bounds = this.bounds,
-        selection = options.selection;
-
-    // makeCalcdata runs d2c (data-to-coordinate) on every point
-    var x = this.pickXData = xaxis.makeCalcdata(options, 'x').slice();
-    var y = this.pickYData = yaxis.makeCalcdata(options, 'y').slice();
-
-    this.xData = x.slice();
-    this.yData = y.slice();
-
-    // get error values
-    var errorVals = ErrorBars.calcFromTrace(options, scene.fullLayout);
-
-    var len = x.length,
-        idToIndex = new Array(len),
-        positions = new Float64Array(2 * len),
-        errorsX = new Float64Array(4 * len),
-        errorsY = new Float64Array(4 * len),
-        pId = 0,
-        ptr = 0,
-        ptrX = 0,
-        ptrY = 0;
-
-    var getX = (xaxis.type === 'log') ? xaxis.d2l : function(x) { return x; };
-    var getY = (yaxis.type === 'log') ? yaxis.d2l : function(y) { return y; };
-
-    var i, xx, yy, ex0, ex1, ey0, ey1;
-
-    for(i = 0; i < len; ++i) {
-        this.xData[i] = xx = getX(x[i]);
-        this.yData[i] = yy = getY(y[i]);
-
-        if(isNaN(xx) || isNaN(yy)) continue;
-
-        idToIndex[pId++] = i;
-
-        positions[ptr++] = xx;
-        positions[ptr++] = yy;
-
-        ex0 = errorsX[ptrX++] = xx - errorVals[i].xs || 0;
-        ex1 = errorsX[ptrX++] = errorVals[i].xh - xx || 0;
-        errorsX[ptrX++] = 0;
-        errorsX[ptrX++] = 0;
-
-        errorsY[ptrY++] = 0;
-        errorsY[ptrY++] = 0;
-        ey0 = errorsY[ptrY++] = yy - errorVals[i].ys || 0;
-        ey1 = errorsY[ptrY++] = errorVals[i].yh - yy || 0;
-
-        bounds[0] = Math.min(bounds[0], xx - ex0);
-        bounds[1] = Math.min(bounds[1], yy - ey0);
-        bounds[2] = Math.max(bounds[2], xx + ex1);
-        bounds[3] = Math.max(bounds[3], yy + ey1);
-    }
-
-    positions = truncate(positions, ptr);
-    this.idToIndex = idToIndex;
-
-    this.updateLines(options, positions);
-    this.updateError('X', options, positions, errorsX);
-    this.updateError('Y', options, positions, errorsY);
-
-    var sizes, selIds;
-
-    if(selection && selection.length) {
-        selIds = {};
-        for(i = 0; i < selection.length; i++) {
-            selIds[selection[i].pointNumber] = true;
-        }
-    }
-
-    if(this.hasMarkers) {
-        this.scatter.options.positions = positions;
-
-        // TODO rewrite convert function so that
-        // we don't have to loop through the data another time
-
-        this.scatter.options.sizes = new Array(pId);
-        this.scatter.options.glyphs = new Array(pId);
-        this.scatter.options.borderWidths = new Array(pId);
-        this.scatter.options.colors = new Array(pId * 4);
-        this.scatter.options.borderColors = new Array(pId * 4);
-
-        var markerSizeFunc = makeBubbleSizeFn(options);
-        var markerOpts = options.marker;
-        var markerOpacity = markerOpts.opacity;
-        var traceOpacity = options.opacity;
-        var symbols = convertSymbol(markerOpts.symbol, len);
-        var colors = convertColorScale(markerOpts, markerOpacity, traceOpacity, len);
-        var borderWidths = convertNumber(markerOpts.line.width, len);
-        var borderColors = convertColorScale(markerOpts.line, markerOpacity, traceOpacity, len);
-        var index, size, symbol, symbolSpec, isOpen, isDimmed, _colors, _borderColors, bw, minBorderWidth;
-
-        sizes = convertArray(markerSizeFunc, markerOpts.size, len);
-
-        for(i = 0; i < pId; ++i) {
-            index = idToIndex[i];
-
-            symbol = symbols[index];
-            symbolSpec = MARKER_SYMBOLS[symbol];
-            isOpen = isSymbolOpen(symbol);
-            isDimmed = selIds && !selIds[index];
-
-            if(symbolSpec.noBorder && !isOpen) {
-                _colors = borderColors;
-            } else {
-                _colors = colors;
-            }
-
-            if(isOpen) {
-                _borderColors = colors;
-            } else {
-                _borderColors = borderColors;
-            }
-
-            // See  https://github.com/plotly/plotly.js/pull/1781#discussion_r121820798
-            // for more info on this logic
-            size = sizes[index];
-            bw = borderWidths[index];
-            minBorderWidth = (symbolSpec.noBorder || symbolSpec.noFill) ? 0.1 * size : 0;
-
-            this.scatter.options.sizes[i] = 4.0 * size;
-            this.scatter.options.glyphs[i] = symbolSpec.unicode;
-            this.scatter.options.borderWidths[i] = 0.5 * ((bw > minBorderWidth) ? bw - minBorderWidth : 0);
-
-            if(isOpen && !symbolSpec.noBorder && !symbolSpec.noFill) {
-                fillColor(this.scatter.options.colors, TRANSPARENT, i, 0);
-            } else {
-                fillColor(this.scatter.options.colors, _colors, i, index, isDimmed);
-            }
-            fillColor(this.scatter.options.borderColors, _borderColors, i, index, isDimmed);
-        }
-
-        // prevent scatter from resnapping points
-        if(selIds) {
-            this.scatter.options.positions = null;
-            this.fancyScatter.update();
-            this.scatter.options.positions = positions;
-        }
-        else {
-            this.fancyScatter.update();
-        }
-    }
-    else {
-        this.fancyScatter.clear();
-    }
-
-    // turn off fast scatter plot
-    this.scatter.clear();
-
-    // add item for autorange routine
-    this.expandAxesFancy(x, y, sizes);
-};
-
-proto.updateLines = function(options, positions) {
-    var i;
-
-    if(this.hasLines) {
-        var linePositions = positions;
-
-        if(!options.connectgaps) {
-            var p = 0;
-            var x = this.xData;
-            var y = this.yData;
-            linePositions = new Float64Array(2 * x.length);
-
-            for(i = 0; i < x.length; ++i) {
-                linePositions[p++] = x[i];
-                linePositions[p++] = y[i];
-            }
-        }
-
-        this.line.options.positions = linePositions;
-
-        var lineColor = convertColor(options.line.color, options.opacity, 1),
-            lineWidth = Math.round(0.5 * this.line.options.width),
-            dashes = (DASHES[options.line.dash] || [1]).slice();
-
-        for(i = 0; i < dashes.length; ++i) dashes[i] *= lineWidth;
-
-        switch(options.fill) {
-            case 'tozeroy':
-                this.line.options.fill = [false, true, false, false];
-                break;
-            case 'tozerox':
-                this.line.options.fill = [true, false, false, false];
-                break;
-            default:
-                this.line.options.fill = [false, false, false, false];
-                break;
-        }
-
-        var fillColor = str2RGBArray(options.fillcolor);
-
-        this.line.options.color = lineColor;
-        this.line.options.width = 2.0 * options.line.width;
-        this.line.options.dashes = dashes;
-        this.line.options.fillColor = [fillColor, fillColor, fillColor, fillColor];
-
-        this.line.update();
-    }
-    else {
-        this.line.clear();
-    }
-};
-
-proto.updateError = function(axLetter, options, positions, errors) {
-    var errorObj = this['error' + axLetter],
-        errorOptions = options['error_' + axLetter.toLowerCase()];
-
-    if(axLetter.toLowerCase() === 'x' && errorOptions.copy_ystyle) {
-        errorOptions = options.error_y;
-    }
-
-    if(this['hasError' + axLetter]) {
-        errorObj.options.positions = positions;
-        errorObj.options.errors = errors;
-        errorObj.options.capSize = errorOptions.width;
-        errorObj.options.lineWidth = errorOptions.thickness / 2;  // ballpark rescaling
-        errorObj.options.color = convertColor(errorOptions.color, 1, 1);
-
-        errorObj.update();
-    }
-    else {
-        errorObj.clear();
-    }
-};
-
-proto.expandAxesFast = function(bounds, markerSize) {
-    var pad = markerSize || 10;
-    var ax, min, max;
-
-    for(var i = 0; i < 2; i++) {
-        ax = this.scene[AXES[i]];
-
-        min = ax._min;
-        if(!min) min = [];
-        min.push({ val: bounds[i], pad: pad });
-
-        max = ax._max;
-        if(!max) max = [];
-        max.push({ val: bounds[i + 2], pad: pad });
-    }
-};
-
-// not quite on-par with 'scatter' (scatter fill in several other expand options)
-// but close enough for now
-proto.expandAxesFancy = function(x, y, ppad) {
-    var scene = this.scene,
-        expandOpts = { padded: true, ppad: ppad };
-
-    Axes.expand(scene.xaxis, x, expandOpts);
-    Axes.expand(scene.yaxis, y, expandOpts);
-};
-
-proto.dispose = function() {
-    this.line.dispose();
-    this.errorX.dispose();
-    this.errorY.dispose();
-    this.scatter.dispose();
-    this.fancyScatter.dispose();
-};
-
-function createLineWithMarkers(scene, data, cdscatter) {
-    var plot = new LineWithMarkers(scene, data.uid);
-    plot.update(data, cdscatter);
-
-    return plot;
-}
-
-module.exports = createLineWithMarkers;
diff --git a/src/traces/scattergl/defaults.js b/src/traces/scattergl/defaults.js
index 498135065fd..24b1281b129 100644
--- a/src/traces/scattergl/defaults.js
+++ b/src/traces/scattergl/defaults.js
@@ -27,6 +27,9 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
         return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
     }
 
+    var isOpen = traceIn.marker ? /-open/.test(traceIn.marker.symbol) : false;
+    var isBubble = subTypes.isBubble(traceIn);
+
     var len = handleXYDefaults(traceIn, traceOut, layout, coerce);
     if(!len) {
         traceOut.visible = false;
@@ -41,8 +44,12 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
         handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
     }
 
+    var dfltHoverOn = [];
+
     if(subTypes.hasMarkers(traceOut)) {
-        handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noSelect: true});
+        handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce);
+        coerce('marker.line.width', isOpen || isBubble ? 1 : 0);
+        dfltHoverOn.push('points');
     }
 
     coerce('fill');
@@ -50,6 +57,14 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
         handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce);
     }
 
+    if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') {
+        dfltHoverOn.push('fills');
+    }
+
+    coerce('hoveron', dfltHoverOn.join('+') || 'points');
+
     errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'y'});
     errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'x', inherit: 'y'});
+
+    Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
 };
diff --git a/src/traces/scattergl/index.js b/src/traces/scattergl/index.js
index 648ff49fb38..3ee553e6992 100644
--- a/src/traces/scattergl/index.js
+++ b/src/traces/scattergl/index.js
@@ -8,29 +8,1226 @@
 
 'use strict';
 
-var ScatterGl = {};
-
-ScatterGl.attributes = require('./attributes');
-ScatterGl.supplyDefaults = require('./defaults');
-ScatterGl.colorbar = require('../scatter/colorbar');
-ScatterGl.hoverPoints = require('../scatter/hover');
-
-// reuse the Scatter3D 'dummy' calc step so that legends know what to do
-ScatterGl.calc = require('./calc');
-ScatterGl.plot = require('./convert');
-ScatterGl.selectPoints = require('./select');
-
-ScatterGl.moduleType = 'trace';
-ScatterGl.name = 'scattergl';
-ScatterGl.basePlotModule = require('../../plots/gl2d');
-ScatterGl.categories = ['gl', 'gl2d', 'symbols', 'errorBarsOK', 'markerColorscale', 'showLegend', 'scatter-like'];
-ScatterGl.meta = {
-    description: [
-        'The data visualized as scatter point or lines is set in `x` and `y`',
-        'using the WebGl plotting engine.',
-        'Bubble charts are achieved by setting `marker.size` and/or `marker.color`',
-        'to a numerical arrays.'
-    ].join(' ')
-};
+var Lib = require('../../lib');
+var getTraceColor = require('../scatter/get_trace_color');
+var ErrorBars = require('../../components/errorbars');
+var extend = require('object-assign');
+var Axes = require('../../plots/cartesian/axes');
+var kdtree = require('kdgrass');
+var Fx = require('../../components/fx');
+var subTypes = require('../scatter/subtypes');
+var calcColorscales = require('../scatter/colorscale_calc');
+var Drawing = require('../../components/drawing');
+var makeBubbleSizeFn = require('../scatter/make_bubble_size_func');
+var DASHES = require('../../constants/gl2d_dashes');
+var formatColor = require('../../lib/gl_format_color');
+var linkTraces = require('../scatter/link_traces');
+var createScatter = require('regl-scatter2d');
+var createLine = require('regl-line2d');
+var createError = require('regl-error2d');
+var rgba = require('color-normalize');
+var svgSdf = require('svg-path-sdf');
+var createRegl = require('regl');
+var fillHoverText = require('../scatter/fill_hover_text');
+var isNumeric = require('fast-isnumeric');
+
+var MAXDIST = Fx.constants.MAXDIST;
+var SYMBOL_SDF_SIZE = 200;
+var SYMBOL_SIZE = 20;
+var SYMBOL_STROKE = SYMBOL_SIZE / 20;
+var SYMBOL_SDF = {};
+var SYMBOL_SVG_CIRCLE = Drawing.symbolFuncs[0](SYMBOL_SIZE * 0.05);
+var TOO_MANY_POINTS = 1e5;
+var DOT_RE = /-dot/;
+
+function calc(container, trace) {
+    var layout = container._fullLayout;
+    var positions;
+    var stash = {};
+    var xaxis = Axes.getFromId(container, trace.xaxis);
+    var yaxis = Axes.getFromId(container, trace.yaxis);
+
+    var subplot = layout._plots[trace.xaxis + trace.yaxis];
+
+    var x = xaxis.type === 'linear' ? trace.x : xaxis.makeCalcdata(trace, 'x');
+    var y = yaxis.type === 'linear' ? trace.y : yaxis.makeCalcdata(trace, 'y');
+
+    var count = (x || y).length, i, l, xx, yy;
+
+    if(!x) {
+        x = Array(count);
+        for(i = 0; i < count; i++) {
+            x[i] = i;
+        }
+    }
+    if(!y) {
+        y = Array(count);
+        for(i = 0; i < count; i++) {
+            y[i] = i;
+        }
+    }
+
+    // get log converted positions
+    var rawx, rawy;
+    if(xaxis.type === 'log') {
+        rawx = Array(x.length);
+        for(i = 0, l = x.length; i < l; i++) {
+            rawx[i] = x[i];
+            x[i] = xaxis.d2l(x[i]);
+        }
+    }
+    else {
+        rawx = x;
+        for(i = 0, l = x.length; i < l; i++) {
+            x[i] = parseFloat(x[i]);
+        }
+    }
+    if(yaxis.type === 'log') {
+        rawy = Array(y.length);
+        for(i = 0, l = y.length; i < l; i++) {
+            rawy[i] = y[i];
+            y[i] = yaxis.d2l(y[i]);
+        }
+    }
+    else {
+        rawy = y;
+        for(i = 0, l = y.length; i < l; i++) {
+            y[i] = parseFloat(y[i]);
+        }
+    }
+
+    // we need hi-precision for scatter2d
+    positions = new Array(count * 2);
+
+    for(i = 0; i < count; i++) {
+        // if no x defined, we are creating simple int sequence (API)
+        // we use parseFloat because it gives NaN (we need that for empty values to avoid drawing lines) and it is incredibly fast
+        xx = isNumeric(x[i]) ? +x[i] : NaN;
+        yy = isNumeric(y[i]) ? +y[i] : NaN;
+
+        positions[i * 2] = xx;
+        positions[i * 2 + 1] = yy;
+    }
+
+    // we don't build a tree for log axes since it takes long to convert log2px
+    // and it is also
+    if(xaxis.type !== 'log' && yaxis.type !== 'log') {
+        // FIXME: delegate this to webworker
+        stash.tree = kdtree(positions, 512);
+    }
+    else {
+        var ids = stash.ids = Array(count);
+        for(i = 0; i < count; i++) {
+            ids[i] = i;
+        }
+    }
+
+
+    calcColorscales(trace);
+
+    var options = sceneOptions(container, subplot, trace, positions);
+
+    // expanding axes is separate from options
+    if(!options.markers) {
+        Axes.expand(xaxis, rawx, { padded: true });
+        Axes.expand(yaxis, rawy, { padded: true });
+    }
+    else if(Array.isArray(options.markers.sizes)) {
+        var sizes = options.markers.sizes;
+        Axes.expand(xaxis, rawx, { padded: true, ppad: sizes });
+        Axes.expand(yaxis, rawy, { padded: true, ppad: sizes });
+    }
+    else {
+        var xbounds = [Infinity, -Infinity], ybounds = [Infinity, -Infinity];
+        var size = options.markers.size;
+
+        // axes bounds
+        for(i = 0; i < count; i++) {
+            xx = x[i], yy = y[i];
+            if(xbounds[0] > xx) xbounds[0] = xx;
+            if(xbounds[1] < xx) xbounds[1] = xx;
+            if(ybounds[0] > yy) ybounds[0] = yy;
+            if(ybounds[1] < yy) ybounds[1] = yy;
+        }
+
+        // FIXME: is there a better way to separate expansion?
+        if(count < TOO_MANY_POINTS) {
+            Axes.expand(xaxis, rawx, { padded: true, ppad: size });
+            Axes.expand(yaxis, rawy, { padded: true, ppad: size });
+        }
+        // update axes fast for big number of points
+        else {
+            if(xaxis._min) {
+                xaxis._min.push({ val: xbounds[0], pad: size });
+            }
+            if(xaxis._max) {
+                xaxis._max.push({ val: xbounds[1], pad: size });
+            }
+
+            if(yaxis._min) {
+                yaxis._min.push({ val: ybounds[0], pad: size });
+            }
+            if(yaxis._max) {
+                yaxis._max.push({ val: ybounds[1], pad: size });
+            }
+        }
+    }
+
+    // create scene
+    var scene = sceneUpdate(container, subplot);
+
+    // set flags to create scene renderers
+    if(options.fill && !scene.fill2d) scene.fill2d = true;
+    if(options.marker && !scene.scatter2d) scene.scatter2d = true;
+    if(options.line && !scene.line2d) scene.line2d = true;
+    if((options.errorX || options.errorY) && !scene.error2d) scene.error2d = true;
+
+    // save scene options batch
+    scene.lineOptions.push(options.line);
+    scene.errorXOptions.push(options.errorX);
+    scene.errorYOptions.push(options.errorY);
+    scene.fillOptions.push(options.fill);
+    scene.markerOptions.push(options.marker);
+    scene.selectedOptions.push(options.selected);
+    scene.unselectedOptions.push(options.unselected);
+    scene.count++;
+
+    // stash scene ref
+    stash.scene = scene;
+    stash.index = scene.count - 1;
+    stash.x = x;
+    stash.y = y;
+    stash.rawx = rawx;
+    stash.rawy = rawy;
+    stash.positions = positions;
+    stash.count = count;
+
+    return [{x: false, y: false, t: stash, trace: trace}];
+}
+
+// create scene options
+function sceneOptions(container, subplot, trace, positions) {
+    var layout = container._fullLayout;
+    var count = positions.length / 2;
+    var markerOpts = trace.marker;
+    var i, ptrX = 0, ptrY = 0;
+    var xaxis = Axes.getFromId(container, trace.xaxis);
+    var yaxis = Axes.getFromId(container, trace.yaxis);
+
+    var hasLines, hasErrorX, hasErrorY, hasError, hasMarkers, hasFill;
+
+    if(trace.visible !== true) {
+        hasLines = false;
+        hasErrorX = false;
+        hasErrorY = false;
+        hasMarkers = false;
+        hasFill = false;
+    }
+    else {
+        hasLines = subTypes.hasLines(trace) && positions.length > 1;
+        hasErrorX = trace.error_x && trace.error_x.visible === true;
+        hasErrorY = trace.error_y && trace.error_y.visible === true;
+        hasError = hasErrorX || hasErrorY;
+        hasMarkers = subTypes.hasMarkers(trace);
+        hasFill = !!trace.fill && trace.fill !== 'none';
+    }
+
+    var lineOptions, markerOptions, errorXOptions, errorYOptions, fillOptions, selectedOptions, unselectedOptions;
+    var linePositions;
+
+    // get error values
+    var errorVals = hasError ? ErrorBars.calcFromTrace(trace, layout) : null;
+
+    if(hasErrorX) {
+        errorXOptions = {};
+        errorXOptions.positions = positions;
+        var errorsX = new Float64Array(4 * count);
+
+        if(xaxis.type === 'log') {
+            for(i = 0; i < count; ++i) {
+                errorsX[ptrX++] = positions[i * 2] - xaxis.d2l(errorVals[i].xs) || 0;
+                errorsX[ptrX++] = xaxis.d2l(errorVals[i].xh) - positions[i * 2] || 0;
+                errorsX[ptrX++] = 0;
+                errorsX[ptrX++] = 0;
+            }
+        } else {
+            for(i = 0; i < count; ++i) {
+                errorsX[ptrX++] = positions[i * 2] - errorVals[i].xs || 0;
+                errorsX[ptrX++] = errorVals[i].xh - positions[i * 2] || 0;
+                errorsX[ptrX++] = 0;
+                errorsX[ptrX++] = 0;
+            }
+        }
+
+        if(trace.error_x.copy_ystyle) {
+            trace.error_x = trace.error_y;
+        }
+
+        errorXOptions.errors = errorsX;
+        errorXOptions.capSize = trace.error_x.width * 2;
+        errorXOptions.lineWidth = trace.error_x.thickness;
+        errorXOptions.color = trace.error_x.color;
+    }
+
+    if(hasErrorY) {
+        errorYOptions = {};
+        errorYOptions.positions = positions;
+        var errorsY = new Float64Array(4 * count);
+
+        if(yaxis.type === 'log') {
+            for(i = 0; i < count; ++i) {
+                errorsY[ptrY++] = 0;
+                errorsY[ptrY++] = 0;
+                errorsY[ptrY++] = positions[i * 2 + 1] - yaxis.d2l(errorVals[i].ys) || 0;
+                errorsY[ptrY++] = yaxis.d2l(errorVals[i].yh) - positions[i * 2 + 1] || 0;
+            }
+        } else {
+            for(i = 0; i < count; ++i) {
+                errorsY[ptrY++] = 0;
+                errorsY[ptrY++] = 0;
+                errorsY[ptrY++] = positions[i * 2 + 1] - errorVals[i].ys || 0;
+                errorsY[ptrY++] = errorVals[i].yh - positions[i * 2 + 1] || 0;
+            }
+        }
+
+        errorYOptions.errors = errorsY;
+        errorYOptions.capSize = trace.error_y.width * 2;
+        errorYOptions.lineWidth = trace.error_y.thickness;
+        errorYOptions.color = trace.error_y.color;
+    }
+
+    if(hasLines) {
+        lineOptions = {};
+        lineOptions.thickness = trace.line.width;
+        lineOptions.color = trace.line.color;
+        lineOptions.opacity = trace.opacity;
+        lineOptions.overlay = true;
+
+        var dashes = (DASHES[trace.line.dash] || [1]).slice();
+        for(i = 0; i < dashes.length; ++i) dashes[i] *= lineOptions.thickness;
+        lineOptions.dashes = dashes;
+
+        if(trace.line.shape === 'hv') {
+            linePositions = [];
+            for(i = 0; i < Math.floor(positions.length / 2) - 1; i++) {
+                if(isNaN(positions[i * 2]) || isNaN(positions[i * 2 + 1])) {
+                    linePositions.push(NaN);
+                    linePositions.push(NaN);
+                    linePositions.push(NaN);
+                    linePositions.push(NaN);
+                }
+                else {
+                    linePositions.push(positions[i * 2]);
+                    linePositions.push(positions[i * 2 + 1]);
+                    linePositions.push(positions[i * 2 + 2]);
+                    linePositions.push(positions[i * 2 + 1]);
+                }
+            }
+            linePositions.push(positions[positions.length - 2]);
+            linePositions.push(positions[positions.length - 1]);
+        }
+        else if(trace.line.shape === 'vh') {
+            linePositions = [];
+            for(i = 0; i < Math.floor(positions.length / 2) - 1; i++) {
+                if(isNaN(positions[i * 2]) || isNaN(positions[i * 2 + 1])) {
+                    linePositions.push(NaN);
+                    linePositions.push(NaN);
+                    linePositions.push(NaN);
+                    linePositions.push(NaN);
+                }
+                else {
+                    linePositions.push(positions[i * 2]);
+                    linePositions.push(positions[i * 2 + 1]);
+                    linePositions.push(positions[i * 2]);
+                    linePositions.push(positions[i * 2 + 3]);
+                }
+            }
+            linePositions.push(positions[positions.length - 2]);
+            linePositions.push(positions[positions.length - 1]);
+        }
+        else {
+            linePositions = positions;
+        }
+
+        // If we have data with gaps, we ought to use rect joins
+        // FIXME: get rid of this
+        var hasNaN = false;
+        for(i = 0; i < linePositions.length; i++) {
+            if(isNaN(linePositions[i])) {
+                hasNaN = true;
+                break;
+            }
+        }
+        lineOptions.join = (hasNaN || linePositions.length > TOO_MANY_POINTS) ? 'rect' : hasMarkers ? 'rect' : 'round';
+
+        // fill gaps
+        if(hasNaN && trace.connectgaps) {
+            var lastX = linePositions[0], lastY = linePositions[1];
+            for(i = 0; i < linePositions.length; i += 2) {
+                if(isNaN(linePositions[i]) || isNaN(linePositions[i + 1])) {
+                    linePositions[i] = lastX;
+                    linePositions[i + 1] = lastY;
+                }
+                else {
+                    lastX = linePositions[i];
+                    lastY = linePositions[i + 1];
+                }
+            }
+        }
+
+        lineOptions.positions = linePositions;
+    }
+
+    if(hasFill) {
+        fillOptions = {};
+        fillOptions.fill = trace.fillcolor;
+        fillOptions.thickness = 0;
+        fillOptions.closed = true;
+    }
+
+    if(hasMarkers) {
+        markerOptions = makeMarkerOptions(markerOpts);
+        selectedOptions = trace.selected ? makeMarkerOptions(extend({}, markerOpts, trace.selected.marker)) : markerOptions;
+        unselectedOptions = trace.unselected ? makeMarkerOptions(extend({}, markerOpts, trace.unselected.marker)) : markerOptions;
+
+        markerOptions.positions = positions;
+    }
+
+    function makeMarkerOptions(markerOpts) {
+        var markerOptions = {}, i;
+
+        // get basic symbol info
+        var multiMarker = Array.isArray(markerOpts.symbol);
+        var isOpen, symbol;
+        if(!multiMarker) {
+            isOpen = /-open/.test(markerOpts.symbol);
+        }
+
+        // prepare colors
+        if(multiMarker || Array.isArray(markerOpts.color) || Array.isArray(markerOpts.line.color) || Array.isArray(markerOpts.line) || Array.isArray(markerOpts.opacity)) {
+            markerOptions.colors = new Array(count);
+            markerOptions.borderColors = new Array(count);
+            var colors = formatColor(markerOpts, markerOpts.opacity, count);
+            var borderColors = formatColor(markerOpts.line, markerOpts.opacity, count);
+
+            if(!Array.isArray(borderColors[0])) {
+                var borderColor = borderColors;
+                borderColors = Array(count);
+                for(i = 0; i < count; i++) {
+                    borderColors[i] = borderColor;
+                }
+            }
+            if(!Array.isArray(colors[0])) {
+                var color = colors;
+                colors = Array(count);
+                for(i = 0; i < count; i++) {
+                    colors[i] = color;
+                }
+            }
+
+            markerOptions.colors = colors;
+            markerOptions.borderColors = borderColors;
+
+            for(i = 0; i < count; i++) {
+                if(multiMarker) {
+                    symbol = markerOpts.symbol[i];
+                    isOpen = /-open/.test(symbol);
+                }
+                if(isOpen) {
+                    borderColors[i] = colors[i].slice();
+                    colors[i] = colors[i].slice();
+                    colors[i][3] = 0;
+                }
+            }
+
+            markerOptions.opacity = trace.opacity;
+        }
+        else {
+            if(isOpen) {
+                markerOptions.color = rgba(markerOpts.color, 'uint8');
+                markerOptions.color[3] = 0;
+                markerOptions.borderColor = rgba(markerOpts.color, 'uint8');
+            }
+            else {
+                markerOptions.color = rgba(markerOpts.color, 'uint8');
+                markerOptions.borderColor = rgba(markerOpts.line.color, 'uint8');
+            }
+
+            markerOptions.opacity = trace.opacity * markerOpts.opacity;
+        }
+
+        // prepare markers
+        if(Array.isArray(markerOpts.symbol)) {
+            markerOptions.markers = new Array(count);
+            for(i = 0; i < count; ++i) {
+                markerOptions.markers[i] = getSymbolSdf(markerOpts.symbol[i]);
+            }
+        }
+        else {
+            markerOptions.marker = getSymbolSdf(markerOpts.symbol);
+        }
+
+        // prepare sizes and expand axes
+        var multiSize = markerOpts && (Array.isArray(markerOpts.size) || Array.isArray(markerOpts.line.width));
+        var markerSizeFunc = makeBubbleSizeFn(trace);
+        var size, sizes;
+
+        if(multiSize) {
+            sizes = markerOptions.sizes = new Array(count);
+            var borderSizes = markerOptions.borderSizes = new Array(count);
+
+            if(Array.isArray(markerOpts.size)) {
+                for(i = 0; i < count; ++i) {
+                    sizes[i] = markerSizeFunc(markerOpts.size[i]);
+                }
+            }
+            else {
+                size = markerSizeFunc(markerOpts.size);
+                for(i = 0; i < count; ++i) {
+                    sizes[i] = size;
+                }
+            }
+
+            // See  https://github.com/plotly/plotly.js/pull/1781#discussion_r121820798
+            if(Array.isArray(markerOpts.line.width)) {
+                for(i = 0; i < count; ++i) {
+                    borderSizes[i] = markerSizeFunc(markerOpts.line.width[i]);
+                }
+            }
+            else {
+                size = markerSizeFunc(markerOpts.line.width);
+                for(i = 0; i < count; ++i) {
+                    borderSizes[i] = size;
+                }
+            }
+        }
+        else {
+            size = markerOptions.size = markerSizeFunc(markerOpts && markerOpts.size || 10);
+            markerOptions.borderSizes = markerSizeFunc(markerOpts.line.width);
+        }
+
+        return markerOptions;
+    }
+
+    return {
+        line: lineOptions,
+        marker: markerOptions,
+        errorX: errorXOptions,
+        errorY: errorYOptions,
+        fill: fillOptions,
+        selected: selectedOptions,
+        unselected: unselectedOptions
+    };
+}
+
+// make sure scene exists on subplot, return it
+function sceneUpdate(container, subplot) {
+    var scene = subplot._scene;
+    var layout = container._fullLayout;
+
+    if(!subplot._scene) {
+        scene = subplot._scene = {
+            // number of traces in subplot, since scene:subplot → 1:1
+            count: 0,
+
+            // whether scene requires init hook in plot call (dirty plot call)
+            dirty: true,
+
+            // last used options
+            lineOptions: [],
+            fillOptions: [],
+            markerOptions: [],
+            selectedOptions: [],
+            unselectedOptions: [],
+            errorXOptions: [],
+            errorYOptions: [],
+            selectBatch: null,
+            unselectBatch: null,
+
+            // regl- component stubs, initialized in dirty plot call
+            fill2d: false,
+            scatter2d: false,
+            error2d: false,
+            line2d: false,
+            select2d: null
+        };
+
+        // apply new option to all regl components
+        scene.update = function update(opt) {
+            var opts = Array(scene.count);
+            for(var i = 0; i < scene.count; i++) {
+                opts[i] = opt;
+            }
+            if(scene.fill2d) scene.fill2d.update(opts);
+            if(scene.scatter2d) scene.scatter2d.update(opts);
+            if(scene.line2d) scene.line2d.update(opts);
+            if(scene.error2d) scene.error2d.update(opts.concat(opts));
+            if(scene.select2d) scene.select2d.update(opts);
+
+            scene.draw();
+        };
+
+        // draw traces in proper order
+        scene.draw = function draw() {
+            var i;
+            for(i = 0; i < scene.count; i++) {
+                if(scene.fill2d) scene.fill2d.draw(i);
+            }
+            for(i = 0; i < scene.count; i++) {
+                if(scene.line2d) {
+                    scene.line2d.draw(i);
+                }
+                if(scene.error2d) {
+                    scene.error2d.draw(i);
+                    scene.error2d.draw(i + scene.count);
+                }
+                if(scene.scatter2d && !scene.selectBatch) {
+                    scene.scatter2d.draw(i);
+                }
+            }
+
+            // persistent selection draw
+            if(scene.select2d && scene.selectBatch) {
+                scene.select2d.draw(scene.selectBatch);
+                scene.scatter2d.draw(scene.unselectBatch);
+            }
+
+            scene.dirty = false;
+        };
+
+        // make sure canvas is clear
+        scene.clear = function clear() {
+            var vpSize = layout._size, width = layout.width, height = layout.height, vp, gl, regl;
+            var xaxis = subplot.xaxis;
+            var yaxis = subplot.yaxis;
+
+            // multisubplot case
+            if(xaxis && xaxis.domain && yaxis && yaxis.domain) {
+                vp = [
+                    vpSize.l + xaxis.domain[0] * vpSize.w,
+                    vpSize.b + yaxis.domain[0] * vpSize.h,
+                    (width - vpSize.r) - (1 - xaxis.domain[1]) * vpSize.w,
+                    (height - vpSize.t) - (1 - yaxis.domain[1]) * vpSize.h
+                ];
+            }
+            else {
+                vp = [
+                    vpSize.l,
+                    vpSize.b,
+                    (width - vpSize.r),
+                    (height - vpSize.t)
+                ];
+            }
+
+            if(scene.select2d) {
+                regl = scene.select2d.regl;
+                gl = regl._gl;
+                gl.enable(gl.SCISSOR_TEST);
+                gl.scissor(vp[0], vp[1], vp[2] - vp[0], vp[3] - vp[1]);
+                gl.clearColor(0, 0, 0, 0);
+                gl.clear(gl.COLOR_BUFFER_BIT);
+            }
+
+            if(scene.scatter2d) {
+                regl = scene.scatter2d.regl;
+                gl = regl._gl;
+                gl.enable(gl.SCISSOR_TEST);
+                gl.scissor(vp[0], vp[1], vp[2] - vp[0], vp[3] - vp[1]);
+                gl.clearColor(0, 0, 0, 0);
+                gl.clear(gl.COLOR_BUFFER_BIT);
+            }
+        };
+
+        // remove selection
+        scene.clearSelect = function clearSelect() {
+            if(!scene.selectBatch) return;
+            scene.selectBatch = null;
+            scene.unselectBatch = null;
+            scene.scatter2d.update(scene.markerOptions);
+            scene.clear();
+            scene.draw();
+        };
+
+        // remove scene resources
+        scene.destroy = function destroy() {
+            if(scene.fill2d) scene.fill2d.destroy();
+            if(scene.scatter2d) scene.scatter2d.destroy();
+            if(scene.error2d) scene.error2d.destroy();
+            if(scene.line2d) scene.line2d.destroy();
+            if(scene.select2d) scene.select2d.destroy();
+
+            scene.lineOptions = null;
+            scene.fillOptions = null;
+            scene.markerOptions = null;
+            scene.selectedOptions = null;
+            scene.unselectedOptions = null;
+            scene.errorXOptions = null;
+            scene.errorYOptions = null;
+            scene.selectBatch = null;
+            scene.unselectBatch = null;
+
+            delete subplot._scene;
+        };
+    }
+
+    // In case if we have scene from the last calc - reset data
+    if(!scene.dirty) {
+        scene.dirty = true;
+        scene.count = 0;
+        scene.lineOptions = [];
+        scene.fillOptions = [];
+        scene.markerOptions = [];
+        scene.selectedOptions = [];
+        scene.unselectedOptions = [];
+        scene.errorXOptions = [];
+        scene.errorYOptions = [];
+    }
 
-module.exports = ScatterGl;
+    return scene;
+}
+
+function getSymbolSdf(symbol) {
+    if(symbol === 'circle') return null;
+
+    var symbolPath, symbolSdf;
+    var symbolNumber = Drawing.symbolNumber(symbol);
+    var symbolFunc = Drawing.symbolFuncs[symbolNumber % 100];
+    var symbolNoDot = !!Drawing.symbolNoDot[symbolNumber % 100];
+    var symbolNoFill = !!Drawing.symbolNoFill[symbolNumber % 100];
+
+    var isDot = DOT_RE.test(symbol);
+
+    // get symbol sdf from cache or generate it
+    if(SYMBOL_SDF[symbol]) return SYMBOL_SDF[symbol];
+
+    if(isDot && !symbolNoDot) {
+        symbolPath = symbolFunc(SYMBOL_SIZE * 1.1) + SYMBOL_SVG_CIRCLE;
+    }
+    else {
+        symbolPath = symbolFunc(SYMBOL_SIZE);
+    }
+
+    symbolSdf = svgSdf(symbolPath, {
+        w: SYMBOL_SDF_SIZE,
+        h: SYMBOL_SDF_SIZE,
+        viewBox: [-SYMBOL_SIZE, -SYMBOL_SIZE, SYMBOL_SIZE, SYMBOL_SIZE],
+        stroke: symbolNoFill ? SYMBOL_STROKE : -SYMBOL_STROKE
+    });
+    SYMBOL_SDF[symbol] = symbolSdf;
+
+    return symbolSdf || null;
+}
+
+function plot(container, subplot, cdata) {
+    if(!cdata.length) return;
+
+    var layout = container._fullLayout;
+    var stash = cdata[0][0].t;
+    var scene = stash.scene;
+
+    // we may have more subplots than initialized data due to Axes.getSubplots method
+    if(!scene) return;
+
+    var vpSize = layout._size, width = layout.width, height = layout.height;
+
+    // make sure proper regl instances are created
+    layout._glcanvas.each(function(d) {
+        if(d.regl || d.pick) return;
+        d.regl = createRegl({
+            canvas: this,
+            attributes: {
+                antialias: !d.pick,
+                preserveDrawingBuffer: true
+            },
+            extensions: ['ANGLE_instanced_arrays', 'OES_element_index_uint'],
+            pixelRatio: container._context.plotGlPixelRatio || global.devicePixelRatio
+        });
+    });
+
+    var regl = layout._glcanvas.data()[0].regl;
+
+    // that is needed for fills
+    linkTraces(container, subplot, cdata);
+
+    if(scene.dirty) {
+        // make sure scenes are created
+        if(scene.error2d === true) {
+            scene.error2d = createError(regl);
+        }
+        if(scene.line2d === true) {
+            scene.line2d = createLine(regl);
+        }
+        if(scene.scatter2d === true) {
+            scene.scatter2d = createScatter(regl);
+        }
+        if(scene.fill2d === true) {
+            scene.fill2d = createLine(regl);
+        }
+
+        if(scene.line2d) {
+            scene.line2d.update(scene.lineOptions);
+        }
+        if(scene.error2d) {
+            var errorBatch = (scene.errorXOptions || []).concat(scene.errorYOptions || []);
+            scene.error2d.update(errorBatch);
+        }
+        if(scene.scatter2d) {
+            if(!scene.selectBatch) {
+                scene.scatter2d.update(scene.markerOptions);
+            }
+            else {
+                scene.scatter2d.update(scene.unselectedOptions);
+                scene.select2d.update(scene.selectedOptions);
+            }
+        }
+        // fill requires linked traces, so we generate it's positions here
+        if(scene.fill2d) {
+            scene.fillOptions.forEach(function(fillOptions, i) {
+                var cdscatter = cdata[i];
+                if(!fillOptions || !cdscatter || !cdscatter[0] || !cdscatter[0].trace) return;
+                var cd = cdscatter[0];
+                var trace = cd.trace;
+                var stash = cd.t;
+                var lineOptions = scene.lineOptions[i];
+                var last, j;
+
+                var pos = [], srcPos = (lineOptions && lineOptions.positions) || stash.positions;
+
+                if(trace.fill === 'tozeroy') {
+                    pos = [srcPos[0], 0];
+                    pos = pos.concat(srcPos);
+                    pos.push(srcPos[srcPos.length - 2]);
+                    pos.push(0);
+                }
+                else if(trace.fill === 'tozerox') {
+                    pos = [0, srcPos[1]];
+                    pos = pos.concat(srcPos);
+                    pos.push(0);
+                    pos.push(srcPos[srcPos.length - 1]);
+                }
+                else if(trace.fill === 'toself' || trace.fill === 'tonext') {
+                    pos = [];
+                    last = 0;
+                    for(j = 0; j < srcPos.length; j += 2) {
+                        if(isNaN(srcPos[j]) || isNaN(srcPos[j + 1])) {
+                            pos = pos.concat(srcPos.slice(last, j));
+                            pos.push(srcPos[last], srcPos[last + 1]);
+                            last = j + 2;
+                        }
+                    }
+                    pos = pos.concat(srcPos.slice(last));
+                    if(last) {
+                        pos.push(srcPos[last], srcPos[last + 1]);
+                    }
+                }
+                else {
+                    var nextTrace = trace._nexttrace;
+
+                    if(nextTrace) {
+                        var nextOptions = scene.lineOptions[i + 1];
+
+                        if(nextOptions) {
+                            var nextPos = nextOptions.positions;
+                            if(trace.fill === 'tonexty') {
+                                pos = srcPos.slice();
+
+                                for(i = Math.floor(nextPos.length / 2); i--;) {
+                                    var xx = nextPos[i * 2], yy = nextPos[i * 2 + 1];
+                                    if(isNaN(xx) || isNaN(yy)) continue;
+                                    pos.push(xx);
+                                    pos.push(yy);
+                                }
+                                fillOptions.fill = nextTrace.fillcolor;
+                            }
+                        }
+                    }
+                }
+
+                // detect prev trace positions to exclude from current fill
+                if(trace._prevtrace && trace._prevtrace.fill === 'tonext') {
+                    var prevLinePos = scene.lineOptions[i - 1].positions;
+
+                    // FIXME: likely this logic should be tested better
+                    var offset = pos.length / 2;
+                    last = offset;
+                    var hole = [last];
+                    for(j = 0; j < prevLinePos.length; j += 2) {
+                        if(isNaN(prevLinePos[j]) || isNaN(prevLinePos[j + 1])) {
+                            hole.push(j / 2 + offset + 1);
+                            last = j + 2;
+                        }
+                    }
+
+                    pos = pos.concat(prevLinePos);
+                    fillOptions.hole = hole;
+                }
+
+                fillOptions.opacity = trace.opacity;
+                fillOptions.positions = pos;
+            });
+
+            scene.fill2d.update(scene.fillOptions);
+        }
+    }
+
+    // make sure selection layer is initialized if we require selection
+    var dragmode = layout.dragmode;
+
+    if(dragmode === 'lasso' || dragmode === 'select') {
+        if(scene.select2d && scene.selectBatch) {
+            scene.scatter2d.update(scene.unselectedOptions);
+        }
+    }
+
+    // provide viewport and range
+    var vpRange = cdata.map(function(cdscatter) {
+        if(!cdscatter || !cdscatter[0] || !cdscatter[0].trace) return;
+        var cd = cdscatter[0];
+        var trace = cd.trace;
+        var stash = cd.t;
+        var x = stash.rawx,
+            y = stash.rawy;
+
+        var xaxis = subplot.xaxis || Axes.getFromId(container, trace.xaxis || 'x');
+        var yaxis = subplot.yaxis || Axes.getFromId(container, trace.yaxis || 'y');
+        var i;
+
+        var range = [
+            (xaxis._rl || xaxis.range)[0],
+            (yaxis._rl || yaxis.range)[0],
+            (xaxis._rl || xaxis.range)[1],
+            (yaxis._rl || yaxis.range)[1]
+        ];
+
+        var viewport = [
+            vpSize.l + xaxis.domain[0] * vpSize.w,
+            vpSize.b + yaxis.domain[0] * vpSize.h,
+            (width - vpSize.r) - (1 - xaxis.domain[1]) * vpSize.w,
+            (height - vpSize.t) - (1 - yaxis.domain[1]) * vpSize.h
+        ];
+
+        if(trace.selectedpoints || dragmode === 'lasso' || dragmode === 'select') {
+            // create select2d
+            if(!scene.select2d && scene.scatter2d) {
+                var selectRegl = layout._glcanvas.data()[1].regl;
+
+                // smol hack to create scatter instance by cloning scatter2d
+                scene.select2d = createScatter(selectRegl, {clone: scene.scatter2d});
+                scene.select2d.update(scene.selectedOptions);
+
+                // create selection style once we have something selected
+                if(trace.selectedpoints && !scene.selectBatch) {
+                    scene.selectBatch = Array(scene.count);
+                    scene.unselectBatch = Array(scene.count);
+                    scene.scatter2d.update(scene.unselectedOptions);
+                }
+            }
+            else {
+                // update selection positions, since they may have changed by panning or alike
+                scene.select2d.update(scene.selectedOptions);
+            }
+
+            // form unselected batch
+            if(trace.selectedpoints && !scene.unselectBatch[stash.index]) {
+                scene.selectBatch[stash.index] = trace.selectedpoints;
+                var selPts = trace.selectedpoints;
+                var selDict = {};
+                for(i = 0; i < selPts.length; i++) {
+                    selDict[selPts[i]] = true;
+                }
+                var unselPts = [];
+                for(i = 0; i < stash.count; i++) {
+                    if(!selDict[i]) unselPts.push(i);
+                }
+                scene.unselectBatch[stash.index] = unselPts;
+            }
+
+            // precalculate px coords since we are not going to pan during select
+            var xpx = Array(stash.count), ypx = Array(stash.count);
+            for(i = 0; i < stash.count; i++) {
+                xpx[i] = xaxis.c2p(x[i]);
+                ypx[i] = yaxis.c2p(y[i]);
+            }
+            stash.xpx = xpx;
+            stash.ypx = ypx;
+        }
+        else {
+            stash.xpx = stash.ypx = null;
+        }
+
+        return trace.visible ? {
+            viewport: viewport,
+            range: range
+        } : null;
+    });
+
+    // uploat viewport/range data to GPU
+    if(scene.fill2d) {
+        scene.fill2d.update(vpRange);
+    }
+    if(scene.line2d) {
+        scene.line2d.update(vpRange);
+    }
+    if(scene.error2d) {
+        scene.error2d.update(vpRange.concat(vpRange));
+    }
+    if(scene.scatter2d) {
+        scene.scatter2d.update(vpRange);
+    }
+    if(scene.select2d) {
+        scene.select2d.update(vpRange);
+    }
+
+    scene.draw();
+
+    return;
+}
+
+function hoverPoints(pointData, xval, yval, hovermode) {
+    var cd = pointData.cd,
+        stash = cd[0].t,
+        trace = cd[0].trace,
+        xa = pointData.xa,
+        ya = pointData.ya,
+        x = stash.rawx,
+        y = stash.rawy,
+        xpx = xa.c2p(xval),
+        ypx = ya.c2p(yval),
+        ids;
+
+    // FIXME: make sure this is a proper way to calc search radius
+    if(stash.tree) {
+        var xl = xa.p2c(xpx - MAXDIST),
+            xr = xa.p2c(xpx + MAXDIST),
+            yl = ya.p2c(ypx - MAXDIST),
+            yr = ya.p2c(ypx + MAXDIST);
+
+        if(hovermode === 'x') {
+            ids = stash.tree.range(
+                Math.min(xl, xr), Math.min(ya._rl[0], ya._rl[1]),
+                Math.max(xl, xr), Math.max(ya._rl[0], ya._rl[1])
+            );
+        }
+        else {
+            ids = stash.tree.range(
+                Math.min(xl, xr), Math.min(yl, yr),
+                Math.max(xl, xr), Math.max(yl, yr)
+            );
+        }
+    }
+    else if(stash.ids) {
+        ids = stash.ids;
+    }
+    else return [pointData];
+
+    // pick the id closest to the point
+    // note that point possibly may not be found
+    var min = MAXDIST, id, ptx, pty, i, dx, dy, dist;
+
+    if(hovermode === 'x') {
+        for(i = 0; i < ids.length; i++) {
+            ptx = x[ids[i]];
+            dx = Math.abs(xa.c2p(ptx) - xpx);
+            if(dx < min) {
+                min = dx;
+                id = ids[i];
+            }
+        }
+    }
+    else {
+        for(i = 0; i < ids.length; i++) {
+            ptx = x[ids[i]];
+            pty = y[ids[i]];
+            dx = xa.c2p(ptx) - xpx, dy = ya.c2p(pty) - ypx;
+
+            dist = Math.sqrt(dx * dx + dy * dy);
+            if(dist < min) {
+                min = dist;
+                id = ids[i];
+            }
+        }
+    }
+
+    pointData.index = id;
+
+    if(id === undefined) return [pointData];
+
+    // the closest data point
+    var di = {
+        pointNumber: id,
+        x: x[id],
+        y: y[id]
+    };
+
+    // that is single-item arrays_to_calcdata excerpt, since we are doing it for a single point and we don't have to do it beforehead for 1e6 points
+    di.tx = Array.isArray(trace.text) ? trace.text[id] : trace.text;
+    di.htx = Array.isArray(trace.hovertext) ? trace.hovertext[id] : trace.hovertext;
+    di.data = Array.isArray(trace.customdata) ? trace.customdata[id] : trace.customdata;
+    di.tp = Array.isArray(trace.textposition) ? trace.textposition[id] : trace.textposition;
+
+    var font = trace.textfont;
+    if(font) {
+        di.ts = Array.isArray(font.size) ? font.size[id] : font.size;
+        di.tc = Array.isArray(font.color) ? font.color[id] : font.color;
+        di.tf = Array.isArray(font.family) ? font.family[id] : font.family;
+    }
+
+    var marker = trace.marker;
+    if(marker) {
+        di.ms = Array.isArray(marker.size) ? marker.size[id] : marker.size;
+        di.mo = Array.isArray(marker.opacity) ? marker.opacity[id] : marker.opacity;
+        di.mx = Array.isArray(marker.symbol) ? marker.symbol[id] : marker.symbol;
+        di.mc = Array.isArray(marker.color) ? marker.color[id] : marker.color;
+    }
+
+    var line = marker && marker.line;
+    if(line) {
+        di.mlc = Array.isArray(line.color) ? line.color[id] : line.color;
+        di.mlw = Array.isArray(line.width) ? line.width[id] : line.width;
+    }
+
+    var grad = marker && marker.gradient;
+    if(grad && grad.type !== 'none') {
+        di.mgt = Array.isArray(grad.type) ? grad.type[id] : grad.type;
+        di.mgc = Array.isArray(grad.color) ? grad.color[id] : grad.color;
+    }
+
+    var xc = xa.c2p(di.x, true),
+        yc = ya.c2p(di.y, true),
+        rad = di.mrc || 1;
+
+    var hoverlabel = trace.hoverlabel;
+
+    if(hoverlabel) {
+        di.hbg = Array.isArray(hoverlabel.bgcolor) ? hoverlabel.bgcolor[id] : hoverlabel.bgcolor;
+        di.hbc = Array.isArray(hoverlabel.bordercolor) ? hoverlabel.bordercolor[id] : hoverlabel.bordercolor;
+        di.hts = Array.isArray(hoverlabel.font.size) ? hoverlabel.font.size[id] : hoverlabel.font.size;
+        di.htc = Array.isArray(hoverlabel.font.color) ? hoverlabel.font.color[id] : hoverlabel.font.color;
+        di.htf = Array.isArray(hoverlabel.font.family) ? hoverlabel.font.family[id] : hoverlabel.font.family;
+        di.hnl = Array.isArray(hoverlabel.namelength) ? hoverlabel.namelength[id] : hoverlabel.namelength;
+    }
+    var hoverinfo = trace.hoverinfo;
+    if(hoverinfo) {
+        di.hi = Array.isArray(hoverinfo) ? hoverinfo[id] : hoverinfo;
+    }
+
+    var fakeCd = {};
+    fakeCd[pointData.index] = di;
+
+    Lib.extendFlat(pointData, {
+        color: getTraceColor(trace, di),
+
+        x0: xc - rad,
+        x1: xc + rad,
+        xLabelVal: di.x,
+
+        y0: yc - rad,
+        y1: yc + rad,
+        yLabelVal: di.y,
+
+        cd: fakeCd
+    });
+
+    if(di.htx) pointData.text = di.htx;
+    else if(di.tx) pointData.text = di.tx;
+    else if(trace.text) pointData.text = trace.text;
+
+    fillHoverText(di, trace, pointData);
+    ErrorBars.hoverInfo(di, trace, pointData);
+
+    return [pointData];
+}
+
+function selectPoints(searchInfo, polygon) {
+    var cd = searchInfo.cd,
+        selection = [],
+        trace = cd[0].trace,
+        stash = cd[0].t,
+        x = stash.x,
+        y = stash.y,
+        rawx = stash.rawx,
+        rawy = stash.rawy;
+
+    var scene = stash.scene;
+
+    if(!scene) return selection;
+
+    var hasOnlyLines = (!subTypes.hasMarkers(trace) && !subTypes.hasText(trace));
+    if(trace.visible !== true || hasOnlyLines) return selection;
+
+    // degenerate polygon does not enable selection
+    // filter out points by visible scatter ones
+    var els = null, unels = null, i;
+    if(polygon !== false && !polygon.degenerate) {
+        els = [], unels = [];
+        for(i = 0; i < stash.count; i++) {
+            if(polygon.contains([stash.xpx[i], stash.ypx[i]])) {
+                els.push(i);
+                selection.push({
+                    pointNumber: i,
+                    x: rawx ? rawx[i] : x[i],
+                    y: rawy ? rawy[i] : y[i]
+                });
+            }
+            else {
+                unels.push(i);
+            }
+        }
+    }
+    else {
+        unels = Array(stash.count);
+        for(i = 0; i < stash.count; i++) {
+            unels[i] = i;
+        }
+    }
+
+    // create selection style once we have something selected
+    if(!scene.selectBatch) {
+        scene.selectBatch = Array(scene.count);
+        scene.unselectBatch = Array(scene.count);
+        scene.scatter2d.update(scene.unselectedOptions);
+    }
+    scene.selectBatch[stash.index] = els;
+    scene.unselectBatch[stash.index] = unels;
+
+    return selection;
+}
+
+function style(gd, cd) {
+    if(cd) {
+        var stash = cd[0].t;
+        var scene = stash.scene;
+        scene.clear();
+        scene.draw();
+    }
+}
+
+module.exports = {
+    moduleType: 'trace',
+    name: 'scattergl',
+    basePlotModule: require('../../plots/cartesian'),
+    categories: ['gl', 'regl', 'cartesian', 'symbols', 'errorBarsOK', 'markerColorscale', 'showLegend', 'scatter-like'],
+
+    attributes: require('./attributes'),
+    supplyDefaults: require('./defaults'),
+    cleanData: require('../scatter/clean_data'),
+    colorbar: require('../scatter/colorbar'),
+    calc: calc,
+    plot: plot,
+    hoverPoints: hoverPoints,
+    style: style,
+    selectPoints: selectPoints,
+
+    sceneOptions: sceneOptions,
+    sceneUpdate: sceneUpdate,
+
+    meta: {
+        hrName: 'scatter_gl',
+        description: [
+            'The data visualized as scatter point or lines is set in `x` and `y`',
+            'using the WebGL plotting engine.',
+            'Bubble charts are achieved by setting `marker.size` and/or `marker.color`',
+            'to a numerical arrays.'
+        ].join(' ')
+    }
+};
diff --git a/src/traces/scattergl/select.js b/src/traces/scattergl/select.js
deleted file mode 100644
index c2b425bba81..00000000000
--- a/src/traces/scattergl/select.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
-* Copyright 2012-2018, Plotly, Inc.
-* All rights reserved.
-*
-* This source code is licensed under the MIT license found in the
-* LICENSE file in the root directory of this source tree.
-*/
-
-
-'use strict';
-
-var subtypes = require('../scatter/subtypes');
-
-module.exports = function selectPoints(searchInfo, polygon) {
-    var cd = searchInfo.cd,
-        xa = searchInfo.xaxis,
-        ya = searchInfo.yaxis,
-        selection = [],
-        trace = cd[0].trace,
-        i,
-        di,
-        x,
-        y;
-
-    var glTrace = cd[0]._glTrace;
-
-    if(!glTrace) return;
-
-    var scene = glTrace.scene;
-
-    var hasOnlyLines = (!subtypes.hasMarkers(trace) && !subtypes.hasText(trace));
-    if(hasOnlyLines) return [];
-
-    // filter out points by visible scatter ones
-    if(polygon === false) {
-        // clear selection
-        for(i = 0; i < cd.length; i++) cd[i].dim = 0;
-    }
-    else {
-        for(i = 0; i < cd.length; i++) {
-            di = cd[i];
-            x = xa.c2p(di.x);
-            y = ya.c2p(di.y);
-            if(polygon.contains([x, y])) {
-                selection.push({
-                    pointNumber: i,
-                    x: xa.c2d(di.x),
-                    y: ya.c2d(di.y)
-                });
-                di.dim = 0;
-            }
-            else di.dim = 1;
-        }
-    }
-
-    // highlight selected points here
-    trace.selection = selection;
-
-    glTrace.update(trace, cd);
-    scene.glplot.setDirty();
-
-    return selection;
-};
diff --git a/src/traces/scatterpolar/hover.js b/src/traces/scatterpolar/hover.js
index b81abf2fa98..8202724a332 100644
--- a/src/traces/scatterpolar/hover.js
+++ b/src/traces/scatterpolar/hover.js
@@ -12,7 +12,7 @@ var scatterHover = require('../scatter/hover');
 var Axes = require('../../plots/cartesian/axes');
 var Lib = require('../../lib');
 
-module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
+function hoverPoints(pointData, xval, yval, hovermode) {
     var scatterPointData = scatterHover(pointData, xval, yval, hovermode);
     if(!scatterPointData || scatterPointData[0].index === false) return;
 
@@ -25,23 +25,29 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
 
     var subplot = pointData.subplot;
     var cdi = newPointData.cd[newPointData.index];
+    var trace = newPointData.trace;
 
     if(!subplot.isPtWithinSector(cdi)) return;
 
     newPointData.xLabelVal = undefined;
     newPointData.yLabelVal = undefined;
+    newPointData.extraText = makeHoverPointText(cdi, trace, subplot);
 
-    var trace = newPointData.trace;
+    return scatterPointData;
+}
+
+function makeHoverPointText(cdi, trace, subplot) {
     var radialAxis = subplot.radialAxis;
     var angularAxis = subplot.angularAxis;
     var hoverinfo = cdi.hi || trace.hoverinfo;
     var parts = hoverinfo.split('+');
     var text = [];
-    var rad = angularAxis._c2rad(cdi.theta, trace.thetaunit);
 
     radialAxis._hovertitle = 'r';
     angularAxis._hovertitle = 'θ';
 
+    var rad = angularAxis._c2rad(cdi.theta, trace.thetaunit);
+
     // show theta value in unit of angular axis
     var theta;
     if(angularAxis.type === 'linear' && trace.thetaunit !== angularAxis.thetaunit) {
@@ -58,7 +64,10 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
     if(parts.indexOf('r') !== -1) textPart(radialAxis, radialAxis.c2r(cdi.r));
     if(parts.indexOf('theta') !== -1) textPart(angularAxis, theta);
 
-    newPointData.extraText = text.join('<br>');
+    return text.join('<br>');
+}
 
-    return scatterPointData;
+module.exports = {
+    hoverPoints: hoverPoints,
+    makeHoverPointText: makeHoverPointText
 };
diff --git a/src/traces/scatterpolar/index.js b/src/traces/scatterpolar/index.js
index cd7b0734a51..4b16c1c7fed 100644
--- a/src/traces/scatterpolar/index.js
+++ b/src/traces/scatterpolar/index.js
@@ -19,16 +19,16 @@ module.exports = {
     calc: require('./calc'),
     plot: require('./plot'),
     style: require('../scatter/style').style,
-    hoverPoints: require('./hover'),
+    hoverPoints: require('./hover').hoverPoints,
     selectPoints: require('../scatter/select'),
 
     meta: {
         hrName: 'scatter_polar',
         description: [
-            'The scatterpolar trace type encompasses line charts, scatter charts, text charts, and bubble charts.',
+            'The scatterpolar trace type encompasses line charts, scatter charts, text charts, and bubble charts',
             'in polar coordinates.',
             'The data visualized as scatter point or lines is set in',
-            '`r` (radial) and `theta` (angular). coordintes',
+            '`r` (radial) and `theta` (angular) coordinates',
             'Text (appearing either on the chart or on hover only) is via `text`.',
             'Bubble charts are achieved by setting `marker.size` and/or `marker.color`',
             'to numerical arrays.'
diff --git a/src/traces/scatterpolar/plot.js b/src/traces/scatterpolar/plot.js
index 5513416d909..3e5cb62a30b 100644
--- a/src/traces/scatterpolar/plot.js
+++ b/src/traces/scatterpolar/plot.js
@@ -11,7 +11,7 @@
 var scatterPlot = require('../scatter/plot');
 var BADNUM = require('../../constants/numerical').BADNUM;
 
-module.exports = function plot(subplot, moduleCalcData) {
+module.exports = function plot(gd, subplot, moduleCalcData) {
     var i, j;
 
     var plotinfo = {
@@ -60,5 +60,5 @@ module.exports = function plot(subplot, moduleCalcData) {
         }
     }
 
-    scatterPlot(subplot.graphDiv, plotinfo, moduleCalcData);
+    scatterPlot(gd, plotinfo, moduleCalcData);
 };
diff --git a/src/traces/scatterpolargl/attributes.js b/src/traces/scatterpolargl/attributes.js
new file mode 100644
index 00000000000..6f1ce8c6dc4
--- /dev/null
+++ b/src/traces/scatterpolargl/attributes.js
@@ -0,0 +1,37 @@
+/**
+* Copyright 2012-2018, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var scatterPolarAttrs = require('../scatterpolar/attributes');
+var scatterGlAttrs = require('../scattergl/attributes');
+
+module.exports = {
+    mode: scatterPolarAttrs.mode,
+    r: scatterPolarAttrs.r,
+    theta: scatterPolarAttrs.theta,
+    thetaunit: scatterPolarAttrs.thetaunit,
+
+    text: scatterPolarAttrs.text,
+    // no hovertext
+
+    line: scatterGlAttrs.line,
+    connectgaps: scatterGlAttrs.connectgaps,
+
+    marker: scatterGlAttrs.marker,
+    // no cliponaxis
+
+    fill: scatterGlAttrs.fill,
+    fillcolor: scatterGlAttrs.fillcolor,
+
+    hoverinfo: scatterPolarAttrs.hoverinfo,
+    hoveron: scatterPolarAttrs.hoveron,
+
+    selected: scatterPolarAttrs.selected,
+    unselected: scatterPolarAttrs.unselected
+};
diff --git a/src/traces/scatterpolargl/defaults.js b/src/traces/scatterpolargl/defaults.js
new file mode 100644
index 00000000000..f34e1e5bce4
--- /dev/null
+++ b/src/traces/scatterpolargl/defaults.js
@@ -0,0 +1,65 @@
+/**
+* Copyright 2012-2018, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var Lib = require('../../lib');
+
+var subTypes = require('../scatter/subtypes');
+var handleMarkerDefaults = require('../scatter/marker_defaults');
+var handleLineDefaults = require('../scatter/line_defaults');
+var handleFillColorDefaults = require('../scatter/fillcolor_defaults');
+var PTS_LINESONLY = require('../scatter/constants').PTS_LINESONLY;
+
+var attributes = require('./attributes');
+
+module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
+    function coerce(attr, dflt) {
+        return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
+    }
+
+    var r = coerce('r');
+    var theta = coerce('theta');
+    var len = (r && theta) ? Math.min(r.length, theta.length) : 0;
+
+    if(!len) {
+        traceOut.visible = false;
+        return;
+    }
+
+    if(len < r.length) traceOut.r = r.slice(0, len);
+    if(len < theta.length) traceOut.theta = theta.slice(0, len);
+
+    coerce('thetaunit');
+    coerce('mode', len < PTS_LINESONLY ? 'lines+markers' : 'lines');
+    coerce('text');
+
+    if(subTypes.hasLines(traceOut)) {
+        handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
+        coerce('connectgaps');
+    }
+
+    var dfltHoverOn = [];
+
+    if(subTypes.hasMarkers(traceOut)) {
+        handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce);
+        dfltHoverOn.push('points');
+    }
+
+    coerce('fill');
+    if(traceOut.fill !== 'none') {
+        handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce);
+    }
+
+    if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') {
+        dfltHoverOn.push('fills');
+    }
+    coerce('hoveron', dfltHoverOn.join('+') || 'points');
+
+    Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
+};
diff --git a/src/traces/scatterpolargl/index.js b/src/traces/scatterpolargl/index.js
new file mode 100644
index 00000000000..6a7a4a1b94a
--- /dev/null
+++ b/src/traces/scatterpolargl/index.js
@@ -0,0 +1,197 @@
+/**
+* Copyright 2012-2018, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var kdtree = require('kdgrass');
+var isNumeric = require('fast-isnumeric');
+
+var ScatterGl = require('../scattergl');
+var calcColorscales = require('../scatter/colorscale_calc');
+var Axes = require('../../plots/cartesian/axes');
+var makeHoverPointText = require('../scatterpolar/hover').makeHoverPointText;
+var subTypes = require('../scatter/subtypes');
+
+function calc(container, trace) {
+    var layout = container._fullLayout;
+    var subplotId = trace.subplot;
+    var radialAxis = layout[subplotId].radialaxis;
+    var angularAxis = layout[subplotId].angularaxis;
+    var rArray = radialAxis.makeCalcdata(trace, 'r');
+    var thetaArray = angularAxis.makeCalcdata(trace, 'theta');
+    var stash = {};
+
+    calcColorscales(trace);
+
+    stash.r = rArray;
+    stash.theta = thetaArray;
+
+    Axes.expand(radialAxis, rArray, {tozero: true});
+
+    if(angularAxis.type !== 'linear') {
+        angularAxis.autorange = true;
+        Axes.expand(angularAxis, thetaArray);
+    }
+
+    return [{x: false, y: false, t: stash, trace: trace}];
+}
+
+function plot(container, subplot, cdata) {
+    var radialAxis = subplot.radialAxis;
+    var angularAxis = subplot.angularAxis;
+    var rRange = radialAxis.range;
+
+    var scene = ScatterGl.sceneUpdate(container, subplot);
+    scene.clear();
+
+    cdata.forEach(function(cdscatter, traceIndex) {
+        if(!cdscatter || !cdscatter[0] || !cdscatter[0].trace) return;
+        var cd = cdscatter[0];
+        var trace = cd.trace;
+        var stash = cd.t;
+        var rArray = stash.r;
+        var thetaArray = stash.theta;
+        var i, r, rr, theta, rad;
+
+        var subRArray = rArray.slice();
+        var subThetaArray = thetaArray.slice();
+
+        // filter out by range
+        for(i = 0; i < rArray.length; i++) {
+            r = rArray[i], theta = thetaArray[i];
+            rad = angularAxis.c2rad(theta, trace.thetaunit);
+
+            if(!subplot.isPtWithinSector({r: r, rad: rad})) {
+                subRArray[i] = NaN;
+                subThetaArray[i] = NaN;
+            }
+        }
+
+        var count = rArray.length;
+        var positions = new Array(count * 2), x = Array(count), y = Array(count);
+
+        function c2rad(v) {
+            return angularAxis.c2rad(v, trace.thetaunit);
+        }
+
+        for(i = 0; i < count; i++) {
+            r = subRArray[i];
+            theta = subThetaArray[i];
+
+            if(isNumeric(r) && isNumeric(theta) && r >= 0) {
+                rr = radialAxis.c2r(r) - rRange[0];
+                rad = c2rad(theta);
+
+                x[i] = positions[i * 2] = rr * Math.cos(rad);
+                y[i] = positions[i * 2 + 1] = rr * Math.sin(rad);
+            } else {
+                x[i] = y[i] = positions[i * 2] = positions[i * 2 + 1] = NaN;
+            }
+        }
+
+        var options = ScatterGl.sceneOptions(container, subplot, trace, positions);
+
+        // set flags to create scene renderers
+        if(options.fill && !scene.fill2d) scene.fill2d = true;
+        if(options.marker && !scene.scatter2d) scene.scatter2d = true;
+        if(options.line && !scene.line2d) scene.line2d = true;
+        if((options.errorX || options.errorY) && !scene.error2d) scene.error2d = true;
+
+        // bring positions to selected/unselected options
+        if(subTypes.hasMarkers(trace)) {
+            options.selected.positions = options.unselected.positions = options.marker.positions;
+        }
+
+        // save scene options batch
+        scene.lineOptions.push(options.line);
+        scene.errorXOptions.push(options.errorX);
+        scene.errorYOptions.push(options.errorY);
+        scene.fillOptions.push(options.fill);
+        scene.markerOptions.push(options.marker);
+        scene.selectedOptions.push(options.selected);
+        scene.unselectedOptions.push(options.unselected);
+        scene.count = cdata.length;
+
+        // stash scene ref
+        stash.scene = scene;
+        stash.index = traceIndex;
+        stash.x = x;
+        stash.y = y;
+        stash.rawx = x;
+        stash.rawy = y;
+        stash.r = rArray;
+        stash.theta = thetaArray;
+        stash.positions = positions;
+        stash.count = count;
+        stash.tree = kdtree(positions, 512);
+    });
+
+    return ScatterGl.plot(container, subplot, cdata);
+}
+
+function hoverPoints(pointData, xval, yval, hovermode) {
+    var cd = pointData.cd;
+    var stash = cd[0].t;
+    var rArray = stash.r;
+    var thetaArray = stash.theta;
+
+    var scatterPointData = ScatterGl.hoverPoints(pointData, xval, yval, hovermode);
+    if(!scatterPointData || scatterPointData[0].index === false) return;
+
+    var newPointData = scatterPointData[0];
+
+    if(newPointData.index === undefined) {
+        return scatterPointData;
+    }
+
+    var subplot = pointData.subplot;
+    var angularAxis = subplot.angularAxis;
+    var cdi = newPointData.cd[newPointData.index];
+    var trace = newPointData.trace;
+
+    // augment pointData with r/theta param
+    cdi.r = rArray[newPointData.index];
+    cdi.theta = thetaArray[newPointData.index];
+    cdi.rad = angularAxis.c2rad(cdi.theta, trace.thetaunit);
+
+    if(!subplot.isPtWithinSector(cdi)) return;
+
+    newPointData.xLabelVal = undefined;
+    newPointData.yLabelVal = undefined;
+    newPointData.extraText = makeHoverPointText(cdi, trace, subplot);
+
+    return scatterPointData;
+}
+
+module.exports = {
+    moduleType: 'trace',
+    name: 'scatterpolargl',
+    basePlotModule: require('../../plots/polar'),
+    categories: ['gl', 'regl', 'polar', 'symbols', 'markerColorscale', 'showLegend', 'scatter-like'],
+
+    attributes: require('./attributes'),
+    supplyDefaults: require('./defaults'),
+
+    calc: calc,
+    plot: plot,
+    hoverPoints: hoverPoints,
+    style: ScatterGl.style,
+    selectPoints: ScatterGl.selectPoints,
+
+    meta: {
+        hrName: 'scatter_polar_gl',
+        description: [
+            'The scatterpolargl trace type encompasses line charts, scatter charts, and bubble charts',
+            'in polar coordinates using the WebGL plotting engine.',
+            'The data visualized as scatter point or lines is set in',
+            '`r` (radial) and `theta` (angular) coordinates',
+            'Bubble charts are achieved by setting `marker.size` and/or `marker.color`',
+            'to numerical arrays.'
+        ].join(' ')
+    }
+};
diff --git a/src/traces/scatterternary/plot.js b/src/traces/scatterternary/plot.js
index 02dee000684..26b323d306e 100644
--- a/src/traces/scatterternary/plot.js
+++ b/src/traces/scatterternary/plot.js
@@ -11,8 +11,7 @@
 
 var scatterPlot = require('../scatter/plot');
 
-
-module.exports = function plot(ternary, moduleCalcData) {
+module.exports = function plot(gd, ternary, moduleCalcData) {
     var plotContainer = ternary.plotContainer;
 
     // remove all nodes inside the scatter layer
@@ -26,5 +25,5 @@ module.exports = function plot(ternary, moduleCalcData) {
         layerClipId: ternary._hasClipOnAxisFalse ? ternary.clipIdRelative : null
     };
 
-    scatterPlot(ternary.graphDiv, plotinfo, moduleCalcData);
+    scatterPlot(gd, plotinfo, moduleCalcData);
 };
diff --git a/tasks/ci_test.sh b/tasks/ci_test.sh
index 12608154d92..de7ddd51719 100755
--- a/tasks/ci_test.sh
+++ b/tasks/ci_test.sh
@@ -8,7 +8,6 @@ case $CIRCLE_NODE_INDEX in
         npm run test-image      || EXIT_STATE=$?
         npm run test-image-gl2d || EXIT_STATE=$?
         npm run test-bundle     || EXIT_STATE=$?
-        npm run test-syntax     || EXIT_STATE=$?
         npm run lint            || EXIT_STATE=$?
         exit $EXIT_STATE
         ;;
@@ -16,6 +15,7 @@ case $CIRCLE_NODE_INDEX in
     1)
         npm run test-jasmine || EXIT_STATE=$?
         npm run test-export  || EXIT_STATE=$?
+        npm run test-syntax  || EXIT_STATE=$?
         exit $EXIT_STATE
         ;;
 
diff --git a/test/image/baselines/gl2d_10.png b/test/image/baselines/gl2d_10.png
index 36932d9a204..b3d8ce18148 100644
Binary files a/test/image/baselines/gl2d_10.png and b/test/image/baselines/gl2d_10.png differ
diff --git a/test/image/baselines/gl2d_12.png b/test/image/baselines/gl2d_12.png
index dbda9d4c0bb..9f35f0bb4fe 100644
Binary files a/test/image/baselines/gl2d_12.png and b/test/image/baselines/gl2d_12.png differ
diff --git a/test/image/baselines/gl2d_14.png b/test/image/baselines/gl2d_14.png
index a8ede620d7d..9a4ae8542e8 100644
Binary files a/test/image/baselines/gl2d_14.png and b/test/image/baselines/gl2d_14.png differ
diff --git a/test/image/baselines/gl2d_17.png b/test/image/baselines/gl2d_17.png
index 5797e3ce4a3..85a564fd72c 100644
Binary files a/test/image/baselines/gl2d_17.png and b/test/image/baselines/gl2d_17.png differ
diff --git a/test/image/baselines/gl2d_annotations.png b/test/image/baselines/gl2d_annotations.png
index f1d41279b72..7fc9eeae93a 100644
Binary files a/test/image/baselines/gl2d_annotations.png and b/test/image/baselines/gl2d_annotations.png differ
diff --git a/test/image/baselines/gl2d_axes_booleans.png b/test/image/baselines/gl2d_axes_booleans.png
index 378d57d0226..e0350f5c8de 100644
Binary files a/test/image/baselines/gl2d_axes_booleans.png and b/test/image/baselines/gl2d_axes_booleans.png differ
diff --git a/test/image/baselines/gl2d_axes_labels.png b/test/image/baselines/gl2d_axes_labels.png
index 79126e72024..5cf474017f0 100644
Binary files a/test/image/baselines/gl2d_axes_labels.png and b/test/image/baselines/gl2d_axes_labels.png differ
diff --git a/test/image/baselines/gl2d_axes_labels2.png b/test/image/baselines/gl2d_axes_labels2.png
new file mode 100644
index 00000000000..07d41bf98a3
Binary files /dev/null and b/test/image/baselines/gl2d_axes_labels2.png differ
diff --git a/test/image/baselines/gl2d_axes_lines.png b/test/image/baselines/gl2d_axes_lines.png
index d6c1f079692..6d2711ff435 100644
Binary files a/test/image/baselines/gl2d_axes_lines.png and b/test/image/baselines/gl2d_axes_lines.png differ
diff --git a/test/image/baselines/gl2d_axes_range_manual.png b/test/image/baselines/gl2d_axes_range_manual.png
index d11f89af02d..0f9047db8fa 100644
Binary files a/test/image/baselines/gl2d_axes_range_manual.png and b/test/image/baselines/gl2d_axes_range_manual.png differ
diff --git a/test/image/baselines/gl2d_axes_range_mode.png b/test/image/baselines/gl2d_axes_range_mode.png
index 18692d3c313..22f6212f49c 100644
Binary files a/test/image/baselines/gl2d_axes_range_mode.png and b/test/image/baselines/gl2d_axes_range_mode.png differ
diff --git a/test/image/baselines/gl2d_axes_range_type.png b/test/image/baselines/gl2d_axes_range_type.png
index a038b80cdda..ba6739c52cc 100644
Binary files a/test/image/baselines/gl2d_axes_range_type.png and b/test/image/baselines/gl2d_axes_range_type.png differ
diff --git a/test/image/baselines/gl2d_connect_gaps.png b/test/image/baselines/gl2d_connect_gaps.png
index 1ca8bb38f1f..69d5a638652 100644
Binary files a/test/image/baselines/gl2d_connect_gaps.png and b/test/image/baselines/gl2d_connect_gaps.png differ
diff --git a/test/image/baselines/gl2d_date_axes.png b/test/image/baselines/gl2d_date_axes.png
index a40439641a0..309ec31a081 100644
Binary files a/test/image/baselines/gl2d_date_axes.png and b/test/image/baselines/gl2d_date_axes.png differ
diff --git a/test/image/baselines/gl2d_error_bars.png b/test/image/baselines/gl2d_error_bars.png
index 2ea2a53889c..7c2ef3fe710 100644
Binary files a/test/image/baselines/gl2d_error_bars.png and b/test/image/baselines/gl2d_error_bars.png differ
diff --git a/test/image/baselines/gl2d_error_bars_log.png b/test/image/baselines/gl2d_error_bars_log.png
new file mode 100644
index 00000000000..d8b75b45d95
Binary files /dev/null and b/test/image/baselines/gl2d_error_bars_log.png differ
diff --git a/test/image/baselines/gl2d_fonts.png b/test/image/baselines/gl2d_fonts.png
index 2171e6a505e..038bcef6f67 100644
Binary files a/test/image/baselines/gl2d_fonts.png and b/test/image/baselines/gl2d_fonts.png differ
diff --git a/test/image/baselines/gl2d_layout_image.png b/test/image/baselines/gl2d_layout_image.png
new file mode 100644
index 00000000000..89a268319f9
Binary files /dev/null and b/test/image/baselines/gl2d_layout_image.png differ
diff --git a/test/image/baselines/gl2d_line_dash.png b/test/image/baselines/gl2d_line_dash.png
new file mode 100644
index 00000000000..af3a62f6466
Binary files /dev/null and b/test/image/baselines/gl2d_line_dash.png differ
diff --git a/test/image/baselines/gl2d_marker_line_width.png b/test/image/baselines/gl2d_marker_line_width.png
index 86431cb8663..3ceaa69dce0 100644
Binary files a/test/image/baselines/gl2d_marker_line_width.png and b/test/image/baselines/gl2d_marker_line_width.png differ
diff --git a/test/image/baselines/gl2d_marker_size.png b/test/image/baselines/gl2d_marker_size.png
new file mode 100644
index 00000000000..2aaffd39d60
Binary files /dev/null and b/test/image/baselines/gl2d_marker_size.png differ
diff --git a/test/image/baselines/gl2d_marker_symbols.png b/test/image/baselines/gl2d_marker_symbols.png
index ab5da027992..f8b4b8eba58 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/gl2d_multiple-traces-axes-labels.png b/test/image/baselines/gl2d_multiple-traces-axes-labels.png
new file mode 100644
index 00000000000..c38de480a7f
Binary files /dev/null and b/test/image/baselines/gl2d_multiple-traces-axes-labels.png differ
diff --git a/test/image/baselines/gl2d_multiple-traces-axes.png b/test/image/baselines/gl2d_multiple-traces-axes.png
new file mode 100644
index 00000000000..024f7b7578b
Binary files /dev/null and b/test/image/baselines/gl2d_multiple-traces-axes.png differ
diff --git a/test/image/baselines/gl2d_multiple_subplots.png b/test/image/baselines/gl2d_multiple_subplots.png
index 058ce315bbb..6335bb92491 100644
Binary files a/test/image/baselines/gl2d_multiple_subplots.png and b/test/image/baselines/gl2d_multiple_subplots.png differ
diff --git a/test/image/baselines/gl2d_open_marker_line_width.png b/test/image/baselines/gl2d_open_marker_line_width.png
new file mode 100644
index 00000000000..389948b230f
Binary files /dev/null and b/test/image/baselines/gl2d_open_marker_line_width.png differ
diff --git a/test/image/baselines/gl2d_scatter-colorscale-colorbar.png b/test/image/baselines/gl2d_scatter-colorscale-colorbar.png
index 73b9ae98016..87f05bdc541 100644
Binary files a/test/image/baselines/gl2d_scatter-colorscale-colorbar.png and b/test/image/baselines/gl2d_scatter-colorscale-colorbar.png differ
diff --git a/test/image/baselines/gl2d_scatter-colorscale-points.png b/test/image/baselines/gl2d_scatter-colorscale-points.png
new file mode 100644
index 00000000000..0242f8616aa
Binary files /dev/null and b/test/image/baselines/gl2d_scatter-colorscale-points.png differ
diff --git a/test/image/baselines/gl2d_scatter-marker-line-colorscales.png b/test/image/baselines/gl2d_scatter-marker-line-colorscales.png
index ead2030832c..0de117bf3d0 100644
Binary files a/test/image/baselines/gl2d_scatter-marker-line-colorscales.png and b/test/image/baselines/gl2d_scatter-marker-line-colorscales.png differ
diff --git a/test/image/baselines/gl2d_scatter-subplot-panel.png b/test/image/baselines/gl2d_scatter-subplot-panel.png
new file mode 100644
index 00000000000..e65cdfc48ed
Binary files /dev/null and b/test/image/baselines/gl2d_scatter-subplot-panel.png differ
diff --git a/test/image/baselines/gl2d_scatter_fill_self_next.png b/test/image/baselines/gl2d_scatter_fill_self_next.png
new file mode 100644
index 00000000000..42cd181a489
Binary files /dev/null and b/test/image/baselines/gl2d_scatter_fill_self_next.png differ
diff --git a/test/image/baselines/gl2d_shapes_below_traces.png b/test/image/baselines/gl2d_shapes_below_traces.png
new file mode 100644
index 00000000000..7eae642d04a
Binary files /dev/null and b/test/image/baselines/gl2d_shapes_below_traces.png differ
diff --git a/test/image/baselines/gl2d_simple_inset.png b/test/image/baselines/gl2d_simple_inset.png
index b7d0f20651f..b87b1d68337 100644
Binary files a/test/image/baselines/gl2d_simple_inset.png and b/test/image/baselines/gl2d_simple_inset.png differ
diff --git a/test/image/baselines/gl2d_size_margins.png b/test/image/baselines/gl2d_size_margins.png
index 700af178465..1cea44693ad 100644
Binary files a/test/image/baselines/gl2d_size_margins.png and b/test/image/baselines/gl2d_size_margins.png differ
diff --git a/test/image/baselines/gl2d_stacked_coupled_subplots.png b/test/image/baselines/gl2d_stacked_coupled_subplots.png
index bda5a6a2f36..534e823812d 100644
Binary files a/test/image/baselines/gl2d_stacked_coupled_subplots.png and b/test/image/baselines/gl2d_stacked_coupled_subplots.png differ
diff --git a/test/image/baselines/gl2d_stacked_subplots.png b/test/image/baselines/gl2d_stacked_subplots.png
index 7734163777c..79cef6fec93 100644
Binary files a/test/image/baselines/gl2d_stacked_subplots.png and b/test/image/baselines/gl2d_stacked_subplots.png differ
diff --git a/test/image/baselines/gl2d_subplots_anchor.png b/test/image/baselines/gl2d_subplots_anchor.png
new file mode 100644
index 00000000000..078e27ed403
Binary files /dev/null and b/test/image/baselines/gl2d_subplots_anchor.png differ
diff --git a/test/image/baselines/gl2d_tick-labels.png b/test/image/baselines/gl2d_tick-labels.png
new file mode 100644
index 00000000000..437905db4ca
Binary files /dev/null and b/test/image/baselines/gl2d_tick-labels.png differ
diff --git a/test/image/baselines/gl2d_ultra_zoom.png b/test/image/baselines/gl2d_ultra_zoom.png
new file mode 100644
index 00000000000..63d2efa81db
Binary files /dev/null and b/test/image/baselines/gl2d_ultra_zoom.png differ
diff --git a/test/image/baselines/glpolar_scatter.png b/test/image/baselines/glpolar_scatter.png
new file mode 100644
index 00000000000..71c4841359d
Binary files /dev/null and b/test/image/baselines/glpolar_scatter.png differ
diff --git a/test/image/baselines/glpolar_style.png b/test/image/baselines/glpolar_style.png
new file mode 100644
index 00000000000..35f91a65b18
Binary files /dev/null and b/test/image/baselines/glpolar_style.png differ
diff --git a/test/image/compare_pixels_test.js b/test/image/compare_pixels_test.js
index 3a51c9182cd..1094eb7b6c2 100644
--- a/test/image/compare_pixels_test.js
+++ b/test/image/compare_pixels_test.js
@@ -51,8 +51,6 @@ var QUEUE_WAIT = 10;
 var pattern = process.argv[2];
 var mockList = getMockList(pattern);
 var isInQueue = (process.argv[3] === '--queue');
-var isCI = process.env.CIRCLECI;
-
 
 if(mockList.length === 0) {
     throw new Error('No mocks found with pattern ' + pattern);
@@ -67,18 +65,12 @@ if(!pattern) {
 
 // gl2d have limited image-test support
 if(pattern === 'gl2d_*') {
-
     if(!isInQueue) {
         console.log('WARN: Running gl2d image tests in batch may lead to unwanted results\n');
     }
-
-    if(isCI) {
-        console.log('Filtering out multiple-subplot gl2d mocks:');
-        mockList = mockList
-            .filter(untestableGL2DonCIfilter)
-            .sort(sortForGL2DonCI);
-        console.log('\n');
-    }
+    console.log('\nSorting gl2d mocks to avoid gl-shader conflicts');
+    sortGl2dMockList(mockList);
+    console.log('');
 }
 
 // main
@@ -111,48 +103,32 @@ function untestableFilter(mockName) {
     return cond;
 }
 
-/* gl2d mocks that have multiple subplots
- * can't be generated properly on CircleCI
- * at the moment.
- *
- * For more info see:
- * https://github.com/plotly/plotly.js/pull/980
+/* gl2d pointcloud and other non-regl gl2d mock(s)
+ * must be tested first on in order to work;
+ * sort them here.
  *
- */
-function untestableGL2DonCIfilter(mockName) {
-    var cond = [
-        'gl2d_multiple_subplots',
-        'gl2d_simple_inset',
-        'gl2d_stacked_coupled_subplots',
-        'gl2d_stacked_subplots'
-    ].indexOf(mockName) === -1;
-
-    if(!cond) console.log(' -', mockName);
-
-    return cond;
-}
-
-/* gl2d pointcloud mock(s) must be tested first
- * on CircleCI in order to work; sort them here.
- *
- * Pointcloud relies on gl-shader@4.2.1 whereas
- * other gl2d trace modules rely on gl-shader@4.2.0,
- * we suspect that the lone gl context on CircleCI is
+ * gl-shader appears to conflict with regl.
+ * We suspect that the lone gl context on CircleCI is
  * having issues with dealing with the two different
- * gl-shader versions.
+ * program binding algorithm.
+ *
+ * The problem will be solved by switching all our
+ * WebGL-based trace types to regl.
  *
  * More info here:
  * https://github.com/plotly/plotly.js/pull/1037
  */
-function sortForGL2DonCI(a, b) {
-    var root = 'gl2d_pointcloud',
-        ai = a.indexOf(root),
-        bi = b.indexOf(root);
-
-    if(ai < bi) return 1;
-    if(ai > bi) return -1;
-
-    return 0;
+function sortGl2dMockList(mockList) {
+    var mockNames = ['gl2d_pointcloud-basic', 'gl2d_heatmapgl'];
+    var pos = 0;
+
+    mockNames.forEach(function(m) {
+        var ind = mockList.indexOf(m);
+        var tmp = mockList[pos];
+        mockList[pos] = m;
+        mockList[ind] = tmp;
+        pos++;
+    });
 }
 
 function runInBatch(mockList) {
diff --git a/test/image/mocks/gl2d_12.json b/test/image/mocks/gl2d_12.json
index 01d477ea386..1dd1a744dad 100644
--- a/test/image/mocks/gl2d_12.json
+++ b/test/image/mocks/gl2d_12.json
@@ -529,6 +529,7 @@
                 15389.924680000002,
                 20509.64777,
                 10808.47561,
+                9101.25,
                 9786.534714,
                 18678.31435,
                 25768.25759,
@@ -561,6 +562,7 @@
                 75.563,
                 78.098,
                 72.476,
+                67.59,
                 74.002,
                 74.663,
                 77.926,
@@ -595,6 +597,7 @@
                 "Country: Poland<br>Life Expectancy: 75.563<br>GDP per capita: 15389.92468<br>Population: 38518241.0<br>Year: 2007",
                 "Country: Portugal<br>Life Expectancy: 78.098<br>GDP per capita: 20509.64777<br>Population: 10642836.0<br>Year: 2007",
                 "Country: Romania<br>Life Expectancy: 72.476<br>GDP per capita: 10808.47561<br>Population: 22276056.0<br>Year: 2007",
+                "Country: Russia<br>Life Expectancy: 67.59<br>GDP per capita: 9101.25<br>Population: 142800000.0<br>Year: 2007",
                 "Country: Serbia<br>Life Expectancy: 74.002<br>GDP per capita: 9786.534714<br>Population: 10150265.0<br>Year: 2007",
                 "Country: Slovak Republic<br>Life Expectancy: 74.663<br>GDP per capita: 18678.31435<br>Population: 5447502.0<br>Year: 2007",
                 "Country: Slovenia<br>Life Expectancy: 77.926<br>GDP per capita: 25768.25759<br>Population: 2009245.0<br>Year: 2007",
diff --git a/test/image/mocks/gl2d_axes_labels2.json b/test/image/mocks/gl2d_axes_labels2.json
new file mode 100644
index 00000000000..94a3a7d16a7
--- /dev/null
+++ b/test/image/mocks/gl2d_axes_labels2.json
@@ -0,0 +1,15 @@
+{
+    "data": [
+        {
+            "x": ["apples", "oranges", "bananananananas", "cantaloupeeeeww", "khljh"],
+            "y": [8, 7, 6, 5, 4, 3, 2, 1, 0],
+            "type": "scattergl",
+            "fill": "tozeroy"
+        }
+    ],
+    "layout": {
+        "width": 400,
+        "height": 300,
+        "showlegend": false
+    }
+}
diff --git a/test/image/mocks/gl2d_error_bars_log.json b/test/image/mocks/gl2d_error_bars_log.json
new file mode 100644
index 00000000000..02471ffbba1
--- /dev/null
+++ b/test/image/mocks/gl2d_error_bars_log.json
@@ -0,0 +1,10 @@
+{
+    "data": [{
+      "type": "scattergl",
+      "y": [10, 2e4, 5e6],
+      "error_y": {"array": [5, 1e4, 4e6]}
+    }],
+    "layout": {
+      "yaxis": {"type": "log"}
+    }
+}
diff --git a/test/image/mocks/gl2d_layout_image.json b/test/image/mocks/gl2d_layout_image.json
new file mode 100644
index 00000000000..bbdf341604f
--- /dev/null
+++ b/test/image/mocks/gl2d_layout_image.json
@@ -0,0 +1,104 @@
+{
+  "data": [
+    {
+      "x": [1,2,3],
+      "y": ["a", "b", "c"],
+      "type": "scattergl"
+    }, {
+      "x": ["2001-01-01","2002-01-01","2003-01-01"],
+      "y": [10,100,1000],
+      "type": "scattergl",
+      "xaxis": "x2",
+      "yaxis": "y2"
+    }
+  ],
+  "layout": {
+    "plot_bgcolor": "rgba(0,0,0,0)",
+    "xaxis2": {
+        "anchor": "y2"
+    },
+    "yaxis": {
+        "domain": [0, 0.45]
+    },
+    "yaxis2": {
+        "domain": [0.55, 1],
+        "type": "log",
+        "anchor": "x2"
+    },
+    "images": [
+      {
+        "source": "https://images.plot.ly/language-icons/api-home/python-logo.png",
+        "xref": "paper",
+        "yref": "paper",
+        "x": 0,
+        "y": 1,
+        "sizex": 0.2,
+        "sizey": 0.2,
+        "xanchor": "right",
+        "yanchor": "bottom"
+      },
+      {
+        "source": "https://images.plot.ly/language-icons/api-home/js-logo.png",
+        "xref": "x",
+        "yref": "y",
+        "x": 1.5,
+        "y": "b",
+        "sizex": 1,
+        "sizey": 1,
+        "xanchor": "right",
+        "yanchor": "bottom"
+      },
+      {
+        "source": "https://images.plot.ly/language-icons/api-home/r-logo.png",
+        "xref": "x2",
+        "yref": "y2",
+        "x": "2001-01-01",
+        "y": 3,
+        "sizex": 63072000000,
+        "sizey": 2,
+        "sizing": "stretch",
+        "opacity": 0.4,
+        "layer": "below"
+      },
+      {
+          "visible": false,
+          "source": "https://images.plot.ly/language-icons/api-home/python-logo.png",
+          "xref": "x",
+          "yref": "y",
+          "x": 1,
+          "y": 3,
+          "sizex": 2,
+          "sizey": 2,
+          "sizing": "stretch",
+          "opacity": 0.4,
+          "layer": "below"
+      },
+      {
+        "source": "https://images.plot.ly/language-icons/api-home/matlab-logo.png",
+        "xref": "x",
+        "yref": "paper",
+        "x": 3,
+        "y": 0,
+        "sizex": 0.5,
+        "sizey": 1,
+        "opacity": 1,
+        "xanchor": "right",
+        "yanchor": "middle"
+      },
+      {
+        "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANwAAAD1CAMAAAAMJ2tNAAAC9FBMVEUAAABEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEettEets5iYiFAAAA+3RSTlMAABVISkcS8/nwPvY/AUZraWpLAwSrtgZSmJZ7DIzRFIvQCAkXqcHAwlce4XQCI7G/L+z+X+sfBx0K5OWEMNTeN/H8VPuXy8jMdiW5ycc6wzJtyqQPho8FGLBbK73FQgsODVxWWRnZLUAqG876pfeFvDbupyYRXn+mvodoO1qauqNiNRD9tSETet9QVbPpb2dyGvXiIuO4M/jTYNZ1YdLmJy7nUbKOrbehU5mD2JFsT0lOMeCBfqLccZXdiETyOO/Pu9tzgCQ5zeqgqImc1xzGKUxF9EOTeRZ3rCB96NpNbp+SXZR8m7SqY6+NncQoLGWugpDtWD3VNDxwZtDaXE4AAAAJcEhZcwAALiMAAC4jAXilP3YAAAu4SURBVHja7Z15fEzXHsDzIyLBtIkZhmna1ExIJQZPyEYsIYKHR2jHTlQsEbEm9n2ndqlYaq21qK3UmvcoLX1FUBTP0r7XV6V9S9u3dP55c3/nZrZ77ph7eTXvvt/vD5+P+zv3nPO995zf+Z3f79xMUBAJCQkJCQkJCQkJCQkJCQkJyS8lUKZssFTKhYAm4MqWD5VKWAVtwAWH2qVSsRLBERzBERzBERzBERzBERzBEdz/KxzoXngxXCIRlfWgBThDFc5N9qpGgiM4giM4giM4giM4giM4giM4ggsAOKhW3SSR6i9FgibgXuYRvBKlDbhXeQQ1CI7gCI7gCI7gCI7gCI7gCI7gCI7gCI7gCI7gCI7gCI7gCI7gCI7g/qfgQGe2cMQAmoCLrlkrRiKv1dYGXGwcT1WH4AiO4AiO4AiO4AiO4AiO4Ajuv0Zg5e4gha8wNQBXtx5nB1m/nDbgftWA02B8Q43ANSI4giM4giM4giM4giM4giM47cOBLjohViIJwp++DQw4SEyK5UiyP9xgrpkSJ5HGTQIHLrWptH9xzYL9grPE8Ko1BQ5c8xYcTVpLDcO1IjiCIziCIziCIziCIziCCxy4Z5fCeuX57AoS01tnSKRNW6Hadr9uL1V1EHZS0PE3Uk37Wp0EVWdOfZldujoI9K9zVK3fsDlU3bpncu7qIdTXsxenF737+AUHfftxxCxUq8/iqWyCysDT9BPOJIOZp4nUOQggmadKBofKym3K6LsXJCQkJCQkJCQkJIEpENk/Ijw84k3zM/ZaIXmAUG//yOfpDYvbLmHn+WzrzR4o1BsXS3AER3AER3BYgW7Q4MFDdKBNuJyhobnD9BqFK5dnt1cxaBIOYLhdu3CRIzQMN3KUhuE62zULB4bRzxcOQG+2mA0AauBAZ7RYEq0gpx4T9wvDQX5CbGwS26FB8qCCseNGxIyvMmHipHxpH33AAWT1nNx2StWYmKnTxk6fMVNys6NAn9GzhNtnz3EeLMxh+Qx3OIAQ4YxkQrQOuM9+rg+ttPi8lLi4+QuEarsufGvRrNIMWIuMxbHePZSFA13CkqmL0pw5vFG9l46xgkeBZQVTyzPtcufBwhWtV3LglhQ6VIVvc4cHmIcJJyhTmvgJh8nC4QCwqnuRR4Jv1uo1FvALDvJfXDvLK0fZbN1c17MB/Tvrczk5xA0bJXBBkNpY+E9eZS7cps2o3OLfAGZw79pgwXxJ23lbZ8KT4QDabWvFSX5uH+Skk8nf8uEMw1C3Q88b/EtRl5GvBO615J2NxUeeG5ob7+zgrq7wJDiA5pmutx0amuZKhFcqpVMCFwS1MSc9vx0HLovV8x4ogdvdshlOtKl79r6/b/9e01BxELVq4p7C5MEBHFgvgh0cdqhHcPCADw4fEQF3dxQ7AbYPq5tMR7vjCz7m/G2b9OPRPLgofFizJnIs2omTgqrRKT/NKoMr2uD4p/jopEisECB6zW9Z/8IWuDXBhSvzO1by9JkxbCCBdWXEK+xaRoLHO65U0Z+lAOAjvLlXpNTist/FecusCA6f6E5XqwArq7DBmdHNJxzoj2O5+O2pbvYDzk5js3BKIiiGC4IDOEWKz0nKRX2MbU0EpXCfvO9xC2RXZdPOrSYe3IwN7MDFMk8j0/U8W1Aqu794f+FsF/DmTyVLUTWcjUfq+rvYO+HG6rxs4KbTeL13vg84MPweC33W06sjcPESg+6rAg4m45Rfv8yrTl11rHOXVSncam/fB+AMDriiar7gLhcKV3IjJA8ZSnDun7yiHM5pUt7xrBWSPhMuX73mt5dWCie1rpDDzMLnruXKGw7gOhZpc0Nq2CzdUVXdNSD8h4ObeO8XRs81Zzp6Ch3ylcJdrQD8fbNjXGbJw1nYxDzOmeHQH8fW6TnK4YKgZ4pwYfMqDzhbTWzsOiiFa9qN073BywXVok3ycLcOotmowGkOEnDpLCqnBs6GmyP7bXcOuIw+1J1U//cOItwIztIBObtx6PeXh7uG+H9YxoNLrIc1z1MBFwR383DQuLl/APeULXIuuPscNxss41HXWR5uHWsvkQcHD5htc01ZBXBRbfAc4F03OPNs9Jm+BMVwY3nds3ZB3QSniyiB+woLpHPbg8WM3KgGDv4oMUczFnnNYb/hJnK71wR103QycADpWOAN/r59P1qUoSEq4Er3NgPnuAzzPGzrT1bFcPEFIBvQsX9tlIOzst3JS3y4jrg7nZ+kCs74Z09PC+bi2rf8BSWhCAaXtpALtwaX8foWOTjbu56T0vPuVDxp23iMGrggWFDkYT7gBD6qzBAVcG9y4QoQLkYWzsiWuXA+HDsjLe7ZFMPdQCe5waRSl+xTcZAoh5MZlt+wvYdZ9s09xAKLfb65durgYKxdDICwIk3Ru++kKEAmGpQlXLgzqKtnk4PTnZc1tQ7tJBxIxy6qgnM8mzvCxbU5DG4/LnwXbGrgrnNXqm9R9whkraVJ9D65NW/BPV2bfJVwxm3ok6M1ACu2lPalssimCGfieYfiqBsrv4izm6fw1zm2wj+0qYQTd2+H8cT4rWP4GhNUwfH+yDdEPkZdhDxcQ7Q4vZK57917hVcGFwRlOghXCwVrC7VxFGwFVXAdsjhw36Fv3qqaPNwmNIjNeHHw0h3DTTW+pduy7djVOfbE55Uvci64O99z4Lagi3GwrjxcVA10AXkBVFiJhfOuqIULggNoUraZAc5iXTH56uA4H8eAfgdbCSw+Nqu72NDjud1/wQDf2iRvuL/6DWeYhgvlZYD+WNc6UAdnHydtMhVDCPa/ye/EHVuTq7jnWSl9NJFv491HwRtuhMXfLA8sFExK/HSwHcZF7oDSLFAp3IYZ3jXr2EIQNthXDCWkg9cDcNk6xG7hFvCACgi3NtpvuOjWwvUu+lvzVSxy7tGvHWav6XzlE7w+O9kXHOxFM1Y4yTv6Fcti7F+7R78GYY0NVvkNB7fRXuW0FCZ/WoHi9J0TruI9vYf3szGDBVdKfMcto3pjscwhnlHPEJwu9uJ97nHLmazKjySvWS75CKuEx5F39+8YnL+lHs5eXCfLNbtgUC129Yu+viPOUPIDlvu4gmujBbqNVVi64HOPVAOwuGPKYO/XLAvHTMqe1fgvqITLE3pYNLtkrpgryN57hLHFdXpSrkB/iEXONx8fJOZJrbc+ZPFc++OLnkP9R1Z0d2Uba8ZsA99pY1goDMhGgl8Z9pPypDKDK6yDe/i8jPR1LffdjTDVEBNuLRoCPCmFlTWFvaX4DVWGF1QO/vH2jtNilucfI72iqtlismvUhXsl+2rfPPrwn0+Cy25TOrBi+qmFO/mvgkWln4O65eda/NvwxPxcEORvzePm59p39B5+0OOq3ZkGTBM/BPUJx0yKIG1BLZyjlZ0DJbnBwm8MfmRWHY7WXmlW1p53fqU0iJ34XpHkK1efpxlgSIrYl+9UHHUQ51wfgO8f/eDRu6sXRur8zIlD6iOvL5NDH/fnetOWtpuVwYnhWRWLnBMud7/DOzX+9GBg6bApijscLPUkci6tEA4aSF08sJ06vj6sdDgvb9ZlYYjMURbrufuFYto2vjizJ4O7Ub8wLm7FpRze46iGYz50spozKgyOuZagX3Ztybw96V99EF4yhxNoBUNOQmxsQraVG+Qsc2r60ldNppfb/nwiycevJIGxbst7r5tMExavGRnFhoajXaHeHN6PDEHzURjuz35auAAUgOP4lh+AFuGicVNVXEFV/wIdbgCa1/pZGoQDY00xVaRFuBkY+0y5rPIQZCDDARzCFzfapkW4s5j+LF+i9mhuAMOBgZ0Ymp2lPTiHnx2GfuB+1d8xBCwc6Huwk4TjkjUDB/pEq/ArkImdTOh42VesUt23wIM7tf3+oTrrvp0qbh/ywkFDcJ5/3yh3wlN8/hvgcCebPM0nyAENl1aj9lP9xGzgwZ3bzUIR8YvGd74I8HR1RYSHhy+pG0DWMqHsz44uhTdM7Qv0N6JISEhISEhISEhISEhISEieg/wHXMU13xThNeUAAABhdEVYdGNvbW1lbnQARmlsZSBzb3VyY2U6IGh0dHA6Ly9jb21tb25zLndpa2ltZWRpYS5vcmcvd2lraS9GaWxlOlBsb3RseV9sb2dvX2Zvcl9kaWdpdGFsX2ZpbmFsXyg2KS5wbmetqmbcAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE0LTA5LTIxVDAzOjM1OjExKzAwOjAwKhOa5QAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNC0wOS0yMVQwMzozNToxMSswMDowMFtOIlkAAABGdEVYdHNvZnR3YXJlAEltYWdlTWFnaWNrIDYuNi45LTcgMjAxNC0wMy0wNiBRMTYgaHR0cDovL3d3dy5pbWFnZW1hZ2ljay5vcmeB07PDAAAAGHRFWHRUaHVtYjo6RG9jdW1lbnQ6OlBhZ2VzADGn/7svAAAAGXRFWHRUaHVtYjo6SW1hZ2U6OmhlaWdodAAxNzA0VsMjbQAAABh0RVh0VGh1bWI6OkltYWdlOjpXaWR0aAAxNTMxndUDgQAAABl0RVh0VGh1bWI6Ok1pbWV0eXBlAGltYWdlL3BuZz+yVk4AAAAXdEVYdFRodW1iOjpNVGltZQAxNDExMjcwNTEx0Rvi6AAAABN0RVh0VGh1bWI6OlNpemUAMjkuOUtCQvmCzmYAAAAzdEVYdFRodW1iOjpVUkkAZmlsZTovLy90bXAvbG9jYWxjb3B5X2UwNDYwYmM4MDVlYS0xLnBuZ1YfjUoAAAAASUVORK5CYII=",
+        "xref": "paper",
+        "yref": "paper",
+        "x": 0.5,
+        "y": 1,
+        "sizex": 0.2,
+        "sizey": 0.2,
+        "opacity": 1,
+        "xanchor": "middle",
+        "yanchor": "bottom"
+      }
+    ],
+    "width": 800,
+    "height": 500
+  }
+}
diff --git a/test/image/mocks/gl2d_line_dash.json b/test/image/mocks/gl2d_line_dash.json
new file mode 100644
index 00000000000..61723af8144
--- /dev/null
+++ b/test/image/mocks/gl2d_line_dash.json
@@ -0,0 +1,65 @@
+{
+    "data": [
+        {
+            "x": [
+                1,
+                2,
+                3
+            ],
+            "y": [
+                4,
+                5,
+                6
+            ],
+            "marker": {
+                "color": "rgb(54,144,192)",
+                "size": 12,
+                "symbol": "square",
+                "line": {
+                    "color": "darkblue",
+                    "width": 3
+                },
+                "opacity": 1
+            },
+            "line": {
+                "color": "rgb(3,78,123)",
+                "width": 6,
+                "dash": "dot"
+            },
+            "type": "scattergl"
+        },
+        {
+            "x": [
+                1,
+                2,
+                3
+            ],
+            "y": [
+                2,
+                10,
+                12
+            ],
+            "marker": {
+                "color": "fuchsia",
+                "size": 16,
+                "symbol": "cross",
+                "line": {
+                    "color": "",
+                    "width": 0
+                },
+                "opacity": 0.9
+            },
+            "line": {
+                "color": "purple",
+                "width": 4,
+                "dash": "dashdot"
+            },
+            "type": "scattergl"
+        }
+    ],
+    "layout": {
+        "showlegend": false,
+        "width": 800,
+        "height": 506
+    }
+}
diff --git a/test/image/mocks/gl2d_marker_size.json b/test/image/mocks/gl2d_marker_size.json
new file mode 100644
index 00000000000..cfd66112331
--- /dev/null
+++ b/test/image/mocks/gl2d_marker_size.json
@@ -0,0 +1,75 @@
+{
+  "data": [
+    {
+      "type": "scattergl",
+      "y": [5, 5, 5, 5],
+      "x": [1, 2, 3, 4],
+      "mode": "markers",
+      "marker": {
+        "color": "rgba(156, 165, 196, 0.8)",
+        "line": {
+          "color": "rgba(0, 0, 0, 0.8)",
+          "width": 5
+        },
+        "symbol": ["circle", "circle-cross-open", "circle-cross", "circle-open"],
+        "size": [20, 20, 20, 20]
+      }
+    },
+    {
+      "type": "scattergl",
+      "y": [10, 10, 10, 10],
+      "x": [1, 2, 3, 4],
+      "mode": "markers",
+      "marker": {
+        "color": "rgba(156, 165, 196, 0.8)",
+        "line": {
+          "color": "rgba(0, 0, 0, 0.8)",
+          "width": 5
+        },
+        "symbol": ["circle", "circle-cross-open", "circle-cross", "circle-open"],
+        "size": 20
+      }
+    },
+    {
+      "type": "scattergl",
+      "y": [15, 15, 15, 15],
+      "x": [1, 2, 3, 4],
+      "mode": "markers",
+      "marker": {
+        "color": "rgba(156, 165, 196, 0.8)",
+        "line": {
+          "color": "rgba(0, 0, 0, 0.8)",
+          "width": [5, 5, 5, 5]
+        },
+        "symbol": ["circle", "circle-cross-open", "circle-cross", "circle-open"],
+        "size": 20
+      }
+    },
+    {
+      "type": "scattergl",
+      "y": [20, 20, 20, 20],
+      "x": [1, 2, 3, 4],
+      "mode": "markers",
+      "marker": {
+        "color": "rgba(156, 165, 196, 0.8)",
+        "line": {
+          "color": "rgba(0, 0, 0, 0.8)",
+          "width": [5, 5, 5, 5]
+        },
+        "symbol": ["circle", "circle-cross-open", "circle-cross", "circle-open"],
+        "size": [20, 20, 20, 20]
+      }
+    }
+  ],
+  "layout": {
+    "margin": {
+      "l": 60,
+      "r": 60,
+      "b": 60,
+      "t": 60
+    },
+    "height": 400,
+    "width": 480,
+    "showlegend": false
+  }
+}
diff --git a/test/image/mocks/gl2d_marker_symbols.json b/test/image/mocks/gl2d_marker_symbols.json
index a0026b59db1..8460619f1b4 100644
--- a/test/image/mocks/gl2d_marker_symbols.json
+++ b/test/image/mocks/gl2d_marker_symbols.json
@@ -4,98 +4,18 @@
       "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
+        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
       ],
       "y": [
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0,
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0,
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0,
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0,
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0
+        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
       ],
       "marker": {
         "symbol": [
@@ -146,104 +66,58 @@
           "line-nw"
         ],
         "color": "blue",
-        "size": [
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          1,
-          20,
-          1,
-          1,
-          1,
-          1,
-          1,
-          20,
-          1,
-          1,
-          20,
-          1,
-          1,
-          1,
-          1,
-          1,
-          20,
-          20,
-          1,
-          20,
-          1,
-          20,
-          20,
-          1,
-          1,
-          20,
-          20,
-          1,
-          1
-        ],
+        "size": 20,
         "line": {
           "color": "orange",
           "width": 1.5
         }
       },
       "text": [
-        "marker symbol: circle",
-        "marker symbol: square",
-        "marker symbol: diamond",
-        "marker symbol: cross",
-        "marker symbol: x",
-        "marker symbol: triangle-up",
-        "marker symbol: triangle-down",
-        "marker symbol: triangle-left",
-        "marker symbol: triangle-right",
-        "marker symbol: triangle-ne",
-        "marker symbol: triangle-se",
-        "marker symbol: triangle-sw",
-        "marker symbol: triangle-nw",
-        "marker symbol: pentagon",
-        "marker symbol: hexagon",
-        "marker symbol: hexagon2",
-        "marker symbol: octagon<br>NOT AVAILABLE",
-        "marker symbol: star",
-        "marker symbol: hexagram<br>NOT AVAILABLE",
-        "marker symbol: star-triangle-up<br>NOT AVAILABLE",
-        "marker symbol: star-triangle-down<br>NOT AVAILABLE",
-        "marker symbol: star-square<br>NOT AVAILABLE",
-        "marker symbol: star-diamond<br>NOT AVAILABLE",
-        "marker symbol: diamond-tall",
-        "marker symbol: diamond-wide<br>NOT AVAILABLE",
-        "marker symbol: hourglass<br>NOT AVAILABLE",
-        "marker symbol: bowtie",
-        "marker symbol: circle-cross<br>NOT AVAILABLE",
-        "marker symbol: circle-x<br>NOT AVAILABLE",
-        "marker symbol: square-cross<br>NOT AVAILABLE",
-        "marker symbol: square-x<br>NOT AVAILABLE",
-        "marker symbol: diamond-cross<br>NOT AVAILABLE",
-        "marker symbol: diamond-x",
-        "marker symbol: cross-thin",
-        "marker symbol: x-thin<br>NOT AVAILABLE",
-        "marker symbol: asterisk",
-        "marker symbol: hash<br>NOT AVAILABLE",
-        "marker symbol: y-up",
-        "marker symbol: y-down",
-        "marker symbol: y-left<br>NOT AVAILABLE",
-        "marker symbol: y-right<br>NOT AVAILABLE",
-        "marker symbol: line-ew",
-        "marker symbol: line-ns",
-        "marker symbol: line-ne<br>NOT AVAILABLE",
-        "marker symbol: line-nw<br>NOT AVAILABLE"
+        "marker symbol: circle<br>number: 0",
+        "marker symbol: square<br>number: 1",
+        "marker symbol: diamond<br>number: 2",
+        "marker symbol: cross<br>number: 3",
+        "marker symbol: x<br>number: 4",
+        "marker symbol: triangle-up<br>number: 5",
+        "marker symbol: triangle-down<br>number: 6",
+        "marker symbol: triangle-left<br>number: 7",
+        "marker symbol: triangle-right<br>number: 8",
+        "marker symbol: triangle-ne<br>number: 9",
+        "marker symbol: triangle-se<br>number: 10",
+        "marker symbol: triangle-sw<br>number: 11",
+        "marker symbol: triangle-nw<br>number: 12",
+        "marker symbol: pentagon<br>number: 13",
+        "marker symbol: hexagon<br>number: 14",
+        "marker symbol: hexagon2<br>number: 15",
+        "marker symbol: octagon<br>number: 16",
+        "marker symbol: star<br>number: 17",
+        "marker symbol: hexagram<br>number: 18",
+        "marker symbol: star-triangle-up<br>number: 19",
+        "marker symbol: star-triangle-down<br>number: 20",
+        "marker symbol: star-square<br>number: 21",
+        "marker symbol: star-diamond<br>number: 22",
+        "marker symbol: diamond-tall<br>number: 23",
+        "marker symbol: diamond-wide<br>number: 24",
+        "marker symbol: hourglass<br>number: 25",
+        "marker symbol: bowtie<br>number: 26",
+        "marker symbol: circle-cross<br>number: 27",
+        "marker symbol: circle-x<br>number: 28",
+        "marker symbol: square-cross<br>number: 29",
+        "marker symbol: square-x<br>number: 30",
+        "marker symbol: diamond-cross<br>number: 31",
+        "marker symbol: diamond-x<br>number: 32",
+        "marker symbol: cross-thin<br>number: 33",
+        "marker symbol: x-thin<br>number: 34",
+        "marker symbol: asterisk<br>number: 35",
+        "marker symbol: hash<br>number: 36",
+        "marker symbol: y-up<br>number: 37",
+        "marker symbol: y-down<br>number: 38",
+        "marker symbol: y-left<br>number: 39",
+        "marker symbol: y-right<br>number: 40",
+        "marker symbol: line-ew<br>number: 41",
+        "marker symbol: line-ns<br>number: 42",
+        "marker symbol: line-ne<br>number: 43",
+        "marker symbol: line-nw<br>number: 44"
       ],
       "hoverinfo": "text"
     },
@@ -251,98 +125,18 @@
       "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
+        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
       ],
       "y": [
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0,
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0,
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0,
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0,
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0
+        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
       ],
       "marker": {
         "symbol": [
@@ -393,104 +187,58 @@
           "line-nw-open"
         ],
         "color": "blue",
-        "size": [
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          20,
-          1,
-          20,
-          1,
-          1,
-          1,
-          1,
-          1,
-          20,
-          1,
-          1,
-          20,
-          20,
-          20,
-          20,
-          20,
-          1,
-          1,
-          20,
-          1,
-          20,
-          1,
-          20,
-          20,
-          1,
-          1,
-          20,
-          20,
-          1,
-          1
-        ],
+        "size": 20,
         "line": {
           "color": "orange",
           "width": 1.5
         }
       },
       "text": [
-        "marker symbol: circle-open",
-        "marker symbol: square-open",
-        "marker symbol: diamond-open",
-        "marker symbol: cross-open",
-        "marker symbol: x-open",
-        "marker symbol: triangle-up-open",
-        "marker symbol: triangle-down-open",
-        "marker symbol: triangle-left-open",
-        "marker symbol: triangle-right-open",
-        "marker symbol: triangle-ne-open",
-        "marker symbol: triangle-se-open",
-        "marker symbol: triangle-sw-open",
-        "marker symbol: triangle-nw-open",
-        "marker symbol: pentagon-open",
-        "marker symbol: hexagon-open",
-        "marker symbol: hexagon2-open",
-        "marker symbol: octagon-open<br>NOT AVAILABLE",
-        "marker symbol: star-open",
-        "marker symbol: hexagram-open<br>NOT AVAILABLE",
-        "marker symbol: star-triangle-up-open<br>NOT AVAILABLE",
-        "marker symbol: star-triangle-down-open<br>NOT AVAILABLE",
-        "marker symbol: star-square-open<br>NOT AVAILABLE",
-        "marker symbol: star-diamond-open<br>NOT AVAILABLE",
-        "marker symbol: diamond-tall-open",
-        "marker symbol: diamond-wide-open<br>NOT AVAILABLE",
-        "marker symbol: hourglass-open<br>NOT AVAILABLE",
-        "marker symbol: bowtie-open",
-        "marker symbol: circle-cross-open",
-        "marker symbol: circle-x-open",
-        "marker symbol: square-cross-open",
-        "marker symbol: square-x-open",
-        "marker symbol: diamond-cross-open<br>NOT AVAILABLE",
-        "marker symbol: diamond-x-open<br>NOT AVAILABLE",
-        "marker symbol: cross-thin-open",
-        "marker symbol: x-thin-open<br>NOT AVAILABLE",
-        "marker symbol: asterisk-open",
-        "marker symbol: hash-open<br>NOT AVAILABLE",
-        "marker symbol: y-up-open",
-        "marker symbol: y-down-open",
-        "marker symbol: y-left-open<br>NOT AVAILABLE",
-        "marker symbol: y-right-open<br>NOT AVAILABLE",
-        "marker symbol: line-ew-open",
-        "marker symbol: line-ns-open",
-        "marker symbol: line-ne-open<br>NOT AVAILABLE",
-        "marker symbol: line-nw-open<br>NOT AVAILABLE"
+        "marker symbol: circle-open<br>number: 100",
+        "marker symbol: square-open<br>number: 101",
+        "marker symbol: diamond-open<br>number: 102",
+        "marker symbol: cross-open<br>number: 103",
+        "marker symbol: x-open<br>number: 104",
+        "marker symbol: triangle-up-open<br>number: 105",
+        "marker symbol: triangle-down-open<br>number: 106",
+        "marker symbol: triangle-left-open<br>number: 107",
+        "marker symbol: triangle-right-open<br>number: 108",
+        "marker symbol: triangle-ne-open<br>number: 109",
+        "marker symbol: triangle-se-open<br>number: 110",
+        "marker symbol: triangle-sw-open<br>number: 111",
+        "marker symbol: triangle-nw-open<br>number: 112",
+        "marker symbol: pentagon-open<br>number: 113",
+        "marker symbol: hexagon-open<br>number: 114",
+        "marker symbol: hexagon2-open<br>number: 115",
+        "marker symbol: octagon-open<br>number: 116",
+        "marker symbol: star-open<br>number: 117",
+        "marker symbol: hexagram-open<br>number: 118",
+        "marker symbol: star-triangle-up-open<br>number: 119",
+        "marker symbol: star-triangle-down-open<br>number: 120",
+        "marker symbol: star-square-open<br>number: 121",
+        "marker symbol: star-diamond-open<br>number: 122",
+        "marker symbol: diamond-tall-open<br>number: 123",
+        "marker symbol: diamond-wide-open<br>number: 124",
+        "marker symbol: hourglass-open<br>number: 125",
+        "marker symbol: bowtie-open<br>number: 126",
+        "marker symbol: circle-cross-open<br>number: 127",
+        "marker symbol: circle-x-open<br>number: 128",
+        "marker symbol: square-cross-open<br>number: 129",
+        "marker symbol: square-x-open<br>number: 130",
+        "marker symbol: diamond-cross-open<br>number: 131",
+        "marker symbol: diamond-x-open<br>number: 132",
+        "marker symbol: cross-thin-open<br>number: 133",
+        "marker symbol: x-thin-open<br>number: 134",
+        "marker symbol: asterisk-open<br>number: 135",
+        "marker symbol: hash-open<br>number: 136",
+        "marker symbol: y-up-open<br>number: 137",
+        "marker symbol: y-down-open<br>number: 138",
+        "marker symbol: y-left-open<br>number: 139",
+        "marker symbol: y-right-open<br>number: 140",
+        "marker symbol: line-ew-open<br>number: 141",
+        "marker symbol: line-ns-open<br>number: 142",
+        "marker symbol: line-ne-open<br>number: 143",
+        "marker symbol: line-nw-open<br>number: 144"
       ],
       "hoverinfo": "text"
     },
@@ -498,98 +246,18 @@
       "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
+        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
       ],
       "y": [
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0,
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0,
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0,
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0,
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0
+        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
       ],
       "marker": {
         "symbol": [
@@ -640,104 +308,58 @@
           "line-nw-dot"
         ],
         "color": "blue",
-        "size": [
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1
-        ],
+        "size": 20,
         "line": {
           "color": "orange",
           "width": 1.5
         }
       },
       "text": [
-        "marker symbol: circle-dot<br>NOT AVAILABLE",
-        "marker symbol: square-dot<br>NOT AVAILABLE",
-        "marker symbol: diamond-dot<br>NOT AVAILABLE",
-        "marker symbol: cross-dot<br>NOT AVAILABLE",
-        "marker symbol: x-dot<br>NOT AVAILABLE",
-        "marker symbol: triangle-up-dot<br>NOT AVAILABLE",
-        "marker symbol: triangle-down-dot<br>NOT AVAILABLE",
-        "marker symbol: triangle-left-dot<br>NOT AVAILABLE",
-        "marker symbol: triangle-right-dot<br>NOT AVAILABLE",
-        "marker symbol: triangle-ne-dot<br>NOT AVAILABLE",
-        "marker symbol: triangle-se-dot<br>NOT AVAILABLE",
-        "marker symbol: triangle-sw-dot<br>NOT AVAILABLE",
-        "marker symbol: triangle-nw-dot<br>NOT AVAILABLE",
-        "marker symbol: pentagon-dot<br>NOT AVAILABLE",
-        "marker symbol: hexagon-dot<br>NOT AVAILABLE",
-        "marker symbol: hexagon2-dot<br>NOT AVAILABLE",
-        "marker symbol: octagon-dot<br>NOT AVAILABLE",
-        "marker symbol: star-dot<br>NOT AVAILABLE",
-        "marker symbol: hexagram-dot<br>NOT AVAILABLE",
-        "marker symbol: star-triangle-up-dot<br>NOT AVAILABLE",
-        "marker symbol: star-triangle-down-dot<br>NOT AVAILABLE",
-        "marker symbol: star-square-dot<br>NOT AVAILABLE",
-        "marker symbol: star-diamond-dot<br>NOT AVAILABLE",
-        "marker symbol: diamond-tall-dot<br>NOT AVAILABLE",
-        "marker symbol: diamond-wide-dot<br>NOT AVAILABLE",
-        "marker symbol: hourglass-dot<br>NOT AVAILABLE",
-        "marker symbol: bowtie-dot<br>NOT AVAILABLE",
-        "marker symbol: circle-cross-dot<br>NOT AVAILABLE",
-        "marker symbol: circle-x-dot<br>NOT AVAILABLE",
-        "marker symbol: square-cross-dot<br>NOT AVAILABLE",
-        "marker symbol: square-x-dot<br>NOT AVAILABLE",
-        "marker symbol: diamond-cross-dot<br>NOT AVAILABLE",
-        "marker symbol: diamond-x-dot<br>NOT AVAILABLE",
-        "marker symbol: cross-thin-dot<br>NOT AVAILABLE",
-        "marker symbol: x-thin-dot<br>NOT AVAILABLE",
-        "marker symbol: asterisk-dot<br>NOT AVAILABLE",
-        "marker symbol: hash-dot<br>NOT AVAILABLE",
-        "marker symbol: y-up-dot<br>NOT AVAILABLE",
-        "marker symbol: y-down-dot<br>NOT AVAILABLE",
-        "marker symbol: y-left-dot<br>NOT AVAILABLE",
-        "marker symbol: y-right-dot<br>NOT AVAILABLE",
-        "marker symbol: line-ew-dot<br>NOT AVAILABLE",
-        "marker symbol: line-ns-dot<br>NOT AVAILABLE",
-        "marker symbol: line-ne-dot<br>NOT AVAILABLE",
-        "marker symbol: line-nw-dot<br>NOT AVAILABLE"
+        "marker symbol: circle-dot<br>number: 200",
+        "marker symbol: square-dot<br>number: 201",
+        "marker symbol: diamond-dot<br>number: 202",
+        "marker symbol: cross-dot<br>number: 203",
+        "marker symbol: x-dot<br>number: 204",
+        "marker symbol: triangle-up-dot<br>number: 205",
+        "marker symbol: triangle-down-dot<br>number: 206",
+        "marker symbol: triangle-left-dot<br>number: 207",
+        "marker symbol: triangle-right-dot<br>number: 208",
+        "marker symbol: triangle-ne-dot<br>number: 209",
+        "marker symbol: triangle-se-dot<br>number: 210",
+        "marker symbol: triangle-sw-dot<br>number: 211",
+        "marker symbol: triangle-nw-dot<br>number: 212",
+        "marker symbol: pentagon-dot<br>number: 213",
+        "marker symbol: hexagon-dot<br>number: 214",
+        "marker symbol: hexagon2-dot<br>number: 215",
+        "marker symbol: octagon-dot<br>number: 216",
+        "marker symbol: star-dot<br>number: 217",
+        "marker symbol: hexagram-dot<br>number: 218",
+        "marker symbol: star-triangle-up-dot<br>number: 219",
+        "marker symbol: star-triangle-down-dot<br>number: 220",
+        "marker symbol: star-square-dot<br>number: 221",
+        "marker symbol: star-diamond-dot<br>number: 222",
+        "marker symbol: diamond-tall-dot<br>number: 223",
+        "marker symbol: diamond-wide-dot<br>number: 224",
+        "marker symbol: hourglass-dot<br>number: 225",
+        "marker symbol: bowtie-dot<br>number: 226",
+        "marker symbol: circle-cross-dot<br>number: 227",
+        "marker symbol: circle-x-dot<br>number: 228",
+        "marker symbol: square-cross-dot<br>number: 229",
+        "marker symbol: square-x-dot<br>number: 230",
+        "marker symbol: diamond-cross-dot<br>number: 231",
+        "marker symbol: diamond-x-dot<br>number: 232",
+        "marker symbol: cross-thin-dot<br>number: 233",
+        "marker symbol: x-thin-dot<br>number: 234",
+        "marker symbol: asterisk-dot<br>number: 235",
+        "marker symbol: hash-dot<br>number: 236",
+        "marker symbol: y-up-dot<br>number: 237",
+        "marker symbol: y-down-dot<br>number: 238",
+        "marker symbol: y-left-dot<br>number: 239",
+        "marker symbol: y-right-dot<br>number: 240",
+        "marker symbol: line-ew-dot<br>number: 241",
+        "marker symbol: line-ns-dot<br>number: 242",
+        "marker symbol: line-ne-dot<br>number: 243",
+        "marker symbol: line-nw-dot<br>number: 244"
       ],
       "hoverinfo": "text"
     },
@@ -745,98 +367,18 @@
       "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
+        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
       ],
       "y": [
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0,
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0,
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0,
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0,
-        8,
-        7,
-        6,
-        5,
-        4,
-        3,
-        2,
-        1,
-        0
+        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
       ],
       "marker": {
         "symbol": [
@@ -887,104 +429,58 @@
           "line-nw-open-dot"
         ],
         "color": "blue",
-        "size": [
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1,
-          1
-        ],
+        "size": 20,
         "line": {
           "color": "orange",
           "width": 1.5
         }
       },
       "text": [
-        "marker symbol: circle-open-dot<br>NOT AVAILABLE",
-        "marker symbol: square-open-dot<br>NOT AVAILABLE",
-        "marker symbol: diamond-open-dot<br>NOT AVAILABLE",
-        "marker symbol: cross-open-dot<br>NOT AVAILABLE",
-        "marker symbol: x-open-dot<br>NOT AVAILABLE",
-        "marker symbol: triangle-up-open-dot<br>NOT AVAILABLE",
-        "marker symbol: triangle-down-open-dot<br>NOT AVAILABLE",
-        "marker symbol: triangle-left-open-dot<br>NOT AVAILABLE",
-        "marker symbol: triangle-right-open-dot<br>NOT AVAILABLE",
-        "marker symbol: triangle-ne-open-dot<br>NOT AVAILABLE",
-        "marker symbol: triangle-se-open-dot<br>NOT AVAILABLE",
-        "marker symbol: triangle-sw-open-dot<br>NOT AVAILABLE",
-        "marker symbol: triangle-nw-open-dot<br>NOT AVAILABLE",
-        "marker symbol: pentagon-open-dot<br>NOT AVAILABLE",
-        "marker symbol: hexagon-open-dot<br>NOT AVAILABLE",
-        "marker symbol: hexagon2-open-dot<br>NOT AVAILABLE",
-        "marker symbol: octagon-open-dot<br>NOT AVAILABLE",
-        "marker symbol: star-open-dot<br>NOT AVAILABLE",
-        "marker symbol: hexagram-open-dot<br>NOT AVAILABLE",
-        "marker symbol: star-triangle-up-open-dot<br>NOT AVAILABLE",
-        "marker symbol: star-triangle-down-open-dot<br>NOT AVAILABLE",
-        "marker symbol: star-square-open-dot<br>NOT AVAILABLE",
-        "marker symbol: star-diamond-open-dot<br>NOT AVAILABLE",
-        "marker symbol: diamond-tall-open-dot<br>NOT AVAILABLE",
-        "marker symbol: diamond-wide-open-dot<br>NOT AVAILABLE",
-        "marker symbol: hourglass-open-dot<br>NOT AVAILABLE",
-        "marker symbol: bowtie-open-dot<br>NOT AVAILABLE",
-        "marker symbol: circle-cross-open-dot<br>NOT AVAILABLE",
-        "marker symbol: circle-x-open-dot<br>NOT AVAILABLE",
-        "marker symbol: square-cross-open-dot<br>NOT AVAILABLE",
-        "marker symbol: square-x-open-dot<br>NOT AVAILABLE",
-        "marker symbol: diamond-cross-open-dot<br>NOT AVAILABLE",
-        "marker symbol: diamond-x-open-dot<br>NOT AVAILABLE",
-        "marker symbol: cross-thin-open-dot<br>NOT AVAILABLE",
-        "marker symbol: x-thin-open-dot<br>NOT AVAILABLE",
-        "marker symbol: asterisk-open-dot<br>NOT AVAILABLE",
-        "marker symbol: hash-open-dot<br>NOT AVAILABLE",
-        "marker symbol: y-up-open-dot<br>NOT AVAILABLE",
-        "marker symbol: y-down-open-dot<br>NOT AVAILABLE",
-        "marker symbol: y-left-open-dot<br>NOT AVAILABLE",
-        "marker symbol: y-right-open-dot<br>NOT AVAILABLE",
-        "marker symbol: line-ew-open-dot<br>NOT AVAILABLE",
-        "marker symbol: line-ns-open-dot<br>NOT AVAILABLE",
-        "marker symbol: line-ne-open-dot<br>NOT AVAILABLE",
-        "marker symbol: line-nw-open-dot<br>NOT AVAILABLE"
+        "marker symbol: circle-open-dot<br>number: 300",
+        "marker symbol: square-open-dot<br>number: 301",
+        "marker symbol: diamond-open-dot<br>number: 302",
+        "marker symbol: cross-open-dot<br>number: 303",
+        "marker symbol: x-open-dot<br>number: 304",
+        "marker symbol: triangle-up-open-dot<br>number: 305",
+        "marker symbol: triangle-down-open-dot<br>number: 306",
+        "marker symbol: triangle-left-open-dot<br>number: 307",
+        "marker symbol: triangle-right-open-dot<br>number: 308",
+        "marker symbol: triangle-ne-open-dot<br>number: 309",
+        "marker symbol: triangle-se-open-dot<br>number: 310",
+        "marker symbol: triangle-sw-open-dot<br>number: 311",
+        "marker symbol: triangle-nw-open-dot<br>number: 312",
+        "marker symbol: pentagon-open-dot<br>number: 313",
+        "marker symbol: hexagon-open-dot<br>number: 314",
+        "marker symbol: hexagon2-open-dot<br>number: 315",
+        "marker symbol: octagon-open-dot<br>number: 316",
+        "marker symbol: star-open-dot<br>number: 317",
+        "marker symbol: hexagram-open-dot<br>number: 318",
+        "marker symbol: star-triangle-up-open-dot<br>number: 319",
+        "marker symbol: star-triangle-down-open-dot<br>number: 320",
+        "marker symbol: star-square-open-dot<br>number: 321",
+        "marker symbol: star-diamond-open-dot<br>number: 322",
+        "marker symbol: diamond-tall-open-dot<br>number: 323",
+        "marker symbol: diamond-wide-open-dot<br>number: 324",
+        "marker symbol: hourglass-open-dot<br>number: 325",
+        "marker symbol: bowtie-open-dot<br>number: 326",
+        "marker symbol: circle-cross-open-dot<br>number: 327",
+        "marker symbol: circle-x-open-dot<br>number: 328",
+        "marker symbol: square-cross-open-dot<br>number: 329",
+        "marker symbol: square-x-open-dot<br>number: 330",
+        "marker symbol: diamond-cross-open-dot<br>number: 331",
+        "marker symbol: diamond-x-open-dot<br>number: 332",
+        "marker symbol: cross-thin-open-dot<br>number: 333",
+        "marker symbol: x-thin-open-dot<br>number: 334",
+        "marker symbol: asterisk-open-dot<br>number: 335",
+        "marker symbol: hash-open-dot<br>number: 336",
+        "marker symbol: y-up-open-dot<br>number: 337",
+        "marker symbol: y-down-open-dot<br>number: 338",
+        "marker symbol: y-left-open-dot<br>number: 339",
+        "marker symbol: y-right-open-dot<br>number: 340",
+        "marker symbol: line-ew-open-dot<br>number: 341",
+        "marker symbol: line-ns-open-dot<br>number: 342",
+        "marker symbol: line-ne-open-dot<br>number: 343",
+        "marker symbol: line-nw-open-dot<br>number: 344"
       ],
       "hoverinfo": "text"
     }
@@ -1004,26 +500,11 @@
     },
     "yaxis": {
       "showgrid": false,
-      "zeroline": false
+      "zeroline": false,
+      "autorange": "reversed"
     },
     "showlegend": false,
-    "hovermode": "closest",
     "plot_bgcolor": "#d3d3d3",
-    "annotations": [
-      {
-        "showarrow": false,
-        "xref": "paper",
-        "yref": "paper",
-        "x": 1,
-        "xanchor": "right",
-        "y": 0.5,
-        "yanchor": "middle",
-        "xshift": -15,
-        "text": "<b>IMPORTANT</b>: marker symbol 'x' and 'x-open'<br>do not render in the imagetest container",
-        "font": {
-          "size": 16
-        }
-      }
-    ]
+    "hovermode": "closest"
   }
 }
diff --git a/test/image/mocks/gl2d_multiple-traces-axes-labels.json b/test/image/mocks/gl2d_multiple-traces-axes-labels.json
new file mode 100644
index 00000000000..8fef3c2a7e7
--- /dev/null
+++ b/test/image/mocks/gl2d_multiple-traces-axes-labels.json
@@ -0,0 +1,64 @@
+{
+    "data": [
+        {
+            "x": [
+                0,
+                1,
+                2
+            ],
+            "y": [
+                0,
+                1,
+                2
+            ],
+            "mode": "lines+markers",
+            "type": "scattergl"
+        },
+        {
+            "x": [
+                0,
+                1,
+                2
+            ],
+            "y": [
+                2,
+                1,
+                0
+            ],
+            "mode": "lines+markers",
+            "type": "scattergl",
+            "yaxis": "y2",
+            "xaxis": "x2"
+        }
+    ],
+    "layout": {
+        "width": 700,
+        "height": 500,
+        "showlegend": false,
+        "yaxis": {
+            "side": "right",
+            "tickmode": "array",
+            "ticktext": ["RightZero", "RightOne", "RightTwo"],
+            "tickvals": [0,1,2]
+        },
+        "yaxis2": {
+            "side": "left",
+            "tickmode": "array",
+            "ticktext": ["LeftZero", "LeftOne", "LeftTwo"],
+            "tickvals": [0,1,2],
+            "overlaying": "y"
+        },
+        "xaxis": {
+            "side": "top",
+            "tickmode": "array",
+            "ticktext": ["TopZero", "TopOne", "TopTwo"],
+            "tickvals": [0,1,2]
+        },
+        "xaxis2": {
+            "side": "bottom",
+            "tickmode": "array",
+            "ticktext": ["BottomZero", "BottomOne", "BottomTwo"],
+            "tickvals": [0,1,2]
+        }
+    }
+}
diff --git a/test/image/mocks/gl2d_multiple-traces-axes.json b/test/image/mocks/gl2d_multiple-traces-axes.json
new file mode 100644
index 00000000000..4818a13964c
--- /dev/null
+++ b/test/image/mocks/gl2d_multiple-traces-axes.json
@@ -0,0 +1,45 @@
+{
+    "data": [
+        {
+            "x": [
+                0,
+                1,
+                2
+            ],
+            "y": [
+                0,
+                1,
+                2
+            ],
+            "mode": "lines+markers",
+            "type": "scattergl"
+        },
+        {
+            "x": [
+                0,
+                1,
+                2
+            ],
+            "y": [
+                2,
+                1,
+                0
+            ],
+            "mode": "lines+markers",
+            "type": "scattergl",
+            "yaxis": "y2"
+        }
+    ],
+    "layout": {
+        "width": 700,
+        "height": 500,
+        "showlegend": false,
+        "yaxis": {
+            "side": "left"
+        },
+        "yaxis2": {
+            "side": "right",
+            "overlaying": "y"
+        }
+    }
+}
diff --git a/test/image/mocks/gl2d_open_marker_line_width.json b/test/image/mocks/gl2d_open_marker_line_width.json
new file mode 100644
index 00000000000..1b85cb2d9a3
--- /dev/null
+++ b/test/image/mocks/gl2d_open_marker_line_width.json
@@ -0,0 +1,12 @@
+{
+  "data": [{
+    "y": [1, 2, 3],
+    "marker": {"symbol": "circle-open", "size": 20},
+    "type": "scattergl",
+    "mode": "markers"
+  }],
+  "layout": {
+    "height": 400,
+    "width": 480
+  }
+}
diff --git a/test/image/mocks/gl2d_scatter-colorscale-points.json b/test/image/mocks/gl2d_scatter-colorscale-points.json
new file mode 100644
index 00000000000..71610e14304
--- /dev/null
+++ b/test/image/mocks/gl2d_scatter-colorscale-points.json
@@ -0,0 +1,1237 @@
+{
+  "data": [
+    {
+      "x": [
+        0,
+        1,
+        2,
+        3,
+        4,
+        5,
+        6,
+        7,
+        8,
+        9,
+        10,
+        11,
+        12,
+        13,
+        14,
+        15,
+        16,
+        17,
+        18,
+        19,
+        20,
+        21,
+        22,
+        23,
+        24,
+        25,
+        26,
+        27,
+        28,
+        29,
+        30,
+        31,
+        32,
+        33,
+        34,
+        35,
+        36,
+        37,
+        38,
+        39,
+        40,
+        41,
+        42,
+        43,
+        44,
+        45,
+        46,
+        47,
+        48,
+        49,
+        50,
+        51,
+        52,
+        53,
+        54,
+        55,
+        56,
+        57,
+        58,
+        59,
+        60,
+        61,
+        62,
+        63,
+        64,
+        65,
+        66,
+        67,
+        68,
+        69,
+        70,
+        71,
+        72,
+        73,
+        74,
+        75,
+        76,
+        77,
+        78,
+        79,
+        80,
+        81,
+        82,
+        83,
+        84,
+        85,
+        86,
+        87,
+        88,
+        89,
+        90,
+        91,
+        92,
+        93,
+        94,
+        95,
+        96,
+        97,
+        98,
+        99,
+        100,
+        101,
+        102,
+        103,
+        104,
+        105,
+        106,
+        107,
+        108,
+        109,
+        110,
+        111,
+        112,
+        113,
+        114,
+        115,
+        116,
+        117,
+        118,
+        119,
+        120,
+        121,
+        122,
+        123,
+        124,
+        125,
+        126,
+        127,
+        128,
+        129,
+        130,
+        131,
+        132,
+        133,
+        134,
+        135,
+        136,
+        137,
+        138,
+        139,
+        140,
+        141,
+        142,
+        143,
+        144,
+        145,
+        146,
+        147,
+        148,
+        149,
+        150,
+        151,
+        152,
+        153,
+        154,
+        155,
+        156,
+        157,
+        158,
+        159,
+        160,
+        161,
+        162,
+        163,
+        164,
+        165,
+        166,
+        167,
+        168,
+        169,
+        170,
+        171,
+        172,
+        173,
+        174,
+        175,
+        176,
+        177,
+        178,
+        179,
+        180,
+        181,
+        182,
+        183,
+        184,
+        185,
+        186,
+        187,
+        188,
+        189,
+        190,
+        191,
+        192,
+        193,
+        194,
+        195,
+        196,
+        197,
+        198,
+        199,
+        200,
+        201,
+        202,
+        203,
+        204,
+        205,
+        206,
+        207,
+        208,
+        209,
+        210,
+        211,
+        212,
+        213,
+        214,
+        215,
+        216,
+        217,
+        218,
+        219,
+        220,
+        221,
+        222,
+        223,
+        224,
+        225,
+        226,
+        227,
+        228,
+        229,
+        230,
+        231,
+        232,
+        233,
+        234,
+        235,
+        236,
+        237,
+        238,
+        239,
+        240,
+        241,
+        242,
+        243,
+        244,
+        245,
+        246,
+        247,
+        248,
+        249,
+        250,
+        251,
+        252,
+        253,
+        254,
+        255,
+        256,
+        257,
+        258,
+        259,
+        260,
+        261,
+        262,
+        263,
+        264,
+        265,
+        266,
+        267,
+        268,
+        269,
+        270,
+        271,
+        272,
+        273,
+        274,
+        275,
+        276,
+        277,
+        278,
+        279,
+        280,
+        281,
+        282,
+        283,
+        284,
+        285,
+        286,
+        287,
+        288,
+        289,
+        290,
+        291,
+        292,
+        293,
+        294,
+        295,
+        296,
+        297,
+        298,
+        299,
+        300,
+        301,
+        302,
+        303,
+        304,
+        305,
+        306,
+        307,
+        308,
+        309,
+        310,
+        311,
+        312,
+        313,
+        314,
+        315,
+        316,
+        317,
+        318,
+        319,
+        320,
+        321,
+        322,
+        323,
+        324,
+        325,
+        326,
+        327,
+        328,
+        329,
+        330,
+        331,
+        332,
+        333,
+        334,
+        335,
+        336,
+        337,
+        338,
+        339,
+        340,
+        341,
+        342,
+        343,
+        344,
+        345,
+        346,
+        347,
+        348,
+        349,
+        350,
+        351,
+        352,
+        353,
+        354,
+        355,
+        356,
+        357,
+        358,
+        359,
+        360,
+        361,
+        362,
+        363,
+        364,
+        365,
+        366,
+        367,
+        368,
+        369,
+        370,
+        371,
+        372,
+        373,
+        374,
+        375,
+        376,
+        377,
+        378,
+        379,
+        380,
+        381,
+        382,
+        383,
+        384,
+        385,
+        386,
+        387,
+        388,
+        389,
+        390,
+        391,
+        392,
+        393,
+        394,
+        395,
+        396,
+        397,
+        398,
+        399
+      ],
+      "y": [
+        0.4616274593988976,
+        0.6159658554953507,
+        0.14733135643810646,
+        0.5667641205161871,
+        0.7354441683995987,
+        0.09338282979231982,
+        0.9903377749890259,
+        0.5374308078056822,
+        0.9277476505822664,
+        0.08323004128218803,
+        0.9262424431459373,
+        0.07721861833625465,
+        0.316806534455903,
+        0.6905645938270328,
+        0.37499535022676067,
+        0.5237723768049043,
+        0.07464909758755889,
+        0.7077805788454765,
+        0.45225009860017584,
+        0.6674148981584085,
+        0.5628508733412807,
+        0.8018137470292497,
+        0.343843747049178,
+        0.9852293530378662,
+        0.1066338394930828,
+        0.24260373567303994,
+        0.2581380475938495,
+        0.057177653812069185,
+        0.35629696373744535,
+        0.7436452620337728,
+        0.6692344231471397,
+        0.5949462020755851,
+        0.6576636129468314,
+        0.019977683605029606,
+        0.7754966777776147,
+        0.3409475469762202,
+        0.10505752857151562,
+        0.5382664472162011,
+        0.23422301844320104,
+        0.23120408514083857,
+        0.17626801625836364,
+        0.9591028652096454,
+        0.08931992353086238,
+        0.3932366071090292,
+        0.39540802372591943,
+        0.4470022120346193,
+        0.4082122355215174,
+        0.28267768821700145,
+        0.8055901137799373,
+        0.45208617372232274,
+        0.39706239364332907,
+        0.631886787018654,
+        0.14090506326963959,
+        0.6371557878101777,
+        0.8693697292009528,
+        0.9103872263778481,
+        0.07206796438983165,
+        0.7696288091366807,
+        0.6904741191669677,
+        0.44478304308488803,
+        0.29879600726642175,
+        0.06813133964789775,
+        0.46375344844705224,
+        0.6361148045916376,
+        0.3714141768131616,
+        0.4689894633900966,
+        0.05376791543207715,
+        0.9582074400644287,
+        0.14149996361590467,
+        0.9626720891064184,
+        0.1598460224513516,
+        0.3109994711314612,
+        0.4510794057508736,
+        0.9752152356511024,
+        0.962017602946641,
+        0.04206549382806779,
+        0.4271502037227095,
+        0.7338711489031002,
+        0.19179244410313867,
+        0.12479799491908894,
+        0.40058917261911997,
+        0.10793111399724986,
+        0.9506121571546589,
+        0.9980834693872784,
+        0.2616729510381006,
+        0.19918907515056117,
+        0.26124827064167366,
+        0.04700351146651194,
+        0.643077267949713,
+        0.16204159494738213,
+        0.9389644666328048,
+        0.8190032324635506,
+        0.5914276573736743,
+        0.08259525135228696,
+        0.48296177521703476,
+        0.558865410234807,
+        0.5216047222486184,
+        0.8388268216175738,
+        0.26564121945521646,
+        0.2642720521826212,
+        0.7058731057826744,
+        0.9821568189706535,
+        0.39951561657266943,
+        0.9905094625707764,
+        0.8450910732070214,
+        0.9492521533827545,
+        0.26888799320812673,
+        0.911118018018567,
+        0.10742216581354813,
+        0.17801260994893053,
+        0.6280583783264748,
+        0.5385121890208695,
+        0.9079632784981913,
+        0.12680855373314448,
+        0.12770805911064564,
+        0.25995662925105845,
+        0.4296569502097418,
+        0.837785176224509,
+        0.643330550187764,
+        0.6808400004172752,
+        0.458710827955672,
+        0.16009454133122136,
+        0.8202231073239015,
+        0.9053967045598783,
+        0.5365994711573248,
+        0.9960075151497787,
+        0.02510969842621913,
+        0.35105842745514293,
+        0.8849890732014707,
+        0.7573538142230878,
+        0.17956914547596736,
+        0.08647183700547911,
+        0.779984937808065,
+        0.6690858604662955,
+        0.1825514085080555,
+        0.7359140104581448,
+        0.4832450064132081,
+        0.004680006424105487,
+        0.054129657907077355,
+        0.9182711683702454,
+        0.17426199250373742,
+        0.7853499017423882,
+        0.2392366970036739,
+        0.7045245458632703,
+        0.7397575678970407,
+        0.5808965620927102,
+        0.1573642283554464,
+        0.7838078499029744,
+        0.022809643682487835,
+        0.25947607091712444,
+        0.07927158226754316,
+        0.7572333743691615,
+        0.3480185053274718,
+        0.5025769378049088,
+        0.6349254301237339,
+        0.44220130982305283,
+        0.2941873838636848,
+        0.8658457980943166,
+        0.33718988202636924,
+        0.36399771149412774,
+        0.2557140683022996,
+        0.4260817100484571,
+        0.12621985914398337,
+        0.620518125979105,
+        0.71987614384816,
+        0.8003673309522548,
+        0.9996415897476298,
+        0.06719734135151567,
+        0.9322866387654336,
+        0.9283012232007972,
+        0.08059383278114773,
+        0.3063677423829876,
+        0.08986664990867044,
+        0.0975448245056838,
+        0.7379632992176663,
+        0.03416009401780329,
+        0.8624901126846831,
+        0.09334710918984412,
+        0.6351625533840559,
+        0.9342129690958965,
+        0.08816460719920438,
+        0.6500019434623974,
+        0.13755828831488137,
+        0.7445637838163035,
+        0.6771472592740897,
+        0.8693468361877688,
+        0.28271259888642786,
+        0.9471270953710687,
+        0.6432180599730659,
+        0.8881795753841952,
+        0.7497327903558746,
+        0.20031859209199676,
+        0.4889183672926736,
+        0.44223497603677986,
+        0.07762978987423819,
+        0.9698795565530265,
+        0.8720673433469355,
+        0.5848448655198397,
+        0.6093877444520419,
+        0.2600479448594426,
+        0.8961071076491984,
+        0.7827239150324312,
+        0.03243647484491774,
+        0.4347393417626588,
+        0.48205770886363086,
+        0.1407579077330765,
+        0.8570455272102135,
+        0.5991507866565635,
+        0.07544671160677918,
+        0.7653429124104552,
+        0.6773009306766822,
+        0.45537431330712064,
+        0.8142623987213244,
+        0.12005385753629261,
+        0.6280673349705737,
+        0.6411165107241685,
+        0.6484334035034007,
+        0.7662279929095923,
+        0.37726416856525136,
+        0.17609887569284655,
+        0.38801140570442905,
+        0.39754218864115765,
+        0.9257069667188491,
+        0.22229435308889123,
+        0.8100035792813067,
+        0.6308637969856226,
+        0.3958087729357176,
+        0.5115743574773932,
+        0.4243398691996936,
+        0.07883717817748748,
+        0.15170830509978162,
+        0.2816360723559719,
+        0.9711659817994986,
+        0.044461230265380625,
+        0.821238189366148,
+        0.5395859183099476,
+        0.2722803990279281,
+        0.4237742209137838,
+        0.8492378365236639,
+        0.07052566067182942,
+        0.7384299618235564,
+        0.44307968171108536,
+        0.626851435775337,
+        0.8141433753393437,
+        0.7716442985320457,
+        0.052922587778580166,
+        0.9838041253840564,
+        0.07522072756748854,
+        0.09932648402162414,
+        0.8632757907071749,
+        0.2438048746885606,
+        0.6994226198340694,
+        0.17754833982132778,
+        0.27249382866046634,
+        0.953726012453473,
+        0.6496793216100725,
+        0.6595413735302542,
+        0.6020029422536388,
+        0.7164563260483665,
+        0.5076768549802999,
+        0.049402208839229855,
+        0.8704399548068575,
+        0.8183909638112867,
+        0.6246196275834262,
+        0.34055277199342293,
+        0.6375528626944473,
+        0.2176538874850178,
+        0.8855950471919678,
+        0.2729897053353294,
+        0.922898222002597,
+        0.34124823862742093,
+        0.7724731409980496,
+        0.9301356652479389,
+        0.5069872906570465,
+        0.8907391661563082,
+        0.32950557196005503,
+        0.985746097010779,
+        0.9887326595275832,
+        0.8708031335294233,
+        0.4581414968403985,
+        0.6417776426968713,
+        0.1988895008216951,
+        0.14201433649723483,
+        0.2519165771331364,
+        0.4893264293228885,
+        0.5938184986886181,
+        0.9915985915641636,
+        0.07932976885305809,
+        0.3177939359004449,
+        0.021452758251003567,
+        0.13933128825095897,
+        0.5726480825059779,
+        0.5189949016845317,
+        0.6891035069799827,
+        0.49304429113395964,
+        0.6619469056212011,
+        0.15392767997249202,
+        0.295502756789187,
+        0.9836316237991622,
+        0.9852703411276889,
+        0.5335280196070722,
+        0.014950690276562373,
+        0.27343049990512514,
+        0.6808495149772484,
+        0.9449623361180979,
+        0.5815473574186956,
+        0.6876907395597738,
+        0.6074256288551354,
+        0.6616727427389402,
+        0.36095501815680064,
+        0.41762697729996634,
+        0.3068641146519695,
+        0.333544545080694,
+        0.2515657935855262,
+        0.705714848144513,
+        0.5255521635430529,
+        0.4822159228974139,
+        0.6217747511283676,
+        0.08087460026706172,
+        0.038418823857711226,
+        0.191238850363338,
+        0.772025062029148,
+        0.5836617926964085,
+        0.6257770436229244,
+        0.3360858834698621,
+        0.07102860301201153,
+        0.35231606761455847,
+        0.4395219250109519,
+        0.29145547851709463,
+        0.3828891018746814,
+        0.1586315392361195,
+        0.681625950179632,
+        0.6390249571272832,
+        0.18127915456338606,
+        0.3074673692068177,
+        0.11409699865595568,
+        0.2084284233791489,
+        0.97923423111305,
+        0.39726880385758756,
+        0.1507803451137819,
+        0.7071508081014066,
+        0.6789899906980974,
+        0.7504243731249811,
+        0.3250115192256111,
+        0.08257633416391008,
+        0.5806999030604296,
+        0.7620316920198831,
+        0.4035009404475842,
+        0.3068822262867634,
+        0.3445143919468354,
+        0.5711454787289016,
+        0.1449634076508688,
+        0.8695309515694036,
+        0.8570541881803591,
+        0.898864374325179,
+        0.8192475103851975,
+        0.47431367407308067,
+        0.8428434350964595,
+        0.8701949713056438,
+        0.5048752773887357,
+        0.35052839245352385,
+        0.11810761952684778,
+        0.08778526548004928,
+        0.6372428709171547,
+        0.9888725923474206,
+        0.2864261958904437,
+        0.07213482061244814,
+        0.15845866642053563,
+        0.5062299144844185,
+        0.5746856612680256,
+        0.8659909684424811,
+        0.8414353824886924,
+        0.7672879096854097,
+        0.06459741136385855,
+        0.3604498875597726,
+        0.6006588577364183,
+        0.056856595390261555,
+        0.7016467737931367,
+        0.2123993086079614,
+        0.7132617466759801,
+        0.007242742044783146,
+        0.13564612958400502,
+        0.33005333741350995,
+        0.736021023245836,
+        0.9406546989427267,
+        0.2925280225016629,
+        0.2655802942570038,
+        0.016877181546108178,
+        0.15929715792991916,
+        0.6391938328375499,
+        0.6329167268211631,
+        0.4985094645037693,
+        0.7167806799705279,
+        0.8531428725877723,
+        0.6405531773686648,
+        0.377705624398585,
+        0.13785533323928845,
+        0.33942934167686034,
+        0.4536063371227177,
+        0.34963259129971425
+      ],
+      "mode": "markers",
+      "marker": {
+        "size": 5,
+        "color": [
+          0.4616274593988976,
+          0.6159658554953507,
+          0.14733135643810646,
+          0.5667641205161871,
+          0.7354441683995987,
+          0.09338282979231982,
+          0.9903377749890259,
+          0.5374308078056822,
+          0.9277476505822664,
+          0.08323004128218803,
+          0.9262424431459373,
+          0.07721861833625465,
+          0.316806534455903,
+          0.6905645938270328,
+          0.37499535022676067,
+          0.5237723768049043,
+          0.07464909758755889,
+          0.7077805788454765,
+          0.45225009860017584,
+          0.6674148981584085,
+          0.5628508733412807,
+          0.8018137470292497,
+          0.343843747049178,
+          0.9852293530378662,
+          0.1066338394930828,
+          0.24260373567303994,
+          0.2581380475938495,
+          0.057177653812069185,
+          0.35629696373744535,
+          0.7436452620337728,
+          0.6692344231471397,
+          0.5949462020755851,
+          0.6576636129468314,
+          0.019977683605029606,
+          0.7754966777776147,
+          0.3409475469762202,
+          0.10505752857151562,
+          0.5382664472162011,
+          0.23422301844320104,
+          0.23120408514083857,
+          0.17626801625836364,
+          0.9591028652096454,
+          0.08931992353086238,
+          0.3932366071090292,
+          0.39540802372591943,
+          0.4470022120346193,
+          0.4082122355215174,
+          0.28267768821700145,
+          0.8055901137799373,
+          0.45208617372232274,
+          0.39706239364332907,
+          0.631886787018654,
+          0.14090506326963959,
+          0.6371557878101777,
+          0.8693697292009528,
+          0.9103872263778481,
+          0.07206796438983165,
+          0.7696288091366807,
+          0.6904741191669677,
+          0.44478304308488803,
+          0.29879600726642175,
+          0.06813133964789775,
+          0.46375344844705224,
+          0.6361148045916376,
+          0.3714141768131616,
+          0.4689894633900966,
+          0.05376791543207715,
+          0.9582074400644287,
+          0.14149996361590467,
+          0.9626720891064184,
+          0.1598460224513516,
+          0.3109994711314612,
+          0.4510794057508736,
+          0.9752152356511024,
+          0.962017602946641,
+          0.04206549382806779,
+          0.4271502037227095,
+          0.7338711489031002,
+          0.19179244410313867,
+          0.12479799491908894,
+          0.40058917261911997,
+          0.10793111399724986,
+          0.9506121571546589,
+          0.9980834693872784,
+          0.2616729510381006,
+          0.19918907515056117,
+          0.26124827064167366,
+          0.04700351146651194,
+          0.643077267949713,
+          0.16204159494738213,
+          0.9389644666328048,
+          0.8190032324635506,
+          0.5914276573736743,
+          0.08259525135228696,
+          0.48296177521703476,
+          0.558865410234807,
+          0.5216047222486184,
+          0.8388268216175738,
+          0.26564121945521646,
+          0.2642720521826212,
+          0.7058731057826744,
+          0.9821568189706535,
+          0.39951561657266943,
+          0.9905094625707764,
+          0.8450910732070214,
+          0.9492521533827545,
+          0.26888799320812673,
+          0.911118018018567,
+          0.10742216581354813,
+          0.17801260994893053,
+          0.6280583783264748,
+          0.5385121890208695,
+          0.9079632784981913,
+          0.12680855373314448,
+          0.12770805911064564,
+          0.25995662925105845,
+          0.4296569502097418,
+          0.837785176224509,
+          0.643330550187764,
+          0.6808400004172752,
+          0.458710827955672,
+          0.16009454133122136,
+          0.8202231073239015,
+          0.9053967045598783,
+          0.5365994711573248,
+          0.9960075151497787,
+          0.02510969842621913,
+          0.35105842745514293,
+          0.8849890732014707,
+          0.7573538142230878,
+          0.17956914547596736,
+          0.08647183700547911,
+          0.779984937808065,
+          0.6690858604662955,
+          0.1825514085080555,
+          0.7359140104581448,
+          0.4832450064132081,
+          0.004680006424105487,
+          0.054129657907077355,
+          0.9182711683702454,
+          0.17426199250373742,
+          0.7853499017423882,
+          0.2392366970036739,
+          0.7045245458632703,
+          0.7397575678970407,
+          0.5808965620927102,
+          0.1573642283554464,
+          0.7838078499029744,
+          0.022809643682487835,
+          0.25947607091712444,
+          0.07927158226754316,
+          0.7572333743691615,
+          0.3480185053274718,
+          0.5025769378049088,
+          0.6349254301237339,
+          0.44220130982305283,
+          0.2941873838636848,
+          0.8658457980943166,
+          0.33718988202636924,
+          0.36399771149412774,
+          0.2557140683022996,
+          0.4260817100484571,
+          0.12621985914398337,
+          0.620518125979105,
+          0.71987614384816,
+          0.8003673309522548,
+          0.9996415897476298,
+          0.06719734135151567,
+          0.9322866387654336,
+          0.9283012232007972,
+          0.08059383278114773,
+          0.3063677423829876,
+          0.08986664990867044,
+          0.0975448245056838,
+          0.7379632992176663,
+          0.03416009401780329,
+          0.8624901126846831,
+          0.09334710918984412,
+          0.6351625533840559,
+          0.9342129690958965,
+          0.08816460719920438,
+          0.6500019434623974,
+          0.13755828831488137,
+          0.7445637838163035,
+          0.6771472592740897,
+          0.8693468361877688,
+          0.28271259888642786,
+          0.9471270953710687,
+          0.6432180599730659,
+          0.8881795753841952,
+          0.7497327903558746,
+          0.20031859209199676,
+          0.4889183672926736,
+          0.44223497603677986,
+          0.07762978987423819,
+          0.9698795565530265,
+          0.8720673433469355,
+          0.5848448655198397,
+          0.6093877444520419,
+          0.2600479448594426,
+          0.8961071076491984,
+          0.7827239150324312,
+          0.03243647484491774,
+          0.4347393417626588,
+          0.48205770886363086,
+          0.1407579077330765,
+          0.8570455272102135,
+          0.5991507866565635,
+          0.07544671160677918,
+          0.7653429124104552,
+          0.6773009306766822,
+          0.45537431330712064,
+          0.8142623987213244,
+          0.12005385753629261,
+          0.6280673349705737,
+          0.6411165107241685,
+          0.6484334035034007,
+          0.7662279929095923,
+          0.37726416856525136,
+          0.17609887569284655,
+          0.38801140570442905,
+          0.39754218864115765,
+          0.9257069667188491,
+          0.22229435308889123,
+          0.8100035792813067,
+          0.6308637969856226,
+          0.3958087729357176,
+          0.5115743574773932,
+          0.4243398691996936,
+          0.07883717817748748,
+          0.15170830509978162,
+          0.2816360723559719,
+          0.9711659817994986,
+          0.044461230265380625,
+          0.821238189366148,
+          0.5395859183099476,
+          0.2722803990279281,
+          0.4237742209137838,
+          0.8492378365236639,
+          0.07052566067182942,
+          0.7384299618235564,
+          0.44307968171108536,
+          0.626851435775337,
+          0.8141433753393437,
+          0.7716442985320457,
+          0.052922587778580166,
+          0.9838041253840564,
+          0.07522072756748854,
+          0.09932648402162414,
+          0.8632757907071749,
+          0.2438048746885606,
+          0.6994226198340694,
+          0.17754833982132778,
+          0.27249382866046634,
+          0.953726012453473,
+          0.6496793216100725,
+          0.6595413735302542,
+          0.6020029422536388,
+          0.7164563260483665,
+          0.5076768549802999,
+          0.049402208839229855,
+          0.8704399548068575,
+          0.8183909638112867,
+          0.6246196275834262,
+          0.34055277199342293,
+          0.6375528626944473,
+          0.2176538874850178,
+          0.8855950471919678,
+          0.2729897053353294,
+          0.922898222002597,
+          0.34124823862742093,
+          0.7724731409980496,
+          0.9301356652479389,
+          0.5069872906570465,
+          0.8907391661563082,
+          0.32950557196005503,
+          0.985746097010779,
+          0.9887326595275832,
+          0.8708031335294233,
+          0.4581414968403985,
+          0.6417776426968713,
+          0.1988895008216951,
+          0.14201433649723483,
+          0.2519165771331364,
+          0.4893264293228885,
+          0.5938184986886181,
+          0.9915985915641636,
+          0.07932976885305809,
+          0.3177939359004449,
+          0.021452758251003567,
+          0.13933128825095897,
+          0.5726480825059779,
+          0.5189949016845317,
+          0.6891035069799827,
+          0.49304429113395964,
+          0.6619469056212011,
+          0.15392767997249202,
+          0.295502756789187,
+          0.9836316237991622,
+          0.9852703411276889,
+          0.5335280196070722,
+          0.014950690276562373,
+          0.27343049990512514,
+          0.6808495149772484,
+          0.9449623361180979,
+          0.5815473574186956,
+          0.6876907395597738,
+          0.6074256288551354,
+          0.6616727427389402,
+          0.36095501815680064,
+          0.41762697729996634,
+          0.3068641146519695,
+          0.333544545080694,
+          0.2515657935855262,
+          0.705714848144513,
+          0.5255521635430529,
+          0.4822159228974139,
+          0.6217747511283676,
+          0.08087460026706172,
+          0.038418823857711226,
+          0.191238850363338,
+          0.772025062029148,
+          0.5836617926964085,
+          0.6257770436229244,
+          0.3360858834698621,
+          0.07102860301201153,
+          0.35231606761455847,
+          0.4395219250109519,
+          0.29145547851709463,
+          0.3828891018746814,
+          0.1586315392361195,
+          0.681625950179632,
+          0.6390249571272832,
+          0.18127915456338606,
+          0.3074673692068177,
+          0.11409699865595568,
+          0.2084284233791489,
+          0.97923423111305,
+          0.39726880385758756,
+          0.1507803451137819,
+          0.7071508081014066,
+          0.6789899906980974,
+          0.7504243731249811,
+          0.3250115192256111,
+          0.08257633416391008,
+          0.5806999030604296,
+          0.7620316920198831,
+          0.4035009404475842,
+          0.3068822262867634,
+          0.3445143919468354,
+          0.5711454787289016,
+          0.1449634076508688,
+          0.8695309515694036,
+          0.8570541881803591,
+          0.898864374325179,
+          0.8192475103851975,
+          0.47431367407308067,
+          0.8428434350964595,
+          0.8701949713056438,
+          0.5048752773887357,
+          0.35052839245352385,
+          0.11810761952684778,
+          0.08778526548004928,
+          0.6372428709171547,
+          0.9888725923474206,
+          0.2864261958904437,
+          0.07213482061244814,
+          0.15845866642053563,
+          0.5062299144844185,
+          0.5746856612680256,
+          0.8659909684424811,
+          0.8414353824886924,
+          0.7672879096854097,
+          0.06459741136385855,
+          0.3604498875597726,
+          0.6006588577364183,
+          0.056856595390261555,
+          0.7016467737931367,
+          0.2123993086079614,
+          0.7132617466759801,
+          0.007242742044783146,
+          0.13564612958400502,
+          0.33005333741350995,
+          0.736021023245836,
+          0.9406546989427267,
+          0.2925280225016629,
+          0.2655802942570038,
+          0.016877181546108178,
+          0.15929715792991916,
+          0.6391938328375499,
+          0.6329167268211631,
+          0.4985094645037693,
+          0.7167806799705279,
+          0.8531428725877723,
+          0.6405531773686648,
+          0.377705624398585,
+          0.13785533323928845,
+          0.33942934167686034,
+          0.4536063371227177,
+          0.34963259129971425
+        ],
+        "mode": "markers",
+        "colorscale": [
+          [
+            0,
+            "rgb(255, 0, 0)"
+          ],
+          [
+            0.5,
+            "rgb(0, 255, 0)"
+          ],
+          [
+            1,
+            "rgb(0, 0, 255)"
+          ]
+        ]
+      },
+      "type": "scattergl"
+    }
+  ],
+  "layout": {
+    "height": 400,
+    "width": 680,
+    "showlegend": false
+  }
+}
diff --git a/test/image/mocks/gl2d_scatter-subplot-panel.json b/test/image/mocks/gl2d_scatter-subplot-panel.json
new file mode 100644
index 00000000000..d393e940c16
--- /dev/null
+++ b/test/image/mocks/gl2d_scatter-subplot-panel.json
@@ -0,0 +1,7442 @@
+{
+  "data": [
+    {
+      "uid": "ab6939",
+      "yaxis": "y",
+      "ysrc": "jackp:17616:013c39",
+      "xsrc": "jackp:17616:7ad609",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "y": [
+        1061,
+        11380,
+        1069,
+        5421,
+        3047,
+        6670,
+        4634,
+        5839,
+        8969,
+        4022,
+        10763,
+        630,
+        7780,
+        4189,
+        605,
+        574,
+        8549,
+        1113,
+        1056,
+        8854,
+        10091,
+        5086,
+        828,
+        1072,
+        4022,
+        2515,
+        3696,
+        7357,
+        698,
+        1813,
+        574,
+        2100,
+        2042,
+        2746,
+        868,
+        10221,
+        1013,
+        666,
+        2932,
+        10980,
+        2863,
+        5329,
+        3896,
+        4750,
+        11189,
+        16336,
+        5864,
+        1009,
+        5436,
+        540,
+        1362,
+        12738,
+        15217,
+        6471,
+        666,
+        982,
+        907,
+        1059.3106836371142,
+        11378.76484672601,
+        1067.0389029875535,
+        5415.541473829024,
+        3043.126678834689,
+        6682.202531020598,
+        4632.764177524695,
+        5837.135082018547,
+        8967.108954475267,
+        4014.6060439357625,
+        10777.562183384925,
+        630.0843436021011,
+        7792.429719427687,
+        4188.778161876925,
+        604.6273993049891,
+        573.6459148827832,
+        8548.560509085333,
+        1111.2457705740364,
+        1056.5659908824675,
+        8860.783498917568,
+        10086.173456646731,
+        5079.967861774502,
+        826.7868758425707,
+        1071.2667462601792,
+        4018.764264449159,
+        2515.9697949094284,
+        3690.9329781712863,
+        7356.598687202641,
+        697.2408759881968,
+        1815.3169858769968,
+        574.2153777691822,
+        2099.6659196786677,
+        2044.8665356939257,
+        2744.0759894866906,
+        867.7389585808415,
+        10240.579077960845,
+        1012.9971626792702,
+        667.0748525023557,
+        2928.065494655128,
+        10985.054394743776,
+        2864.971318928985,
+        5335.032998937184,
+        3892.1668470582094,
+        4745.210590946567,
+        11168.340669411537,
+        16306.636464701785,
+        5854.627923096243,
+        1008.9460493429467,
+        5428.903570182665,
+        539.5172639869355,
+        1361.353562174305,
+        12757.430112057562,
+        15196.167209163692,
+        6483.516325416377,
+        667.2087721042134,
+        981.4175993943619,
+        907.9886333478165
+      ],
+      "x": [
+        0.41,
+        1.58,
+        0.38,
+        1.3,
+        0.91,
+        1.01,
+        1.33,
+        1.01,
+        1.54,
+        0.9,
+        1.3,
+        0.3,
+        1.34,
+        1,
+        0.3,
+        0.3,
+        1.27,
+        0.43,
+        0.51,
+        1.3,
+        1.7,
+        1.04,
+        0.44,
+        0.49,
+        1.01,
+        0.81,
+        1.1,
+        1.4,
+        0.31,
+        0.53,
+        0.3,
+        0.7,
+        0.7,
+        0.73,
+        0.33,
+        1.52,
+        0.3,
+        0.33,
+        0.7,
+        1.55,
+        0.83,
+        1.12,
+        1.24,
+        0.93,
+        1.6,
+        2.11,
+        1.01,
+        0.39,
+        1.22,
+        0.32,
+        0.39,
+        1.52,
+        2.04,
+        1.07,
+        0.33,
+        0.4,
+        0.31,
+        0.4107633943409979,
+        1.5795268263899218,
+        0.38072422743350887,
+        1.2992023481094161,
+        0.9084414288633553,
+        1.0116025472072634,
+        1.327444354301417,
+        1.0110722184438945,
+        1.540813364292889,
+        0.9000600744887673,
+        1.3007100475964846,
+        0.3004194857789524,
+        1.3376976092341328,
+        1.0003384339498795,
+        0.30019572959320673,
+        0.30014256392276406,
+        1.2684557442171318,
+        0.430377870776487,
+        0.5099499992096155,
+        1.2991165485479506,
+        1.6976184575853792,
+        1.0411884059899226,
+        0.44039755055311974,
+        0.48982554697630276,
+        1.0091428289328386,
+        0.8095036979536122,
+        1.0985213214003793,
+        1.3982163328397756,
+        0.30981488160175413,
+        0.5299901266493092,
+        0.30024091987167484,
+        0.6995160214373853,
+        0.7011595222944403,
+        0.7313843607749203,
+        0.330022579446216,
+        1.5204666142287895,
+        0.30030336921065653,
+        0.3303750661860965,
+        0.6993857808733479,
+        1.5519782017466428,
+        0.8289423384590348,
+        1.1211663120229776,
+        1.2423879793881645,
+        0.9314068935133135,
+        1.6002285766446496,
+        2.109944716249183,
+        1.0091686636845043,
+        0.38987499009998666,
+        1.2193341334210623,
+        0.31963991664707536,
+        0.3899022780991603,
+        1.5193376972037451,
+        2.0420751554671646,
+        1.0703653709951608,
+        0.3306395991025956,
+        0.4004810714207143,
+        0.3096309154770747
+      ],
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "f1cdee",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "410e1a",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "5d824e",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "94535a",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "5bcb71",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "f17ffc",
+      "yaxis": "y",
+      "ysrc": "jackp:17616:ee32d5",
+      "xsrc": "jackp:17616:0d67c1",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "y": [
+        5452,
+        540,
+        7247,
+        6648,
+        2005,
+        802,
+        868,
+        3561,
+        906,
+        2423,
+        4556,
+        2247,
+        12255,
+        666,
+        559,
+        1580,
+        2004,
+        891,
+        2757,
+        627,
+        3089,
+        863,
+        8051,
+        2375,
+        1946,
+        814,
+        3536,
+        814,
+        4079,
+        13135,
+        1084,
+        863,
+        775,
+        1080,
+        1093,
+        847,
+        2413,
+        8080,
+        2133,
+        1202,
+        886,
+        1306,
+        764,
+        1257,
+        8728,
+        9282,
+        7109,
+        1103,
+        1229,
+        929,
+        9252,
+        6465,
+        827,
+        6486,
+        4044,
+        946,
+        974,
+        2111,
+        827,
+        1056,
+        2335,
+        5361,
+        12618,
+        1348,
+        907,
+        14650,
+        891,
+        6561,
+        794,
+        1728,
+        1046,
+        6683,
+        905,
+        8645,
+        6377,
+        2533,
+        932,
+        9243,
+        477,
+        1246,
+        7577,
+        10378,
+        2377,
+        2870,
+        1865,
+        943,
+        1055,
+        5887,
+        847,
+        612,
+        695,
+        461,
+        984,
+        8794,
+        8645,
+        2271,
+        5460.514232315189,
+        540.6060672978252,
+        7256.265245472035,
+        6655.447023455413,
+        2004.8900411818047,
+        803.2272457475142,
+        866.4272920672585,
+        3555.5608027391595,
+        905.4057653960003,
+        2423.450247400853,
+        4563.647658194975,
+        2244.810176778345,
+        12268.819633511086,
+        665.796490791041,
+        557.9561700986359,
+        1580.6926540117697,
+        2007.135148784566,
+        890.7376213256438,
+        2757.5133412847895,
+        626.3684140664654,
+        3083.2238126689845,
+        863.9658590224391,
+        8054.2098231461005,
+        2371.1842450381405,
+        1949.0958783891047,
+        815.4099749125593,
+        3540.7394673309614,
+        813.503123276087,
+        4084.1156609733484,
+        13152.793371056214,
+        1084.8871585739962,
+        862.2414317058289,
+        776.3361805510342,
+        1078.7139986310528,
+        1091.2887501009477,
+        847.1809576005015,
+        2410.9855877293326,
+        8070.931268387648,
+        2136.697679751855,
+        1200.4323711991233,
+        885.1469963465314,
+        1304.235826806892,
+        764.8838481570778,
+        1255.1088546122362,
+        8719.96020657201,
+        9269.057767245818,
+        7108.632766719113,
+        1102.7567754703193,
+        1228.4247635459587,
+        930.3616174346655,
+        9265.544727737326,
+        6459.126179694332,
+        828.4893456008689,
+        6497.782392641875,
+        4040.585178190286,
+        945.7475993454485,
+        975.5270027693304,
+        2109.6997596171605,
+        826.8406535505052,
+        1057.6085391199654,
+        2336.1553990916623,
+        5364.313849599517,
+        12640.212307930415,
+        1349.633695774002,
+        907.0386161863622,
+        14641.42218921677,
+        890.6239810758949,
+        6559.3876484607,
+        793.913053197136,
+        1724.929285255018,
+        1047.3114335263103,
+        6688.270730968713,
+        906.4834024809417,
+        8630.737503918022,
+        6372.725694410858,
+        2536.0704430653595,
+        931.6775250317295,
+        9256.264390030623,
+        476.53076580013993,
+        1246.5680762017519,
+        7563.233523707304,
+        10370.733079595597,
+        2380.0429180122455,
+        2871.7319728426646,
+        1861.5993102560928,
+        944.7107687296119,
+        1054.0692056848984,
+        5895.928090272499,
+        846.273698854019,
+        612.3725464049963,
+        695.2253743927627,
+        460.18430884786017,
+        984.9395068334029,
+        8785.927975320888,
+        8641.791884255083,
+        2272.411602788348
+      ],
+      "x": [
+        1.18,
+        0.34,
+        1.05,
+        1.02,
+        0.51,
+        0.31,
+        0.33,
+        0.79,
+        0.35,
+        0.74,
+        1.01,
+        0.53,
+        1.33,
+        0.33,
+        0.32,
+        0.56,
+        0.56,
+        0.31,
+        0.7,
+        0.31,
+        0.73,
+        0.3,
+        1.13,
+        0.7,
+        0.53,
+        0.32,
+        0.65,
+        0.32,
+        1.01,
+        1.5,
+        0.34,
+        0.3,
+        0.34,
+        0.32,
+        0.43,
+        0.42,
+        0.6,
+        1.12,
+        0.58,
+        0.44,
+        0.39,
+        0.46,
+        0.3,
+        0.4,
+        1.32,
+        1.25,
+        1.11,
+        0.42,
+        0.4,
+        0.38,
+        1.18,
+        1,
+        0.41,
+        1.05,
+        0.9,
+        0.33,
+        0.34,
+        0.51,
+        0.41,
+        0.37,
+        0.7,
+        1.05,
+        2.03,
+        0.44,
+        0.31,
+        1.54,
+        0.31,
+        1.08,
+        0.3,
+        0.57,
+        0.31,
+        1.02,
+        0.32,
+        1.05,
+        1,
+        0.6,
+        0.36,
+        1.26,
+        0.3,
+        0.49,
+        1.07,
+        1.35,
+        0.73,
+        0.8,
+        0.5,
+        0.43,
+        0.45,
+        1,
+        0.42,
+        0.32,
+        0.38,
+        0.32,
+        0.42,
+        1.51,
+        1.05,
+        0.54,
+        1.1780061569018656,
+        0.34019906154924956,
+        1.050291041064077,
+        1.0193819889523505,
+        0.5101857008071604,
+        0.31012570662799926,
+        0.32987999810065116,
+        0.7911857405047632,
+        0.3500676902101524,
+        0.7388492676822586,
+        1.0103594952359454,
+        0.5305726865778144,
+        1.3312427504773812,
+        0.33033548350188935,
+        0.3203175476767066,
+        0.5609542186362863,
+        0.5607119079376008,
+        0.31015589985333475,
+        0.7003245680589847,
+        0.3105000118607063,
+        0.7304900287597976,
+        0.2998436406739367,
+        1.129498593039082,
+        0.6992181245292223,
+        0.5293288698849064,
+        0.319785290476097,
+        0.6502396936869513,
+        0.31945751824934054,
+        1.0117271992438635,
+        1.497072122568163,
+        0.33972947650862756,
+        0.2996293015653554,
+        0.3402660465980502,
+        0.3201663543225943,
+        0.4291895920104291,
+        0.4207500888240124,
+        0.6009587468415049,
+        1.1196065206843027,
+        0.5809384687111587,
+        0.44082332185656087,
+        0.39026449881842856,
+        0.45927289774590446,
+        0.29989745264205814,
+        0.39984787583859394,
+        1.3206843722164034,
+        1.2485929314172612,
+        1.1098084539747521,
+        0.42047914988763374,
+        0.40003540311036123,
+        0.38027346964416486,
+        1.1814761344883558,
+        1.001445009705241,
+        0.4104341206319438,
+        1.0507704520537682,
+        0.9017249902223466,
+        0.32998521469187597,
+        0.3405790459844217,
+        0.509786008415465,
+        0.4106008776075467,
+        0.3693497036413711,
+        0.6989925506915445,
+        1.0509160153892532,
+        2.0289020309016212,
+        0.4404142654581719,
+        0.30971940075847304,
+        1.539178732364313,
+        0.30949763811252123,
+        1.0783931896228875,
+        0.3000341665766932,
+        0.5698800217799176,
+        0.3101780498103983,
+        1.018537004143419,
+        0.32031866648767165,
+        1.0483455946391567,
+        1.0007852494609037,
+        0.5998626613470537,
+        0.36067691071352004,
+        1.2609639454986543,
+        0.2999488130229284,
+        0.49060079518448313,
+        1.0679930392121713,
+        1.350417480496671,
+        0.7314060651672839,
+        0.8000523901671874,
+        0.5009670357606365,
+        0.43069107615453184,
+        0.44965982069442784,
+        1.0000238226208142,
+        0.4201890266004826,
+        0.31980324678858824,
+        0.3793050183965843,
+        0.3201689969160442,
+        0.41960338584979623,
+        1.5112505003644572,
+        1.049817781569988,
+        0.5404896698863518
+      ],
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "14788a",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "5931cd",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "990c34",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "3e3b4d",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "14d30a",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "dace31",
+      "yaxis": "y",
+      "ysrc": "jackp:17616:3908ce",
+      "xsrc": "jackp:17616:45a03c",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "y": [
+        1193,
+        2277,
+        2117,
+        544,
+        585,
+        678,
+        2274,
+        852,
+        541,
+        1220,
+        561,
+        4712,
+        1046,
+        819,
+        13337,
+        2287,
+        3039,
+        625,
+        544,
+        431,
+        11161,
+        673,
+        1838,
+        3006,
+        2336,
+        2171,
+        713,
+        447,
+        432,
+        15829,
+        3288,
+        4714,
+        4291,
+        8712,
+        4997,
+        13596,
+        559,
+        1912,
+        6998,
+        1193.3876365931562,
+        2272.5994017331814,
+        2120.476250371547,
+        544.7276944898541,
+        585.8049310349996,
+        679.0830540732029,
+        2273.6876113911367,
+        851.6658067740736,
+        541.6088153194902,
+        1222.364904630746,
+        559.9926307366161,
+        4707.50157828803,
+        1045.3028541499064,
+        820.4382781997845,
+        13316.544001971582,
+        2288.1894040806665,
+        3038.2466612846124,
+        625.3990473635954,
+        544.6146688412308,
+        430.41668161251334,
+        11153.947817576975,
+        672.8319791387142,
+        1838.4136253693468,
+        3001.1264162321445,
+        2331.654802069871,
+        2171.0517348908475,
+        714.2128028747433,
+        446.60845078345693,
+        431.80108988686476,
+        15828.453901656627,
+        3284.1654911792984,
+        4707.745817982617,
+        4298.488296208745,
+        8716.909810010398,
+        4988.341606206554,
+        13620.479855798849,
+        559.8988175709475,
+        1912.1301945982304,
+        7004.688500517063
+      ],
+      "x": [
+        0.45,
+        0.74,
+        0.7,
+        0.31,
+        0.3,
+        0.36,
+        0.71,
+        0.43,
+        0.29,
+        0.51,
+        0.32,
+        1.2,
+        0.31,
+        0.34,
+        1.51,
+        0.7,
+        0.7,
+        0.31,
+        0.31,
+        0.23,
+        1.51,
+        0.4,
+        0.5,
+        0.91,
+        0.56,
+        0.71,
+        0.34,
+        0.3,
+        0.24,
+        1.71,
+        0.73,
+        1.31,
+        1.02,
+        1.2,
+        1,
+        1.5,
+        0.31,
+        0.6,
+        1.07,
+        0.4500545841868139,
+        0.7388814618948583,
+        0.6997529055296279,
+        0.31012748850149074,
+        0.3001000613528415,
+        0.36046681208094744,
+        0.7100010642622773,
+        0.42930725342676873,
+        0.28974031075134327,
+        0.5092263179124418,
+        0.319882621963655,
+        1.201037838714529,
+        0.31016700101683087,
+        0.3396264155710481,
+        1.5119895141174995,
+        0.7010995490004005,
+        0.6998043302379092,
+        0.3097951744381917,
+        0.30939857515606023,
+        0.23014068982257535,
+        1.5086008018297963,
+        0.4004868992406772,
+        0.4994509430074582,
+        0.9094317685770236,
+        0.5607455152009062,
+        0.710446767609082,
+        0.34023416582692473,
+        0.3003293518892345,
+        0.2399528981339443,
+        1.7127774042695152,
+        0.7309734014523372,
+        1.3088749701633184,
+        1.0194294657153335,
+        1.2004316689138208,
+        1.0015334048414977,
+        1.5026112420740132,
+        0.31050676069316624,
+        0.6010486865462888,
+        1.0699924794723807
+      ],
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "247be7",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "3c91dc",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "ee9596",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "7f1ed8",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "ff5f92",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "c6e1cb",
+      "yaxis": "y",
+      "ysrc": "jackp:17616:1e013b",
+      "xsrc": "jackp:17616:4b5447",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "y": [
+        7572,
+        1120,
+        5701,
+        4397,
+        4485,
+        4861,
+        4093,
+        1437,
+        3941,
+        4183,
+        1388,
+        7569.64952963075,
+        1118.6105875913026,
+        5706.382903847428,
+        4403.2914914143175,
+        4476.420490760296,
+        4851.352547488572,
+        4099.272504570691,
+        1437.371924384714,
+        3948.8748294224533,
+        4183.574123983911,
+        1389.3022525377155
+      ],
+      "x": [
+        1,
+        0.4,
+        1.01,
+        1.01,
+        0.9,
+        1.07,
+        0.9,
+        0.45,
+        1,
+        0.91,
+        0.58,
+        1.001502815405228,
+        0.40019268849685025,
+        1.0120114558083193,
+        1.0105425242900785,
+        0.9008198540913427,
+        1.0701202290505765,
+        0.8984569856310916,
+        0.4498874278017571,
+        0.9985395676165942,
+        0.9099205870813175,
+        0.5806506945003799
+      ],
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "d7da66",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "8563d1",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "ce18ff",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "58b45e",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "dd81de",
+      "yaxis": "y",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "c62c21",
+      "yaxis": "y",
+      "ysrc": "jackp:17616:479c89",
+      "xsrc": "jackp:17616:548eee",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "y": [
+        1257,
+        840,
+        3765,
+        1255.437898920403,
+        840.0460713785501,
+        3769.123318256676
+      ],
+      "x": [
+        0.5,
+        0.4,
+        1.03,
+        0.4996413954309529,
+        0.400455934918678,
+        1.02979273634638
+      ],
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "08893d",
+      "yaxis": "y2",
+      "ysrc": "jackp:17616:36d993",
+      "xsrc": "jackp:17616:6d7fe8",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "y": [
+        10424,
+        5139,
+        4082,
+        3984,
+        5171,
+        1015,
+        1035,
+        630,
+        773,
+        3775,
+        3137,
+        2935,
+        6520,
+        7006,
+        3816,
+        4077,
+        7459,
+        743,
+        6210,
+        4172,
+        5364,
+        610,
+        1244,
+        700,
+        15801,
+        828,
+        9697,
+        3676,
+        765,
+        1116,
+        2295,
+        11226,
+        3629,
+        13872,
+        4707,
+        14574,
+        3141,
+        3584,
+        11455,
+        12409,
+        10534,
+        6129,
+        4662,
+        1024,
+        9768,
+        10412.77646816203,
+        5143.130823997301,
+        4074.637750970314,
+        3991.5784120824123,
+        5161.954237735938,
+        1016.5945814472792,
+        1036.8033542462274,
+        631.0097053085964,
+        772.6159500126535,
+        3771.2175780154507,
+        3139.0146691143964,
+        2933.649611180177,
+        6527.140546752975,
+        7002.233444396038,
+        3809.203815012949,
+        4077.7712321598933,
+        7455.023850175573,
+        742.3409562072713,
+        6206.458991972971,
+        4177.459829630505,
+        5363.717726228926,
+        610.0460915241994,
+        1245.7579124988156,
+        700.2289189151198,
+        15801.74250478056,
+        828.9851334561575,
+        9692.88085374692,
+        3669.365255486337,
+        764.172932333939,
+        1116.9579922958892,
+        2294.320762763826,
+        11245.350847275393,
+        3634.6521912581165,
+        13865.24749877891,
+        4711.0596574712,
+        14565.073854175265,
+        3141.9485527330235,
+        3581.8365468515467,
+        11439.976031356477,
+        12411.596103880902,
+        10519.979526708892,
+        6137.7342317256325,
+        4661.872989119402,
+        1025.916096448264,
+        9780.25055015974
+      ],
+      "x": [
+        2.77,
+        1,
+        0.9,
+        0.95,
+        1.02,
+        0.53,
+        0.4,
+        0.35,
+        0.32,
+        1.07,
+        0.72,
+        0.8,
+        1.32,
+        1.39,
+        0.93,
+        0.92,
+        1.31,
+        0.33,
+        1.28,
+        0.97,
+        1.03,
+        0.33,
+        0.47,
+        0.38,
+        2.19,
+        0.32,
+        1.5,
+        1.01,
+        0.34,
+        0.32,
+        0.75,
+        2.26,
+        0.9,
+        2.21,
+        1.01,
+        2.02,
+        1.02,
+        1,
+        1.72,
+        1.59,
+        1.71,
+        1.2,
+        1.11,
+        0.35,
+        1.5,
+        2.76611807859154,
+        1.0014172186648804,
+        0.8998927183051715,
+        0.9490105536815838,
+        1.0208011121706744,
+        0.5304188244032657,
+        0.39941516096880725,
+        0.3493668205399811,
+        0.3200274684550692,
+        1.0698092759058495,
+        0.7192085045595243,
+        0.8003935983866456,
+        1.318372998395809,
+        1.389641303212772,
+        0.9315983619738519,
+        0.9191181682945864,
+        1.3121930937244446,
+        0.33052112407632367,
+        1.2805560458285843,
+        0.9685861818759816,
+        1.0282540017764927,
+        0.3305799774805996,
+        0.46951187328977617,
+        0.37993345791541067,
+        2.185965518452483,
+        0.3200681065578067,
+        1.5007864833560696,
+        1.0114377893618371,
+        0.3395561846306629,
+        0.32062498422140706,
+        0.7499866270530111,
+        2.2572056862114906,
+        0.900737007198055,
+        2.20888986267167,
+        1.0115799519996087,
+        2.0222637880075047,
+        1.020379189260676,
+        0.9998971735101524,
+        1.721735741546772,
+        1.5910782290335463,
+        1.7094456116135857,
+        1.1978488899909912,
+        1.1122168333727005,
+        0.3495876676095213,
+        1.50280867282154
+      ],
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "7fdc8c",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "ad49c4",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "90ea0d",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "aa9a84",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "84c415",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "fa9571",
+      "yaxis": "y2",
+      "ysrc": "jackp:17616:89729a",
+      "xsrc": "jackp:17616:512573",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "y": [
+        3146,
+        1400,
+        1845,
+        5384,
+        6541,
+        1240,
+        776,
+        1402,
+        4314,
+        754,
+        473,
+        573,
+        4283,
+        4368,
+        5358,
+        2040,
+        6397,
+        448,
+        4249,
+        684,
+        554,
+        1017,
+        3732,
+        4537,
+        645,
+        648,
+        1664,
+        2113,
+        614,
+        1668,
+        3016,
+        5773,
+        743,
+        3254,
+        3770,
+        462,
+        5951,
+        1187,
+        1134,
+        2006,
+        2869,
+        2961,
+        1304,
+        7821,
+        7780,
+        10861,
+        1163,
+        906,
+        1218,
+        579,
+        1678,
+        397,
+        5143,
+        562,
+        5347,
+        2398,
+        3140.8086305808993,
+        1397.906178857768,
+        1843.4184402625544,
+        5383.399868586026,
+        6532.881409581059,
+        1238.2891983652735,
+        776.589524966972,
+        1401.195711323075,
+        4316.934634828833,
+        754.8055185146902,
+        472.87834001573805,
+        572.8863954580543,
+        4281.590323220257,
+        4369.755732774143,
+        5350.12101873217,
+        2041.7567060287292,
+        6386.09064817482,
+        447.742917604294,
+        4247.672553339955,
+        684.2004316873882,
+        553.762582443086,
+        1017.7273848351296,
+        3730.36942787397,
+        4540.225660975722,
+        644.9132539875111,
+        647.5688774383001,
+        1664.6452623439018,
+        2110.442555436066,
+        612.887516781999,
+        1667.228394902406,
+        3016.4582785472912,
+        5767.418536941765,
+        741.7267589323542,
+        3258.565869920871,
+        3770.100716045485,
+        461.97795662448453,
+        5943.07409577641,
+        1188.5844335614718,
+        1133.4477397574572,
+        2003.7069087467034,
+        2867.134628242455,
+        2957.473678084288,
+        1304.6641733777456,
+        7835.480129898021,
+        7777.493995607494,
+        10861.554828743283,
+        1161.5417534653106,
+        906.6511146970864,
+        1218.2969007753525,
+        578.9103028361784,
+        1680.550138673035,
+        397.7138234740735,
+        5136.150152476496,
+        561.3353015928315,
+        5352.122818906456,
+        2401.989676116762
+      ],
+      "x": [
+        0.71,
+        0.5,
+        0.55,
+        1.13,
+        1.22,
+        0.54,
+        0.3,
+        0.55,
+        1.07,
+        0.38,
+        0.3,
+        0.31,
+        1.03,
+        1.2,
+        1.09,
+        0.63,
+        1.7,
+        0.26,
+        1.21,
+        0.37,
+        0.3,
+        0.4,
+        1.19,
+        1.1,
+        0.32,
+        0.32,
+        0.55,
+        0.7,
+        0.35,
+        0.55,
+        0.9,
+        1.22,
+        0.41,
+        0.74,
+        0.9,
+        0.28,
+        1.23,
+        0.41,
+        0.55,
+        0.71,
+        0.72,
+        0.72,
+        0.57,
+        1.07,
+        1.51,
+        1.51,
+        0.51,
+        0.35,
+        0.43,
+        0.33,
+        0.53,
+        0.26,
+        1.06,
+        0.31,
+        1.1,
+        0.72,
+        0.7111339777607055,
+        0.4994884904461816,
+        0.5493512312187724,
+        1.1321011707505835,
+        1.2190837385700815,
+        0.5409626951479006,
+        0.2994550717059788,
+        0.5504492669416847,
+        1.070471897719336,
+        0.3801233447656072,
+        0.2999338201627972,
+        0.3095583418293734,
+        1.0315372198190234,
+        1.2010109434121479,
+        1.091976512742137,
+        0.6307545361095129,
+        1.6992661830836782,
+        0.2604202702155868,
+        1.212203353616784,
+        0.36940739155978497,
+        0.299795475600578,
+        0.39983634145073604,
+        1.1895328641714293,
+        1.0990980585281178,
+        0.31990663835236127,
+        0.32040919153150527,
+        0.5498289860698858,
+        0.6990033428090267,
+        0.350693382217959,
+        0.5506035142868777,
+        0.8989748463128192,
+        1.218684651371456,
+        0.409690047113998,
+        0.7406522630317631,
+        0.89932311629362,
+        0.2799685289825418,
+        1.2315118308088653,
+        0.41038775652720166,
+        0.5489356773969423,
+        0.7111002951160296,
+        0.7211773208520164,
+        0.7186277856782949,
+        0.5707868009202163,
+        1.0714047427636404,
+        1.5126696824438066,
+        1.5128715779151687,
+        0.5092340059214038,
+        0.34948820438172357,
+        0.42980724332218584,
+        0.329441570151787,
+        0.5291041086009575,
+        0.2601948517187721,
+        1.0591038864640532,
+        0.3103498452537972,
+        1.1003801925084704,
+        0.7189529705838424
+      ],
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "e771ed",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "21e50c",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "64d2c7",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "fc938f",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "5f7fa9",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "7677a5",
+      "yaxis": "y2",
+      "ysrc": "jackp:17616:567114",
+      "xsrc": "jackp:17616:2bf765",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "y": [
+        2779,
+        921,
+        552,
+        4432,
+        4538,
+        2335,
+        5456,
+        8541,
+        5627,
+        8219,
+        1389,
+        2330,
+        647,
+        12686,
+        4064,
+        14150,
+        1709,
+        10608,
+        537,
+        5255,
+        446,
+        15746,
+        2784.0891882709925,
+        919.4394682707192,
+        551.0255032388903,
+        4431.402793708898,
+        4532.074621231837,
+        2332.3701670604687,
+        5451.373192556821,
+        8527.422635327583,
+        5616.948054509229,
+        8224.123822918935,
+        1387.7587547065266,
+        2331.9925803066576,
+        647.0251053310153,
+        12683.598824787783,
+        4065.7841856221626,
+        14131.082587428302,
+        1710.6393712564948,
+        10597.578923512478,
+        536.7958596900687,
+        5259.862072434795,
+        445.1803439678226,
+        15751.295924196802
+      ],
+      "x": [
+        0.73,
+        0.42,
+        0.35,
+        1.01,
+        1.12,
+        0.7,
+        1.06,
+        1.51,
+        1.06,
+        1.51,
+        0.52,
+        0.7,
+        0.35,
+        1.65,
+        0.9,
+        2.01,
+        0.62,
+        1.58,
+        0.34,
+        1.02,
+        0.31,
+        2.48,
+        0.7296668084672592,
+        0.4204559656223413,
+        0.34973268668994983,
+        1.0119182198808359,
+        1.1221279006949385,
+        0.6987872687759356,
+        1.0587773120199977,
+        1.5129104821712054,
+        1.0594720763855323,
+        1.5116976945998972,
+        0.5206048179810856,
+        0.6987743043575617,
+        0.3497267153487859,
+        1.6526174692366742,
+        0.8997553281014519,
+        2.011405506181477,
+        0.6187712125815792,
+        1.5804901885141154,
+        0.3396234100568092,
+        1.0182148468730474,
+        0.30959402900137517,
+        2.4802924468059957
+      ],
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "033de3",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "f6e73c",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "14dc4b",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "44b8c9",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "5f7fa1",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "59b4c9",
+      "yaxis": "y2",
+      "ysrc": "jackp:17616:42cadc",
+      "xsrc": "jackp:17616:1ea406",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "y": [
+        3669,
+        4475,
+        16565,
+        5314,
+        15919,
+        3193,
+        4569,
+        3669.100120314125,
+        4476.889906507577,
+        16537.846400564747,
+        5308.750209547583,
+        15904.305296694685,
+        3188.583934150139,
+        4560.655470173187
+      ],
+      "x": [
+        0.9,
+        1.01,
+        2.02,
+        1.16,
+        2.01,
+        0.9,
+        1,
+        0.9002013524445966,
+        1.0107320724879023,
+        2.0170503357200134,
+        1.1596227373901677,
+        2.0093263061134414,
+        0.8995271327185058,
+        0.9980620609339724
+      ],
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "33d4ca",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "b8d304",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "65e3d1",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "6c7e23",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "e4ea3c",
+      "yaxis": "y2",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "524443",
+      "yaxis": "y2",
+      "ysrc": "jackp:17616:b896d3",
+      "xsrc": "jackp:17616:0f5f52",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "y": [
+        2293,
+        4166,
+        11655,
+        2291.2579453210374,
+        4163.061091310426,
+        11649.610193064767
+      ],
+      "x": [
+        1.05,
+        1,
+        2,
+        1.048161417706883,
+        0.9998849509107828,
+        1.999517668963265
+      ],
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "0481fb",
+      "yaxis": "y3",
+      "ysrc": "jackp:17616:474ec3",
+      "xsrc": "jackp:17616:36d4e6",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "y": [
+        18242,
+        4746,
+        1667,
+        14775,
+        12338,
+        4719,
+        9498,
+        2753,
+        17000,
+        628,
+        13182,
+        16190,
+        1073,
+        9850,
+        8630,
+        1923,
+        552,
+        3183,
+        473,
+        5269,
+        1821,
+        537,
+        15065,
+        13753,
+        4131,
+        842,
+        8736,
+        4476,
+        608,
+        18260.24257817437,
+        4755.466564383378,
+        1667.6968278130844,
+        14802.797230622908,
+        12332.946531154392,
+        4719.776244421079,
+        9493.149588811244,
+        2751.031996916293,
+        16969.039964066902,
+        627.8496436408922,
+        13192.177820692472,
+        16186.288184368308,
+        1074.3305003765563,
+        9848.140898542066,
+        8645.816281907193,
+        1925.9455417559818,
+        551.5412558938524,
+        3186.2492368382464,
+        472.0951607515579,
+        5275.220643616439,
+        1824.5562181424898,
+        537.563473644462,
+        15056.802876898995,
+        13757.002668058696,
+        4129.388750775684,
+        843.5495873693501,
+        8735.038860268722,
+        4479.908116535391,
+        608.4425251211663
+      ],
+      "x": [
+        3.01,
+        1.13,
+        0.63,
+        2.04,
+        2.01,
+        1.32,
+        1.51,
+        0.76,
+        2.31,
+        0.31,
+        2.03,
+        2.2,
+        0.52,
+        2.05,
+        1.6,
+        0.71,
+        0.3,
+        1.11,
+        0.3,
+        1.23,
+        0.71,
+        0.31,
+        2.08,
+        2.1,
+        1.2,
+        0.4,
+        1.5,
+        1.08,
+        0.3,
+        3.00866603765751,
+        1.1293281343996375,
+        0.6307844369316802,
+        2.0384023516122025,
+        2.0068633382955934,
+        1.3205287495473481,
+        1.5123914894737311,
+        0.7609997895950064,
+        2.3062099106596197,
+        0.3093868886800573,
+        2.0309794475279808,
+        2.2040908122100307,
+        0.5205192560226196,
+        2.0510789663417963,
+        1.5984065881831886,
+        0.7100310197384573,
+        0.29956138260797893,
+        1.1119834665913224,
+        0.29968560544092815,
+        1.2318413451957055,
+        0.7099753164163385,
+        0.3099803155084778,
+        2.0768215764733498,
+        2.097908957227755,
+        1.2017904406022437,
+        0.39985575489000624,
+        1.5027728023766775,
+        1.0785274739922557,
+        0.3005943007678021
+      ],
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "26e520",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "e3f2c2",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "aca325",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "95a8c5",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "6b87ad",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "ea480c",
+      "yaxis": "y3",
+      "ysrc": "jackp:17616:d4eb68",
+      "xsrc": "jackp:17616:856c9d",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "y": [
+        540,
+        16407,
+        626,
+        17747,
+        2422,
+        5506,
+        648,
+        2852,
+        449,
+        2083,
+        6405,
+        1365,
+        11255,
+        675,
+        463,
+        715,
+        2490,
+        452,
+        4641,
+        11238,
+        4602,
+        628,
+        1589,
+        4511,
+        589,
+        692,
+        1173,
+        418,
+        10907,
+        5376,
+        4997,
+        1170,
+        2715,
+        15606,
+        596,
+        6381,
+        403,
+        9465,
+        6511,
+        4596,
+        5421,
+        539.7725159431469,
+        16415.644417706975,
+        625.9201105287154,
+        17734.474151848528,
+        2421.724018263086,
+        5515.048981639579,
+        648.5603397120559,
+        2850.660342266724,
+        448.3285656160375,
+        2086.5848017125345,
+        6394.930184196881,
+        1366.8450125964907,
+        11263.61497326674,
+        675.9383368969148,
+        462.2055712394157,
+        716.2110738489611,
+        2489.2641704036523,
+        451.7295240779792,
+        4634.421224995775,
+        11220.076813934693,
+        4602.122496280042,
+        628.173376989635,
+        1591.395607063416,
+        4502.514064171004,
+        588.491219979844,
+        690.9805691413943,
+        1171.3643511082464,
+        417.4728566784541,
+        10906.651323742877,
+        5378.748596946066,
+        5005.252038466077,
+        1169.2600703095384,
+        2719.8754596200574,
+        15588.315203417436,
+        596.6016201152287,
+        6373.967581835601,
+        402.4902128461435,
+        9483.57559324243,
+        6515.037754360971,
+        4593.431407381753,
+        5422.269201223942
+      ],
+      "x": [
+        0.3,
+        2,
+        0.38,
+        2.17,
+        0.78,
+        1.11,
+        0.32,
+        0.78,
+        0.32,
+        0.7,
+        1.17,
+        0.47,
+        2,
+        0.33,
+        0.33,
+        0.35,
+        0.7,
+        0.31,
+        1.01,
+        2.36,
+        1,
+        0.31,
+        0.64,
+        1,
+        0.34,
+        0.4,
+        0.53,
+        0.31,
+        1.5,
+        1.2,
+        1.04,
+        0.54,
+        0.74,
+        2.26,
+        0.4,
+        1.24,
+        0.33,
+        1.52,
+        1.51,
+        1.2,
+        1.09,
+        0.2999801143707278,
+        2.001098364104527,
+        0.38017679159371537,
+        2.1712101043808825,
+        0.7814061804341412,
+        1.1109268461451032,
+        0.3201287161570626,
+        0.7810373350004609,
+        0.31996713674067073,
+        0.7011231689217999,
+        1.1693914500624067,
+        0.4698872038216249,
+        2.0034739177846124,
+        0.32946085821133164,
+        0.3301183170331197,
+        0.3493499345343906,
+        0.6988900750677473,
+        0.31028712898704086,
+        1.0090075552853044,
+        2.361358655761997,
+        0.9993987030716112,
+        0.3101560244651787,
+        0.6391241852544398,
+        0.9996067791302367,
+        0.34064800044569965,
+        0.3993392802654636,
+        0.530763272619824,
+        0.31033134801184414,
+        1.5006607288171012,
+        1.2021667838614023,
+        1.0409747196572967,
+        0.539404146432422,
+        0.7410911199327412,
+        2.257464456131887,
+        0.40062251745767913,
+        1.2404714837494788,
+        0.33053069487200953,
+        1.5230364058116463,
+        1.5108006891185375,
+        1.1981870999774435,
+        1.09160654867685
+      ],
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "f6efae",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "783be4",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "f40f64",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "b3f280",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "d9f1d9",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "b16b7f",
+      "yaxis": "y3",
+      "ysrc": "jackp:17616:6e5c39",
+      "xsrc": "jackp:17616:f6b328",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "y": [
+        7345,
+        2655,
+        2271,
+        2414,
+        5979,
+        2327,
+        850,
+        2329,
+        2337,
+        4126,
+        12221,
+        3570,
+        16392,
+        1883,
+        14489,
+        694,
+        3581,
+        9925,
+        1926,
+        8214,
+        383,
+        626,
+        7346.677684565021,
+        2650.8651508338826,
+        2273.7171735005018,
+        2413.3467611955653,
+        5981.958951306695,
+        2327.977690141955,
+        850.2034510756258,
+        2327.680462558811,
+        2335.8288230967846,
+        4132.8069921999495,
+        12221.8725895644,
+        3572.7557581970905,
+        16359.746698766015,
+        1885.5004412264695,
+        14493.517929580792,
+        694.0033081154417,
+        3577.6625599189574,
+        9920.774354051882,
+        1927.790871329409,
+        8222.719401387305,
+        382.41198590592063,
+        626.2649608412387
+      ],
+      "x": [
+        1.59,
+        0.81,
+        0.78,
+        0.8,
+        1.22,
+        0.71,
+        0.43,
+        0.8,
+        0.72,
+        0.9,
+        2,
+        0.9,
+        2.03,
+        0.71,
+        1.94,
+        0.33,
+        0.9,
+        1.59,
+        0.71,
+        1.51,
+        0.31,
+        0.34,
+        1.587413246333523,
+        0.8093944505318844,
+        0.7792947889080841,
+        0.7988755864760525,
+        1.2209031786135156,
+        0.7111650214112349,
+        0.4295065217821689,
+        0.8007849837049644,
+        0.7212283333855459,
+        0.9004491071818032,
+        2.000496708970912,
+        0.9007649721135049,
+        2.032061797051653,
+        0.7092212731828491,
+        1.9376138601910464,
+        0.3295836235537146,
+        0.8993272291180375,
+        1.5929734670428513,
+        0.7108383521600118,
+        1.5072312835039463,
+        0.3097129253084051,
+        0.33970899408100125
+      ],
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "155d8f",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "8854fa",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "cb95c0",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "b14904",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "9fbea2",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "c6d9fa",
+      "yaxis": "y3",
+      "ysrc": "jackp:17616:b8c075",
+      "xsrc": "jackp:17616:e7d470",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "y": [
+        9702,
+        3696,
+        2142,
+        755,
+        8975,
+        3079,
+        3399,
+        2700,
+        5054,
+        4273,
+        521,
+        10184,
+        7815,
+        13956,
+        2142,
+        15147,
+        9712.46835850596,
+        3688.702106751374,
+        2142.21678675203,
+        754.9198607337196,
+        8975.070818071235,
+        3083.4183555000013,
+        3403.6812388912713,
+        2697.8749221674393,
+        5044.650649644011,
+        4273.0245142306085,
+        521.8758058909739,
+        10188.305907121061,
+        7805.547668405433,
+        13953.229708296907,
+        2140.275248694395,
+        15171.416219778084
+      ],
+      "x": [
+        1.5,
+        1.5,
+        0.7,
+        0.41,
+        1.51,
+        0.94,
+        1.01,
+        0.91,
+        1.11,
+        1,
+        0.33,
+        2.01,
+        1.51,
+        2.08,
+        0.7,
+        2.01,
+        1.4990507930286983,
+        1.497842616299511,
+        0.6997722255449608,
+        0.40924318449184616,
+        1.5097072253472426,
+        0.9387888155067724,
+        1.0115644363303002,
+        0.9096070990757399,
+        1.1112199062258352,
+        1.0001057928066628,
+        0.330158170767867,
+        2.0112904224843655,
+        1.5128784392469679,
+        2.084067410707246,
+        0.7012891862703828,
+        2.007063102306926
+      ],
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "2abad2",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "1bb905",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "355955",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "ecbc16",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "201556",
+      "yaxis": "y3",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "c631a8",
+      "yaxis": "y3",
+      "ysrc": "jackp:17616:63bc1a",
+      "xsrc": "jackp:17616:106531",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "y": [
+        919,
+        7912,
+        2129,
+        2208,
+        1799,
+        2666,
+        918.4704898769342,
+        7907.136291060538,
+        2128.1423739982088,
+        2208.788758644016,
+        1802.5154943362259,
+        2668.025260312547
+      ],
+      "x": [
+        0.47,
+        1.5,
+        0.91,
+        0.71,
+        0.72,
+        0.9,
+        0.4704023297305381,
+        1.4977863936967992,
+        0.9084133296853414,
+        0.7103930062458634,
+        0.7209212404651739,
+        0.8998394272706466
+      ],
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "b906f7",
+      "yaxis": "y4",
+      "ysrc": "jackp:17616:292289",
+      "xsrc": "jackp:17616:878a2c",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "y": [
+        2863,
+        2307,
+        2705,
+        10046,
+        4916,
+        1963,
+        1845,
+        9274,
+        5762,
+        5701,
+        552,
+        16793,
+        1752,
+        772,
+        1720,
+        1167,
+        803,
+        1215,
+        1814,
+        689,
+        4037,
+        1734,
+        2251,
+        941,
+        593,
+        1208,
+        552,
+        5206,
+        4304,
+        1582,
+        2331,
+        3048,
+        644,
+        404,
+        5543,
+        816,
+        911,
+        4903,
+        1040,
+        772,
+        709,
+        8108,
+        1994,
+        2203,
+        851,
+        1910,
+        2864.5589721284523,
+        2310.0187806754184,
+        2703.8303484277467,
+        10039.361388761881,
+        4908.813784305043,
+        1960.5394363759692,
+        1842.0316422617161,
+        9280.250540212272,
+        5761.135659336807,
+        5695.22040652117,
+        551.1873613407281,
+        16797.37021372704,
+        1751.1851312032595,
+        771.4388582041303,
+        1718.4199648560812,
+        1167.114098659569,
+        802.590958072453,
+        1214.3302580140542,
+        1814.8757499624326,
+        687.7640113767613,
+        4032.5116523496285,
+        1730.9754595973682,
+        2252.5433615319075,
+        942.2774857092497,
+        593.4562663976149,
+        1206.869071531859,
+        551.1241213678821,
+        5206.232955364515,
+        4298.008944041489,
+        1579.5840732381446,
+        2333.110359116706,
+        3047.686995568163,
+        642.8744784894147,
+        404.7766021887578,
+        5541.1892269877,
+        816.016916255178,
+        912.3349622136429,
+        4897.662113178408,
+        1040.4998113083197,
+        770.9130398976133,
+        708.0534136985216,
+        8106.382844886812,
+        1995.560389003191,
+        2205.036000808757,
+        851.2062770757921,
+        1913.4779164405904
+      ],
+      "x": [
+        0.71,
+        0.75,
+        0.7,
+        1,
+        1.24,
+        0.56,
+        0.5,
+        1,
+        1.05,
+        1.01,
+        0.3,
+        1.5,
+        0.5,
+        0.4,
+        0.52,
+        0.35,
+        0.37,
+        0.5,
+        0.6,
+        0.36,
+        0.77,
+        0.53,
+        0.61,
+        0.38,
+        0.31,
+        0.3,
+        0.3,
+        1.01,
+        0.9,
+        0.41,
+        0.55,
+        0.72,
+        0.35,
+        0.22,
+        1.01,
+        0.31,
+        0.3,
+        1.06,
+        0.42,
+        0.4,
+        0.3,
+        1.5,
+        0.54,
+        0.51,
+        0.36,
+        0.56,
+        0.7095952457810268,
+        0.7493102382520047,
+        0.6993105454875977,
+        1.0018097943694881,
+        1.2400104080181733,
+        0.559097435012193,
+        0.4994893317079819,
+        0.9980763245195371,
+        1.0501376293339906,
+        1.0115922906684707,
+        0.3002733285948228,
+        1.502165273044929,
+        0.49970485820754434,
+        0.3993573688685752,
+        0.5201559493014003,
+        0.3494308281482808,
+        0.3695749338142994,
+        0.49950144124219814,
+        0.600804388726145,
+        0.3595810642696111,
+        0.7712647739646804,
+        0.5293944132354899,
+        0.6100420971179406,
+        0.3803928197016165,
+        0.30962958159511333,
+        0.29940857916770136,
+        0.3001940097060251,
+        1.0082958535232003,
+        0.8988417603040997,
+        0.40921643628818016,
+        0.5498768501182337,
+        0.7207189503957052,
+        0.34976793621809926,
+        0.22039340558917098,
+        1.0106201277903797,
+        0.30956169633828223,
+        0.3002853919703458,
+        1.0612485142531116,
+        0.4202931113466637,
+        0.4000921901650729,
+        0.30006470721243417,
+        1.4987085824921036,
+        0.5406675066398264,
+        0.5100146826664943,
+        0.3598241547900064,
+        0.5604615113364197
+      ],
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "358fb1",
+      "yaxis": "y4",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "4fc378",
+      "yaxis": "y4",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "3f4a7d",
+      "yaxis": "y4",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "ffd6da",
+      "yaxis": "y4",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "6bfb73",
+      "yaxis": "y4",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "d2a84e",
+      "yaxis": "y4",
+      "ysrc": "jackp:17616:c068e9",
+      "xsrc": "jackp:17616:0d9a13",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "y": [
+        2867,
+        1235,
+        2061,
+        917,
+        4839,
+        2041,
+        4861,
+        1103,
+        3322,
+        1704,
+        552,
+        1034,
+        684,
+        2575,
+        12688,
+        537,
+        7220,
+        7564,
+        7118,
+        1427,
+        998,
+        689,
+        2954,
+        3223,
+        4377,
+        636,
+        911,
+        891,
+        12261,
+        1882,
+        10441,
+        4932,
+        807,
+        803,
+        1266,
+        15845,
+        893,
+        3321,
+        769,
+        6250,
+        1230,
+        709,
+        707,
+        681,
+        5694,
+        1665,
+        971,
+        3035,
+        9870,
+        1882,
+        2423,
+        1787,
+        1690,
+        12417,
+        663,
+        2482,
+        2993,
+        689,
+        5717,
+        734,
+        709,
+        713,
+        884,
+        663,
+        2421,
+        827,
+        2056,
+        9625,
+        552,
+        2863.3073846875254,
+        1235.0410754277873,
+        2061.1394030167376,
+        915.5460946470334,
+        4840.9846383887125,
+        2044.5735038711914,
+        4865.148402462572,
+        1103.191363544857,
+        3320.779683599345,
+        1704.7010892779192,
+        551.8548962306251,
+        1033.3040308505065,
+        682.9997097438029,
+        2579.7997552364773,
+        12698.30467149865,
+        537.3304689572768,
+        7213.1034246567315,
+        7552.605922415122,
+        7118.707269159842,
+        1424.2734001919348,
+        998.4908298383382,
+        688.9514289857805,
+        2952.9428414578947,
+        3220.766467597102,
+        4375.32885504362,
+        635.2022222747906,
+        910.5862918571029,
+        892.3567592817293,
+        12257.5533002125,
+        1881.3450320704349,
+        10446.743428736338,
+        4929.580388367656,
+        805.569939905001,
+        802.6998692137066,
+        1268.0852528054163,
+        15846.969660047213,
+        894.0691627019108,
+        3315.565390592984,
+        769.0875218910231,
+        6243.133909175408,
+        1231.3364188389496,
+        709.7742956728271,
+        706.9069655201511,
+        680.7050827492476,
+        5687.610631325589,
+        1663.7096872279442,
+        972.5164136016865,
+        3031.0993950104835,
+        9874.405826360817,
+        1878.6737842173793,
+        2423.082094818438,
+        1784.7823820019962,
+        1690.458973083242,
+        12401.487462051002,
+        664.216597363298,
+        2485.1778587845793,
+        2993.0418137039082,
+        689.6024853765649,
+        5726.922382170822,
+        734.3113300086499,
+        708.8944508014835,
+        714.2284257554941,
+        885.3497085754199,
+        661.7202060431199,
+        2425.1177636878965,
+        827.8640952398549,
+        2055.189506639383,
+        9619.484248129835,
+        552.4883231960048
+      ],
+      "x": [
+        0.7,
+        0.44,
+        0.56,
+        0.4,
+        1.03,
+        0.54,
+        1,
+        0.42,
+        0.72,
+        0.53,
+        0.3,
+        0.31,
+        0.31,
+        0.7,
+        1.51,
+        0.36,
+        1.04,
+        1.02,
+        1.02,
+        0.41,
+        0.34,
+        0.36,
+        0.72,
+        0.51,
+        0.76,
+        0.36,
+        0.3,
+        0.4,
+        1.13,
+        0.51,
+        1.25,
+        1.05,
+        0.43,
+        0.34,
+        0.35,
+        1.55,
+        0.33,
+        0.52,
+        0.26,
+        1.03,
+        0.41,
+        0.3,
+        0.32,
+        0.33,
+        1.01,
+        0.4,
+        0.36,
+        0.78,
+        1.11,
+        0.51,
+        0.7,
+        0.51,
+        0.61,
+        1.24,
+        0.36,
+        0.5,
+        0.72,
+        0.36,
+        0.91,
+        0.31,
+        0.3,
+        0.32,
+        0.38,
+        0.36,
+        0.58,
+        0.35,
+        0.51,
+        1.06,
+        0.3,
+        0.6989623468091251,
+        0.44015492068136575,
+        0.558992498106414,
+        0.3992779520495528,
+        1.028118218451617,
+        0.5395123197558108,
+        1.000447007915268,
+        0.42027486445645146,
+        0.720756686110868,
+        0.5298696150742593,
+        0.29953225833437297,
+        0.30951319547269907,
+        0.3103490915050528,
+        0.699056019161115,
+        1.5084146118225819,
+        0.3601027536915589,
+        1.04108897947698,
+        1.0209987652060446,
+        1.0197083080901237,
+        0.40964708208221423,
+        0.33972433424752196,
+        0.3607130175399627,
+        0.7214324629475471,
+        0.5094805648818637,
+        0.7592626300409893,
+        0.359833881185996,
+        0.300007443355754,
+        0.4004962206845817,
+        1.1299763936301004,
+        0.5093346764817639,
+        1.2496053829670777,
+        1.0482035538393493,
+        0.4294321141775121,
+        0.3399703880862411,
+        0.3498491543749766,
+        1.5506865396353218,
+        0.3295000189302351,
+        0.5209645347964528,
+        0.26002682877117406,
+        1.0301101594320927,
+        0.40935360635645507,
+        0.30024077594040544,
+        0.32042061969909846,
+        0.3293481742358264,
+        1.0108083834784685,
+        0.39956265247931133,
+        0.35963482817250303,
+        0.7808643364198934,
+        1.1118227708916855,
+        0.5095944528676186,
+        0.6990935615569753,
+        0.5105255611019138,
+        0.6102585544224337,
+        1.2420464321606084,
+        0.3604096008981896,
+        0.5008643651869537,
+        0.7205925998023248,
+        0.3602002413686343,
+        0.9083702651943231,
+        0.30965397753340557,
+        0.30048250550429756,
+        0.3197498031771598,
+        0.37964838648217664,
+        0.3600979132655104,
+        0.5799034377628591,
+        0.3495500956953886,
+        0.5092887189411638,
+        1.058866266124176,
+        0.3005072830515016
+      ],
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "364998",
+      "yaxis": "y4",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "542f19",
+      "yaxis": "y4",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "f7264e",
+      "yaxis": "y4",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "4d1ada",
+      "yaxis": "y4",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "d4e245",
+      "yaxis": "y4",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "732989",
+      "yaxis": "y4",
+      "ysrc": "jackp:17616:262a53",
+      "xsrc": "jackp:17616:30666d",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "y": [
+        912,
+        910,
+        1616,
+        3480,
+        470,
+        720,
+        3048,
+        700,
+        3406,
+        2318,
+        6125,
+        720,
+        5086,
+        462,
+        1787,
+        4869,
+        2628,
+        2677,
+        1368,
+        492,
+        952,
+        1380,
+        4472,
+        1990,
+        6863,
+        911.7571258263765,
+        911.4501018096493,
+        1615.472756430965,
+        3485.4787985684948,
+        470.029458704632,
+        719.7160814098978,
+        3042.0403334864905,
+        699.7741920437832,
+        3404.934169479795,
+        2318.7113929737493,
+        6124.594593191709,
+        720.2984662869684,
+        5095.530013638114,
+        462.68144173408405,
+        1784.9307118231027,
+        4869.513391131503,
+        2626.3476896651614,
+        2680.739084362534,
+        1367.3713984458125,
+        491.7807187611099,
+        952.6408842225588,
+        1380.2263337012007,
+        4467.785680864238,
+        1992.1058219231454,
+        6868.143239755623
+      ],
+      "x": [
+        0.42,
+        0.42,
+        0.51,
+        0.96,
+        0.27,
+        0.4,
+        0.75,
+        0.42,
+        0.71,
+        0.7,
+        1.21,
+        0.4,
+        0.92,
+        0.31,
+        0.51,
+        0.84,
+        0.7,
+        0.7,
+        0.55,
+        0.33,
+        0.4,
+        0.51,
+        1.01,
+        0.7,
+        1.02,
+        0.4198071647972373,
+        0.4198591637979997,
+        0.5099331029118354,
+        0.9618861620618347,
+        0.2704313844586991,
+        0.39924110039544597,
+        0.7494835679403926,
+        0.4200527624738516,
+        0.708807760979284,
+        0.7011569105750158,
+        1.2119519250440798,
+        0.39960779864756396,
+        0.9208751340219598,
+        0.30958787738277777,
+        0.5090276662721537,
+        0.8390247918114567,
+        0.6987722601252729,
+        0.7002103356071783,
+        0.5491438340036795,
+        0.32953039479305485,
+        0.40059861096045923,
+        0.5110159931709428,
+        1.0099122318425084,
+        0.7012360006760092,
+        1.02062607608907
+      ],
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "4e5ea2",
+      "yaxis": "y4",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "729d16",
+      "yaxis": "y4",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "2728e1",
+      "yaxis": "y4",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "745982",
+      "yaxis": "y4",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "e083f0",
+      "yaxis": "y4",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "6a8130",
+      "yaxis": "y4",
+      "ysrc": "jackp:17616:cecf5b",
+      "xsrc": "jackp:17616:8d8322",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "y": [
+        552,
+        4739,
+        589,
+        4191,
+        1605,
+        3773,
+        4672,
+        6113,
+        571,
+        589,
+        551.2096258820169,
+        4732.222562861587,
+        588.0936305859233,
+        4195.275085329128,
+        1601.9295456751415,
+        3767.6887724443454,
+        4678.473412888158,
+        6120.376710154089,
+        572.0760307506761,
+        588.0398503500066
+      ],
+      "x": [
+        0.3,
+        0.9,
+        0.32,
+        1.04,
+        0.52,
+        1.02,
+        1.01,
+        1.01,
+        0.31,
+        0.32,
+        0.30041440318281726,
+        0.8993025185928925,
+        0.3206203195607978,
+        1.0379315457690532,
+        0.5209108978358578,
+        1.018025003889451,
+        1.0085006430810832,
+        1.0101443262180516,
+        0.3104840531850404,
+        0.3199495401743513
+      ],
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "aebf8f",
+      "yaxis": "y4",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "showlegend": false,
+      "uid": "29557f",
+      "yaxis": "y4",
+      "ysrc": "jackp:17616:1f8d2e",
+      "xsrc": "jackp:17616:1dd020",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "showlegend": false,
+      "uid": "3e367a",
+      "yaxis": "y4",
+      "ysrc": "jackp:17616:1f8d2e",
+      "xsrc": "jackp:17616:1dd020",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "showlegend": false,
+      "uid": "337db2",
+      "yaxis": "y4",
+      "ysrc": "jackp:17616:1f8d2e",
+      "xsrc": "jackp:17616:1dd020",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "showlegend": false,
+      "uid": "daacfd",
+      "yaxis": "y4",
+      "ysrc": "jackp:17616:1f8d2e",
+      "xsrc": "jackp:17616:1dd020",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "showlegend": false,
+      "uid": "ec7e38",
+      "yaxis": "y4",
+      "ysrc": "jackp:17616:1f8d2e",
+      "xsrc": "jackp:17616:1dd020",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "4c0c61",
+      "yaxis": "y5",
+      "ysrc": "jackp:17616:e2020b",
+      "xsrc": "jackp:17616:097e51",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "y": [
+        3936,
+        2318,
+        2392,
+        2491,
+        17803,
+        4200,
+        706,
+        773,
+        1433,
+        4134,
+        583,
+        421,
+        2724,
+        982,
+        1010,
+        1163,
+        4062,
+        5288,
+        880,
+        7127,
+        574,
+        1192,
+        6702,
+        8946,
+        3218,
+        5544,
+        6554,
+        802,
+        5292,
+        4063,
+        2874,
+        5942,
+        6033,
+        990,
+        605,
+        2290,
+        576,
+        1415,
+        4497,
+        4682,
+        933,
+        2234,
+        3158,
+        3931.837708030491,
+        2319.699422315125,
+        2390.062813733005,
+        2493.494090227297,
+        17798.504702894414,
+        4194.454143130031,
+        706.4647994870666,
+        771.6668454068943,
+        1435.6234735072787,
+        4141.438903500546,
+        583.9041152004794,
+        421.0349395584988,
+        2724.3031939353505,
+        983.1398188527362,
+        1008.6027134639477,
+        1164.6774117204175,
+        4060.7967517406437,
+        5298.318791505002,
+        880.6010356863378,
+        7118.916866791324,
+        573.2537403169265,
+        1192.900853313141,
+        6705.594880031928,
+        8952.039106546421,
+        3217.3226110976234,
+        5544.582586219176,
+        6542.950615156624,
+        800.4542157076876,
+        5296.577853667223,
+        4056.9083804933903,
+        2869.1349213892913,
+        5948.7399079809165,
+        6028.28084119197,
+        989.6854945471318,
+        605.9759567958016,
+        2288.3328474928917,
+        574.8881561527019,
+        1415.4727675977429,
+        4490.227928693989,
+        4680.324129368353,
+        932.2692171005122,
+        2237.0724057591005,
+        3152.827709927717
+      ],
+      "x": [
+        0.9,
+        0.56,
+        0.61,
+        0.76,
+        2.03,
+        1,
+        0.35,
+        0.32,
+        0.5,
+        1.11,
+        0.35,
+        0.32,
+        0.72,
+        0.4,
+        0.39,
+        0.4,
+        0.9,
+        1.01,
+        0.34,
+        1.01,
+        0.34,
+        0.41,
+        1.01,
+        1.04,
+        0.77,
+        1,
+        1.52,
+        0.31,
+        1,
+        0.97,
+        0.7,
+        1.06,
+        1.14,
+        0.32,
+        0.3,
+        0.56,
+        0.25,
+        0.55,
+        1.01,
+        0.95,
+        0.38,
+        0.7,
+        1.06,
+        0.9009796560684872,
+        0.5592132566863012,
+        0.6104504350348424,
+        0.7605709762454774,
+        2.0307821742516885,
+        1.0009342687462592,
+        0.3493498568845443,
+        0.32061982696181046,
+        0.5001642403039719,
+        1.1100730307750273,
+        0.34945757393641713,
+        0.3200454105306684,
+        0.7189488997252828,
+        0.40017756174158015,
+        0.39003018683452495,
+        0.3998666910295283,
+        0.8992521344047488,
+        1.0110641019784592,
+        0.339898866480955,
+        1.0092814011281794,
+        0.3394471883114631,
+        0.41036293316604955,
+        1.008668649220819,
+        1.0382033070976184,
+        0.77110350357629,
+        1.0016037883546938,
+        1.51861229729931,
+        0.30955512273164554,
+        1.0005159102324452,
+        0.970899305661036,
+        0.6992740675910675,
+        1.059523326403535,
+        1.1421594290895956,
+        0.3200213935232487,
+        0.3004145188239654,
+        0.5596716812883158,
+        0.2504291299059961,
+        0.5507217384509376,
+        1.01186180914583,
+        0.9500947756409788,
+        0.3805198320484217,
+        0.6994693063063538,
+        1.06107419306477
+      ],
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "3f9f46",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "2f6286",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "6661f8",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "f6820a",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "fab8c9",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "0224d9",
+      "yaxis": "y5",
+      "ysrc": "jackp:17616:7a9127",
+      "xsrc": "jackp:17616:0baf91",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "y": [
+        802,
+        2841,
+        1085,
+        4591,
+        2525,
+        3531,
+        1041,
+        1629,
+        9996,
+        1451,
+        8486,
+        3018,
+        1131,
+        880,
+        3248,
+        865,
+        985,
+        858,
+        863,
+        2087,
+        2504,
+        3404,
+        3945,
+        2491,
+        7654,
+        1142,
+        906,
+        8958,
+        977,
+        870,
+        1168,
+        16513,
+        625,
+        1340,
+        4412,
+        3713,
+        572,
+        4458,
+        5894,
+        3405,
+        906,
+        1021,
+        1305,
+        6360,
+        1122,
+        3524,
+        901,
+        1196,
+        814,
+        1806,
+        1915,
+        828,
+        821,
+        873,
+        1908,
+        3066,
+        7589,
+        1080,
+        1666,
+        9257,
+        1221,
+        914,
+        3293,
+        426,
+        963,
+        3879,
+        9881,
+        7602,
+        801.8474226736259,
+        2843.533820734616,
+        1083.8040490910673,
+        4584.375315720565,
+        2530.0491864706514,
+        3526.4469417212895,
+        1041.1604499152845,
+        1627.4335736810858,
+        10004.86570031459,
+        1452.5339514475966,
+        8474.824207317666,
+        3012.113476820919,
+        1129.4064086525898,
+        880.771298663351,
+        3247.1698460237963,
+        865.1654802002445,
+        984.000708622825,
+        857.0818405209352,
+        862.7400230139052,
+        2086.773982544986,
+        2501.17401395372,
+        3406.2508044034103,
+        3943.395918142712,
+        2495.467025522719,
+        7667.467733471126,
+        1142.0501611568695,
+        904.6978645715419,
+        8946.950883521036,
+        976.322720414314,
+        869.8924795778994,
+        1168.7505709406078,
+        16483.14285816954,
+        623.9516709082503,
+        1337.8212923410697,
+        4406.40050075415,
+        3713.6890660065305,
+        572.4218010211939,
+        4451.375824246316,
+        5901.825540934005,
+        3405.237771810646,
+        906.6763614534037,
+        1022.3535939186175,
+        1306.3054040898544,
+        6355.1785034089835,
+        1121.0690913225824,
+        3528.731506665465,
+        902.7482185124352,
+        1195.14106611502,
+        813.1550026082033,
+        1805.5055266342133,
+        1916.495774650391,
+        828.5667287789233,
+        819.7380355303768,
+        872.7004265779482,
+        1906.0541103611608,
+        3066.2712269169515,
+        7578.229245192539,
+        1079.6791822361645,
+        1665.2484416675534,
+        9261.973067891313,
+        1223.0209703397654,
+        914.0661974289,
+        3291.473018208659,
+        425.75806543224076,
+        962.7509534114408,
+        3884.2319710408947,
+        9898.629592854317,
+        7602.982252117563
+      ],
+      "x": [
+        0.31,
+        0.52,
+        0.41,
+        1.05,
+        0.71,
+        0.78,
+        0.37,
+        0.52,
+        1.5,
+        0.52,
+        1.2,
+        0.71,
+        0.3,
+        0.34,
+        0.61,
+        0.38,
+        0.31,
+        0.34,
+        0.41,
+        0.73,
+        0.52,
+        0.71,
+        1.01,
+        0.71,
+        1.02,
+        0.42,
+        0.34,
+        1,
+        0.31,
+        0.41,
+        0.4,
+        1.8,
+        0.31,
+        0.56,
+        0.81,
+        0.8,
+        0.24,
+        1.01,
+        1.23,
+        0.71,
+        0.35,
+        0.33,
+        0.5,
+        0.92,
+        0.31,
+        0.73,
+        0.31,
+        0.44,
+        0.3,
+        0.52,
+        0.56,
+        0.3,
+        0.39,
+        0.3,
+        0.53,
+        0.71,
+        1.12,
+        0.4,
+        0.58,
+        1.05,
+        0.59,
+        0.31,
+        0.8,
+        0.34,
+        0.24,
+        0.74,
+        1.03,
+        1.01,
+        0.30954994652519724,
+        0.5208032566044443,
+        0.4095967887970371,
+        1.048061887713359,
+        0.7087092382320656,
+        0.7785665698142189,
+        0.37043046492809606,
+        0.5210399194069976,
+        1.4984718564634394,
+        0.5195351243763988,
+        1.199990919104789,
+        0.7096586301000417,
+        0.299579794050352,
+        0.34066082057393476,
+        0.6107481367389077,
+        0.3799796464644953,
+        0.3098558299018244,
+        0.33967695544089865,
+        0.4099123904073333,
+        0.7291295943004695,
+        0.5198624390822058,
+        0.7100630526312683,
+        1.0108265669806162,
+        0.7101544089530725,
+        1.0186251728286808,
+        0.4205436946006773,
+        0.3400266446995739,
+        0.9983076956814654,
+        0.3102882252437644,
+        0.41033615196824,
+        0.4000340535477765,
+        1.796992877466342,
+        0.3103140548369838,
+        0.5591828883942883,
+        0.809121287578271,
+        0.8005488478457026,
+        0.2403822173493155,
+        1.0083262901776855,
+        1.231438522374149,
+        0.7093562066012159,
+        0.34931124848568634,
+        0.3297726065481198,
+        0.5003700542505818,
+        0.920151213840957,
+        0.30939407564251603,
+        0.730548433099732,
+        0.30985726210289327,
+        0.44033934134464586,
+        0.30002098406116245,
+        0.5190041520981057,
+        0.5610522625439187,
+        0.30007177322138245,
+        0.3893728789013147,
+        0.30014145505530004,
+        0.5310226281555821,
+        0.710446506954066,
+        1.1202002277591714,
+        0.3994691323887583,
+        0.5789970620924505,
+        1.0517354943020338,
+        0.5893605356638563,
+        0.3098142813172692,
+        0.7988049819714432,
+        0.34054372155118495,
+        0.23992379133725777,
+        0.740616112816685,
+        1.0304172815589203,
+        1.0090599821290713
+      ],
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "dc8094",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "54591c",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "ed48ac",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "7611cd",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "a10b4e",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "631b1f",
+      "yaxis": "y5",
+      "ysrc": "jackp:17616:8e2b9c",
+      "xsrc": "jackp:17616:87a56a",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "y": [
+        4088,
+        792,
+        5988,
+        832,
+        2274,
+        4617,
+        3471,
+        8922,
+        4373,
+        1940,
+        530,
+        2230,
+        657,
+        3950,
+        518,
+        3420,
+        2161,
+        1338,
+        3356,
+        3692,
+        10650,
+        608,
+        2808,
+        10173,
+        4399,
+        6061,
+        2812,
+        4082,
+        2041,
+        3457,
+        3104,
+        5491,
+        7037,
+        4498,
+        2914,
+        3854,
+        4091.770619905916,
+        791.8788152924913,
+        5998.39969636659,
+        832.8714517237546,
+        2275.131637346825,
+        4616.819165396445,
+        3476.2955005463364,
+        8912.77689339611,
+        4366.100837007636,
+        1937.894842803201,
+        529.6517426062028,
+        2231.746589191861,
+        657.7648220730073,
+        3950.411051414913,
+        518.3856456521538,
+        3426.7978358521314,
+        2162.7536391200597,
+        1339.627878286515,
+        3359.537441057553,
+        3689.663295742099,
+        10659.724832252119,
+        606.8528159815402,
+        2812.2656439797756,
+        10169.829600305939,
+        4396.512692389016,
+        6072.326325002493,
+        2816.519249887763,
+        4077.7254471800475,
+        2040.8879260021874,
+        3460.5973784232256,
+        3099.504700844642,
+        5498.246022664184,
+        7050.921451852076,
+        4502.138714803353,
+        2910.913199404449,
+        3861.1658736012314
+      ],
+      "x": [
+        1,
+        0.43,
+        1.01,
+        0.39,
+        0.75,
+        0.9,
+        0.8,
+        1.01,
+        1.02,
+        0.6,
+        0.23,
+        0.7,
+        0.26,
+        0.9,
+        0.27,
+        0.93,
+        0.7,
+        0.56,
+        0.89,
+        0.71,
+        1.23,
+        0.24,
+        0.7,
+        1.2,
+        0.77,
+        1.07,
+        0.7,
+        1,
+        0.51,
+        0.9,
+        0.71,
+        1.06,
+        1.01,
+        0.9,
+        0.8,
+        0.92,
+        1.0003035426970706,
+        0.4295109142819016,
+        1.0107488989296045,
+        0.38982412875121975,
+        0.7485760271588692,
+        0.8991887231312831,
+        0.7996952088228714,
+        1.0100519962121524,
+        1.020542362047063,
+        0.6011621118392716,
+        0.22990015122688792,
+        0.6996961098579478,
+        0.26043509120756186,
+        0.9008026447369735,
+        0.270306912152565,
+        0.9285262018613064,
+        0.7010417352789942,
+        0.5600026627313066,
+        0.8908166154810649,
+        0.7113268763995968,
+        1.2286815775462263,
+        0.23991792007518498,
+        0.6986845887263227,
+        1.2016528890195273,
+        0.7694426417218985,
+        1.072120652172668,
+        0.7002418782913061,
+        1.0017451301179852,
+        0.5101388127166598,
+        0.9015531870984818,
+        0.7093694663125639,
+        1.0600599894089113,
+        1.010360923484627,
+        0.9007818255124138,
+        0.8009325806511676,
+        0.9198594783578563
+      ],
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "39c683",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "989bc9",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "50519f",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "d92979",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "a7b6f1",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "92020c",
+      "yaxis": "y5",
+      "ysrc": "jackp:17616:d23bb6",
+      "xsrc": "jackp:17616:b1d52c",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "y": [
+        2098,
+        6332,
+        1182,
+        2313,
+        1563,
+        6446,
+        1406,
+        4678,
+        3532,
+        645,
+        3445,
+        2130,
+        4899,
+        2281,
+        2094.728777840808,
+        6338.591957753566,
+        1182.9184017402188,
+        2317.471584183058,
+        1562.4000576664196,
+        6451.7835090991,
+        1406.0335701822944,
+        4670.846873956501,
+        3530.875314246411,
+        645.324403427794,
+        3441.931964132381,
+        2129.4262064945306,
+        4897.635831583063,
+        2277.0287810459713
+      ],
+      "x": [
+        1,
+        1.01,
+        0.49,
+        0.7,
+        0.54,
+        1.01,
+        0.51,
+        1.02,
+        1.01,
+        0.32,
+        0.7,
+        0.71,
+        1.01,
+        0.53,
+        0.9980571099735377,
+        1.0091673563802186,
+        0.49056167950757007,
+        0.6995485812259165,
+        0.5400468359947491,
+        1.010325110140703,
+        0.5100215591615811,
+        1.0200657900929562,
+        1.009896322010345,
+        0.32023712619894307,
+        0.7005182318299936,
+        0.7107101623313072,
+        1.00877267630766,
+        0.5297233084986743
+      ],
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "c9fbf7",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "0f85c8",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "cfbf7e",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "120f88",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "d9a8e8",
+      "yaxis": "y5",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "537fed",
+      "yaxis": "y5",
+      "ysrc": "jackp:17616:3c7520",
+      "xsrc": "jackp:17616:b91412",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "y": [
+        2691,
+        16280,
+        810,
+        613,
+        1158,
+        1974,
+        4561,
+        3688,
+        13622,
+        2695.4766957642546,
+        16258.216833453082,
+        808.4758077845083,
+        613.3897000366561,
+        1159.0920608061306,
+        1970.6879983168462,
+        4566.461330167714,
+        3691.1995207916834,
+        13603.894418813674
+      ],
+      "x": [
+        0.72,
+        2.03,
+        0.36,
+        0.5,
+        0.7,
+        0.7,
+        1.01,
+        0.96,
+        1.72,
+        0.7206387693237523,
+        2.029408151693385,
+        0.35977063423793854,
+        0.500216241272243,
+        0.7008310300098876,
+        0.6996785953510553,
+        1.0103142588592704,
+        0.9612923697382663,
+        1.7167738455757051
+      ],
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "f44104",
+      "yaxis": "y6",
+      "ysrc": "jackp:17616:3c2a0b",
+      "xsrc": "jackp:17616:19613b",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "y": [
+        553,
+        17175,
+        4077,
+        2433,
+        1851,
+        3450,
+        842,
+        1005,
+        5485,
+        7670,
+        792,
+        658,
+        9900,
+        14882,
+        544,
+        6875,
+        1613,
+        698,
+        2854,
+        1116,
+        1097,
+        2673,
+        4044,
+        2064,
+        1028,
+        11644,
+        3662,
+        956,
+        1037,
+        9072,
+        1079,
+        964,
+        816,
+        1294,
+        1079,
+        17552,
+        1000,
+        1003,
+        4334,
+        3133,
+        4027,
+        1746,
+        4853,
+        2629,
+        1708,
+        552.320766463179,
+        17177.169117874604,
+        4080.8276363802984,
+        2432.5985426744555,
+        1849.7000496150404,
+        3453.6583981820963,
+        842.0315796959627,
+        1006.7838099735794,
+        5487.181653665482,
+        7658.597309279918,
+        792.7549738545077,
+        657.1490495084415,
+        9891.747771988723,
+        14881.610133341625,
+        544.5462006344087,
+        6870.911647595399,
+        1610.5324184305398,
+        697.1248936820117,
+        2858.554912933277,
+        1117.8199343265728,
+        1097.5692547502435,
+        2669.8774155178576,
+        4039.155009583268,
+        2064.514504146819,
+        1029.71067411172,
+        11627.073532009625,
+        3654.919171151088,
+        957.5069548972714,
+        1036.1474456326698,
+        9081.215452610659,
+        1079.0344968284244,
+        963.6257008560758,
+        816.5646731532389,
+        1294.0845065305014,
+        1076.8537774408392,
+        17585.43484438099,
+        1000.6654631204958,
+        1002.4006442667917,
+        4330.404471012981,
+        3128.161073621237,
+        4032.1007163784816,
+        1748.2312151354295,
+        4856.939699017142,
+        2627.4390729771785,
+        1705.0485634883596
+      ],
+      "x": [
+        0.24,
+        1.57,
+        1,
+        0.66,
+        0.57,
+        1,
+        0.32,
+        0.48,
+        1.06,
+        1.07,
+        0.43,
+        0.3,
+        1.5,
+        1.71,
+        0.31,
+        1.24,
+        0.57,
+        0.31,
+        0.7,
+        0.35,
+        0.3,
+        0.7,
+        1.01,
+        0.63,
+        0.44,
+        1.41,
+        0.9,
+        0.34,
+        0.52,
+        1.5,
+        0.41,
+        0.41,
+        0.31,
+        0.46,
+        0.41,
+        1.59,
+        0.38,
+        0.44,
+        0.9,
+        0.74,
+        0.94,
+        0.5,
+        1.01,
+        0.71,
+        0.66,
+        0.240082383608907,
+        1.5677747059252893,
+        1.0013592402614278,
+        0.6591117043824954,
+        0.5698849183015202,
+        1.0018252160221826,
+        0.3197349568953318,
+        0.4796179070735658,
+        1.061165388911516,
+        1.070473045335228,
+        0.4303683726855205,
+        0.29946392074051287,
+        1.5027492694547653,
+        1.7091398772825894,
+        0.310198594201264,
+        1.2400441113281861,
+        0.5701497640764086,
+        0.31021567520090665,
+        0.7012437010052769,
+        0.3494795597792197,
+        0.3003389643664923,
+        0.7002414149073978,
+        1.0105928275040212,
+        0.6294736070883389,
+        0.44032727665148874,
+        1.4087212639024023,
+        0.9000761921266145,
+        0.3403859347526661,
+        0.5190013243443063,
+        1.4986404798251245,
+        0.40991088090262656,
+        0.4092035401248669,
+        0.3103234684003706,
+        0.45939365923818676,
+        0.41020448033079704,
+        1.590047612075604,
+        0.38052940601089924,
+        0.4393354374982491,
+        0.8997379629481208,
+        0.7413346610234965,
+        0.9395712103213891,
+        0.5009865084170507,
+        1.0114866363328459,
+        0.7113134668075434,
+        0.6598192258765629
+      ],
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "8b57ac",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "a14450",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "2d9c83",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "deb670",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "c63123",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "8fb1ff",
+      "yaxis": "y6",
+      "ysrc": "jackp:17616:aca112",
+      "xsrc": "jackp:17616:1f9da8",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "y": [
+        1724,
+        4199,
+        1675,
+        911,
+        6048,
+        7631,
+        1613,
+        1609,
+        7198,
+        866,
+        865,
+        4832,
+        1069,
+        2491,
+        3381,
+        4504,
+        787,
+        3729,
+        766,
+        7088,
+        723,
+        723,
+        1668,
+        975,
+        487,
+        3553,
+        1755,
+        792,
+        881,
+        1781,
+        956,
+        1104,
+        5599,
+        702,
+        1819,
+        956,
+        1074,
+        3330,
+        698,
+        435,
+        658,
+        1746,
+        516,
+        741,
+        427,
+        1205,
+        904,
+        8413,
+        702,
+        16507,
+        1094,
+        702,
+        11642,
+        2952,
+        3510,
+        4205,
+        5534,
+        6328,
+        1197,
+        987,
+        621,
+        1754,
+        16650,
+        1847,
+        1883,
+        649,
+        2656,
+        1041,
+        1177,
+        625,
+        3941,
+        10104,
+        530,
+        3620,
+        781,
+        5372,
+        1722.8901803752985,
+        4199.875579890047,
+        1674.9853723739323,
+        910.8769283050219,
+        6056.986989880234,
+        7624.59624956712,
+        1612.3315019369463,
+        1610.895406150162,
+        7197.992588690986,
+        867.3805766982041,
+        865.3552837119032,
+        4826.152964286931,
+        1070.1254852366874,
+        2488.2513345177667,
+        3375.944211083391,
+        4497.222395726983,
+        785.4913907587396,
+        3732.614190908278,
+        767.4485201967035,
+        7087.272150495703,
+        723.8631674059392,
+        723.0704518337116,
+        1666.9162502489662,
+        976.7674538362112,
+        486.5147121395696,
+        3554.050375522784,
+        1758.4336664991376,
+        792.1071818211401,
+        881.1870748214133,
+        1782.3696839491074,
+        957.5566901054617,
+        1102.5263309892848,
+        5606.1964737770295,
+        702.5445235570187,
+        1822.1282557654602,
+        957.705904498667,
+        1074.7143773948137,
+        3325.3790823290587,
+        699.2111343796319,
+        435.45920606339007,
+        659.0993380680658,
+        1745.4106253468112,
+        516.6916008396566,
+        741.9793264984147,
+        427.0124593404038,
+        1205.8064502006291,
+        902.314924188603,
+        8412.571143152316,
+        701.6902992890869,
+        16503.287086499324,
+        1094.7943956128338,
+        702.3847974482447,
+        11659.257380972997,
+        2947.8390967002647,
+        3504.5153048739726,
+        4200.133702797785,
+        5530.01223138437,
+        6317.239129159934,
+        1194.6504982266056,
+        985.2869126397331,
+        622.0818219260613,
+        1754.6384783217818,
+        16626.22674083621,
+        1850.0078920503545,
+        1885.9988564477771,
+        649.7115284704144,
+        2654.720445895572,
+        1043.0001653236757,
+        1177.410495688278,
+        625.9605249156893,
+        3936.9743640674014,
+        10085.61610690339,
+        529.285576750833,
+        3617.590103542725,
+        779.5338805029787,
+        5381.705324989795
+      ],
+      "x": [
+        0.5,
+        1.03,
+        0.55,
+        0.3,
+        1,
+        1.27,
+        0.54,
+        0.55,
+        1.01,
+        0.38,
+        0.31,
+        1.01,
+        0.38,
+        0.57,
+        0.71,
+        1.01,
+        0.28,
+        1.13,
+        0.3,
+        1.21,
+        0.33,
+        0.33,
+        0.52,
+        0.4,
+        0.28,
+        0.71,
+        0.59,
+        0.33,
+        0.34,
+        0.51,
+        0.3,
+        0.5,
+        1.01,
+        0.32,
+        0.57,
+        0.34,
+        0.31,
+        0.7,
+        0.31,
+        0.31,
+        0.3,
+        0.5,
+        0.32,
+        0.33,
+        0.32,
+        0.51,
+        0.4,
+        1.06,
+        0.32,
+        1.62,
+        0.36,
+        0.32,
+        1.52,
+        0.7,
+        0.73,
+        0.7,
+        1.01,
+        1.18,
+        0.35,
+        0.31,
+        0.3,
+        0.54,
+        2,
+        0.54,
+        0.59,
+        0.37,
+        0.7,
+        0.37,
+        0.42,
+        0.28,
+        0.92,
+        1.07,
+        0.23,
+        0.73,
+        0.33,
+        1.01,
+        0.49900484807230416,
+        1.0299805043073862,
+        0.5506604648878496,
+        0.3003800591808506,
+        0.9996492274662403,
+        1.2714178004710106,
+        0.5398442217352499,
+        0.5501429259689692,
+        1.0119923083139604,
+        0.37929081745711807,
+        0.3100542075183445,
+        1.0096110003524488,
+        0.37985413637523524,
+        0.5693254753624232,
+        0.7104405587103362,
+        1.0086621742026696,
+        0.280488060323646,
+        1.128699017472166,
+        0.30028118152764033,
+        1.2118649535577726,
+        0.32997638333472157,
+        0.3304415795482866,
+        0.5204102791159161,
+        0.3999846438520429,
+        0.2801618903283543,
+        0.7099987408614844,
+        0.5902688662100554,
+        0.32947218542016465,
+        0.3400801425514219,
+        0.5101483821145416,
+        0.2998254996456135,
+        0.5007180759103517,
+        1.00990923322329,
+        0.3195487052571707,
+        0.5689386690040519,
+        0.339921679176885,
+        0.3100781254441127,
+        0.7012708172480682,
+        0.30964833117288687,
+        0.31041195683799716,
+        0.2994424437701638,
+        0.50034058278844,
+        0.3205205800066817,
+        0.330230338079349,
+        0.31957283796630276,
+        0.5091914545658371,
+        0.4000600213594887,
+        1.0610214791497647,
+        0.3205533838922689,
+        1.6221064949120607,
+        0.35961984082180826,
+        0.3198128057980894,
+        1.5225734461355696,
+        0.700340479818071,
+        0.7287532567434658,
+        0.6989176631692738,
+        1.0108829992990223,
+        1.1777240511175244,
+        0.35031872370426365,
+        0.3103325311205659,
+        0.3001418655636086,
+        0.5406142289751227,
+        1.9984601086678988,
+        0.5402987461284409,
+        0.5904882411711434,
+        0.37039817138892833,
+        0.6989943104001308,
+        0.36940623476358003,
+        0.42043426564428055,
+        0.2799901748947451,
+        0.9211514780582396,
+        1.0688836132683857,
+        0.22987285585108314,
+        0.7296406368104583,
+        0.3304154873110362,
+        1.00910336278114
+      ],
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "3c5552",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "52c2dc",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "0d1561",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "3c4552",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "088474",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "e676c7",
+      "yaxis": "y6",
+      "ysrc": "jackp:17616:24335b",
+      "xsrc": "jackp:17616:2f0d1f",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "y": [
+        13015,
+        811,
+        653,
+        4170,
+        879,
+        2310,
+        3358,
+        1629,
+        6300,
+        16064,
+        3374,
+        4011,
+        705,
+        2894,
+        698,
+        2793,
+        2043,
+        2516,
+        3886,
+        1883,
+        15840,
+        622,
+        772,
+        13892,
+        7306,
+        2040,
+        2967,
+        9475,
+        2757,
+        1362,
+        11460,
+        485,
+        707,
+        995,
+        2363,
+        737,
+        1175,
+        7677,
+        13025.75829910102,
+        809.8451977189434,
+        651.7192249334846,
+        4172.7321773727745,
+        878.6387169281485,
+        2307.8077616829205,
+        3354.2018667353773,
+        1631.8146389411634,
+        6297.359370406381,
+        16063.23619902139,
+        3376.107672913597,
+        4007.7542798577397,
+        704.4385021505707,
+        2898.056201620157,
+        696.8331849406733,
+        2790.5601910406112,
+        2046.0018332523427,
+        2516.2939395273384,
+        3890.2020341521174,
+        1886.3764379503073,
+        15819.499439411098,
+        621.101812781229,
+        772.2540295633265,
+        13883.716383093695,
+        7300.4304217164845,
+        2043.9810455598906,
+        2968.9553049332,
+        9490.416013516326,
+        2758.1947480683543,
+        1361.9103862987956,
+        11452.692502115951,
+        485.24263989902187,
+        707.9471990848626,
+        993.663277742166,
+        2362.9576836171645,
+        737.0585439266869,
+        1175.2883456415364,
+        7686.848864815678
+      ],
+      "x": [
+        1.2,
+        0.37,
+        0.42,
+        1.11,
+        0.4,
+        0.7,
+        0.7,
+        0.5,
+        1.5,
+        2,
+        0.72,
+        0.92,
+        0.41,
+        0.81,
+        0.31,
+        0.7,
+        0.53,
+        0.71,
+        0.94,
+        0.57,
+        2,
+        0.3,
+        0.33,
+        1.5,
+        1.01,
+        0.56,
+        0.7,
+        1.22,
+        0.7,
+        0.59,
+        1.51,
+        0.24,
+        0.28,
+        0.3,
+        0.75,
+        0.4,
+        0.52,
+        1.03,
+        1.2005349982364548,
+        0.37028931090244405,
+        0.4194512783808139,
+        1.1078337782160257,
+        0.40060158708129534,
+        0.6989616132300857,
+        0.6994770029241004,
+        0.4999956507923215,
+        1.498897632045621,
+        1.9996306399897568,
+        0.7185990276393558,
+        0.9215716038135582,
+        0.4100256751279456,
+        0.8088526848613312,
+        0.31034149440875736,
+        0.7005823239146423,
+        0.5296457659178686,
+        0.710828133447656,
+        0.9418661634595475,
+        0.5698863258580035,
+        1.9972013131045436,
+        0.3003463902176272,
+        0.3300024009448402,
+        1.5020407443396695,
+        1.0094174066629473,
+        0.5609881936266335,
+        0.6997748576477741,
+        1.2223379806704078,
+        0.6987027108771681,
+        0.589770349648716,
+        1.5087430292371877,
+        0.2399652650405737,
+        0.2804759825384551,
+        0.30000323431241654,
+        0.7505818934272024,
+        0.40070291502091443,
+        0.5199751531429737,
+        1.0279595744053134
+      ],
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "28ccac",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "e0c904",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "f8cc43",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "80ec78",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "2f9354",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "0df67e",
+      "yaxis": "y6",
+      "ysrc": "jackp:17616:ab87d8",
+      "xsrc": "jackp:17616:5cf5f8",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "y": [
+        1554,
+        5226,
+        2777,
+        1224,
+        18236,
+        2020,
+        1336,
+        588,
+        16582,
+        1906,
+        818,
+        2305,
+        603,
+        3218,
+        4307,
+        1395,
+        2038,
+        5871,
+        2573,
+        1554.82679634372,
+        5229.637262409208,
+        2776.556266746367,
+        1226.3428644712444,
+        18225.29149999851,
+        2016.3363500299324,
+        1338.0192597944567,
+        587.8559859298695,
+        16598.40152269338,
+        1909.1776224636033,
+        818.9306906997704,
+        2307.9922895417158,
+        604.0656406173046,
+        3219.7036970836352,
+        4307.956119607175,
+        1393.2588074119992,
+        2040.5793456531917,
+        5863.881001850177,
+        2570.820248102243
+      ],
+      "x": [
+        0.52,
+        1.01,
+        0.7,
+        0.53,
+        2.02,
+        0.51,
+        0.58,
+        0.35,
+        2.01,
+        0.51,
+        0.41,
+        0.72,
+        0.3,
+        0.9,
+        1,
+        0.5,
+        0.7,
+        1.68,
+        0.71,
+        0.520789692478644,
+        1.0111478450077682,
+        0.699997673113697,
+        0.5296351634987143,
+        2.0183020167395758,
+        0.5100954750848816,
+        0.5798291524507336,
+        0.3498260154557881,
+        2.006447247570367,
+        0.5099278932911349,
+        0.40997920599961973,
+        0.720881576319888,
+        0.29953727426899396,
+        0.8988683873371643,
+        0.9980367147532674,
+        0.5008025322854965,
+        0.6989047666651016,
+        1.6821206422486499,
+        0.7111415983770736
+      ],
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "9c4c87",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "e5fa55",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "a77d42",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "d56609",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "b3dc64",
+      "yaxis": "y6",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "182eac",
+      "yaxis": "y6",
+      "ysrc": "jackp:17616:601f7d",
+      "xsrc": "jackp:17616:12eb26",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "y": [
+        2241,
+        4140,
+        829,
+        818,
+        3407,
+        1641,
+        2242.925253387802,
+        4147.072830942468,
+        830.3155842019498,
+        817.9753582654272,
+        3402.31278199832,
+        1643.1965704151419
+      ],
+      "x": [
+        0.7,
+        1,
+        0.47,
+        0.41,
+        0.92,
+        0.58,
+        0.6986007710827323,
+        1.0000527453298353,
+        0.47052746043580773,
+        0.4101842905936925,
+        0.9214184711784675,
+        0.5800807083668212
+      ],
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "376ffd",
+      "yaxis": "y7",
+      "ysrc": "jackp:17616:375858",
+      "xsrc": "jackp:17616:725e8e",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "y": [
+        15035,
+        4347,
+        7757,
+        13703,
+        1931,
+        8770,
+        11966,
+        12369,
+        4200,
+        12369,
+        15062.902455167796,
+        4338.961958906902,
+        7769.210456296972,
+        13703.220747815232,
+        1932.2894796404178,
+        8755.730187476243,
+        11965.185424809546,
+        12352.45020984692,
+        4204.349098242828,
+        12392.587560058308
+      ],
+      "x": [
+        2.09,
+        1.15,
+        1.71,
+        2.22,
+        0.74,
+        1.51,
+        2,
+        2.01,
+        1.25,
+        2.01,
+        2.086005047884031,
+        1.1516691656148632,
+        1.7127093010205887,
+        2.2204492059573364,
+        0.7409341289336699,
+        1.510592768848777,
+        2.0022666903811523,
+        2.0063055978129785,
+        1.2515511924834368,
+        2.011281381954683
+      ],
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "611cdd",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "713024",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "6bc46f",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "3c8aac",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "6c6bf7",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "5d7bed",
+      "yaxis": "y7",
+      "ysrc": "jackp:17616:a2b0f6",
+      "xsrc": "jackp:17616:9cbb59",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "y": [
+        1843,
+        706,
+        8687,
+        2850,
+        4361,
+        601,
+        3048,
+        2139,
+        5083,
+        3922,
+        4860,
+        7550,
+        1899,
+        4092,
+        1841.3601040191468,
+        705.0089045819386,
+        8700.662967375847,
+        2846.8969096114097,
+        4352.502914491186,
+        600.010558662529,
+        3043.745239213812,
+        2136.0156869435637,
+        5079.6071077225915,
+        3922.5938784664722,
+        4868.644673061462,
+        7542.439807411715,
+        1901.6944864928223,
+        4095.667245305671
+      ],
+      "x": [
+        0.7,
+        0.43,
+        1.52,
+        0.75,
+        1.09,
+        0.37,
+        0.9,
+        0.72,
+        1.23,
+        1.03,
+        1.26,
+        1.53,
+        0.71,
+        1.11,
+        0.6986172379787887,
+        0.43060540251016755,
+        1.51802673647325,
+        0.7504261062807371,
+        1.0892754392423503,
+        0.36977360758923017,
+        0.8991605153201632,
+        0.7211999010546872,
+        1.2305311529218212,
+        1.0305125681710885,
+        1.2604320890615313,
+        1.5274280662291178,
+        0.7106160170944144,
+        1.1091913541364666
+      ],
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "f77a23",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "31c19e",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "bb8e17",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x2",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "506be4",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "fb235a",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "1b9a1f",
+      "yaxis": "y7",
+      "ysrc": "jackp:17616:93c145",
+      "xsrc": "jackp:17616:4bf754",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "y": [
+        3576,
+        731,
+        3534,
+        5029,
+        6316,
+        3112,
+        13693,
+        17920,
+        5759,
+        2774,
+        12342,
+        3576.0816876163076,
+        731.8450534184641,
+        3538.3881629269017,
+        5019.916592161721,
+        6318.027848339106,
+        3116.588720689764,
+        13671.827147967186,
+        17946.88177845104,
+        5752.4720331142635,
+        2772.283520765185,
+        12342.948879100728
+      ],
+      "x": [
+        0.93,
+        0.43,
+        1.02,
+        1.22,
+        1.45,
+        0.91,
+        2.02,
+        2.39,
+        1.35,
+        1.17,
+        2,
+        0.9314646758862032,
+        0.42984234969015706,
+        1.0182413961916694,
+        1.2210483932827825,
+        1.4493372013514345,
+        0.9084700243314493,
+        2.0211261895508548,
+        2.3888521722984284,
+        1.3489145458927079,
+        1.170069082769639,
+        1.9987036510530596
+      ],
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "e4c450",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "765cbc",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x3",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "a67094",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "0f1cf4",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "633596",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "26c716",
+      "yaxis": "y7",
+      "ysrc": "jackp:17616:ec6fcf",
+      "xsrc": "jackp:17616:6fd863",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "y": [
+        4824,
+        1959,
+        3000,
+        15231,
+        3285,
+        5096,
+        10666,
+        4831.47446349996,
+        1956.9411648180162,
+        3000.162971265123,
+        15248.028051785392,
+        3290.3892755081297,
+        5106.18019222679,
+        10646.73696276632
+      ],
+      "x": [
+        1.21,
+        0.72,
+        0.93,
+        2.56,
+        1.04,
+        1.21,
+        2.02,
+        1.2112145980970856,
+        0.720411584031692,
+        0.9305587282798337,
+        2.558684517116645,
+        1.041797811331994,
+        1.2115245981725518,
+        2.0225361219537636
+      ],
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "56b479",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x4",
+      "type": "scattergl",
+      "name": "Fair"
+    },
+    {
+      "uid": "d8c533",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(31, 119, 180)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Premium"
+    },
+    {
+      "uid": "fae6ca",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(255, 127, 14)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Ideal"
+    },
+    {
+      "uid": "9d702c",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(44, 160, 44)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Very Good"
+    },
+    {
+      "uid": "d2e073",
+      "yaxis": "y7",
+      "marker": {
+        "color": "rgb(214, 39, 40)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "type": "scattergl",
+      "name": "Good"
+    },
+    {
+      "uid": "db52b5",
+      "yaxis": "y7",
+      "ysrc": "jackp:17616:e406b3",
+      "xsrc": "jackp:17616:1f1b53",
+      "marker": {
+        "color": "rgb(148, 103, 189)",
+        "line": {
+          "color": "black",
+          "width": 0.1
+        },
+        "size": 4
+      },
+      "mode": "markers",
+      "xaxis": "x5",
+      "y": [
+        15824,
+        15845.340584731564
+      ],
+      "x": [
+        2.4,
+        2.402684126513055
+      ],
+      "type": "scattergl",
+      "name": "Fair"
+    }
+  ],
+  "layout": {
+    "width": 700,
+    "height": 700,
+    "showlegend": true,
+    "xaxis4": {
+      "domain": [
+        0.609,
+        0.7969999999999999
+      ],
+      "ticklen": 0,
+      "dtick": 0,
+      "range": [
+        -0.2,
+        4.21
+      ],
+      "zeroline": false,
+      "position": 0,
+      "type": "linear",
+      "anchor": "free"
+    },
+    "yaxis6": {
+      "domain": [
+        0.14500000000000002,
+        0.275
+      ],
+      "ticklen": 0,
+      "dtick": 3942,
+      "range": [
+        -511.95000000000005,
+        19199.6475
+      ],
+      "zeroline": false,
+      "position": 0,
+      "type": "linear",
+      "anchor": "free"
+    },
+    "yaxis": {
+      "domain": [
+        0.8700000000000001,
+        1
+      ],
+      "ticklen": 0,
+      "dtick": 3942,
+      "range": [
+        -511.95000000000005,
+        19199.6475
+      ],
+      "zeroline": false,
+      "position": 0,
+      "type": "linear",
+      "anchor": "free"
+    },
+    "paper_bgcolor": "rgb(251, 251, 251)",
+    "xaxis5": {
+      "domain": [
+        0.812,
+        1
+      ],
+      "ticklen": 0,
+      "dtick": 0,
+      "range": [
+        -0.2,
+        4.21
+      ],
+      "zeroline": false,
+      "position": 0,
+      "type": "linear",
+      "anchor": "free"
+    },
+    "dragmode": "select",
+    "xaxis3": {
+      "domain": [
+        0.406,
+        0.5940000000000001
+      ],
+      "ticklen": 0,
+      "dtick": 0,
+      "range": [
+        -0.2,
+        4.21
+      ],
+      "zeroline": false,
+      "position": 0,
+      "type": "linear",
+      "anchor": "free"
+    },
+    "autosize": true,
+    "xaxis2": {
+      "domain": [
+        0.203,
+        0.391
+      ],
+      "ticklen": 0,
+      "dtick": 0,
+      "range": [
+        -0.2,
+        4.21
+      ],
+      "zeroline": false,
+      "position": 0,
+      "type": "linear",
+      "anchor": "free"
+    },
+    "yaxis2": {
+      "domain": [
+        0.7250000000000001,
+        0.8550000000000001
+      ],
+      "ticklen": 0,
+      "dtick": 3942,
+      "range": [
+        -511.95000000000005,
+        19199.6475
+      ],
+      "zeroline": false,
+      "position": 0,
+      "type": "linear",
+      "anchor": "free"
+    },
+    "yaxis3": {
+      "domain": [
+        0.5800000000000001,
+        0.7100000000000001
+      ],
+      "ticklen": 0,
+      "dtick": 3942,
+      "range": [
+        -511.95000000000005,
+        19199.6475
+      ],
+      "zeroline": false,
+      "position": 0,
+      "type": "linear",
+      "anchor": "free"
+    },
+    "yaxis4": {
+      "domain": [
+        0.43500000000000005,
+        0.5650000000000001
+      ],
+      "ticklen": 0,
+      "dtick": 3942,
+      "range": [
+        -511.95000000000005,
+        19199.6475
+      ],
+      "zeroline": false,
+      "position": 0,
+      "type": "linear",
+      "anchor": "free"
+    },
+    "yaxis5": {
+      "domain": [
+        0.29000000000000004,
+        0.42000000000000004
+      ],
+      "ticklen": 0,
+      "dtick": 3942,
+      "range": [
+        -511.95000000000005,
+        19199.6475
+      ],
+      "zeroline": false,
+      "position": 0,
+      "type": "linear",
+      "anchor": "free"
+    },
+    "xaxis": {
+      "domain": [
+        0,
+        0.188
+      ],
+      "ticklen": 0,
+      "dtick": 0,
+      "range": [
+        -0.2,
+        4.21
+      ],
+      "zeroline": false,
+      "type": "linear",
+      "anchor": "y7"
+    },
+    "yaxis7": {
+      "domain": [
+        0,
+        0.13
+      ],
+      "ticklen": 0,
+      "dtick": 3942,
+      "range": [
+        -511.95000000000005,
+        19199.6475
+      ],
+      "zeroline": false,
+      "type": "linear",
+      "anchor": "x"
+    },
+    "title": "",
+    "hovermode": "closest",
+    "annotations": [
+      {
+        "yanchor": "middle",
+        "xref": "paper",
+        "xanchor": "center",
+        "yref": "paper",
+        "text": "Premium",
+        "textangle": 0,
+        "y": 1.03,
+        "x": 0.094,
+        "font": {
+          "color": "#0f0f0f",
+          "size": 13
+        },
+        "showarrow": false
+      },
+      {
+        "yanchor": "middle",
+        "xref": "paper",
+        "xanchor": "center",
+        "yref": "paper",
+        "text": "Ideal",
+        "textangle": 0,
+        "y": 1.03,
+        "x": 0.29700000000000004,
+        "font": {
+          "color": "#0f0f0f",
+          "size": 13
+        },
+        "showarrow": false
+      },
+      {
+        "yanchor": "middle",
+        "xref": "paper",
+        "xanchor": "center",
+        "yref": "paper",
+        "text": "Very Good",
+        "textangle": 0,
+        "y": 1.03,
+        "x": 0.5,
+        "font": {
+          "color": "#0f0f0f",
+          "size": 13
+        },
+        "showarrow": false
+      },
+      {
+        "yanchor": "middle",
+        "xref": "paper",
+        "xanchor": "center",
+        "yref": "paper",
+        "text": "Good",
+        "textangle": 0,
+        "y": 1.03,
+        "x": 0.703,
+        "font": {
+          "color": "#0f0f0f",
+          "size": 13
+        },
+        "showarrow": false
+      },
+      {
+        "yanchor": "middle",
+        "xref": "paper",
+        "xanchor": "center",
+        "yref": "paper",
+        "text": "Fair",
+        "textangle": 0,
+        "y": 1.03,
+        "x": 0.906,
+        "font": {
+          "color": "#0f0f0f",
+          "size": 13
+        },
+        "showarrow": false
+      },
+      {
+        "yanchor": "middle",
+        "xref": "paper",
+        "xanchor": "center",
+        "yref": "paper",
+        "text": "G",
+        "textangle": 90,
+        "y": 0.935,
+        "x": 1.03,
+        "font": {
+          "color": "#0f0f0f",
+          "size": 13
+        },
+        "showarrow": false
+      },
+      {
+        "yanchor": "middle",
+        "xref": "paper",
+        "xanchor": "center",
+        "yref": "paper",
+        "text": "H",
+        "textangle": 90,
+        "y": 0.79,
+        "x": 1.03,
+        "font": {
+          "color": "#0f0f0f",
+          "size": 13
+        },
+        "showarrow": false
+      },
+      {
+        "yanchor": "middle",
+        "xref": "paper",
+        "xanchor": "center",
+        "yref": "paper",
+        "text": "I",
+        "textangle": 90,
+        "y": 0.645,
+        "x": 1.03,
+        "font": {
+          "color": "#0f0f0f",
+          "size": 13
+        },
+        "showarrow": false
+      },
+      {
+        "yanchor": "middle",
+        "xref": "paper",
+        "xanchor": "center",
+        "yref": "paper",
+        "text": "D",
+        "textangle": 90,
+        "y": 0.5,
+        "x": 1.03,
+        "font": {
+          "color": "#0f0f0f",
+          "size": 13
+        },
+        "showarrow": false
+      },
+      {
+        "yanchor": "middle",
+        "xref": "paper",
+        "xanchor": "center",
+        "yref": "paper",
+        "text": "F",
+        "textangle": 90,
+        "y": 0.35500000000000004,
+        "x": 1.03,
+        "font": {
+          "color": "#0f0f0f",
+          "size": 13
+        },
+        "showarrow": false
+      },
+      {
+        "yanchor": "middle",
+        "xref": "paper",
+        "xanchor": "center",
+        "yref": "paper",
+        "text": "E",
+        "textangle": 90,
+        "y": 0.21000000000000002,
+        "x": 1.03,
+        "font": {
+          "color": "#0f0f0f",
+          "size": 13
+        },
+        "showarrow": false
+      },
+      {
+        "yanchor": "middle",
+        "xref": "paper",
+        "xanchor": "center",
+        "yref": "paper",
+        "text": "J",
+        "textangle": 90,
+        "y": 0.065,
+        "x": 1.03,
+        "font": {
+          "color": "#0f0f0f",
+          "size": 13
+        },
+        "showarrow": false
+      },
+      {
+        "yanchor": "middle",
+        "xref": "paper",
+        "xanchor": "center",
+        "yref": "paper",
+        "text": "carat",
+        "textangle": 0,
+        "y": -0.1,
+        "x": 0.5,
+        "font": {
+          "color": "#000000",
+          "size": 12
+        },
+        "showarrow": false
+      },
+      {
+        "yanchor": "middle",
+        "xref": "paper",
+        "xanchor": "center",
+        "yref": "paper",
+        "text": "price",
+        "textangle": 270,
+        "y": 0.5,
+        "x": -0.1,
+        "font": {
+          "color": "#000000",
+          "size": 12
+        },
+        "showarrow": false
+      }
+    ],
+    "legend": {
+      "y": 1,
+      "x": 1.05,
+      "yanchor": "top",
+      "borderwidth": 1,
+      "bgcolor": "#efefef"
+    }
+  }
+}
diff --git a/test/image/mocks/gl2d_scatter_fill_self_next.json b/test/image/mocks/gl2d_scatter_fill_self_next.json
new file mode 100644
index 00000000000..5fee62ccc61
--- /dev/null
+++ b/test/image/mocks/gl2d_scatter_fill_self_next.json
@@ -0,0 +1,28 @@
+{
+    "data":[
+        {
+            "x": [1, 2, 3, 1, null, 4, 5, 6],
+            "y": [2, 3, 2, 2, null, 3, 4, 3],
+            "fill": "tonext",
+            "line":{"shape": "spline"},
+            "type": "scattergl"
+        },
+        {
+            "x": [-1, 4, 9, null, 0, 1, 2],
+            "y": [1, 6, 1, null, 5, 6, 5],
+            "fill": "tonext",
+            "type": "scattergl"
+        },
+        {
+            "x": [6, 7, 8],
+            "y": [5, 6, 5],
+            "fill": "toself",
+            "type": "scattergl"
+        }
+    ],
+    "layout":{
+        "title": "Fill toself and tonext",
+        "width": 400,
+        "height": 400
+    }
+}
diff --git a/test/image/mocks/gl2d_shapes_below_traces.json b/test/image/mocks/gl2d_shapes_below_traces.json
new file mode 100644
index 00000000000..b2989a0a06b
--- /dev/null
+++ b/test/image/mocks/gl2d_shapes_below_traces.json
@@ -0,0 +1,149 @@
+{
+    "data": [
+        {
+            "type": "scattergl",
+            "y": [
+                1,
+                2,
+                1,
+                0,
+                -1,
+                2,
+                3,
+                5
+            ]
+        },
+        {
+            "type": "scattergl",
+            "xaxis": "x2",
+            "y": [
+                7.071067811865475,
+                10,
+                7.071067811865475,
+                0,
+                -7.071067811865475,
+                10,
+                7.0710678118654755,
+                -7.071067811865475
+            ]
+        },
+        {
+            "type": "scattergl",
+            "y": [
+                7.0710678118654755,
+                6.123233995736766e-16,
+                7.0710678118654755,
+                10,
+                7.0710678118654755,
+                6.123233995736766e-16,
+                -7.071067811865475,
+                -7.071067811865477
+            ],
+            "yaxis": "y2"
+        },
+        {
+            "type": "scattergl",
+            "xaxis": "x2",
+            "y": [
+                2,
+                1.6666666666666667,
+                2,
+                2.5,
+                3.3333333333333335,
+                1.6666666666666667,
+                1.4285714285714286,
+                1.1111111111111112
+            ],
+            "yaxis": "y2"
+        }
+    ],
+    "layout": {
+        "dragmode": "pan",
+        "shapes": [
+            {
+                "fillcolor": "#c7eae5",
+                "layer": "below",
+                "type": "rect",
+                "x0": 3.5,
+                "x1": 4.5,
+                "xref": "x",
+                "y0": 0,
+                "y1": 1,
+                "yref": "paper"
+            },
+            {
+                "fillcolor": "#c7eae5",
+                "layer": "above",
+                "opacity": 0.5,
+                "type": "rect",
+                "x0": 5.5,
+                "x1": 6.5,
+                "xref": "x2",
+                "y0": 0,
+                "y1": 1,
+                "yref": "paper"
+            },
+            { "visible": false },
+            {
+                "fillcolor": "#f6e8c3",
+                "layer": "below",
+                "type": "rect",
+                "x0": 0,
+                "x1": 1,
+                "xref": "paper",
+                "y0": 0,
+                "y1": 3,
+                "yref": "y"
+            },
+            {
+                "fillcolor": "#f6e8c3",
+                "layer": "above",
+                "opacity": 0.5,
+                "type": "rect",
+                "x0": 0,
+                "x1": 1,
+                "xref": "paper",
+                "y0": 1,
+                "y1": 4,
+                "yref": "y2"
+            },
+            {
+                "fillcolor": "#d3d3d3",
+                "layer": "below",
+                "type": "rect",
+                "x0": 0.3,
+                "x1": 0.7,
+                "xref": "paper",
+                "y0": 0.3,
+                "y1": 0.7,
+                "yref": "paper"
+            }
+        ],
+        "showlegend": false,
+        "title": "shape shading a region",
+        "xaxis": {
+            "domain": [
+                0,
+                0.45
+            ]
+        },
+        "xaxis2": {
+            "domain": [
+                0.55,
+                1
+            ]
+        },
+        "yaxis": {
+            "domain": [
+                0,
+                0.45
+            ]
+        },
+        "yaxis2": {
+            "domain": [
+                0.55,
+                1
+            ]
+        }
+    }
+}
diff --git a/test/image/mocks/gl2d_subplots_anchor.json b/test/image/mocks/gl2d_subplots_anchor.json
new file mode 100644
index 00000000000..020b10c2d35
--- /dev/null
+++ b/test/image/mocks/gl2d_subplots_anchor.json
@@ -0,0 +1,39 @@
+{
+    "data": [
+    {
+      "x": ["2007-01-03", "2007-01-04", "2007-01-05", "2007-01-08", "2007-01-09", "2007-01-10", "2007-01-11", "2007-01-12", "2007-01-16", "2007-01-17", "2007-01-18", "2007-01-19", "2007-01-22", "2007-01-23", "2007-01-24"],
+      "y": [309579900, 211815100, 208685400, 199276700, 837324600, 738220000, 360063200, 328172600, 311019100, 411565000, 591151400, 341118400, 363506500, 301856100, 231953400],
+      "mode": "lines",
+      "name": "volume",
+      "type": "scattergl",
+      "xaxis": "x",
+      "yaxis": "y"
+    },
+    {
+      "x": ["2007-01-03", "2007-01-04", "2007-01-05", "2007-01-08", "2007-01-09", "2007-01-10", "2007-01-11", "2007-01-12", "2007-01-16", "2007-01-17", "2007-01-18", "2007-01-19", "2007-01-22", "2007-01-23", "2007-01-24"],
+      "y": [10.812461, 11.05245, 10.973744, 11.027936, 11.944027, 12.515616, 12.360785, 12.208533, 12.528519, 12.251111, 11.492433, 11.418888, 11.198251, 11.057612, 11.186639],
+      "mode": "lines",
+      "name": "adjusted",
+      "type": "scattergl",
+      "xaxis": "x",
+      "yaxis": "y2"
+    }
+  ],
+  "layout": {
+    "width": 710,
+    "height": 400,
+    "showlegend": false,
+    "xaxis": {
+      "domain": [0, 1],
+      "anchor": "y2"
+    },
+    "yaxis2": {
+      "domain": [0, 0.45],
+      "anchor": "x"
+    },
+    "yaxis": {
+      "domain": [0.55, 1],
+      "anchor": "x"
+    }
+  }
+}
diff --git a/test/image/mocks/gl2d_tick-labels.json b/test/image/mocks/gl2d_tick-labels.json
new file mode 100644
index 00000000000..201ed9c89d3
--- /dev/null
+++ b/test/image/mocks/gl2d_tick-labels.json
@@ -0,0 +1,22 @@
+{
+    "data": [
+        {
+          "x": [0, 1, 2],
+          "y": ["long long long string", "long long long string 2", "long long longstring 3"],
+          "mode": "markers",
+          "type": "scattergl"
+        }
+    ],
+    "layout": {
+        "width": 600,
+        "height": 500,
+        "xaxis": {
+            "ticklen": 2,
+            "linecolor": "#d3d3d3", "mirror": true, "zeroline": false
+        },
+        "yaxis": {
+            "ticklen": 2,
+            "linecolor": "#d3d3d3", "mirror": true, "zeroline": false
+        }
+    }
+}
diff --git a/test/image/mocks/gl2d_ultra_zoom.json b/test/image/mocks/gl2d_ultra_zoom.json
new file mode 100644
index 00000000000..ecb7f0f02dd
--- /dev/null
+++ b/test/image/mocks/gl2d_ultra_zoom.json
@@ -0,0 +1,53 @@
+{
+    "data": [
+        {
+            "x": [
+                1.0e-2,
+                1.0000001e-2,
+                1.0000002e-2,
+                1.0000003e-2,
+                1.0000004e-2,
+                1.0000005e-2,
+                1.0000006e-2,
+                1.0000007e-2,
+                1.0000008e-2,
+                1.0000009e-2,
+                1.0000010e-2
+            ],
+            "y": [
+                1.0e-2,
+                1.0000001e-2,
+                1.0000002e-2,
+                1.0000003e-2,
+                1.0000004e-2,
+                1.0000005e-2,
+                1.0000006e-2,
+                1.0000007e-2,
+                1.0000008e-2,
+                1.0000009e-2,
+                1.0000010e-2
+            ],
+            "mode": "markers",
+            "type": "scatter"
+        }
+    ],
+    "layout": {
+        "autosize": true,
+        "xaxis": {
+            "range": [
+                1.0e-2,
+                1.0000010e-2
+            ],
+            "type": "linear",
+            "autorange": false
+        },
+        "yaxis": {
+            "range": [
+                1.0e-2,
+                1.0000010e-2
+            ],
+            "type": "linear",
+            "autorange": false
+        }
+    }
+}
diff --git a/test/image/mocks/glpolar_scatter.json b/test/image/mocks/glpolar_scatter.json
new file mode 100644
index 00000000000..fdf8472c0f3
--- /dev/null
+++ b/test/image/mocks/glpolar_scatter.json
@@ -0,0 +1,858 @@
+{
+  "data": [
+    {
+      "type": "scatterpolargl",
+      "r": [
+        6.80498578527,
+        3.38959601061,
+        5.38147211075,
+        8.05954021942,
+        5.31822922787,
+        2.98509993563,
+        1.96658700238,
+        6.76926540821,
+        4.07340189872,
+        6.50437182527,
+        7.556369819,
+        4.04745609407,
+        7.38666249607,
+        5.41362473698,
+        7.47071653116,
+        7.98211021694,
+        4.73781408009,
+        4.20645304293,
+        5.47860480459,
+        4.8245202807,
+        5.5996006099,
+        6.86679521708,
+        3.08567136626,
+        7.77181094323,
+        3.6877944351,
+        5.36035668519,
+        5.1404467393,
+        6.04544568093,
+        6.83392094019,
+        3.62076946254,
+        3.9894305834,
+        5.3118244995,
+        4.60821348028,
+        6.64058471615,
+        3.05518885448,
+        7.49256416375,
+        5.48507817779,
+        3.89779499662,
+        5.97624511403,
+        5.44706156091,
+        5.37703411681,
+        4.69080578773,
+        4.71164049118,
+        3.62991932939,
+        5.95766807637,
+        5.35712128439,
+        3.84923528282,
+        6.25050713632,
+        7.12224335715,
+        3.39940423384,
+        3.51055667227,
+        4.10099760366,
+        4.0963821002,
+        6.23358307481,
+        3.93948852677,
+        3.9254450774,
+        6.11813250146,
+        3.94045034629,
+        7.58301557326,
+        3.51320214534
+      ],
+      "theta": [
+        -30.3529443619,
+        -25.6114598545,
+        -12.4252274527,
+        13.9613805187,
+        -4.95093284067,
+        -25.6922741909,
+        12.4687641616,
+        -4.91376410703,
+        -10.9673802876,
+        30.8141940549,
+        2.47495943114,
+        17.9755437524,
+        0.771130593362,
+        6.13748848563,
+        -14.451963574,
+        28.1845341129,
+        12.538680066,
+        -8.98323033713,
+        5.23128516476,
+        -64.4890025358,
+        11.3574866818,
+        3.45407479151,
+        13.9243466131,
+        -25.3640020468,
+        -16.818006386,
+        -10.2600510306,
+        -13.2121341256,
+        2.5793388653,
+        8.71757496585,
+        -10.6754987192,
+        -2.92636601252,
+        25.1958807548,
+        40.5903293216,
+        -9.12143363019,
+        -24.2973623813,
+        -3.17694450569,
+        10.8504984192,
+        -31.3320597474,
+        4.84956746221,
+        15.0482769541,
+        3.29510469926,
+        -6.19709187313,
+        -8.77857413578,
+        29.5491741194,
+        -5.13744879288,
+        23.0268604879,
+        -6.63481657837,
+        2.75501499186,
+        21.7332501137,
+        -24.8169949601,
+        -7.83054706253,
+        28.3257962102,
+        12.3009774678,
+        -21.56315724,
+        -19.3355162838,
+        26.1464431708,
+        -1.70607120268,
+        16.071723695,
+        2.05326630285,
+        -5.09791161233
+      ],
+      "mode": "markers",
+      "name": "Trial 1",
+      "marker": {
+        "color": "rgb(27,158,119)",
+        "size": 15,
+        "line": {
+          "color": "white"
+        },
+        "opacity": 0.7
+      },
+      "cliponaxis": false
+    },
+    {
+      "type": "scatterpolargl",
+      "r": [
+        3.48804392301,
+        2.91847857636,
+        4.20182735997,
+        8.22732460685,
+        4.77669042724,
+        3.04191230311,
+        4.78994771908,
+        5.66388078036,
+        3.85826239317,
+        8.26021288114,
+        6.86862448643,
+        5.74019759967,
+        6.59497928246,
+        5.69270377821,
+        5.33791657446,
+        9.28360418518,
+        5.76459089314,
+        4.02886455205,
+        5.66234474837,
+        0.422837231101,
+        6.20126646393,
+        6.43926538132,
+        5.09675851306,
+        4.63208190873,
+        3.42184613631,
+        4.36940470335,
+        4.02833441941,
+        5.80576719754,
+        6.84818992143,
+        3.80929551278,
+        4.38526818383,
+        6.98332684555,
+        7.39627318603,
+        5.21512500314,
+        3.08614877924,
+        6.33539449149,
+        6.09041471406,
+        2.4480560069,
+        5.94278402031,
+        6.37312988559,
+        5.45420534118,
+        4.39333761656,
+        4.20594467998,
+        6.15554228796,
+        5.11908717116,
+        6.86986083083,
+        4.10459986058,
+        5.95434812558,
+        8.09233287715,
+        2.96176970545,
+        3.97401218758,
+        6.37338412891,
+        5.41540914318,
+        3.87689091998,
+        3.26144694742,
+        6.1458085297,
+        5.50245198719,
+        5.57155329531,
+        6.85304926109,
+        4.14035507494
+      ],
+      "theta": [
+        14.8066257809,
+        79.0063403726,
+        49.0220655413,
+        49.699083136,
+        54.1374910829,
+        86.4193210205,
+        96.9523919357,
+        41.4634882636,
+        67.1376916934,
+        68.0610394397,
+        42.6819303227,
+        76.3986566081,
+        42.1947934722,
+        59.5778889746,
+        27.5108667993,
+        60.7534448323,
+        68.3708327991,
+        65.7480281495,
+        58.5330083721,
+        -176.744106458,
+        61.17401858,
+        47.451508589,
+        84.4266531858,
+        12.4793465505,
+        72.4808027618,
+        50.5788317578,
+        51.5602282402,
+        52.4378561813,
+        51.5868279921,
+        73.8729447773,
+        70.2170569279,
+        70.7142991543,
+        82.2343944264,
+        38.935390447,
+        84.7093666702,
+        38.1658284365,
+        61.7040536538,
+        70.1969562924,
+        54.4542925901,
+        64.3348949686,
+        58.2738931466,
+        60.4998223904,
+        59.155232539,
+        83.8656184676,
+        47.8734098973,
+        69.2826015659,
+        71.1899104287,
+        51.048396463,
+        59.4275824152,
+        78.5987369617,
+        75.7558645152,
+        79.9704837232,
+        73.8937802463,
+        31.7334111317,
+        68.084751177,
+        80.4110799786,
+        48.9242507089,
+        76.6502557554,
+        42.1828643629,
+        76.0333358945
+      ],
+      "mode": "markers",
+      "name": "Trial 2",
+      "marker": {
+        "color": "rgb(217,95,2)",
+        "size": 20,
+        "line": {
+          "color": "white"
+        },
+        "opacity": 0.7
+      },
+      "cliponaxis": false
+    },
+    {
+      "type": "scatterpolargl",
+      "r": [
+        1.85587083503,
+        5.28696206204,
+        3.88601339194,
+        6.282863313,
+        4.45341484774,
+        5.68800805076,
+        7.33086428261,
+        3.82566059479,
+        4.98960417696,
+        7.89743146977,
+        4.65669311302,
+        6.66715369631,
+        4.43100628714,
+        5.34611325338,
+        2.47994569588,
+        8.11347734853,
+        6.08131168231,
+        4.96821689621,
+        5.24445392063,
+        5.42220788417,
+        5.79277461602,
+        4.78758059223,
+        6.78431863718,
+        1.10893690948,
+        5.13891110524,
+        4.04292965729,
+        4.02289202968,
+        4.82842879131,
+        5.41737837431,
+        5.37863521067,
+        5.42109717546,
+        7.12056197886,
+        8.3493085399,
+        3.41048558832,
+        5.62837847088,
+        3.91493697614,
+        5.76394026236,
+        4.7643741068,
+        5.0762362679,
+        6.1655581832,
+        5.10557651628,
+        4.76103637693,
+        4.59624954094,
+        7.50418841135,
+        4.10703141792,
+        6.92042229938,
+        5.34912894956,
+        4.79806571939,
+        7.0232515323,
+        5.28368096546,
+        5.56907115243,
+        7.38379490845,
+        6.26923321044,
+        2.65652964501,
+        4.8439843388,
+        7.24799236156,
+        4.37295939441,
+        6.57098108136,
+        4.60247924389,
+        5.67005205083
+      ],
+      "theta": [
+        151.294255181,
+        147.188025028,
+        125.282157112,
+        87.0672979717,
+        119.627898357,
+        147.740824147,
+        139.564598145,
+        101.391497102,
+        134.56018428,
+        104.024444705,
+        89.3931429448,
+        123.1940314,
+        91.4743405152,
+        113.332373614,
+        96.1499255673,
+        93.2807345226,
+        118.215565226,
+        132.322937378,
+        112.941186391,
+        -179.746233138,
+        110.303513559,
+        97.7508361661,
+        131.608089257,
+        115.496919231,
+        140.58118216,
+        123.396662119,
+        128.342009045,
+        107.608810398,
+        97.9046897875,
+        137.128447975,
+        130.431244912,
+        112.227084481,
+        118.630202246,
+        106.05822559,
+        146.908109706,
+        90.2773495582,
+        111.505282363,
+        151.089742536,
+        107.721394157,
+        111.300854997,
+        114.680277936,
+        126.569379493,
+        128.218952233,
+        125.354857195,
+        112.418068253,
+        111.797355679,
+        133.418052258,
+        105.184116842,
+        97.2310361206,
+        146.668036804,
+        136.239315201,
+        121.791844193,
+        123.911327971,
+        129.86224497,
+        141.34395085,
+        123.270967749,
+        108.458821723,
+        124.412377056,
+        89.0271107387,
+        134.876701145
+      ],
+      "mode": "markers",
+      "name": "Trial 3",
+      "marker": {
+        "color": "rgb(117,112,179)",
+        "size": 12,
+        "line": {
+          "color": "white"
+        },
+        "opacity": 0.7
+      },
+      "cliponaxis": false
+    },
+    {
+      "type": "scatterpolargl",
+      "r": [
+        5.37247092432,
+        7.09635557204,
+        4.8838239032,
+        2.92013544124,
+        4.72396304568,
+        7.42369395093,
+        8.0909460754,
+        3.30684459137,
+        6.05082848252,
+        5.53023207444,
+        2.47230695264,
+        6.27567053686,
+        2.61589617379,
+        4.65353994458,
+        3.33544001388,
+        4.79588360487,
+        5.47271134648,
+        5.88193049095,
+        4.57158707205,
+        9.0398611698,
+        4.6429075999,
+        3.1727677358,
+        7.04424813882,
+        4.46633651411,
+        6.5573302898,
+        4.82084943725,
+        5.13191551521,
+        3.97001223705,
+        3.40632381283,
+        6.476722964,
+        6.01921850933,
+        5.66450153495,
+        7.15875852255,
+        3.60071266167,
+        7.32412716876,
+        2.55294615625,
+        4.72713386039,
+        6.97175520718,
+        4.07657836107,
+        4.94622340701,
+        4.64215544904,
+        5.36057486441,
+        5.39171906736,
+        7.0725243051,
+        4.10111157028,
+        5.48573262102,
+        6.19253528611,
+        3.76871139184,
+        4.29031138976,
+        7.06019536969,
+        6.53969184418,
+        6.67974440649,
+        6.0608253587,
+        4.78657404093,
+        6.41668652967,
+        6.70328133339,
+        3.88884781048,
+        6.30859108119,
+        2.4370447709,
+        6.5081863479
+      ],
+      "theta": [
+        -140.203327641,
+        -168.084245433,
+        -166.285141329,
+        138.248866753,
+        -174.424386436,
+        -169.960482759,
+        176.991822687,
+        -169.901416249,
+        -172.641581594,
+        142.951668814,
+        172.415746367,
+        168.519359196,
+        177.822053694,
+        172.855190349,
+        -146.014521701,
+        128.177293024,
+        169.167072781,
+        -173.588573789,
+        173.726992705,
+        -151.206104772,
+        166.260477163,
+        172.507566082,
+        173.949183904,
+        -131.806840938,
+        -170.635273831,
+        -168.577085483,
+        -166.765503421,
+        176.070487348,
+        162.297501498,
+        -174.055746313,
+        -178.060929857,
+        156.47126885,
+        155.239142145,
+        -163.000526394,
+        -170.116713265,
+        -170.639272487,
+        167.383143694,
+        -163.098817056,
+        172.880737006,
+        163.386007682,
+        176.182541977,
+        -174.579680174,
+        -172.335844882,
+        165.338025694,
+        -172.525664261,
+        157.542877739,
+        -175.881511093,
+        175.427643994,
+        142.069674723,
+        -168.340734019,
+        -175.805831123,
+        163.063745419,
+        171.720974997,
+        -151.403904569,
+        -168.27136909,
+        165.045327878,
+        -177.315336665,
+        170.042412897,
+        173.59919661,
+        -177.250656746
+      ],
+      "mode": "markers",
+      "name": "Trial 4",
+      "marker": {
+        "color": "rgb(231,41,138)",
+        "size": 22,
+        "line": {
+          "color": "white"
+        },
+        "opacity": 0.7
+      },
+      "cliponaxis": false
+    },
+    {
+      "type": "scatterpolargl",
+      "r": [
+        7.93755787138,
+        7.30274649152,
+        5.92930222144,
+        2.40717871317,
+        5.27092188706,
+        7.40059612754,
+        6.81082033836,
+        4.96775903442,
+        6.19022937045,
+        2.15851865795,
+        4.00412589387,
+        4.77661732163,
+        4.23225045181,
+        4.30765487269,
+        6.20027517286,
+        0.727513848534,
+        4.37800680381,
+        6.00496493944,
+        4.34193170292,
+        10.2379829353,
+        3.8021588887,
+        3.96928117014,
+        5.75898014247,
+        7.67417906914,
+        6.69995353301,
+        5.73431038813,
+        6.0442759153,
+        4.31294306609,
+        3.37754528241,
+        6.36766672727,
+        5.73724418155,
+        3.39635147199,
+        4.21646748139,
+        5.46488501672,
+        7.31113557753,
+        4.74540076936,
+        3.91646853189,
+        7.60297299033,
+        4.12520482944,
+        3.67679494965,
+        4.55123578852,
+        5.60696053152,
+        5.79484425749,
+        5.03052815569,
+        5.10958624099,
+        3.40544020796,
+        6.02630612539,
+        4.22110926364,
+        1.90978293658,
+        7.25466939392,
+        6.26887587203,
+        4.56258056659,
+        4.91805796544,
+        6.83656096253,
+        6.78648654914,
+        4.75101433449,
+        4.71992634764,
+        4.92780521518,
+        4.05919058739,
+        6.12833898429
+      ],
+      "theta": [
+        -101.833785776,
+        -127.478391579,
+        -112.244284997,
+        -82.3259108712,
+        -114.688855621,
+        -130.537863362,
+        -145.010264976,
+        -98.7488450072,
+        -124.441748821,
+        -152.45411927,
+        -89.2942365523,
+        -139.832451718,
+        -91.5435951844,
+        -119.442163004,
+        -92.4558385274,
+        -129.659924316,
+        -131.051235099,
+        -123.852917454,
+        -118.086739004,
+        -121.979217138,
+        -121.915029968,
+        -99.3618475777,
+        -141.467701997,
+        -93.5662631891,
+        -126.336901405,
+        -112.834944178,
+        -114.386479929,
+        -109.796072327,
+        -102.743264712,
+        -128.246728907,
+        -127.792092643,
+        -142.473629745,
+        -161.587294187,
+        -99.9406107796,
+        -130.163117326,
+        -90.2288120096,
+        -122.650491214,
+        -123.267750572,
+        -111.997308801,
+        -127.528316806,
+        -117.931295338,
+        -120.391634245,
+        -119.386871479,
+        -149.674695492,
+        -107.850517506,
+        -138.989931341,
+        -127.595470214,
+        -107.32083544,
+        -117.573807423,
+        -127.481660968,
+        -129.912033166,
+        -148.495211671,
+        -135.33164137,
+        -104.421659276,
+        -123.875440211,
+        -146.816826618,
+        -107.058485424,
+        -138.902564873,
+        -88.8968825195,
+        -130.754467356
+      ],
+      "mode": "markers",
+      "name": "Trial 5",
+      "marker": {
+        "color": "rgb(102,166,30)",
+        "size": 19,
+        "line": {
+          "color": "white"
+        },
+        "opacity": 0.7
+      },
+      "cliponaxis": false
+    },
+    {
+      "type": "scatterpolargl",
+      "r": [
+        8.46918052789,
+        5.82199756737,
+        6.14091832822,
+        5.83172428479,
+        5.54675447186,
+        5.6274877092,
+        3.94832897602,
+        6.49018461461,
+        5.32061824515,
+        3.24359304149,
+        6.44408533158,
+        3.36377810065,
+        6.46311681051,
+        4.73094492578,
+        7.79657841111,
+        4.57012782992,
+        3.926206816,
+        5.25434813987,
+        4.83841110661,
+        8.69452399898,
+        4.39953181822,
+        5.85648390518,
+        3.62157703921,
+        8.89491237311,
+        5.49454283608,
+        5.96898089085,
+        6.0478995736,
+        5.38467139672,
+        5.3812200182,
+        5.11157462274,
+        4.77056110506,
+        3.09833088263,
+        1.66508317194,
+        6.74025853333,
+        5.59449492888,
+        6.87963082567,
+        4.38279246628,
+        6.41084361649,
+        5.15420431777,
+        4.01515851866,
+        4.93914886826,
+        5.29829731449,
+        5.49041717695,
+        2.62375125938,
+        5.95358866167,
+        3.30147937192,
+        4.9548890011,
+        5.50005366961,
+        4.45051234955,
+        5.78662451335,
+        4.90683442406,
+        2.62996947345,
+        3.76970360805,
+        7.3967357155,
+        5.76448190196,
+        2.79458519588,
+        5.78203326982,
+        3.48535191762,
+        6.50065359862,
+        4.74864071013
+      ],
+      "theta": [
+        -66.5358363273,
+        -84.5144226769,
+        -63.3397416996,
+        -24.1468127442,
+        -59.7012453226,
+        -88.06537268,
+        -98.4442045353,
+        -49.1583968172,
+        -73.636223312,
+        -17.9238746786,
+        -38.4123994546,
+        -66.3403623779,
+        -40.8888387392,
+        -52.46063321,
+        -52.6104625591,
+        -7.03935105091,
+        -57.2354586922,
+        -71.642203502,
+        -52.3453961691,
+        -92.7830386735,
+        -47.187163055,
+        -41.9692084629,
+        -82.1442282499,
+        -59.4391656032,
+        -79.1948225932,
+        -62.2999085353,
+        -65.5379040394,
+        -48.9060554476,
+        -37.748311038,
+        -78.0533334583,
+        -71.8731176631,
+        -41.8910928259,
+        -53.1154554855,
+        -52.9976280973,
+        -87.0843610179,
+        -43.6119048384,
+        -48.7979984056,
+        -82.5668031571,
+        -47.9099629957,
+        -46.5704855853,
+        -54.5004832176,
+        -65.9007271268,
+        -66.8733174636,
+        -75.4808072521,
+        -54.7776938669,
+        -42.5983345914,
+        -74.5081662691,
+        -47.1102184434,
+        -22.3568731833,
+        -84.192986745,
+        -78.5052847562,
+        -65.0363717923,
+        -66.5137336813,
+        -63.5267765618,
+        -77.8090785513,
+        -68.5101797401,
+        -51.2968693109,
+        -68.3399130277,
+        -38.6317330684,
+        -77.8518485851
+      ],
+      "mode": "markers",
+      "name": "Trial 6",
+      "marker": {
+        "color": "rgb(230,171,2)",
+        "size": 10,
+        "line": {
+          "color": "white"
+        },
+        "opacity": 0.7
+      },
+      "cliponaxis": false
+    }
+  ],
+  "layout": {
+    "title": "Hobbs-Pearson Trials",
+    "font": {
+      "size": 15
+    },
+    "showlegend": false,
+    "polar": {
+      "bgcolor": "rgb(223, 223, 223)",
+      "angularaxis": {
+        "tickwidth": 2,
+        "linewidth": 3,
+        "layer": "below traces"
+      },
+      "radialaxis": {
+        "side": "counterclockwise",
+        "showline": true,
+        "linewidth": 2,
+        "tickwidth": 2,
+        "gridcolor": "white",
+        "gridwidth": 2
+      }
+    },
+    "paper_bgcolor": "rgb(223, 223, 223)",
+    "width": 450,
+    "height": 450
+  }
+}
diff --git a/test/image/mocks/glpolar_style.json b/test/image/mocks/glpolar_style.json
new file mode 100644
index 00000000000..eb111f1a7de
--- /dev/null
+++ b/test/image/mocks/glpolar_style.json
@@ -0,0 +1,74 @@
+{
+  "data": [{
+    "type": "scatterpolargl",
+    "r": [1, 2, 3],
+    "theta": [50, 100, 200],
+    "marker": {"symbol": "square"}
+  }, {
+    "type": "scatterpolargl",
+    "r": [1, 2, 3],
+    "theta": [1, 2, 3],
+    "thetaunit": "radians"
+  }, {
+    "type": "scatterpolargl",
+    "r": ["a", "b", "c", "b"],
+    "theta": ["D", "C", "B", "A"],
+    "subplot": "polar2"
+  }, {
+    "type": "scatterpolargl",
+    "r": [50, 300, 900],
+    "theta": [0, 90, 180],
+    "subplot": "polar3"
+  }, {
+    "type": "scatterpolargl",
+    "mode": "lines",
+    "r": [3, 3, 4, 3],
+    "theta": [0, 45, 90, 270],
+    "fill": "toself",
+    "subplot": "polar4"
+  }],
+  "layout": {
+    "polar": {
+      "domain": {
+        "x": [0, 0.46],
+        "y": [0.56, 1]
+      },
+      "radialaxis": {
+        "range": [1, 4]
+      },
+      "angularaxis": {
+        "thetaunit": "radians"
+      }
+    },
+    "polar2": {
+      "domain": {
+        "x": [0, 0.46],
+        "y": [0, 0.42]
+      }
+    },
+    "polar3": {
+      "domain": {
+        "x": [0.54, 1],
+        "y": [0.56, 1]
+      },
+      "radialaxis": {
+        "type": "log",
+        "tickangle": 45
+      },
+      "sector": [0, 180]
+    },
+    "polar4": {
+      "domain": {
+        "x": [0.54, 1],
+        "y": [0, 0.44]
+      },
+      "radialaxis": {
+          "visible": false,
+          "range": [0, 6]
+      }
+    },
+    "showlegend": false,
+    "width": 600,
+    "height": 500
+  }
+}
diff --git a/test/jasmine/assets/drag.js b/test/jasmine/assets/drag.js
index 72190e4fd43..35b628e0e07 100644
--- a/test/jasmine/assets/drag.js
+++ b/test/jasmine/assets/drag.js
@@ -7,18 +7,26 @@ var getNodeCoords = require('./get_node_coords');
  * optionally specify an edge ('n', 'se', 'w' etc)
  * to grab it by an edge or corner (otherwise the middle is used)
  */
-module.exports = function(node, dx, dy, edge, x0, y0) {
+module.exports = function(node, dx, dy, edge, x0, y0, nsteps) {
+    nsteps = nsteps || 1;
+
     var coords = getNodeCoords(node, edge);
     var fromX = isNumeric(x0) ? x0 : coords.x;
     var fromY = isNumeric(y0) ? y0 : coords.y;
-    var toX = fromX + dx;
-    var toY = fromY + dy;
 
     mouseEvent('mousemove', fromX, fromY, {element: node});
     mouseEvent('mousedown', fromX, fromY, {element: node});
 
     var promise = waitForDragCover().then(function(dragCoverNode) {
-        mouseEvent('mousemove', toX, toY, {element: dragCoverNode});
+        var toX;
+        var toY;
+
+        for(var i = 1; i <= nsteps; i++) {
+            toX = fromX + i * dx / nsteps;
+            toY = fromY + i * dy / nsteps;
+            mouseEvent('mousemove', toX, toY, {element: dragCoverNode});
+        }
+
         mouseEvent('mouseup', toX, toY, {element: dragCoverNode});
         return waitForDragCoverRemoval();
     });
diff --git a/test/jasmine/assets/read_pixel.js b/test/jasmine/assets/read_pixel.js
new file mode 100644
index 00000000000..fadc705079c
--- /dev/null
+++ b/test/jasmine/assets/read_pixel.js
@@ -0,0 +1,13 @@
+'use strict';
+
+module.exports = function(canvas, x, y) {
+    if(!canvas) return null;
+
+    var gl = canvas.getContext('webgl');
+
+    var pixels = new Uint8Array(4);
+
+    gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
+
+    return pixels;
+};
diff --git a/test/jasmine/tests/gl2d_click_test.js b/test/jasmine/tests/gl2d_click_test.js
index 70609851a15..25c6abb6532 100644
--- a/test/jasmine/tests/gl2d_click_test.js
+++ b/test/jasmine/tests/gl2d_click_test.js
@@ -194,6 +194,85 @@ describe('Test hover and click interactions', function() {
             }
         };
         _mock.data[0].hoverinfo = _mock.data[0].x.map(function(_, i) { return i % 2 ? 'y' : 'x'; });
+
+        _mock.data[0].hoverlabel = {
+            bgcolor: 'blue',
+            bordercolor: _mock.data[0].x.map(function(_, i) { return i % 2 ? 'red' : 'green'; })
+        };
+
+        var run = makeRunner([634, 321], {
+            x: 15.772,
+            y: 0.387,
+            label: ['0.387', null],
+            curveNumber: 0,
+            pointNumber: 33,
+            bgcolor: 'rgb(0, 0, 255)',
+            bordercolor: 'rgb(255, 0, 0)',
+            fontSize: 20,
+            fontFamily: 'Arial',
+            fontColor: 'rgb(255, 255, 0)'
+        }, {
+            msg: 'scattergl'
+        });
+
+        Plotly.plot(gd, _mock)
+        .then(run)
+        .catch(fail)
+        .then(done);
+    });
+
+    it('should output correct event data for scattergl in *select* dragmode', function(done) {
+        var _mock = Lib.extendDeep({}, mock1);
+
+        _mock.layout.dragmode = 'select';
+
+        _mock.layout.hoverlabel = {
+            font: {
+                size: 20,
+                color: 'yellow'
+            }
+        };
+        _mock.data[0].hoverinfo = _mock.data[0].x.map(function(_, i) { return i % 2 ? 'y' : 'x'; });
+
+        _mock.data[0].hoverlabel = {
+            bgcolor: 'blue',
+            bordercolor: _mock.data[0].x.map(function(_, i) { return i % 2 ? 'red' : 'green'; })
+        };
+
+        var run = makeRunner([634, 321], {
+            x: 15.772,
+            y: 0.387,
+            label: ['0.387', null],
+            curveNumber: 0,
+            pointNumber: 33,
+            bgcolor: 'rgb(0, 0, 255)',
+            bordercolor: 'rgb(255, 0, 0)',
+            fontSize: 20,
+            fontFamily: 'Arial',
+            fontColor: 'rgb(255, 255, 0)'
+        }, {
+            msg: 'scattergl'
+        });
+
+        Plotly.plot(gd, _mock)
+        .then(run)
+        .catch(fail)
+        .then(done);
+    });
+
+    it('should output correct event data for scattergl in *lasso* dragmode', function(done) {
+        var _mock = Lib.extendDeep({}, mock1);
+
+        _mock.layout.dragmode = 'lasso';
+
+        _mock.layout.hoverlabel = {
+            font: {
+                size: 20,
+                color: 'yellow'
+            }
+        };
+        _mock.data[0].hoverinfo = _mock.data[0].x.map(function(_, i) { return i % 2 ? 'y' : 'x'; });
+
         _mock.data[0].hoverlabel = {
             bgcolor: 'blue',
             bordercolor: _mock.data[0].x.map(function(_, i) { return i % 2 ? 'red' : 'green'; })
@@ -464,9 +543,10 @@ describe('@noCI Test gl2d lasso/select:', function() {
     });
 
     var gd;
-    var selectPath = [[93, 193], [143, 193]];
+    var selectPath = [[98, 193], [108, 193]];
+    var selectPath2 = [[118, 193], [128, 193]];
     var lassoPath = [[316, 171], [318, 239], [335, 243], [328, 169]];
-    var lassoPath2 = [[93, 193], [143, 193], [143, 500], [93, 500], [93, 193]];
+    var lassoPath2 = [[98, 193], [108, 193], [108, 500], [98, 500], [98, 193]];
 
     afterEach(function() {
         Plotly.purge(gd);
@@ -508,9 +588,6 @@ describe('@noCI Test gl2d lasso/select:', function() {
         });
     }
 
-    function countGlObjects() {
-        return gd._fullLayout._plots.xy._scene2d.glplot.objects.length;
-    }
 
     it('should work under fast mode with *select* dragmode', function(done) {
         var _mock = Lib.extendDeep({}, mockFast);
@@ -520,19 +597,20 @@ describe('@noCI Test gl2d lasso/select:', function() {
         Plotly.plot(gd, _mock)
         .then(delay(100))
         .then(function() {
-            expect(countGlObjects()).toBe(1, 'has on gl-scatter2d object');
+            expect(gd._fullLayout._plots.xy._scene.select2d).not.toBe(undefined, 'scatter2d renderer');
 
             return select(selectPath);
         })
+        .then(delay(100))
         .then(function(eventData) {
             assertEventData(eventData, {
                 points: [
-                    {x: 3.911, y: 0.401},
-                    {x: 5.34, y: 0.403},
-                    {x: 6.915, y: 0.411}
+                    {pointNumber: 25, x: 1.425, y: 0.538},
+                    {pointNumber: 26, x: 1.753, y: 0.5},
+                    {pointNumber: 27, x: 2.22, y: 0.45}
                 ]
             });
-            expect(countGlObjects()).toBe(2, 'adds a dimmed gl-scatter2d objects');
+
         })
         .catch(fail)
         .then(done);
@@ -546,19 +624,17 @@ describe('@noCI Test gl2d lasso/select:', function() {
         Plotly.plot(gd, _mock)
         .then(delay(100))
         .then(function() {
-            expect(countGlObjects()).toBe(1);
-
             return select(lassoPath2);
         })
+        .then(delay(100))
         .then(function(eventData) {
             assertEventData(eventData, {
                 points: [
-                    {x: 3.911, y: 0.401},
-                    {x: 5.34, y: 0.403},
-                    {x: 6.915, y: 0.411}
+                    {pointNumber: 25, x: 1.425, y: 0.538},
+                    {pointNumber: 26, x: 1.753, y: 0.5},
+                    {pointNumber: 27, x: 2.22, y: 0.45}
                 ]
             });
-            expect(countGlObjects()).toBe(2);
         })
         .catch(fail)
         .then(done);
@@ -572,15 +648,13 @@ describe('@noCI Test gl2d lasso/select:', function() {
         Plotly.plot(gd, _mock)
         .then(delay(100))
         .then(function() {
-            expect(countGlObjects()).toBe(2, 'has a gl-line2d and a gl-scatter2d-sdf');
-
-            return select(selectPath);
+            return select(selectPath2);
         })
+        .then(delay(100))
         .then(function(eventData) {
             assertEventData(eventData, {
                 points: [{x: 0.004, y: 12.5}]
             });
-            expect(countGlObjects()).toBe(2, 'only changes colors of gl-scatter2d-sdf object');
         })
         .catch(fail)
         .then(done);
@@ -594,15 +668,12 @@ describe('@noCI Test gl2d lasso/select:', function() {
         Plotly.plot(gd, _mock)
         .then(delay(100))
         .then(function() {
-            expect(countGlObjects()).toBe(2, 'has a gl-line2d and a gl-scatter2d-sdf');
-
             return select(lassoPath);
         })
         .then(function(eventData) {
             assertEventData(eventData, {
                 points: [{ x: 0.099, y: 2.75 }]
             });
-            expect(countGlObjects()).toBe(2, 'only changes colors of gl-scatter2d-sdf object');
         })
         .catch(fail)
         .then(done);
diff --git a/test/jasmine/tests/gl2d_date_axis_render_test.js b/test/jasmine/tests/gl2d_date_axis_render_test.js
index 7a7f5d8a173..d3eff9200e3 100644
--- a/test/jasmine/tests/gl2d_date_axis_render_test.js
+++ b/test/jasmine/tests/gl2d_date_axis_render_test.js
@@ -31,12 +31,13 @@ describe('date axis', function() {
         expect(gd._fullLayout.xaxis.type).toBe('date');
         expect(gd._fullLayout.yaxis.type).toBe('linear');
         expect(gd._fullData[0].type).toBe('scattergl');
-        expect(gd._fullData[0]._module.basePlotModule.name).toBe('gl2d');
+        expect(gd._fullData[0]._module.basePlotModule.name).toBe('cartesian');
 
-        // one way of check which renderer - fancy vs not - we're using
-        var objs = gd._fullLayout._plots.xy._scene2d.glplot.objects;
-        expect(objs.length).toEqual(2);
-        expect(objs[1].points.length).toEqual(4);
+        // one way of check which renderer - fancy vs not - we're
+        var scene = gd._fullLayout._plots.xy._scene;
+        expect(scene.scatter2d).toBeDefined();
+        expect(scene.markerOptions[0].positions.length).toEqual(4);
+        expect(scene.line2d).not.toBeUndefined();
     });
 
     it('should use the fancy gl-vis/gl-scatter2d once again', function() {
@@ -57,18 +58,18 @@ describe('date axis', function() {
         expect(gd._fullLayout.xaxis.type).toBe('date');
         expect(gd._fullLayout.yaxis.type).toBe('linear');
         expect(gd._fullData[0].type).toBe('scattergl');
-        expect(gd._fullData[0]._module.basePlotModule.name).toBe('gl2d');
+        expect(gd._fullData[0]._module.basePlotModule.name).toBe('cartesian');
 
-        // one way of check which renderer - fancy vs not - we're using
-        var objs = gd._fullLayout._plots.xy._scene2d.glplot.objects;
-        expect(objs.length).toEqual(2);
-        expect(objs[1].points.length).toEqual(4);
+        var scene = gd._fullLayout._plots.xy._scene;
+        expect(scene.scatter2d).toBeDefined();
+        expect(scene.markerOptions[0].positions.length).toEqual(4);
+        expect(scene.line2d).toBeDefined();
     });
 
     it('should now use the non-fancy gl-vis/gl-scatter2d', function() {
         Plotly.plot(gd, [{
             type: 'scattergl',
-            mode: 'markers', // important, as otherwise lines are assumed (which needs fancy)
+            mode: 'markers',
             x: [new Date('2016-10-10'), new Date('2016-10-11')],
             y: [15, 16]
         }]);
@@ -76,17 +77,18 @@ describe('date axis', function() {
         expect(gd._fullLayout.xaxis.type).toBe('date');
         expect(gd._fullLayout.yaxis.type).toBe('linear');
         expect(gd._fullData[0].type).toBe('scattergl');
-        expect(gd._fullData[0]._module.basePlotModule.name).toBe('gl2d');
+        expect(gd._fullData[0]._module.basePlotModule.name).toBe('cartesian');
 
-        var objs = gd._fullLayout._plots.xy._scene2d.glplot.objects;
-        expect(objs.length).toEqual(1);
-        expect(objs[0].pointCount).toEqual(2);
+        var scene = gd._fullLayout._plots.xy._scene;
+        expect(scene.scatter2d).toBeDefined();
+        expect(scene.markerOptions[0].positions.length).toEqual(4);
+        expect(scene.line2d).toBeDefined();
     });
 
     it('should use the non-fancy gl-vis/gl-scatter2d with string dates', function() {
         Plotly.plot(gd, [{
             type: 'scattergl',
-            mode: 'markers', // important, as otherwise lines are assumed (which needs fancy)
+            mode: 'markers',
             x: ['2016-10-10', '2016-10-11'],
             y: [15, 16]
         }]);
@@ -94,10 +96,11 @@ describe('date axis', function() {
         expect(gd._fullLayout.xaxis.type).toBe('date');
         expect(gd._fullLayout.yaxis.type).toBe('linear');
         expect(gd._fullData[0].type).toBe('scattergl');
-        expect(gd._fullData[0]._module.basePlotModule.name).toBe('gl2d');
+        expect(gd._fullData[0]._module.basePlotModule.name).toBe('cartesian');
 
-        var objs = gd._fullLayout._plots.xy._scene2d.glplot.objects;
-        expect(objs.length).toEqual(1);
-        expect(objs[0].pointCount).toEqual(2);
+        var scene = gd._fullLayout._plots.xy._scene;
+        expect(scene.scatter2d).toBeDefined();
+        expect(scene.markerOptions[0].positions.length).toEqual(4);
+        expect(scene.line2d).toBeDefined();
     });
 });
diff --git a/test/jasmine/tests/gl_plot_interact_test.js b/test/jasmine/tests/gl_plot_interact_test.js
index 69b0f93a62b..099200dd0e9 100644
--- a/test/jasmine/tests/gl_plot_interact_test.js
+++ b/test/jasmine/tests/gl_plot_interact_test.js
@@ -9,8 +9,10 @@ var createGraphDiv = require('../assets/create_graph_div');
 var destroyGraphDiv = require('../assets/destroy_graph_div');
 var fail = require('../assets/fail_test');
 var mouseEvent = require('../assets/mouse_event');
+var drag = require('../assets/drag');
 var selectButton = require('../assets/modebar_button');
 var delay = require('../assets/delay');
+var readPixel = require('../assets/read_pixel');
 
 var customAssertions = require('../assets/custom_assertions');
 var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle;
@@ -832,11 +834,9 @@ describe('Test gl3d relayout calls', function() {
     });
 });
 
-describe('Test gl2d plots', function() {
+describe('Test gl3d annotations', function() {
     var gd;
 
-    var mock = require('@mocks/gl2d_10.json');
-
     beforeEach(function() {
         gd = createGraphDiv();
     });
@@ -846,274 +846,369 @@ describe('Test gl2d plots', function() {
         destroyGraphDiv();
     });
 
-    function mouseTo(p0, p1) {
-        mouseEvent('mousemove', p0[0], p0[1]);
-        mouseEvent('mousedown', p0[0], p0[1], { buttons: 1 });
-        mouseEvent('mousemove', p1[0], p1[1], { buttons: 1 });
-        mouseEvent('mouseup', p1[0], p1[1]);
-    }
-
-    it('should respond to drag interactions', function(done) {
-        var _mock = Lib.extendDeep({}, mock);
-        var relayoutCallback = jasmine.createSpy('relayoutCallback');
+    function assertAnnotationText(expectations, msg) {
+        var anns = d3.selectAll('g.annotation-text-g');
 
-        var originalX = [-0.3037383177570093, 5.303738317757009];
-        var originalY = [-0.5532219548705213, 6.191112269783224];
-        var newX = [-0.5373831775700935, 5.070093457943925];
-        var newY = [-1.7575673521301187, 4.986766872523626];
-        var precision = 5;
+        expect(anns.size()).toBe(expectations.length, msg);
 
-        Plotly.plot(gd, _mock)
-        .then(delay(20))
-        .then(function() {
-            expect(gd.layout.xaxis.autorange).toBe(true);
-            expect(gd.layout.yaxis.autorange).toBe(true);
-            expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
-            expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
+        anns.each(function(_, i) {
+            var tx = d3.select(this).select('text').text();
+            expect(tx).toEqual(expectations[i], msg + ' - ann ' + i);
+        });
+    }
 
-            // Switch to pan mode
-            var buttonPan = selectButton(gd._fullLayout._modeBar, 'pan2d');
-            expect(buttonPan.isActive()).toBe(false, 'initially, zoom is active');
-            buttonPan.click();
-            expect(buttonPan.isActive()).toBe(true, 'switched on dragmode');
+    function assertAnnotationsXY(expectations, msg) {
+        var TOL = 2.5;
+        var anns = d3.selectAll('g.annotation-text-g');
 
-            // Switching mode must not change visible range
-            expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
-            expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
-        })
-        .then(delay(200))
-        .then(function() {
-            gd.on('plotly_relayout', relayoutCallback);
+        expect(anns.size()).toBe(expectations.length, msg);
 
-            // Drag scene along the X axis
-            mouseTo([200, 200], [220, 200]);
+        anns.each(function(_, i) {
+            var ann = d3.select(this).select('g');
+            var translate = Drawing.getTranslate(ann);
 
-            expect(gd.layout.xaxis.autorange).toBe(false);
-            expect(gd.layout.yaxis.autorange).toBe(false);
+            expect(translate.x).toBeWithin(expectations[i][0], TOL, msg + ' - ann ' + i + ' x');
+            expect(translate.y).toBeWithin(expectations[i][1], TOL, msg + ' - ann ' + i + ' y');
+        });
+    }
 
-            expect(gd.layout.xaxis.range).toBeCloseToArray(newX, precision);
-            expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
+    // more robust (especially on CI) than update camera via mouse events
+    function updateCamera(x, y, z) {
+        var scene = gd._fullLayout.scene._scene;
+        var camera = scene.getCamera();
 
-            // Drag scene back along the X axis
-            mouseTo([220, 200], [200, 200]);
+        camera.eye = {x: x, y: y, z: z};
+        scene.setCamera(camera);
+        // need a fairly long delay to let the camera update here
+        // 200 was not robust for me (AJ), 300 seems to be.
+        return delay(300)();
+    }
 
-            expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
-            expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
+    it('should move with camera', function(done) {
+        Plotly.plot(gd, [{
+            type: 'scatter3d',
+            x: [1, 2, 3],
+            y: [1, 2, 3],
+            z: [1, 2, 1]
+        }], {
+            scene: {
+                camera: {eye: {x: 2.1, y: 0.1, z: 0.9}},
+                annotations: [{
+                    text: 'hello',
+                    x: 1, y: 1, z: 1
+                }, {
+                    text: 'sup?',
+                    x: 1, y: 1, z: 2
+                }, {
+                    text: 'look!',
+                    x: 2, y: 2, z: 1
+                }]
+            }
+        })
+        .then(function() {
+            assertAnnotationsXY([[262, 199], [257, 135], [325, 233]], 'base 0');
 
-            // Drag scene along the Y axis
-            mouseTo([200, 200], [200, 150]);
+            return updateCamera(1.5, 2.5, 1.5);
+        })
+        .then(function() {
+            assertAnnotationsXY([[340, 187], [341, 142], [325, 221]], 'after camera update');
 
-            expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
-            expect(gd.layout.yaxis.range).toBeCloseToArray(newY, precision);
+            return updateCamera(2.1, 0.1, 0.9);
+        })
+        .then(function() {
+            assertAnnotationsXY([[262, 199], [257, 135], [325, 233]], 'base 0');
+        })
+        .catch(fail)
+        .then(done);
+    });
 
-            // Drag scene back along the Y axis
-            mouseTo([200, 150], [200, 200]);
+    it('should be removed when beyond the scene axis ranges', function(done) {
+        var mock = Lib.extendDeep({}, require('@mocks/gl3d_annotations'));
 
-            expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
-            expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
+        // replace text with something easier to identify
+        mock.layout.scene.annotations.forEach(function(ann, i) { ann.text = String(i); });
 
-            // Drag scene along both the X and Y axis
-            mouseTo([200, 200], [220, 150]);
+        Plotly.plot(gd, mock).then(function() {
+            assertAnnotationText(['0', '1', '2', '3', '4', '5', '6'], 'base');
 
-            expect(gd.layout.xaxis.range).toBeCloseToArray(newX, precision);
-            expect(gd.layout.yaxis.range).toBeCloseToArray(newY, precision);
+            return Plotly.relayout(gd, 'scene.yaxis.range', [0.5, 1.5]);
+        })
+        .then(function() {
+            assertAnnotationText(['1', '4', '5', '6'], 'after yaxis range relayout');
 
-            // Drag scene back along the X and Y axis
-            mouseTo([220, 150], [200, 200]);
+            return Plotly.relayout(gd, 'scene.yaxis.range', null);
+        })
+        .then(function() {
+            assertAnnotationText(['0', '1', '2', '3', '4', '5', '6'], 'back to base after yaxis range relayout');
 
-            expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
-            expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
+            return Plotly.relayout(gd, 'scene.zaxis.range', [0, 3]);
         })
-        .then(delay(200))
         .then(function() {
-            // callback count expectation: X and back; Y and back; XY and back
-            expect(relayoutCallback).toHaveBeenCalledTimes(6);
+            assertAnnotationText(['0', '4', '5', '6'], 'after zaxis range relayout');
 
-            // a callback value structure and contents check
-            expect(relayoutCallback).toHaveBeenCalledWith(jasmine.objectContaining({
-                lastInputTime: jasmine.any(Number),
-                xaxis: [jasmine.any(Number), jasmine.any(Number)],
-                yaxis: [jasmine.any(Number), jasmine.any(Number)]
-            }));
+            return Plotly.relayout(gd, 'scene.zaxis.range', null);
+        })
+        .then(function() {
+            assertAnnotationText(['0', '1', '2', '3', '4', '5', '6'], 'back to base after zaxis range relayout');
         })
+        .catch(fail)
         .then(done);
     });
 
-    it('should be able to toggle visibility', function(done) {
-        var _mock = Lib.extendDeep({}, mock);
+    it('should be able to add/remove and hide/unhide themselves via relayout', function(done) {
+        var mock = Lib.extendDeep({}, require('@mocks/gl3d_annotations'));
 
-        // a line object + scatter fancy
-        var OBJECT_PER_TRACE = 2;
+        // replace text with something easier to identify
+        mock.layout.scene.annotations.forEach(function(ann, i) { ann.text = String(i); });
 
-        var objects = function() {
-            try {
-                return gd._fullLayout._plots.xy._scene2d.glplot.objects;
-            }
-            catch(e) {
-                return [];
-            }
+        var annNew = {
+            x: '2017-03-01',
+            y: 'C',
+            z: 3,
+            text: 'new!'
         };
 
-        Plotly.plot(gd, _mock)
-        .then(delay(20))
-        .then(function() {
-            expect(objects().length).toEqual(OBJECT_PER_TRACE);
+        Plotly.plot(gd, mock).then(function() {
+            assertAnnotationText(['0', '1', '2', '3', '4', '5', '6'], 'base');
 
-            return Plotly.restyle(gd, 'visible', 'legendonly');
+            return Plotly.relayout(gd, 'scene.annotations[1].visible', false);
         })
         .then(function() {
-            expect(objects().length).toEqual(OBJECT_PER_TRACE);
-            expect(objects()[0].data.length).toEqual(0);
+            assertAnnotationText(['0', '2', '3', '4', '5', '6'], 'after [1].visible:false');
 
-            return Plotly.restyle(gd, 'visible', true);
+            return Plotly.relayout(gd, 'scene.annotations[1].visible', true);
         })
         .then(function() {
-            expect(objects().length).toEqual(OBJECT_PER_TRACE);
-            expect(objects()[0].data.length).not.toEqual(0);
+            assertAnnotationText(['0', '1', '2', '3', '4', '5', '6'], 'back to base (1)');
 
-            return Plotly.restyle(gd, 'visible', false);
+            return Plotly.relayout(gd, 'scene.annotations[0]', null);
         })
         .then(function() {
-            expect(objects().length).toBe(0);
+            assertAnnotationText(['1', '2', '3', '4', '5', '6'], 'after [0] null');
 
-            return Plotly.restyle(gd, 'visible', true);
+            return Plotly.relayout(gd, 'scene.annotations[0]', annNew);
         })
         .then(function() {
-            expect(objects().length).toEqual(OBJECT_PER_TRACE);
-            expect(objects()[0].data.length).not.toEqual(0);
-        })
-        .catch(fail)
-        .then(done);
-    });
+            assertAnnotationText(['new!', '1', '2', '3', '4', '5', '6'], 'after add new (1)');
 
-    it('should clear orphan cartesian subplots on addTraces', function(done) {
-        Plotly.newPlot(gd, [], {
-            xaxis: { title: 'X' },
-            yaxis: { title: 'Y' }
+            return Plotly.relayout(gd, 'scene.annotations', null);
         })
         .then(function() {
-            return Plotly.addTraces(gd, [{
-                type: 'scattergl',
-                x: [1, 2, 3, 4, 5, 6, 7],
-                y: [0, 5, 8, 9, 8, 5, 0]
-            }]);
+            assertAnnotationText([], 'after rm all');
+
+            return Plotly.relayout(gd, 'scene.annotations[0]', annNew);
         })
         .then(function() {
-            expect(d3.select('.xtitle').size()).toEqual(0);
-            expect(d3.select('.ytitle').size()).toEqual(0);
+            assertAnnotationText(['new!'], 'after add new (2)');
         })
+        .catch(fail)
         .then(done);
     });
 
-    it('supports 1D and 2D Zoom', function(done) {
-        var centerX, centerY;
-        Plotly.newPlot(gd,
-            [{type: 'scattergl', x: [1, 15], y: [1, 15]}],
-            {
-                width: 400,
-                height: 400,
-                margin: {t: 100, b: 100, l: 100, r: 100},
-                xaxis: {range: [0, 16]},
-                yaxis: {range: [0, 16]}
+    it('should work across multiple scenes', function(done) {
+        function assertAnnotationCntPerScene(id, cnt) {
+            expect(d3.selectAll('g.annotation-' + id).size()).toEqual(cnt);
+        }
+
+        Plotly.plot(gd, [{
+            type: 'scatter3d',
+            x: [1, 2, 3],
+            y: [1, 2, 3],
+            z: [1, 2, 1]
+        }, {
+            type: 'scatter3d',
+            x: [1, 2, 3],
+            y: [1, 2, 3],
+            z: [2, 1, 2],
+            scene: 'scene2'
+        }], {
+            scene: {
+                annotations: [{
+                    text: 'hello',
+                    x: 1, y: 1, z: 1
+                }]
+            },
+            scene2: {
+                annotations: [{
+                    text: 'sup?',
+                    x: 1, y: 1, z: 2
+                }, {
+                    text: 'look!',
+                    x: 2, y: 2, z: 1
+                }]
             }
-        )
+        })
         .then(function() {
-            var bBox = gd.getBoundingClientRect();
-            centerX = bBox.left + 200;
-            centerY = bBox.top + 200;
-
-            // 2D
-            mouseTo([centerX - 50, centerY], [centerX + 50, centerY + 50]);
-            expect(gd.layout.xaxis.range).toBeCloseToArray([4, 12], 3);
-            expect(gd.layout.yaxis.range).toBeCloseToArray([4, 8], 3);
-
-            // x only
-            mouseTo([centerX - 50, centerY], [centerX, centerY + 5]);
-            expect(gd.layout.xaxis.range).toBeCloseToArray([6, 8], 3);
-            expect(gd.layout.yaxis.range).toBeCloseToArray([4, 8], 3);
+            assertAnnotationCntPerScene('scene', 1);
+            assertAnnotationCntPerScene('scene2', 2);
 
-            // y only
-            mouseTo([centerX, centerY - 50], [centerX - 5, centerY + 50]);
-            expect(gd.layout.xaxis.range).toBeCloseToArray([6, 8], 3);
-            expect(gd.layout.yaxis.range).toBeCloseToArray([5, 7], 3);
+            return Plotly.deleteTraces(gd, [1]);
+        })
+        .then(function() {
+            assertAnnotationCntPerScene('scene', 1);
+            assertAnnotationCntPerScene('scene2', 2);
 
-            // no change - too small
-            mouseTo([centerX, centerY], [centerX - 5, centerY + 5]);
-            expect(gd.layout.xaxis.range).toBeCloseToArray([6, 8], 3);
-            expect(gd.layout.yaxis.range).toBeCloseToArray([5, 7], 3);
+            return Plotly.deleteTraces(gd, [0]);
+        })
+        .then(function() {
+            assertAnnotationCntPerScene('scene', 1);
+            assertAnnotationCntPerScene('scene2', 2);
         })
         .catch(fail)
         .then(done);
     });
 
-    it('supports axis constraints with zoom', function(done) {
-        var centerX, centerY;
-        Plotly.newPlot(gd,
-            [{type: 'scattergl', x: [1, 15], y: [1, 15]}],
-            {
-                width: 400,
-                height: 400,
-                margin: {t: 100, b: 100, l: 100, r: 100},
-                xaxis: {range: [0, 16]},
-                yaxis: {range: [0, 16]}
-            }
-        )
-        .then(function() {
-            var bBox = gd.getBoundingClientRect();
-            centerX = bBox.left + 200;
-            centerY = bBox.top + 200;
+    it('should contribute to scene axis autorange', function(done) {
+        function assertSceneAxisRanges(xRange, yRange, zRange) {
+            var sceneLayout = gd._fullLayout.scene;
 
-            return Plotly.relayout(gd, {
-                'yaxis.scaleanchor': 'x',
-                'yaxis.scaleratio': 2
-            });
+            expect(sceneLayout.xaxis.range).toBeCloseToArray(xRange, 1, 'xaxis range');
+            expect(sceneLayout.yaxis.range).toBeCloseToArray(yRange, 1, 'yaxis range');
+            expect(sceneLayout.zaxis.range).toBeCloseToArray(zRange, 1, 'zaxis range');
+        }
+
+        Plotly.plot(gd, [{
+            type: 'scatter3d',
+            x: [1, 2, 3],
+            y: [1, 2, 3],
+            z: [1, 2, 1]
+        }], {
+            scene: {
+                annotations: [{
+                    text: 'hello',
+                    x: 1, y: 1, z: 3
+                }]
+            }
         })
         .then(function() {
-            // x range is adjusted to fit constraint
-            expect(gd.layout.xaxis.range).toBeCloseToArray([-8, 24], 3);
-            expect(gd.layout.yaxis.range).toBeCloseToArray([0, 16], 3);
+            assertSceneAxisRanges([0.9375, 3.0625], [0.9375, 3.0625], [0.9375, 3.0625]);
 
-            // now there should only be 2D zooming
-            // dy>>dx
-            mouseTo([centerX, centerY], [centerX - 1, centerY - 50]);
-            expect(gd.layout.xaxis.range).toBeCloseToArray([0, 8], 3);
-            expect(gd.layout.yaxis.range).toBeCloseToArray([8, 12], 3);
+            return Plotly.relayout(gd, 'scene.annotations[0].z', 10);
+        })
+        .then(function() {
+            assertSceneAxisRanges([0.9375, 3.0625], [0.9375, 3.0625], [0.7187, 10.2813]);
+        })
+        .catch(fail)
+        .then(done);
+    });
 
-            // dx>>dy
-            mouseTo([centerX, centerY], [centerX + 50, centerY + 1]);
-            expect(gd.layout.xaxis.range).toBeCloseToArray([4, 6], 3);
-            expect(gd.layout.yaxis.range).toBeCloseToArray([9, 10], 3);
+    it('should allow text and tail position edits under `editable: true`', function(done) {
+        function editText(newText, expectation) {
+            return new Promise(function(resolve) {
+                gd.once('plotly_relayout', function(eventData) {
+                    expect(eventData).toEqual(expectation);
+                    setTimeout(resolve, 0);
+                });
 
-            // no change - too small
-            mouseTo([centerX, centerY], [centerX - 5, centerY + 5]);
-            expect(gd.layout.xaxis.range).toBeCloseToArray([4, 6], 3);
-            expect(gd.layout.yaxis.range).toBeCloseToArray([9, 10], 3);
+                var clickNode = d3.select('g.annotation-text-g').select('g').node();
+                clickNode.dispatchEvent(new window.MouseEvent('click'));
 
-            return Plotly.relayout(gd, {
-                'xaxis.autorange': true,
-                'yaxis.autorange': true
+                var editNode = d3.select('.plugin-editable.editable').node();
+                editNode.dispatchEvent(new window.FocusEvent('focus'));
+
+                editNode.textContent = newText;
+                editNode.dispatchEvent(new window.FocusEvent('focus'));
+                editNode.dispatchEvent(new window.FocusEvent('blur'));
+            });
+        }
+
+        function moveArrowTail(dx, dy, expectation) {
+            var px = 243;
+            var py = 150;
+
+            return new Promise(function(resolve) {
+                gd.once('plotly_relayout', function(eventData) {
+                    expect(eventData).toEqual(expectation);
+                    resolve();
+                });
+
+                mouseEvent('mousemove', px, py);
+                mouseEvent('mousedown', px, py);
+                mouseEvent('mousemove', px + dx, py + dy);
+                mouseEvent('mouseup', px + dx, py + dy);
             });
+        }
+
+        Plotly.plot(gd, [{
+            type: 'scatter3d',
+            x: [1, 2, 3],
+            y: [1, 2, 3],
+            z: [1, 2, 1]
+        }], {
+            scene: {
+                annotations: [{
+                    text: 'hello',
+                    x: 2, y: 2, z: 2,
+                    font: { size: 30 }
+                }]
+            },
+            margin: {l: 0, t: 0, r: 0, b: 0},
+            width: 500,
+            height: 500
+        }, {
+            editable: true
+        })
+        .then(function() {
+            return editText('allo', {'scene.annotations[0].text': 'allo'});
         })
         .then(function() {
-            expect(gd.layout.xaxis.range).toBeCloseToArray([-8.09195, 24.09195], 3);
-            expect(gd.layout.yaxis.range).toBeCloseToArray([-0.04598, 16.04598], 3);
+            return moveArrowTail(-100, -50, {
+                'scene.annotations[0].ax': -110,
+                'scene.annotations[0].ay': -80
+            });
         })
         .catch(fail)
         .then(done);
     });
 
-    it('should change plot type with incomplete data', function(done) {
-        Plotly.plot(gd, [{}]);
+    it('should display hover labels and trigger *plotly_clickannotation* event', function(done) {
+        function dispatch(eventType) {
+            var target = d3.select('g.annotation-text-g').select('g').node();
+            target.dispatchEvent(new MouseEvent(eventType));
+        }
 
-        expect(function() {
-            Plotly.restyle(gd, {type: 'scattergl', x: [[1]]}, 0);
-        }).not.toThrow();
+        Plotly.plot(gd, [{
+            type: 'scatter3d',
+            x: [1, 2, 3],
+            y: [1, 2, 3],
+            z: [1, 2, 1]
+        }], {
+            scene: {
+                annotations: [{
+                    text: 'hello',
+                    x: 2, y: 2, z: 2,
+                    ax: 0, ay: -100,
+                    hovertext: 'HELLO',
+                    hoverlabel: {
+                        bgcolor: 'red',
+                        font: { size: 20 }
+                    }
+                }]
+            },
+            width: 500,
+            height: 500
+        })
+        .then(function() {
+            dispatch('mouseover');
+            expect(d3.select('.hovertext').size()).toEqual(1);
+        })
+        .then(function() {
+            return new Promise(function(resolve, reject) {
+                gd.once('plotly_clickannotation', function(eventData) {
+                    expect(eventData.index).toEqual(0);
+                    expect(eventData.subplotId).toEqual('scene');
+                    resolve();
+                });
 
-        expect(function() {
-            Plotly.restyle(gd, {y: [[1]]}, 0);
-        }).not.toThrow();
+                setTimeout(function() {
+                    reject('plotly_clickannotation did not get called!');
+                }, 100);
 
-        done();
+                dispatch('click');
+            });
+        })
+        .catch(fail)
+        .then(done);
     });
 });
 
@@ -1149,10 +1244,10 @@ describe('Test removal of gl contexts', function() {
             y: [2, 1, 3]
         }])
         .then(function() {
-            expect(gd._fullLayout._plots.xy._scene2d.glplot).toBeDefined();
-
+            expect(gd._fullLayout._plots.xy._scene).toBeDefined();
             Plots.cleanPlot([], {}, gd._fullData, gd._fullLayout);
-            expect(gd._fullLayout._plots).toEqual({});
+
+            expect(gd._fullLayout._plots.xy._scene).toBeUndefined();
         })
         .then(done);
     });
@@ -1210,8 +1305,8 @@ describe('Test removal of gl contexts', function() {
             y: [2, 1, 3]
         }])
         .then(function() {
-            firstGlplotObject = gd._fullLayout._plots.xy._scene2d.glplot;
-            firstGlContext = firstGlplotObject.gl;
+            firstGlplotObject = gd._fullLayout._plots.xy._scene;
+            firstGlContext = firstGlplotObject.scatter2d.gl;
             firstCanvas = firstGlContext.canvas;
 
             expect(firstGlplotObject).toBeDefined();
@@ -1225,8 +1320,8 @@ describe('Test removal of gl contexts', function() {
             }], {});
         })
         .then(function() {
-            var secondGlplotObject = gd._fullLayout._plots.xy._scene2d.glplot;
-            var secondGlContext = secondGlplotObject.gl;
+            var secondGlplotObject = gd._fullLayout._plots.xy._scene;
+            var secondGlContext = secondGlplotObject.scatter2d.gl;
             var secondCanvas = secondGlContext.canvas;
 
             expect(Object.keys(gd._fullLayout._plots).length === 1);
@@ -1291,27 +1386,33 @@ describe('Test gl plot side effects', function() {
             y: [2, 1, 2]
         }];
 
-        Plotly.plot(gd, []).then(function() {
+        Plotly.plot(gd, [])
+        .then(function() {
             countCanvases(0);
 
             return Plotly.plot(gd, data);
-        }).then(function() {
+        })
+        .then(function() {
             countCanvases(3);
 
             return Plotly.purge(gd);
-        }).then(function() {
+        })
+        .then(function() {
             countCanvases(0);
 
             return Plotly.plot(gd, data);
-        }).then(function() {
+        })
+        .then(function() {
             countCanvases(3);
 
             return Plotly.deleteTraces(gd, [0]);
-        }).then(function() {
+        })
+        .then(function() {
             countCanvases(0);
 
             return Plotly.purge(gd);
-        }).then(done);
+        })
+        .then(done);
     });
 
     it('should be able to switch trace type', function(done) {
@@ -1339,10 +1440,13 @@ describe('Test gl plot side effects', function() {
     });
 });
 
-describe('Test gl2d interactions', function() {
+describe('Test gl2d plots', function() {
     var gd;
 
+    var mock = require('@mocks/gl2d_10.json');
+
     beforeEach(function() {
+        jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
         gd = createGraphDiv();
     });
 
@@ -1351,425 +1455,339 @@ describe('Test gl2d interactions', function() {
         destroyGraphDiv();
     });
 
-    it('data-referenced annotations should update on drag', function(done) {
-        function drag(start, end) {
-            mouseEvent('mousemove', start[0], start[1]);
-            mouseEvent('mousedown', start[0], start[1], { buttons: 1 });
-            mouseEvent('mousemove', end[0], end[1], { buttons: 1 });
-            mouseEvent('mouseup', end[0], end[1]);
-        }
+    function mouseTo(p0, p1) {
+        var node = d3.select('.nsewdrag[data-subplot="xy"]').node();
+        var dx = p1[0] - p0[0];
+        var dy = p1[1] - p0[1];
+        return drag(node, dx, dy, null, p0[0], p0[1]);
+    }
 
-        function assertAnnotation(xy) {
-            var ann = d3.select('g.annotation-text-g').select('g');
-            var translate = Drawing.getTranslate(ann);
+    it('should respond to drag interactions', function(done) {
+        var _mock = Lib.extendDeep({}, mock);
 
-            expect(translate.x).toBeWithin(xy[0], 1.5);
-            expect(translate.y).toBeWithin(xy[1], 1.5);
-        }
+        var relayoutCallback = jasmine.createSpy('relayoutCallback');
 
-        Plotly.plot(gd, [{
-            type: 'scattergl',
-            x: [1, 2, 3],
-            y: [2, 1, 2]
-        }], {
-            annotations: [{
-                x: 2,
-                y: 1,
-                text: 'text'
-            }],
-            dragmode: 'pan'
-        })
+        var originalX = [-0.3037383177570093, 5.303738317757009];
+        var originalY = [-0.5, 6.1];
+        var newX = [-0.5, 5];
+        var newY = [-1.7, 4.95];
+        var precision = 1;
+
+        Plotly.newPlot(gd, _mock)
+        .then(delay(20))
         .then(function() {
-            assertAnnotation([327, 315]);
+            expect(gd.layout.xaxis.autorange).toBe(true);
+            expect(gd.layout.yaxis.autorange).toBe(true);
+            expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
+            expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
 
-            drag([250, 200], [200, 150]);
-            assertAnnotation([277, 265]);
+            // Switch to pan mode
+            var buttonPan = selectButton(gd._fullLayout._modeBar, 'pan2d');
+            expect(buttonPan.isActive()).toBe(false, 'initially, zoom is active');
+            buttonPan.click();
+            expect(buttonPan.isActive()).toBe(true, 'switched on dragmode');
 
-            return Plotly.relayout(gd, {
-                'xaxis.range': [1.5, 2.5],
-                'yaxis.range': [1, 1.5]
-            });
+            // Switching mode must not change visible range
+            expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
+            expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
         })
+        .then(delay(200))
         .then(function() {
-            assertAnnotation([327, 331]);
+            gd.on('plotly_relayout', relayoutCallback);
         })
-        .then(done);
-    });
-});
-
-describe('Test gl3d annotations', function() {
-    var gd;
-
-    beforeEach(function() {
-        gd = createGraphDiv();
-    });
-
-    afterEach(function() {
-        Plotly.purge(gd);
-        destroyGraphDiv();
-    });
-
-    function assertAnnotationText(expectations, msg) {
-        var anns = d3.selectAll('g.annotation-text-g');
-
-        expect(anns.size()).toBe(expectations.length, msg);
-
-        anns.each(function(_, i) {
-            var tx = d3.select(this).select('text').text();
-            expect(tx).toEqual(expectations[i], msg + ' - ann ' + i);
-        });
-    }
-
-    function assertAnnotationsXY(expectations, msg) {
-        var TOL = 2.5;
-        var anns = d3.selectAll('g.annotation-text-g');
-
-        expect(anns.size()).toBe(expectations.length, msg);
-
-        anns.each(function(_, i) {
-            var ann = d3.select(this).select('g');
-            var translate = Drawing.getTranslate(ann);
-
-            expect(translate.x).toBeWithin(expectations[i][0], TOL, msg + ' - ann ' + i + ' x');
-            expect(translate.y).toBeWithin(expectations[i][1], TOL, msg + ' - ann ' + i + ' y');
-        });
-    }
-
-    // more robust (especially on CI) than update camera via mouse events
-    function updateCamera(x, y, z) {
-        var scene = gd._fullLayout.scene._scene;
-        var camera = scene.getCamera();
-
-        camera.eye = {x: x, y: y, z: z};
-        scene.setCamera(camera);
-        // need a fairly long delay to let the camera update here
-        // 200 was not robust for me (AJ), 300 seems to be.
-        return delay(300)();
-    }
-
-    it('should move with camera', function(done) {
-        Plotly.plot(gd, [{
-            type: 'scatter3d',
-            x: [1, 2, 3],
-            y: [1, 2, 3],
-            z: [1, 2, 1]
-        }], {
-            scene: {
-                camera: {eye: {x: 2.1, y: 0.1, z: 0.9}},
-                annotations: [{
-                    text: 'hello',
-                    x: 1, y: 1, z: 1
-                }, {
-                    text: 'sup?',
-                    x: 1, y: 1, z: 2
-                }, {
-                    text: 'look!',
-                    x: 2, y: 2, z: 1
-                }]
-            }
+        .then(function() {
+            // Drag scene along the X axis
+            return mouseTo([200, 200], [220, 200]);
         })
         .then(function() {
-            assertAnnotationsXY([[262, 199], [257, 135], [325, 233]], 'base 0');
-
-            return updateCamera(1.5, 2.5, 1.5);
+            expect(gd.layout.xaxis.autorange).toBe(false);
+            expect(gd.layout.yaxis.autorange).toBe(false);
+            expect(gd.layout.xaxis.range).toBeCloseToArray(newX, precision);
+            expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
         })
         .then(function() {
-            assertAnnotationsXY([[340, 187], [341, 142], [325, 221]], 'after camera update');
-
-            return updateCamera(2.1, 0.1, 0.9);
+            // Drag scene back along the X axis
+            return mouseTo([220, 200], [200, 200]);
         })
         .then(function() {
-            assertAnnotationsXY([[262, 199], [257, 135], [325, 233]], 'base 0');
+            expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
+            expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
         })
-        .catch(fail)
-        .then(done);
-    });
-
-    it('should be removed when beyond the scene axis ranges', function(done) {
-        var mock = Lib.extendDeep({}, require('@mocks/gl3d_annotations'));
-
-        // replace text with something easier to identify
-        mock.layout.scene.annotations.forEach(function(ann, i) { ann.text = String(i); });
-
-        Plotly.plot(gd, mock).then(function() {
-            assertAnnotationText(['0', '1', '2', '3', '4', '5', '6'], 'base');
-
-            return Plotly.relayout(gd, 'scene.yaxis.range', [0.5, 1.5]);
+        .then(function() {
+            // Drag scene along the Y axis
+            return mouseTo([200, 200], [200, 150]);
         })
         .then(function() {
-            assertAnnotationText(['1', '4', '5', '6'], 'after yaxis range relayout');
-
-            return Plotly.relayout(gd, 'scene.yaxis.range', null);
+            expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
+            expect(gd.layout.yaxis.range).toBeCloseToArray(newY, precision);
         })
         .then(function() {
-            assertAnnotationText(['0', '1', '2', '3', '4', '5', '6'], 'back to base after yaxis range relayout');
-
-            return Plotly.relayout(gd, 'scene.zaxis.range', [0, 3]);
+            // Drag scene back along the Y axis
+            return mouseTo([200, 150], [200, 200]);
         })
         .then(function() {
-            assertAnnotationText(['0', '4', '5', '6'], 'after zaxis range relayout');
-
-            return Plotly.relayout(gd, 'scene.zaxis.range', null);
+            expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
+            expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
         })
         .then(function() {
-            assertAnnotationText(['0', '1', '2', '3', '4', '5', '6'], 'back to base after zaxis range relayout');
+            // Drag scene along both the X and Y axis
+            return mouseTo([200, 200], [220, 150]);
+        })
+        .then(function() {
+            expect(gd.layout.xaxis.range).toBeCloseToArray(newX, precision);
+            expect(gd.layout.yaxis.range).toBeCloseToArray(newY, precision);
+        })
+        .then(function() {
+            // Drag scene back along the X and Y axis
+            return mouseTo([220, 150], [200, 200]);
+        })
+        .then(function() {
+            expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision);
+            expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision);
+        })
+        .then(delay(200))
+        .then(function() {
+            // callback count expectation: X and back; Y and back; XY and back
+            expect(relayoutCallback).toHaveBeenCalledTimes(6);
+
+            // a callback value structure and contents check
+            expect(relayoutCallback).toHaveBeenCalledWith(jasmine.objectContaining({
+                'xaxis.range[0]': jasmine.any(Number),
+                'xaxis.range[1]': jasmine.any(Number),
+                'yaxis.range[0]': jasmine.any(Number),
+                'yaxis.range[1]': jasmine.any(Number)
+            }));
         })
         .catch(fail)
         .then(done);
     });
 
-    it('should be able to add/remove and hide/unhide themselves via relayout', function(done) {
-        var mock = Lib.extendDeep({}, require('@mocks/gl3d_annotations'));
-
-        // replace text with something easier to identify
-        mock.layout.scene.annotations.forEach(function(ann, i) { ann.text = String(i); });
-
-        var annNew = {
-            x: '2017-03-01',
-            y: 'C',
-            z: 3,
-            text: 'new!'
-        };
-
-        Plotly.plot(gd, mock).then(function() {
-            assertAnnotationText(['0', '1', '2', '3', '4', '5', '6'], 'base');
+    it('@noCI should be able to toggle visibility', function(done) {
+        var _mock = Lib.extendDeep({}, mock);
 
-            return Plotly.relayout(gd, 'scene.annotations[1].visible', false);
+        Plotly.plot(gd, _mock)
+        .then(delay(20))
+        .then(function() {
+            return Plotly.restyle(gd, 'visible', 'legendonly');
         })
         .then(function() {
-            assertAnnotationText(['0', '2', '3', '4', '5', '6'], 'after [1].visible:false');
+            expect(gd.querySelector('.gl-canvas-context')).toBe(null);
 
-            return Plotly.relayout(gd, 'scene.annotations[1].visible', true);
+            return Plotly.restyle(gd, 'visible', true);
         })
         .then(function() {
-            assertAnnotationText(['0', '1', '2', '3', '4', '5', '6'], 'back to base (1)');
+            expect(readPixel(gd.querySelector('.gl-canvas-context'), 108, 100)[0]).not.toBe(0);
 
-            return Plotly.relayout(gd, 'scene.annotations[0]', null);
+            return Plotly.restyle(gd, 'visible', false);
         })
         .then(function() {
-            assertAnnotationText(['1', '2', '3', '4', '5', '6'], 'after [0] null');
+            expect(gd.querySelector('.gl-canvas-context')).toBe(null);
 
-            return Plotly.relayout(gd, 'scene.annotations[0]', annNew);
+            return Plotly.restyle(gd, 'visible', true);
         })
         .then(function() {
-            assertAnnotationText(['new!', '1', '2', '3', '4', '5', '6'], 'after add new (1)');
+            expect(readPixel(gd.querySelector('.gl-canvas-context'), 108, 100)[0]).not.toBe(0);
+        })
+        .catch(fail)
+        .then(done);
+    });
 
-            return Plotly.relayout(gd, 'scene.annotations', null);
+    it('should be able to toggle from svg to gl', function(done) {
+        Plotly.plot(gd, [{
+            y: [1, 2, 1],
+        }])
+        .then(function() {
+            expect(countCanvases()).toBe(0);
+            expect(d3.selectAll('.scatterlayer > .trace').size()).toBe(1);
+
+            return Plotly.restyle(gd, 'type', 'scattergl');
         })
         .then(function() {
-            assertAnnotationText([], 'after rm all');
+            expect(countCanvases()).toBe(3);
+            expect(d3.selectAll('.scatterlayer > .trace').size()).toBe(0);
 
-            return Plotly.relayout(gd, 'scene.annotations[0]', annNew);
+            return Plotly.restyle(gd, 'type', 'scatter');
         })
         .then(function() {
-            assertAnnotationText(['new!'], 'after add new (2)');
+            expect(countCanvases()).toBe(0);
+            expect(d3.selectAll('.scatterlayer > .trace').size()).toBe(1);
         })
         .catch(fail)
         .then(done);
     });
 
-    it('should work across multiple scenes', function(done) {
-        function assertAnnotationCntPerScene(id, cnt) {
-            expect(d3.selectAll('g.annotation-' + id).size()).toEqual(cnt);
-        }
+    it('supports 1D and 2D Zoom', function(done) {
+        var centerX;
+        var centerY;
 
-        Plotly.plot(gd, [{
-            type: 'scatter3d',
-            x: [1, 2, 3],
-            y: [1, 2, 3],
-            z: [1, 2, 1]
-        }, {
-            type: 'scatter3d',
-            x: [1, 2, 3],
-            y: [1, 2, 3],
-            z: [2, 1, 2],
-            scene: 'scene2'
+        Plotly.newPlot(gd, [{
+            type: 'scattergl', x: [1, 15], y: [1, 15]
         }], {
-            scene: {
-                annotations: [{
-                    text: 'hello',
-                    x: 1, y: 1, z: 1
-                }]
-            },
-            scene2: {
-                annotations: [{
-                    text: 'sup?',
-                    x: 1, y: 1, z: 2
-                }, {
-                    text: 'look!',
-                    x: 2, y: 2, z: 1
-                }]
-            }
+            width: 400,
+            height: 400,
+            margin: {t: 100, b: 100, l: 100, r: 100},
+            xaxis: {range: [0, 16]},
+            yaxis: {range: [0, 16]}
         })
         .then(function() {
-            assertAnnotationCntPerScene('scene', 1);
-            assertAnnotationCntPerScene('scene2', 2);
+            var bBox = gd.getBoundingClientRect();
+            centerX = bBox.left + 200;
+            centerY = bBox.top + 200;
 
-            return Plotly.deleteTraces(gd, [1]);
+            return mouseTo([centerX, centerY], [centerX - 5, centerY + 5]);
         })
         .then(function() {
-            assertAnnotationCntPerScene('scene', 1);
-            assertAnnotationCntPerScene('scene2', 2);
-
-            return Plotly.deleteTraces(gd, [0]);
+            // no change - too small
+            expect(gd.layout.xaxis.range).toBeCloseToArray([0, 16], 3);
+            expect(gd.layout.yaxis.range).toBeCloseToArray([0, 16], 3);
         })
         .then(function() {
-            assertAnnotationCntPerScene('scene', 1);
-            assertAnnotationCntPerScene('scene2', 2);
+            return mouseTo([centerX - 50, centerY], [centerX + 50, centerY + 50]);
+        })
+        .then(function() {
+            // 2D
+            expect(gd.layout.xaxis.range).toBeCloseToArray([4, 12], 3);
+            expect(gd.layout.yaxis.range).toBeCloseToArray([4, 8], 3);
+        })
+        .then(function() {
+            return mouseTo([centerX - 50, centerY], [centerX, centerY + 5]);
+        })
+        .then(function() {
+            // x only
+            expect(gd.layout.xaxis.range).toBeCloseToArray([6, 8], 3);
+            expect(gd.layout.yaxis.range).toBeCloseToArray([4, 8], 3);
+        })
+        .then(function() {
+            return mouseTo([centerX, centerY - 50], [centerX - 5, centerY + 50]);
+        })
+        .then(function() {
+            // y only
+            expect(gd.layout.xaxis.range).toBeCloseToArray([6, 8], 3);
+            expect(gd.layout.yaxis.range).toBeCloseToArray([5, 7], 3);
         })
         .catch(fail)
         .then(done);
     });
 
-    it('should contribute to scene axis autorange', function(done) {
-        function assertSceneAxisRanges(xRange, yRange, zRange) {
-            var sceneLayout = gd._fullLayout.scene;
-
-            expect(sceneLayout.xaxis.range).toBeCloseToArray(xRange, 1, 'xaxis range');
-            expect(sceneLayout.yaxis.range).toBeCloseToArray(yRange, 1, 'yaxis range');
-            expect(sceneLayout.zaxis.range).toBeCloseToArray(zRange, 1, 'zaxis range');
-        }
+    it('supports axis constraints with zoom', function(done) {
+        var centerX;
+        var centerY;
 
-        Plotly.plot(gd, [{
-            type: 'scatter3d',
-            x: [1, 2, 3],
-            y: [1, 2, 3],
-            z: [1, 2, 1]
+        Plotly.newPlot(gd, [{
+            type: 'scattergl', x: [1, 15], y: [1, 15]
         }], {
-            scene: {
-                annotations: [{
-                    text: 'hello',
-                    x: 1, y: 1, z: 3
-                }]
-            }
+            width: 400,
+            height: 400,
+            margin: {t: 100, b: 100, l: 100, r: 100},
+            xaxis: {range: [0, 16]},
+            yaxis: {range: [0, 16]}
         })
         .then(function() {
-            assertSceneAxisRanges([0.9375, 3.0625], [0.9375, 3.0625], [0.9375, 3.0625]);
+            var bBox = gd.getBoundingClientRect();
+            centerX = bBox.left + 200;
+            centerY = bBox.top + 200;
 
-            return Plotly.relayout(gd, 'scene.annotations[0].z', 10);
+            return Plotly.relayout(gd, {
+                'yaxis.scaleanchor': 'x',
+                'yaxis.scaleratio': 2
+            });
         })
         .then(function() {
-            assertSceneAxisRanges([0.9375, 3.0625], [0.9375, 3.0625], [0.7187, 10.2813]);
+            // x range is adjusted to fit constraint
+            expect(gd.layout.xaxis.range).toBeCloseToArray([-8, 24], 3);
+            expect(gd.layout.yaxis.range).toBeCloseToArray([0, 16], 3);
+        })
+        .then(function() {
+            return mouseTo([centerX, centerY], [centerX - 5, centerY + 5]);
+        })
+        .then(function() {
+            // no change - too small
+            expect(gd.layout.xaxis.range).toBeCloseToArray([-8, 24], 3);
+            expect(gd.layout.yaxis.range).toBeCloseToArray([0, 16], 3);
+        })
+        .then(function() {
+            // now there should only be 2D zooming
+            // dy>>dx
+            return mouseTo([centerX, centerY], [centerX - 1, centerY - 50]);
+        })
+        .then(function() {
+            expect(gd.layout.xaxis.range).toBeCloseToArray([0, 8], 3);
+            expect(gd.layout.yaxis.range).toBeCloseToArray([8, 12], 3);
+        })
+        .then(function() {
+            return mouseTo([centerX, centerY], [centerX + 50, centerY + 1]);
+        })
+        .then(function() {
+            // dx>>dy
+            expect(gd.layout.xaxis.range).toBeCloseToArray([4, 6], 3);
+            expect(gd.layout.yaxis.range).toBeCloseToArray([9, 10], 3);
+        })
+        .then(function() {
+            return Plotly.relayout(gd, {
+                'xaxis.autorange': true,
+                'yaxis.autorange': true
+            });
+        })
+        .then(function() {
+            expect(gd.layout.xaxis.range).toBeCloseToArray([-7.6, 23.6], 1);
+            expect(gd.layout.yaxis.range).toBeCloseToArray([0.2, 15.8], 1);
         })
         .catch(fail)
         .then(done);
     });
 
-    it('should allow text and tail position edits under `editable: true`', function(done) {
-        function editText(newText, expectation) {
-            return new Promise(function(resolve) {
-                gd.once('plotly_relayout', function(eventData) {
-                    expect(eventData).toEqual(expectation);
-                    setTimeout(resolve, 0);
-                });
-
-                var clickNode = d3.select('g.annotation-text-g').select('g').node();
-                clickNode.dispatchEvent(new window.MouseEvent('click'));
-
-                var editNode = d3.select('.plugin-editable.editable').node();
-                editNode.dispatchEvent(new window.FocusEvent('focus'));
+    it('should change plot type with incomplete data', function(done) {
+        Plotly.plot(gd, [{}]);
+        expect(function() {
+            Plotly.restyle(gd, {type: 'scattergl', x: [[1]]}, 0);
+        }).not.toThrow();
 
-                editNode.textContent = newText;
-                editNode.dispatchEvent(new window.FocusEvent('focus'));
-                editNode.dispatchEvent(new window.FocusEvent('blur'));
-            });
-        }
+        expect(function() {
+            Plotly.restyle(gd, {y: [[1]]}, 0);
+        }).not.toThrow();
 
-        function moveArrowTail(dx, dy, expectation) {
-            var px = 243;
-            var py = 150;
+        done();
+    });
 
-            return new Promise(function(resolve) {
-                gd.once('plotly_relayout', function(eventData) {
-                    expect(eventData).toEqual(expectation);
-                    resolve();
-                });
+    it('data-referenced annotations should update on drag', function(done) {
+        function assertAnnotation(xy) {
+            var ann = d3.select('g.annotation-text-g').select('g');
+            var translate = Drawing.getTranslate(ann);
 
-                mouseEvent('mousemove', px, py);
-                mouseEvent('mousedown', px, py);
-                mouseEvent('mousemove', px + dx, py + dy);
-                mouseEvent('mouseup', px + dx, py + dy);
-            });
+            expect(translate.x).toBeWithin(xy[0], 8);
+            expect(translate.y).toBeWithin(xy[1], 8);
         }
 
-        Plotly.plot(gd, [{
-            type: 'scatter3d',
+        Plotly.newPlot(gd, [{
+            type: 'scattergl',
             x: [1, 2, 3],
-            y: [1, 2, 3],
-            z: [1, 2, 1]
+            y: [2, 1, 2]
         }], {
-            scene: {
-                annotations: [{
-                    text: 'hello',
-                    x: 2, y: 2, z: 2,
-                    font: { size: 30 }
-                }]
-            },
-            margin: {l: 0, t: 0, r: 0, b: 0},
-            width: 500,
-            height: 500
-        }, {
-            editable: true
+            annotations: [{
+                x: 2,
+                y: 1,
+                text: 'text'
+            }],
+            dragmode: 'pan'
         })
         .then(function() {
-            return editText('allo', {'scene.annotations[0].text': 'allo'});
+            assertAnnotation([327, 312]);
         })
         .then(function() {
-            return moveArrowTail(-100, -50, {
-                'scene.annotations[0].ax': -110,
-                'scene.annotations[0].ay': -80
-            });
-        })
-        .catch(fail)
-        .then(done);
-    });
-
-    it('should display hover labels and trigger *plotly_clickannotation* event', function(done) {
-        function dispatch(eventType) {
-            var target = d3.select('g.annotation-text-g').select('g').node();
-            target.dispatchEvent(new MouseEvent(eventType));
-        }
-
-        Plotly.plot(gd, [{
-            type: 'scatter3d',
-            x: [1, 2, 3],
-            y: [1, 2, 3],
-            z: [1, 2, 1]
-        }], {
-            scene: {
-                annotations: [{
-                    text: 'hello',
-                    x: 2, y: 2, z: 2,
-                    ax: 0, ay: -100,
-                    hovertext: 'HELLO',
-                    hoverlabel: {
-                        bgcolor: 'red',
-                        font: { size: 20 }
-                    }
-                }]
-            },
-            width: 500,
-            height: 500
+            return mouseTo([250, 200], [200, 150]);
         })
         .then(function() {
-            dispatch('mouseover');
-            expect(d3.select('.hovertext').size()).toEqual(1);
+            assertAnnotation([277, 262]);
         })
         .then(function() {
-            return new Promise(function(resolve, reject) {
-                gd.once('plotly_clickannotation', function(eventData) {
-                    expect(eventData.index).toEqual(0);
-                    expect(eventData.subplotId).toEqual('scene');
-                    resolve();
-                });
-
-                setTimeout(function() {
-                    reject('plotly_clickannotation did not get called!');
-                }, 100);
-
-                dispatch('click');
+            return Plotly.relayout(gd, {
+                'xaxis.range': [1.5, 2.5],
+                'yaxis.range': [1, 1.5]
             });
         })
+        .then(function() {
+            assertAnnotation([327, 331]);
+        })
         .catch(fail)
         .then(done);
     });
diff --git a/test/jasmine/tests/plots_test.js b/test/jasmine/tests/plots_test.js
index b155d65a808..af025d61ede 100644
--- a/test/jasmine/tests/plots_test.js
+++ b/test/jasmine/tests/plots_test.js
@@ -546,10 +546,11 @@ describe('Test Plots', function() {
     describe('Plots.generalUpdatePerTraceModule', function() {
 
         function _update(subplotCalcData, traceHashOld) {
+            var gd = {};
             var subplot = { traceHash: traceHashOld || {} };
             var calcDataPerModule = [];
 
-            var plot = function(_, moduleCalcData) {
+            var plot = function(gd, subplot, moduleCalcData) {
                 calcDataPerModule.push(moduleCalcData);
             };
 
@@ -557,7 +558,7 @@ describe('Test Plots', function() {
                 calcTrace[0].trace._module = { plot: plot };
             });
 
-            Plots.generalUpdatePerTraceModule(subplot, subplotCalcData, {});
+            Plots.generalUpdatePerTraceModule(gd, subplot, subplotCalcData, {});
 
             return {
                 traceHash: subplot.traceHash,
diff --git a/test/jasmine/tests/polar_test.js b/test/jasmine/tests/polar_test.js
index 27a5ddb91d1..d33b989c813 100644
--- a/test/jasmine/tests/polar_test.js
+++ b/test/jasmine/tests/polar_test.js
@@ -520,6 +520,7 @@ describe('Test polar interactions:', function() {
     ];
 
     beforeEach(function() {
+        jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
         eventData = '';
         eventCnts = {};
         gd = createGraphDiv();
@@ -528,7 +529,7 @@ describe('Test polar interactions:', function() {
     afterEach(destroyGraphDiv);
 
     function _plot(fig) {
-        return Plotly.plot(gd, fig).then(function() {
+        return Plotly.newPlot(gd, fig).then(function() {
             eventNames.forEach(function(k) {
                 eventCnts[k] = 0;
                 gd.on(k, function(d) {
@@ -701,9 +702,12 @@ describe('Test polar interactions:', function() {
         .then(done);
     });
 
-    it('should response to drag interactions on plot area', function(done) {
+    it('@noCI should response to drag interactions on plot area', function(done) {
         var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json'));
 
+        // to avoid dragging on hover labels
+        fig.layout.hovermode = false;
+
         // adjust margins so that middle of plot area is at 300x300
         // with its middle at [200,200]
         fig.layout.width = 400;
@@ -741,14 +745,16 @@ describe('Test polar interactions:', function() {
         }
 
         function _reset() {
-            return _doubleClick(mid).then(function() {
-                relayoutNumber++;
-                resetNumber++;
-
-                var extra = '(reset ' + resetNumber + ')';
-                _assertBase(extra);
-                expect(eventCnts.plotly_doubleclick).toBe(resetNumber, 'doubleclick event #' + extra);
-            });
+            return delay(100)()
+                .then(function() { return _doubleClick(mid); })
+                .then(function() {
+                    relayoutNumber++;
+                    resetNumber++;
+
+                    var extra = '(reset ' + resetNumber + ')';
+                    _assertBase(extra);
+                    expect(eventCnts.plotly_doubleclick).toBe(resetNumber, 'doubleclick event #' + extra);
+                });
         }
 
         _plot(fig)
@@ -787,9 +793,12 @@ describe('Test polar interactions:', function() {
         .then(done);
     });
 
-    it('should response to drag interactions on radial drag area', function(done) {
+    it('@noCI should response to drag interactions on radial drag area', function(done) {
         var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json'));
 
+        // to avoid dragging on hover labels
+        fig.layout.hovermode = false;
+
         // adjust margins so that middle of plot area is at 300x300
         // with its middle at [200,200]
         fig.layout.width = 400;
@@ -803,18 +812,7 @@ describe('Test polar interactions:', function() {
         // to activate the radial drag mode
         function _drag(p0, dp) {
             var node = d3.select('.polar > .draglayer > .radialdrag').node();
-            var p1 = [p0[0] + dp[0] / 2, p0[1] + dp[1] / 2];
-            var p2 = [p0[0] + dp[0], p0[1] + dp[1]];
-
-            mouseEvent('mousemove', p0[0], p0[1], {element: node});
-            mouseEvent('mousedown', p0[0], p0[1], {element: node});
-
-            return delay(250)()
-                .then(function() { mouseEvent('mousemove', p1[0], p1[1], {element: document}); })
-                .then(delay(50))
-                .then(function() { mouseEvent('mousemove', p2[0], p2[1], {element: document}); })
-                .then(function() { mouseEvent('mouseup', p2[0], p2[1], {element: document}); })
-                .then(delay(50));
+            return drag(node, dp[0], dp[1], null, p0[0], p0[1], 2);
         }
 
         function _assert(rng, angle, evtRng1, evtAngle, msg) {
@@ -839,13 +837,15 @@ describe('Test polar interactions:', function() {
         }
 
         function _reset() {
-            return _doubleClick([200, 200]).then(function() {
-                resetNumber++;
-
-                var extra = '(reset ' + resetNumber + ')';
-                _assertBase(extra);
-                expect(eventCnts.plotly_doubleclick).toBe(resetNumber, 'doubleclick event #' + extra);
-            });
+            return delay(100)()
+                .then(function() { return _doubleClick([200, 200]); })
+                .then(function() {
+                    resetNumber++;
+
+                    var extra = '(reset ' + resetNumber + ')';
+                    _assertBase(extra);
+                    expect(eventCnts.plotly_doubleclick).toBe(resetNumber, 'doubleclick event #' + extra);
+                });
         }
 
         _plot(fig)
@@ -877,9 +877,12 @@ describe('Test polar interactions:', function() {
         .then(done);
     });
 
-    it('should response to drag interactions on angular drag area', function(done) {
+    it('@noCI should response to drag interactions on angular drag area', function(done) {
         var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json'));
 
+        // to avoid dragging on hover labels
+        fig.layout.hovermode = false;
+
         // adjust margins so that middle of plot area is at 300x300
         // with its middle at [200,200]
         fig.layout.width = 400;
@@ -909,13 +912,15 @@ describe('Test polar interactions:', function() {
         }
 
         function _reset() {
-            return _doubleClick([200, 200]).then(function() {
-                resetNumber++;
-
-                var extra = '(reset ' + resetNumber + ')';
-                _assertBase(extra);
-                expect(eventCnts.plotly_doubleclick).toBe(resetNumber, 'doubleclick event #' + extra);
-            });
+            return delay(100)()
+                .then(function() { return _doubleClick([200, 200]); })
+                .then(function() {
+                    resetNumber++;
+
+                    var extra = '(reset ' + resetNumber + ')';
+                    _assertBase(extra);
+                    expect(eventCnts.plotly_doubleclick).toBe(resetNumber, 'doubleclick event #' + extra);
+                });
         }
 
         _plot(fig)
diff --git a/test/jasmine/tests/scatterpolargl_test.js b/test/jasmine/tests/scatterpolargl_test.js
new file mode 100644
index 00000000000..766e2551262
--- /dev/null
+++ b/test/jasmine/tests/scatterpolargl_test.js
@@ -0,0 +1,88 @@
+var Plotly = require('@lib');
+var Lib = require('@src/lib');
+
+var createGraphDiv = require('../assets/create_graph_div');
+var destroyGraphDiv = require('../assets/destroy_graph_div');
+var fail = require('../assets/fail_test');
+var mouseEvent = require('../assets/mouse_event');
+
+var customAssertions = require('../assets/custom_assertions');
+var assertHoverLabelContent = customAssertions.assertHoverLabelContent;
+
+describe('Test scatterpolargl hover:', function() {
+    var gd;
+
+    afterEach(function() {
+        Plotly.purge(gd);
+        destroyGraphDiv();
+    });
+
+    function run(specs) {
+        gd = createGraphDiv();
+
+        var fig = Lib.extendDeep(
+            {width: 700, height: 500},
+            specs.mock || require('@mocks/glpolar_scatter.json')
+        );
+
+        if(specs.patch) {
+            fig = specs.patch(fig);
+        }
+
+        var pos = specs.pos || [200, 200];
+
+        return Plotly.newPlot(gd, fig).then(function() {
+            mouseEvent('mousemove', pos[0], pos[1]);
+            assertHoverLabelContent(specs);
+        });
+    }
+
+    [{
+        desc: 'base',
+        nums: 'r: 2.920135\nθ: 138.2489°',
+        name: 'Trial 4'
+    }, {
+        desc: '(no labels - out of sector)',
+        patch: function(fig) {
+            fig.layout.polar.sector = [15, 75];
+            return fig;
+        },
+        pos: [144, 350],
+        nums: '',
+        name: ''
+    }, {
+        desc: 'on a `thetaunit: radians` polar subplot',
+        patch: function(fig) {
+            fig.layout.polar.angularaxis.thetaunit = 'radians';
+            return fig;
+        },
+        nums: 'r: 2.920135\nθ: 2.412898',
+        name: 'Trial 4'
+    }, {
+        desc: 'on log radial axis',
+        patch: function(fig) {
+            fig.layout.polar.radialaxis.type = 'log';
+            return fig;
+        },
+        nums: 'r: 1.108937\nθ: 115.4969°',
+        name: 'Trial 3'
+    }, {
+        desc: 'on category axes',
+        mock: require('@mocks/polar_categories.json'),
+        patch: function(fig) {
+            fig.data.forEach(function(t) {
+                t.type = 'scatterpolargl';
+                t.fill = 'none';
+            });
+            return fig;
+        },
+        pos: [470, 80],
+        nums: 'r: 4\nθ: d',
+        name: 'angular cate...'
+    }]
+    .forEach(function(specs) {
+        it('should generate correct hover labels ' + specs.desc, function(done) {
+            run(specs).catch(fail).then(done);
+        });
+    });
+});