ProEssentialsJS v11 Walkthrough

From npm install to a live, interactive, real-time chart - step by step, in the order you would actually build one

JavaScript chart walkthrough
WebAssembly chart tutorial
npm install javascript chart
javascript chart example code
ProEssentialsJS chart
javascript real time chart tutorial
canvas2d chart getting started
javascript charting library walkthrough

ProEssentialsJS v11 Walkthrough
Your first chart, step by step

This walks through building a real chart from nothing: install, data, axes, interaction, a live real-time trace, and an event handler for clicks on the data. It should take about ten minutes, and there is no build toolchain required if you do not want one.

If you already know ProEssentials on the desktop, you can skim. The JavaScript property names are the WinForms property names, and so are the event names. Step 10 puts the C# and the JavaScript side by side so you can see how little changes.

Prefer to read finished code? Open the live example browser — every example shows its own source beside the chart.

1) Install

Two ways in, and the page you write is identical either way. Only the paths differ.

Clone the starter — the shortest way to a chart on screen. The library is committed beside the page, so there is nothing to resolve and nothing to install:

git clone https://github.com/GigasoftInc/proessentials-js-starter
npm start

That is the whole procedure. The package has no dependencies — the one script it defines runs a small static server, for the reason below.

Or from npm, in a project of your own:

npm install proessentials

Either way, the page is two script tags. The first is the engine, one classic script. The second is your own code, as a module.

<script src="proessentials.iife.js"></script>
<script type="module" src="app.js"></script>

Inside app.js you import the property surface for the chart you want. That is an import in your own JavaScript, not another include:

import { attachApi, Enums as E } from './pe-api-graph.js';

It cannot be one tag, and that is not a packaging shortcoming: the property surfaces are real ES modules and a classic script cannot hold one. Charting libraries that ship both forms split on the same line. Installed from npm the two paths point into node_modules/proessentials/dist/ instead — keep the leading ./, because a leading / is a valid URL that runs perfectly while your editor silently loses every completion.

Nothing else is configured. No bundler, no import map, no jsconfig.json, no copying of assets. The library resolves its own WebAssembly binary and its own built-in bitmaps against its own script URL rather than the page, so they are found wherever the library sits. Editor completion works for the same reason: the TypeScript definitions are siblings of the modules they describe.

Serve the page. Do not open it from disk. A file:// URL has no origin, and a browser will not fetch a WebAssembly module without one — so the page stays blank and nothing in it is at fault. Any static server will do, and npm start above is one. This catches people arriving from libraries that do open from disk: a plain script with no modules and nothing to fetch opens happily. This one has a 2.9 MB engine to fetch, and says so if you try.

No licence key is required to run any of this. There is no activation, no domain registration and no phone-home.

2) Give it somewhere to draw

One element. The chart sizes itself to it.

<div id="chart" style="width:800px; height:520px"></div>

3) Create the chart

The engine is WebAssembly, so it loads once and asynchronously. Everything after that is synchronous property setting.

// 1. The engine. Once per page, however many charts you go on to make //
const m = await ProEssentials();

// 2. The control. This is the WinForms designer step: dropping a Pego
// on the form. autoResize is the web Dock = Fill //
const ctl = new PeControl(document.getElementById('chart'), {
  module: m,
  kind: 'graph',
  autoResize: true,
});

// 3. The property tree //
const Pego1 = ctl.attach(attachApi);

Three objects, and each one has a job. m is the engine and you will not touch it again. ctl is the control — it owns the canvas, the menu, the scrollbars and the events. Pego1 is the property surface, and it is where the rest of this walkthrough happens.

Why Pego1? Because that is what the object is called in every ProEssentials example, on every platform, going back thirty years. Name it whatever you like — but if you keep the name, our examples and our AI assistant paste straight into your project.

4) Pass the data — and mind the order

This is the one step worth reading twice. ProEssentials wants properties in a specific order:

Subsets, then Points, then the data, then everything else, and render() last.

