Getting D3 to update a real time chart in the background even when the page is minimized?

2,776 views
Skip to first unread message

Alex Mastro

unread,
May 27, 2014, 10:52:53 AM5/27/14
to d3...@googlegroups.com

I am sending data using socket.io & nodejs to a page running a real-time D3 visualization.  Currently, the server pushes data to the page every 900 ms.   For this test, I am simply generating a random walk server-side that is being sent to the page.  The D3 script checks for new data every 1000 ms and animates the line and bar charts.  On my server's debug console I can see that data is being sent even when the page is minimized.  The problem is that the line chart does not seem to be updating when the page is minimized.  This causes a jump discontinuity in the line chart after the page is unminimized. Examples of this are visible at 10:08:45, 10:09:10, 10:09:38 (the big one), and 10:09:50.  As a result, the time values along the x-axis reflect the current time, but do not match with line chart values before the page is unminimized.

I have a feeling that this has something to do with my tick() function, which recursively calls itself only when the "end" event is fired at the end of a transition.  Is that the issue, or is it something else?  I am new to any sort of web development, so any "rubber duck" explanations will not be taken with offense :)

<!DOCTYPE html>
<meta charset="utf-8">
<html>
<head>
    <title>Live Pressure Data</title>
    <style>
        svg {
            font: 12px sans-serif;
        }

        .line {
            fill: none;
            stroke: black;
            stroke-width: 1.5px;
        }

        .axis path,
        .axis line {
            fill: none;
            stroke: #000;
            shape-rendering: crispEdges;
        }

        .y.axis path {
            display: none;
        }

        .y.axis line {
            stroke: #777;
            stroke-dasharray: 2px,2px;
        }

        .grid .tick {
            stroke: lightgrey;
            opacity: 0.7;
        }

        .grid path {
            stroke-width: 0;
        }
    </style>
    <script src="http://d3js.org/d3.v3.js" charset="utf-8"></script>
    <script src="/socket.io/socket.io.js"></script>
