Lattice Grid Buy a licence

demo D102

Record and Replay a Live Data Grid: Time-Travel Scrubbing: JavaScript Data Grid Demo

Record a live feed, then drag a playhead back to replay the state the grid held a moment ago

grid.timeline

Record a live feed as it runs, then drag a playhead back to see exactly what the grid held a moment ago. Play the recording forward from any point, or press Live to catch up and follow the feed again, so you can review what the data did and not only what it shows now.

Building…
Loading a live grid…

The configuration

<link rel="stylesheet" href="https://cdn.latticegrid.dev/lattice-grid@1.103.2/lattice-grid.min.css">
<script src="https://cdn.latticegrid.dev/lattice-grid@1.103.2/lattice-grid.min.js"></script>

<div id="grid" style="height: 540px"></div>

<script>
  const grid = LatticeGrid.createGrid(document.getElementById('grid'), {
    rowKey: 'id',
    selection: 'multiple',
    toolPanel: {
      side: 'left',
      panels: ['columns', 'filters', 'views', 'quick'],
      actions: ['pause', 'undo', 'redo', 'export', 'maximise'],
      exportName: 'lattice-demo',
    },
    statusBar: { panels: ['rowCount', 'progress', 'updates', 'selectedCount'] },
    highlightOnChange: { duration: 600 },
    // A smaller log than the default, because this page holds it for as long as
    // the visitor stays. Six hundred changes is a couple of minutes of a busy
    // feed, which is more than enough to scrub through.
    updates: { flush: 'frame', logLimit: 600, logRows: 40000 },
    columns: [
      { field: 'symbol', title: 'Symbol', layout: { width: 130, pin: 'start' } },
      { field: 'desk', title: 'Desk', filter: { type: 'set' } },
      { field: 'region', title: 'Region', filter: { type: 'set' } },
      { field: 'price', title: 'Price', type: 'number', layout: { width: 120 },
        format: { decimals: 4 }, filter: { type: 'number' } },
      { field: 'change', title: 'Change', type: 'number', layout: { width: 110 },
        format: { style: 'percent', decimals: 2 } },
      { field: 'ticks', title: 'Ticks', type: 'number', layout: { width: 90 } },
      { field: 'volume', title: 'Volume', type: 'number', layout: { width: 120 },
        format: { notation: 'compact' }, filter: { type: 'number' } },
      { field: 'status', title: 'Status', filter: { type: 'set' }, layout: { width: 110 },
        cell: { decoration: 'pill', variant: { map: { open: 'success', halted: 'danger', settled: 'neutral' } } } },
    ],
    rows,  // 4,000 instruments
  });

  // Nothing is scrubbable until attach(): what a value used to be is not
  // recoverable after the fact, so the recording has to start first.
  grid.timeline.attach();

  socket.onmessage = (event) => {
    grid.rows.queue({ update: JSON.parse(event.data) });
  };
</script>

Record a live feed and replay any moment

This demo runs a live feed into the grid and records every change as it arrives. The playhead above the grid is a real control you can drag: pull it left and the grid steps back through the recorded changes, showing the prices, movements and tick counts each row held at that earlier point. Cells whose value moved since that moment are marked, so the change you are looking for stands out on a wide row instead of being lost in it. Press Play and the playhead walks forward through the recording one change at a time, replaying the feed as it happened. Press Live and the grid catches up on everything it stepped over and follows the feed again. Dragging the playhead pauses the live follow so it cannot move under your hand, and the readout always names where you are standing: live and following, or a set number of changes back with the timestamp you are viewing.

This suits any live source that needs to answer what happened, not just what is showing: a trading blotter where someone wants the book as it stood a few minutes ago, or an operations board where an engineer steps back through the moments before an alert. The recorded window is bounded, so a busy feed can stay scrubbable for the last stretch of history without holding an entire session in memory.

How do I let people scrub back through a live-updating grid?

Start recording with grid.timeline.attach(), which captures each change as the feed delivers it. Bind a slider and a couple of buttons to the seek API: grid.timeline.seek(steps) stands the grid a number of changes back, grid.timeline.step(1) walks it one change forward toward the present, and grid.timeline.toLive() returns to the feed and applies everything stepped over. Read grid.timeline.depth for how far back you can go, grid.timeline.position for where the grid is standing, and grid.timeline.at() for the timestamp being shown, so your readout always reflects the true state. Pause your live follow while the user is dragging, and resume it when the playhead returns to the present.