Visit Gigasoft's Web Site
ProEssentials v11 Help

Chapter 2: ProEssentialsJS Real-Time Charts

 

Every ProEssentials chart object can be driven in real time. This page covers the JavaScript specifics. The concepts are the desktop concepts, so Strip Chart, RealTime Charts still applies.

 

Two Shapes

Append Add new samples to a trace that scrolls. The chart shifts the data for you. This is the strip chart, and it is the cheaper of the two.
Replace Hand the chart an entirely new dataset each frame. This is what a scope does on each sweep, and what any application does when the data is filtered or recomputed before it is drawn.

 

Append: The Strip Chart

Initialize once. Points is the size of the scrolling buffer, not the number of points visible.

Pego1.PeConfigure.PrepareImages = true;
Pego1.PeData.Subsets = 2;
Pego1.PeData.Points = 200;

// Show the last 20 of the 200 //
Pego1.PeGrid.PointsToGraph = 20;
Pego1.PeGrid.PointsToGraphInit = E.PointsToGraphInit.Last;

// Pin the scale. An autoscaling axis re-fits every frame //
Pego1.PeGrid.Configure.ManualScaleControlY = E.ManualScaleControl.MinMax;
Pego1.PeGrid.Configure.ManualMinY = 1;
Pego1.PeGrid.Configure.ManualMaxY = 100;

// Reserve the widest label so the plot area does not jump //
Pego1.PeGrid.Configure.ManualMaxDataString = "000.000";
Pego1.PeGrid.Configure.ManualMaxPointLabel = "00:00:00xx";

Pego1.PePlot.Allow.StackedData = false;
Pego1.PePlot.Allow.Histogram = false;
Pego1.PeUserInterface.Allow.FocalRect = false;

// Allocate the label array so the append logic has somewhere to shift //
Pego1.PeString.PointLabels[199] = "";

Pego1.PeFunction.ReinitializeResetImage();

 

Then append. The array you pass holds Subsets times amount values, with each subset's samples following the previous subset's.

const AMOUNT = 1;
const newData = new Float64Array(2 * AMOUNT);

newData[0] = readSensorA();  // subset 0 //
newData[AMOUNT] = readSensorB(); // subset 1 //

if (!Pego1.PeData.Y.append(newData, AMOUNT)) {
  // Refused. Almost always a size mismatch //
}
Pego1.PeFunction.ReinitializeResetImage();

 

Two things to know about append. It returns a falsy value if the engine refuses the call, so test it rather than assuming a silent success and then wondering why the chart looks slow. And unlike the desktop, append does not redraw by itself, so the render call is explicit.

 

Point Labels On A Strip Chart

Point labels cannot be appended in JavaScript. On the desktop, PeData.PointLabels.AppendData shifts the label array along with the data. That property is not available in the browser, so shift the labels yourself and write the array back.

// labels is a plain JavaScript array of length Points //
labels.shift();
labels.push(currentTime);
for (let i = 0; i < 200; i++) Pego1.PeString.PointLabels[i] = labels[i];

 

This costs less than it looks. In example 017, measured: 200 label writes 1.2 ms, the append 1.2 ms, the redraw 4.8 ms, about 7 ms against a 25 ms frame budget. Write the labels before the data, which is the order the desktop uses.

 

Replace: A New Dataset Every Frame

Load the whole array and render. The engine does not care that the data changed completely.

const tick = () => {
  Pego1.PeData.Y.load(block, 4, 100000);
  Pego1.PeFunction.ReinitializeResetImage();
  requestAnimationFrame(tick);
};
requestAnimationFrame(tick);

 

Use requestAnimationFrame, not setInterval. It asks for what the display will actually take. With an interval, a frame that runs long does not delay the next callback, so the callbacks queue up and the page falls behind while appearing to work.

 

Going Faster: Skip The Copy

UseDataAtLocation is genuinely zero copy in the browser. The chart allocates the block, you write your values straight into it, and the engine renders those exact bytes rather than a copy of them.

// Once. allocate() takes an ELEMENT COUNT -- the array already decides the width. //
const block = Pego1.PeData.Y.allocate(100000);
Pego1.PeData.Y.useDataAtLocation(block, 100000);

// Every frame: write into the block, then render //
block.set(samples);
Pego1.PeFunction.ReinitializeResetImage();

 

Release before you free. Call useDataAtLocation() with no argument to detach the block and return the chart to its own storage, and only then call block.free(). Freeing first leaves the engine holding a pointer into memory the allocator has reused.

 

Write through block.set(), and never store block.array. It is a view re-derived on every access, because growing the WebAssembly heap detaches existing views while the pointer stays valid. A stored view keeps working until some unrelated allocation grows the heap, and then reads nothing, silently.

 

Going Faster: Put It On The GPU

One property. Set PeConfigure.RenderEngine to Direct3D and the control acquires a WebGPU device and composites a GPU layer into the chart, exactly as the desktop does with the same line of code.

Pego1.PeConfigure.RenderEngine = E.RenderEngine.Direct3D;

 

This is not a 3D feature. On a 2D Scientific Graph it builds a GPU line layer, and that is the path behind the large line-data speed records, which is the usual reason to set it. A contour plotting method gets a contour layer, and the 3D Scientific Graph gets a surface layer.

 

If the browser has no WebGPU device the control falls back to Direct2D and draws. At Direct3D the engine stops emitting the data itself, so without the fallback you would get bare axes. The fallback can be turned off by a page that would rather see the failure than a slower chart.

 

Habits Worth Forming

Pin the axes An autoscaling axis re-fits on every frame and the trace appears to breathe. ManualScaleControlY with a min and max.
Reserve the widest label ManualMaxDataString and ManualMaxPointLabel stop the plot area resizing as label widths change.
Do not rebuild X On the Scientific Graph, set PeData.ReuseDataX when only Y is changing. The Graph object plots equally spaced left to right and has no X series to rebuild.
Measure the frame Time the render, not the whole tick. If the render fits the budget and the page still stutters, the cost is in your data preparation.

 

Examples

Eight examples update live: 017, 115, 116, 117, 118, 145, 146 and 148. Example 017 is the strip chart above, running. See ProEssentialsJS Example Code.