Search code examples
javascriptplotlyvisualizationplotly-dashplotly.js

How to have a faster rendering, when moving a slider for colorscale range, with Plotly?


The following code works to change the “min” of the color scale of a Plotly heatmap with a slider.

But it is very slow : we have a 1 fps movement when dragging the slider.

Solutions like replacing "heatmap" by "heatmapgl" don't really solve the problem (it maybe improves to 2 fps).

How to have a more responsive / faster change of the color scale with a slider?

For many applications, it is crucial to be able to see in "realtime" (more than 10 fps), the result of a change in the color scale, and it is possible to do this with Matplotlib, etc.

How to get the same behaviour in Plotly JS?

var z = Array.from({length: 500}, () => Array.from({length: 1000}, () => Math.floor(Math.random() * 500)));  
var steps = [], i;
for (i = 0; i < 500; i++)
    steps.push({label: i, method: 'restyle', args: ['zmin', i]});
Plotly.newPlot('myDiv',  [{z: z, colorscale: 'Jet', type: 'heatmap'}], {sliders: [{steps: steps}]});
<script src="https://cdn.plot.ly/plotly-2.16.2.min.js"></script>
<div id="myDiv"></div>


Solution

  • The heatmap needs to be entirely redrawn for each slider step, which takes time particularly because of the number of points (500k).

    However, as discussed in the comments, it appears that it takes longer when there is no smoothing algorithm (zsmooth:false) than when there is (zsmooth:'best' or zsmooth:'fast'), which is not logical : it should rather be the other way around because of the interpolation cost. Looking at the code, I can confirm there is a performance issue that could be addressed.

    Until a PR fixes the issue, there is a workaround solution you can use, which consists of setting :

    1. zsmooth:'fast' in order to benefit from the (currently) fastest drawing method.
    2. image-rendering: pixelated to prevent the browser from doing the smoothing.

    Of course, because it would be too easy, you will land on a bug, which is that high resolution heatmaps are somehow "truncated" when using zsmooth:'fast' (this has been fixed in version 2.21.0). To circumvent this one, we need to set explicit width, height and margins in the layout.

    Nb. regarding the plot size, setting an explicit width, height and margins might be necessary if you want to give the user a chance to properly see the heatmap, ie. at least one pixel per brick obviously unless a specific (zooming) range is defined, and for the slider as well one pixel per step (in the end, the resolution might be an issue per se).

    var z = Array.from({length: 500}, () => Array.from({length: 1000}, () => Math.floor(Math.random() * 500)));  
    var steps = [], i;
    
    for (i = 0; i < 500; i++)
        steps.push({label: i, method: 'restyle', args: ['zmin', i]});
    
    const data = [{
      z: z, 
      colorscale: 'Jet', 
      type: 'heatmap', 
      zsmooth: 'fast'
    }];
    
    const layout = {
      sliders: [{steps: steps}],
      width: 1200,
      height: 650,
      margin: {
        t: 40,
        b: 110,
        l: 90,
        r: 110
      }
    };
    
    Plotly.newPlot('myDiv', data, layout);
    .subplot.xy .heatmaplayer image {
      image-rendering: pixelated;  
    }
    <script src="https://cdn.plot.ly/plotly-2.16.2.min.js"></script>
    <div id="myDiv"></div>