Chart animation

Hi everyone!

I am trying to have a chart that has the appearance of being animation / looks like it is updating in real-time. This is to display the velocity of something as a line chart over time in real-time (or close to real time).
The data in my sql database is being updated once per second with 60 data points.
I want those data points to appear gradually on my chart.

I gather I need to have a slight delay and progressively show more rows of data.
However, I am just note quite sure how to implement this. Reading every few milliseconds from my database seems like a poor choice / not-feasible.

Does anyone have a suggestion on how best to implement this?

Thanks!

Hey @bg1900!

This is a fun question! Since it's possible to pass a dynamic dataset into the chart component you can set up a kind of pseudo-animation by having a query return different values based on where the animation currently is, e.g.

return trajectoryData.data.slice(0, frameIndex);

You can then set up another JS query that triggers it at regular intervals using addtionalScope:

const frameSpeedMs = 100;

for(let i = 0; i <= trajectoryData.data.length; i++){
  animatedData.trigger({additionalScope: {frameIndex: i}}); 
  await new Promise(r => setTimeout(r, frameSpeedMs)); //adds delay
}

It may be helpful to fix the range of your chart (Plotly docs on how to do that here).

With that you can get something like this:

This might not work well with larger data sets or more complex animations. If you want to set up something like that it might be worth importing PlotlyJS as a library (docs on how to do that here, link to the PlotlyJS library here). The are some cool examples of animation in their docs (here).

I've attached the JSON of an app you can import to play around with either option!

animated_chart.json

1 Like

Thanks Kabirdas! I'll take a look at all that.
Currently I was trying to do it by having the range of x-axis on the chart linked to one of the timer components. However, the animation is quite jittery.

The animation I am trying to make has the data constantly updated in an SQL database each second. I am not sure if this might be too much for retool!

is there a way to make it show a number of certain points per second? e.g. 60 dots per second?

:thinking: do you want to show 60 dots together in a single frame? If so, you could potentially batch the data, the lodash chunk function is particularly helpful for that and comes built it:

const batchedData = _.chunk(trajectoryData.data, 60);
return batchedData.slice(0, frameIndex).flat();

If you want to show 60 frames per second then I would suggest using a custom component instead to handle the animation.

I'm curious to hear more about the use case too! Would you be querying your SQL database every second? What kind of data is the graph showing?

(Pictures would be awesome!)

oh interesting!
Yes 60 frames per second effectively is the sort of thing I am trying to do (well various frame rates, but one is at about 600 data points per second)
.
What sort of custom component would you recommend?

I would happily share the use case with you out of the forum if you like! (my employer may get a bit mad otherwise :wink: )
Ideally the SQL database would be queried every second but Retool gets very unresponsive if I do this so I instead, for now, run it every 5 seconds or so-- so there is a lag to the data.

Since PlotlyJS is the underlying library for the chart component, building a custom component from that library will look pretty similar and have much of the same configuration. If you were to take this example from their docs, for instance, and put it in a custom component the code might look something like:

<script src="https://cdn.plot.ly/plotly-2.20.0.min.js" charset="utf-8"></script>
<div id="myDiv" style="width:97vw;height:96vh;"></div>
<script>
Retool.subscribe(model => {
  const n = model.data.x.length;
  const x = model.data.x;
  const z = model.data.z;
  const y = model.data.y;
  const dt = 0.015;
  
Plotly.newPlot('myDiv', [{
  x: x,
  y: z,
  mode: 'markers'
}], {
  xaxis: {range: [-40, 40]},
  yaxis: {range: [0, 60]}
})

function compute () {
  var s = 10, b = 8/3, r = 28;
  var dx, dy, dz;
  var xh, yh, zh;
  for (var i = 0; i < n; i++) {
    dx = s * (y[i] - x[i]);
    dy = x[i] * (r - z[i]) - y[i];
    dz = x[i] * y[i] - b * z[i];

    xh = x[i] + dx * dt * 0.5;
    yh = y[i] + dy * dt * 0.5;
    zh = z[i] + dz * dt * 0.5;

    dx = s * (yh - xh);
    dy = xh * (r - zh) - yh;
    dz = xh * yh - b * zh;

    x[i] += dx * dt;
    y[i] += dy * dt;
    z[i] += dz * dt;
  }
}

function update () {
  compute();

  Plotly.animate('myDiv', {
    data: [{x: x, y: z}]
  }, {
    transition: {
      duration: 0
    },
    frame: {
      duration: 0,
      redraw: false
    }
  });

  requestAnimationFrame(update);
}

requestAnimationFrame(update);
})
</script>

That's a bit of a complex example but it looks cool! :sweat_smile:

Jun-26-2023 16-35-52

The key is that you can pass data from your app to the component through the custom component's model by using:

Retool.subscribe(model => {
   /* access properties of the custom component model here! */
});

For the code that handles the actual animation itself the source to look at first is the Plotly docs. It should be usable in a <script> as long as you've included their library in your component as well with:

<script src="https://cdn.plot.ly/plotly-2.20.0.min.js" charset="utf-8"></script>
1 Like

is there a more simple example of just animating a waveform? e.g. from left to right

It looks like this tutorial could be adapted to display a waveform without the stretching that happens in the beginning, does that seem like it could work?