Loading Plotly Graphs on Demand with Waypoints

Posted on 23 November 2016 in Technology

In my last post, 12 Years of Gmail, Part 4: Chat, I included eight Plotly graphs on a single page. All the graphs worked correctly, but the page was taking almost four seconds to render any content at all and up to 6-8 seconds to load completely without cached elements. By contrast, the landing page of chrxs.net takes less than a second to load with visual content rendering almost immediately. The site is intentionally designed to be light weight and uses very few resources on a standard load. But Plotly graphs require a big (1MB+ uncompressed) JavaScript file in order to load with all the bells and whistles. What can be done to improve this slow load time, particularly when many graphs are on a single page?

film strip before Film strip before optimization (webpagetest.org)

The page load film strip above shows almost three whole seconds before any content is rendered. The obvious first step was to move the loading of Plotly's large JavaScript file from the page head (which loads before content is rendered) to the end of the page body, theoretically allowing the page's content to be partially loaded and rendered earlier. However, doing this created a bit of problem because the Plotly graphs were loaded in the body, like so:

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<div class="overflow-x-container">
    <div id="62c34418-006c-4aaf-8df7-15d5ad556369"></div>
    <script type="text/javascript">
        window.PLOTLYENV=window.PLOTLYENV || {};
        window.PLOTLYENV.BASE_URL="https://plot.ly";
        Plotly.newPlot(
            "62c34418-006c-4aaf-8df7-15d5ad556369",
            [...],
            {...}
        );
    </script>
</div>

This means the Plotly command is needed in the body, so Plotly's JavaScript must be loaded before any of the graphs. There are a number of ways to solve this, but the most appealing one I came across is the Waypoints library. Basically, Waypoints provides a framework for executing actions as a user reaches a certain point on the page. This is a common practice for website with lots of images and/or "endless scrolling" features. To make use of it in this situation, I simply removed the JavaScript from the code excerpt above and added the following below the Plotly JavaScript load:

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
new Waypoint({
  element: document.getElementById('62c34418-006c-4aaf-8df7-15d5ad556369'),
  handler: function() {
    window.PLOTLYENV=window.PLOTLYENV || {};
    window.PLOTLYENV.BASE_URL="https://plot.ly";
    Plotly.newPlot(
        "62c34418-006c-4aaf-8df7-15d5ad556369",
        [...],
        {...}
    );
    this.destroy();
  },
  offset: '100%'
});

Line 1 identifies the element ID that will trigger the handler action in Line 2. This ID is the container for the graph that the handler will load. The only addition is Line 10, which destroys the waypoint after it runs once in order to prevent the Plotly graph from loading itself over and over again. This causes the graph to only load a once when the user scrolls to the point on the page that should display the graph.

After I replaced all eight graphs with Waypoints, the page load performance improved massively:

film strip after Film strip after optimization (webpagetest.org)

With these changes the page takes less than one second to render the first bit of content - this can be applied to any number of Plotly graphs on a single page and brings the page load time much more in line with the rest of the website (which does not include the Plotly Javascript at all).