The reason is that every data write is bounds-checked against Subsets × Points. Set the data before you have told the chart how big it is and the writes are rejected — and the chart will still draw, using default data, looking entirely convincing. It is the single most common mistake, on every platform, and it is easy to avoid once you know it.

// 1. Shape first. Subsets = rows, Points = columns //
Pego1.PeData.Subsets = 2;
Pego1.PeData.Points = 6;

// 2. Then the data //
Pego1.PeData.Y[0][0] = 10; Pego1.PeData.Y[0][1] = 30;
Pego1.PeData.Y[0][2] = 20; Pego1.PeData.Y[0][3] = 40;
Pego1.PeData.Y[0][4] = 30; Pego1.PeData.Y[0][5] = 50;
Pego1.PeData.Y[1][0] = 15; Pego1.PeData.Y[1][1] = 63;
Pego1.PeData.Y[1][2] = 74; Pego1.PeData.Y[1][3] = 54;
Pego1.PeData.Y[1][4] = 25; Pego1.PeData.Y[1][5] = 34;

Note the indexing. C# writes PeData.Y[s, p]; JavaScript has no two-dimensional indexer, so it is PeData.Y[s][p]. That is the only shape difference in the whole API.

5) Titles, labels and axes

Pego1.PeString.MainTitle = "Units Sold per Month";
Pego1.PeString.SubTitle = "";
Pego1.PeString.YAxisLabel = "Units Sold";

Pego1.PeString.PointLabels[0] = "Jan";
Pego1.PeString.PointLabels[1] = "Feb";
Pego1.PeString.PointLabels[2] = "Mar";
Pego1.PeString.PointLabels[3] = "Apr";
Pego1.PeString.PointLabels[4] = "May";
Pego1.PeString.PointLabels[5] = "Jun";

Pego1.PeString.SubsetLabels[0] = "Texas";
Pego1.PeString.SubsetLabels[1] = "Florida";

Axis scaling is automatic by default. When you need to pin it — and on a real-time chart you almost always do, because an autoscaling axis re-fits on every frame and the trace appears to breathe — take manual control:

Pego1.PeGrid.Configure.ManualScaleControlY = E.ManualScaleControl.MinMax;
Pego1.PeGrid.Configure.ManualMinY = 0;
Pego1.PeGrid.Configure.ManualMaxY = 100;

A Graph object scales the Y axis only. It plots its points equally spaced from left to right, so there is no X scale to pin and no ManualMinX. If your X values carry their own meaning, such as timestamps or irregular sample intervals, that is a Scientific Graph, which has the full X set under the same names. It is the same API throughout; see the example browser for both.

6) Choose how it draws

Enumerations live on E, and they carry the same names as the .NET enums.

Pego1.PePlot.Method = E.GraphPlottingMethod.Bar;
Pego1.PeGrid.LineControl = E.GridLineControl.Both;
Pego1.PeGrid.Style = E.GridStyle.Dot;
Pego1.PePlot.Option.BarGlassEffect = true;
Pego1.PePlot.DataShadows = E.DataShadows.Shadows;

7) Make it interactive

Zooming, scrollbars, the right-click menu and the tracking cursor are all built in. They are properties, not code you write.

// Left-drag draws a zoom box; z or the popup menu undoes it //
Pego1.PeUserInterface.Allow.Zooming = E.AllowZooming.HorzAndVert;

// Follows the data under the pointer and shows the value //
Pego1.PeUserInterface.Cursor.PromptTracking = true;

// Middle-button drag to pan //
Pego1.PeUserInterface.Scrollbar.MouseDraggingX = true;
Pego1.PeUserInterface.Scrollbar.MouseDraggingY = true;

// This enables data hot spots. Step 9 writes the handler //
Pego1.PeUserInterface.HotSpot.Data = true;

8) Render it — and then keep it moving

One call, at the end, after every property is set.

ctl.render(); // last, always //

For real time, there are two shapes and it is worth knowing which you need.

Append — add new samples to a growing trace. Cheap, and the usual choice for a strip chart.

Replace — hand the chart an entirely new dataset every 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. It is the harder case, and it is the one the engine was built for: the example below replaces four subsets of 100,000 points — 400,000 in total — on every single frame.