</head>
<body>
    <script>
        var n = 120;
        var nSensors = 4;
        var duration = 1000;

        var now = new Date(Date.now() - duration);
        // 1 x nSensors array of zeros
        var count = d3.range(nSensors).map(function () {
            return 0;
        });
        // nSensors x n array of zeros
        var data = count.map(function () {
            return d3.range(n).map(function () {
                return 0;
            });
        });

        var margin = { top: 20, right: 10, bottom: 20, left: 20 };
        var width = 800 - margin.right - margin.left;
        var height = 300 - margin.top - margin.bottom;

        var x = d3.time.scale()
                       .domain([now - (n - 2) * duration, now - duration])
                       .range([0, width]);

        var y = d3.scale.linear()
                        .domain([0, 100])
                        .range([height, 0]);

        var line = d3.svg.line()
                         .interpolate("basis")
                         .x(function (d, i) { return x(now - (n - 1 - i) * duration); })
                         .y(function (d, i) { return y(d); });

        var color = d3.scale.category10();

        var svg = d3.select("body").append("svg")
                    .attr("class", "lineChart")
                    .attr("width", width + margin.left + margin.right)
                    .attr("height", height + margin.top + margin.bottom)
                    .append("g")
                    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

        svg.append("defs").append("clipPath")
           .attr("id", "clip")
           .append("rect")
           .attr("width", width)
           .attr("height", height);

        var yAxis = d3.svg.axis()
                          .scale(y)
                          .tickSize(width)
                          .orient("right")

        var gy = svg.append("g")
                    .attr("class", "y axis")
                    .call(yAxis)

        gy.selectAll("text")
          .attr("text-anchor", "end")
          .attr("x", 4)
          .attr("dy", -4)

        var xAxis = svg.append("g")
                       .attr("class", "x axis")
                       .attr("transform", "translate(0," + height + ")")
                       .call(x.axis = d3.svg.axis().scale(x).orient("bottom"));

        var clipPath = svg.append("g")
                          .attr("clip-path", "url(#clip)");

        var paths = clipPath.append("g")

        for (var series in data) {
            paths.append("path")
                 .attr("class", "line")
                 .data([data[series]])
                 .style("stroke", color(series))
        }

        // Live bar graph
        var barW = 300 - margin.left - margin.right;
        var barH = 190 - margin.top - margin.bottom;
        var rectH = 28;

        var barX = d3.scale.linear()
                               .domain([0, 100])
                               .range([0, barW]);

        var barSvg = d3.select("body").append("svg")
                       .attr("class", "barChart")
                       .attr("width", barW + margin.right + margin.left)
                       .attr("height", barH + margin.top + margin.bottom)
                       .append("g")
                       .attr("transform", "translate(" + margin.left + "," + margin.top + ")")

        barSvg.selectAll("rect")
              .data(count)
              .enter().append("rect")
              .attr("y", function (d, i) { return i * 38; })
              .attr("width", barX)
              .style("fill", function (d, i) { return color(i); })
              .attr("height", rectH);

        barSvg.selectAll("text")
              .data(count)
              .enter().append("text")
              .attr("x", 50)
              .attr("y", function (d, i) { return (i * 38) + 14 })
              .attr("dy", "0.35em")
              .attr("text-anchor", "end")
              .text(function (d) { return d + " psi" })

        barSvg.append("g")
              .attr("class", "x axis")
              .attr("transform", "translate(0," + barH + ")")
              .call(d3.svg.axis().scale(barX).orient("bottom"))

        // Live data
        var socket = io.connect('');

        socket.on('news', function (pressure) {
            oldCount = count;
            count = pressure.values;
        })

        // Animate
        tick();

        function tick() {

            // update the domains
            now = new Date();
            x.domain([now - (n - 2) * duration, now - duration]);

            for (var series in data) {
                data[series].push(count[series]);
            }

            // slide the x-axis left
            xAxis.transition()
                 .duration(duration)
                 .ease("linear")
                 .call(x.axis);

            // redraw the line
            svg.selectAll(".line")
               .attr("d", line)

            // slide the line left
            paths.attr("transform", null)
                 .transition()
                 .duration(duration)
                 .ease("linear")
                 .attr("transform", "translate(" + x(now - (n - 1) * duration) + ")")
                 .each("end", tick);

            // pop the old data point off the front
            for (var series in data) {
                data[series].shift();
            }

            // bar animation
            barSvg.selectAll("rect")
                  .data(count)
                  .transition()
                  .duration(duration)
                  .attr("width", barX);

            barSvg.selectAll("text")
                  .data(count)
                  .text(function (d) { return Math.floor(d) + " psi" })
        }

    </script>
</body>
</html>

Alex Mastro

unread,
May 27, 2014, 11:08:54 AM5/27/14
to d3...@googlegroups.com
Here is a jsfiddle that exhibits the issue.  Here, I am just generating the random walk using a setInterval inside the page. http://jsfiddle.net/3wW6X/

Alex Mastro

unread,
May 28, 2014, 3:50:38 PM5/28/14
to d3...@googlegroups.com
Mike's real-time scrolling demo in his Path Transitions tutorial exhibits this as well.  When the page is minimized, or another tab is in view, the chart's position freezes.

Riccardo Scalco

unread,
Oct 20, 2014, 4:27:18 PM10/20/14
to d3...@googlegroups.com
Hi there, I just noted the issue. Did you find the solution in the meanwhile? Thanks.
Message has been deleted

paul.c...@ideoba.com

unread,
Oct 22, 2014, 8:52:26 AM10/22/14
to d3...@googlegroups.com
That's generally how browsers work - only the active tab is 'active', others in the background are throttled or paused (depending on the browser) AFAIK.

I think that Chrome does (or did) set inactive tabs to update every second or so to save a background tab from hogging resources from the active tab.

paul.c...@ideoba.com

unread,
Oct 22, 2014, 8:53:27 AM10/22/14
to d3...@googlegroups.com

dah...@gmail.com

unread,
Jul 30, 2020, 7:39:38 PM7/30/20
to d3-js
Hi friend!

I have a similar issue now: I have a real time chart, but when i chage tab and passed some minutes, the tab is freeze....
How do you solve that?
BR""

Reply all
Reply to author
Forward
0 new messages