// requestAnimationFrame, not setInterval -- ask for what the display
// will take and never more, or the callbacks queue up //
const tick = () => {
  Pego1.PeData.Y.load(block, 4, 100000); // values, subsets, points //
  ctl.render();
  requestAnimationFrame(tick);
};
requestAnimationFrame(tick);

Two habits worth forming. Pin the axis rather than letting it autoscale, or the chart re-fits on every frame and the trace appears to breathe. And hand the engine a whole typed array with load() rather than assigning point by point — one call crosses the boundary once.

On a Scientific Graph, where the X series is real, there is a third: PeData.ReuseDataX = true when only Y is changing, so the engine does not rebuild an X series that did not move. A Graph object has no X series, so it does not apply here.

9) Respond to a click on a data point

Step 7 enabled data hot spots. This is the handler.

ctl.PeDataHotSpot.add((sender, ev) => {
  alert("Subset " + ev.subset + ", Point " + ev.point +
      " with a value of " + Pego1.PeData.Y[ev.subset][ev.point]);
});

Click a bar. The engine hit-tests on mouse down, fills the hot spot record, and raises the matching event with ev.subset and ev.point already decoded. You do not attach a click listener and you do not decode a type code.

.add is C#'s +=, and .remove is -=. JavaScript has no operator to overload, so the events are methods. They are multicast, like the .NET originals: a second .add adds a second handler rather than replacing the first. Each handle also carries .count and .clear().

A Graph carries 37 events, spelled as .NET spells them — PeSubsetHotSpot, PeGraphHotSpot, PeMainTitle, PeZoomIn, PeCursorMoved and the rest. Events a given control cannot raise are absent rather than dead: there is no handle to subscribe to, so a subscription that could never fire is a TypeError at the line that wrote it instead of silence at runtime.

Note the receiver. Properties are on Pego1; events are on ctl, the control itself. In WinForms both sit on pego1. The web keeps events on the control so that a page which never binds the property surface still receives them — a chart you only ever click needs no attach() at all.

10) If you already know the desktop control

The same chart, twice. This is the whole argument for the API and it is easier to show than to explain.

C# — WinForms, WPF or WinUI:

pego1.PeData.Subsets = 2;
pego1.PeData.Points = 6;
pego1.PeData.Y[0, 0] = 10;
pego1.PeString.MainTitle = "Units Sold per Month";
pego1.PePlot.Method = GraphPlottingMethod.Bar;
pego1.PeUserInterface.HotSpot.Data = true;
pego1.PeDataHotSpot += OnDataHotSpot;
pego1.PeFunction.ReinitializeResetImage();

JavaScript — the browser:

Pego1.PeData.Subsets = 2;
Pego1.PeData.Points = 6;
Pego1.PeData.Y[0][0] = 10;
Pego1.PeString.MainTitle = "Units Sold per Month";
Pego1.PePlot.Method = E.GraphPlottingMethod.Bar;
Pego1.PeUserInterface.HotSpot.Data = true;
ctl.PeDataHotSpot.add(onDataHotSpot);
ctl.render();

Same property names, same order, same engine underneath. Five differences, and that is the list: the two-dimensional indexer, the enum prefix, .add for +=, the receiver for events, and the final call. Everything you know about ProEssentials transfers, and so does everything our AI assistant knows — because it is answering from the same property model.

Transcribing existing desktop code? The last line runs verbatim: Pego1.PeFunction.ReinitializeResetImage() is on the JavaScript control too, along with Reinitialize, Reset, GetRectGraph and the rest of PeFunction. It is the same call as render(), which is the shorter spelling we use in new browser code.

Where to go next
  • The live example browser — the same example set as our desktop demo, each with its source beside it.
  • PE-Query — ask it for the property you need and get an answer validated against the compiled engine rather than searched from documentation.
  • The API explorer — every property, on every control.
  • How it compares — against Highcharts, SciChart.js, LightningChart JS and the rest.
  • Ask us — support is free, unlimited, and answered by the people who wrote the engine.