# Musson Industrial’s Embr-Charts Module

**URL:** <https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618>\
**Category:** 3rd Party Modules\
**Tags:** perspective\
**Created:** [June 10, 2024, 12:53pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618 "2024-06-10T12:53:46Z")\
**Posts on this page:** 20\
**Page:** 6

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [February 13, 2025, 7:12pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/101 "2025-02-13T19:12:29Z")

</div>

> [@ryan.white](#):
>
> Edit: Looked into this some more and it appears to be a custom implementation on the examples site. I think that means it would have to be added to the module to be available. 😕

Yup that’s exactly it. Don’t worry, you’re not the first person to ask about it.

You can still run the same JavaScript using the proxy object, but it does required a gateway round trip before executing.

Maybe a JavaScript button component could come to a 3rd party module near you….

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [February 13, 2025, 7:21pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/102 "2025-02-13T19:21:28Z")

</div>

Currently I'm seeing if I can hijack the `props.plugins` to stuff customizable action buttons onto the canvas. I'm definitely smacking into the difference of just adding some JS to a JS file vs putting it into the Ignition component properties.

I was able to get the example from the docs for [plugin defaults](https://www.chartjs.org/docs/latest/developers/plugins.html#plugin-defaults) to work, but that was simply setting a chart background color.

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [February 13, 2025, 7:29pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/103 "2025-02-13T19:29:34Z")

</div>

> [@ryan.white](#):
>
> difference of just adding some JS to a JS file vs putting it into the Ignition component properties

You “_should_” be able to copy any examples 1-for-1, assuming they’re using arrow function syntax.

Trying to write JS in a component is pretty crap at the moment. If I had my way, I’d make a custom property type with a custom editor, but there’s no way to do that in SDK.

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [February 13, 2025, 7:42pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/104 "2025-02-13T19:42:08Z")

</div>

How are properties not defined in the `Add Object Member` list in`props.plugins[x]` handled, are they just discarded? ie, one of the plugins is configured like

> **Plugin Property Config**
>
> ```python
> {
> "events": [
> "mousemove",
> "mouseout",
> "click",
> "touchstart",
> "touchmove"
> ],
> "id": "custom_action_buttons",
> "actions": [
> {
> "name": "Reset Zoom",
> "handler": "(context) \u003d\u003e {context.resetZoom();}"
> }
> ],
> "afterInit": null,
> "beforeInit": "(chart, args, options) \u003d\u003e {\n\tconst {ctx} \u003d chart;\n\tactions.forEach((a, i) \u003d\u003e {\n\t\t//pass\n\t});\n}"
> }
> 
> ```

I get a reference error when trying to access `actions` so I assume its discarded or just not passed to my `beforeInit` call.

For context I'm trying to figure out how to get the following JS into a plugin property on the component:

> **Source JS**
>
> ```python
> const actions = [
> {
> name: "Randomize",
> handler(chart) {
> chart.data.datasets.forEach((dataset) => {
> dataset.data = Utils.numbers({
> count: chart.data.labels.length,
> min: -100,
> max: 100
> });
> });
> chart.update();
> }
> },
> {
> name: "Add Dataset",
> handler(chart) {
> const data = chart.data;
> const dsColor = Utils.namedColor(chart.data.datasets.length);
> const newDataset = {
> label: "Dataset " + (data.datasets.length + 1),
> backgroundColor: Utils.transparentize(dsColor, 0.5),
> borderColor: dsColor,
> borderWidth: 1,
> data: Utils.numbers({ count: data.labels.length, min: -100, max: 100 })
> };
> chart.data.datasets.push(newDataset);
> chart.update();
> }
> },
> {
> name: "Add Data",
> handler(chart) {
> const data = chart.data;
> if (data.datasets.length > 0) {
> data.labels = Utils.months({ count: data.labels.length + 1 });
> 
> for (var index = 0; index < data.datasets.length; ++index) {
> data.datasets[index].data.push(Utils.rand(-100, 100));
> }
> 
> chart.update();
> }
> }
> },
> {
> name: "Remove Dataset",
> handler(chart) {
> chart.data.datasets.pop();
> chart.update();
> }
> },
> {
> name: "Remove Data",
> handler(chart) {
> chart.data.labels.splice(-1, 1); // remove the label first
> 
> chart.data.datasets.forEach((dataset) => {
> dataset.data.pop();
> });
> 
> chart.update();
> }
> }
> ];
> 
> actions.forEach((a, i) => {
> let button = document.createElement("button");
> button.id = "button"+i;
> button.innerText = a.name;
> button.onclick = () => a.handler(myChart);
> document.querySelector(".buttons").appendChild(button);
> });
> 
> ```

Edit: I know this is absolutely not the intended purpose but I found I can access it by (ab)using the `defaults` property.

And yes I know the `forEach` loop technically won't do anything in the component's context, was just trying to see if I can just get the JS in there.

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [February 13, 2025, 8:49pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/105 "2025-02-13T20:49:06Z")

</div>

> [@ryan.white](#):
>
> How are properties not defined in the `Add Object Member` list in`props.plugins[x]` handled, are they just discarded?

No, all provided properties are transformed (either into functions or CSS variable values) and then passed into the chart.

I haven’t tested, but I imagine you need to put your `actions` under a key named `default` in order to access them by `options.actions`.

From the docs:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/2/b/2bbcdbc2cb346bfa08433d6353b979d798dca618.png)

> **[Plugins | Chart.js](https://www.chartjs.org/docs/latest/developers/plugins.html)**
>
> Open source HTML5 Charts for your website

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [February 13, 2025, 8:52pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/106 "2025-02-13T20:52:52Z")

</div>

> [@bmusson](#):
>
> I haven’t tested, but I imagine you need to put your `actions` under a key named `default` in order to access them by `options.actions`.

Correct, that's exactly what I did to access it. I wonder if adding a specific object key to `props.options` would also work. 🤔 Edit: Yes you absolutely can.

Fun part now is modifying the source JS code to be able to insert buttons, as the context in the component is different from the context that the example code had access to.

I'm also digging in the `legend` plugin source code to see how they dedicated a chunk of space on the canvas. They seem to be defining a class, so that might be interesting to try to implement.

If I understand the lifecycle correctly, I'll probably need to define the class in the `install` event? And maybe stuff it into a key in the context object? I'm not sure how to get it to perpetually exist for the lifetime of the chart.

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [February 13, 2025, 10:18pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/107 "2025-02-13T22:18:30Z")

</div>

> [@ryan.white](#):
>
> If I understand the lifecycle correctly, I'll probably need to define the class in the `install` event? And maybe stuff it into a key in the context object? I'm not sure how to get it to perpetually exist for the lifetime of the chart.

You’re in uncharted (heh) territory here, I’ve never done anything of the sort. If you find you need functionality that the chart isn’t providing, I’m open to suggestions.

There is an `events.beforeRender` function you can utilize, but there’s probably a plugin lifecycle step that operates similarly 🤷‍♂️

But you shouldn’t _need_ to use a class, classes in JS are wacky.

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [February 13, 2025, 10:52pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/108 "2025-02-13T22:52:32Z")

</div>

> [@bmusson](#):
>
> If you find you need functionality that the chart isn’t providing, I’m open to suggestions.

I'm basically trying to 'build' a plugin for charts.js without actually being able to provide a js file. I don't know if there is a 'safe' way to allow your module to accept custom js files for us to make our own plugins for the chart.

Edit: Advanced style sheet just came to mind for a method to expose the ability for user to provide some JS to the module.

I would argue my use case probably falls under 'extreme edge case'.

I'm definitely running into scope issues(not allowed to perform imports, some of the probably needed methods are unavailable.) Though these might just come down to me crawling the context structure.

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [February 13, 2025, 11:00pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/109 "2025-02-13T23:00:12Z")

</div>

> [@ryan.white](#):
>
> I don't know if there is a 'safe' way to allow your module to accept custom js files for us to make our own plugins for the chart.

Nope, not possible with my module.

Off the top of my head, the “simplest” option I can think of is using WebDev to serve a file which is loaded using HTML injection.

> [@Perspective HTML Injection](https://forum.inductiveautomation.com/t/perspective-html-injection/74680):
>
> So... while doing some module debugging, I noticed Ignition trying to access a non-existent folder \<install-directory\>\data\modules\com.inductiveautomation.perspective\injectables. After creating the folder manually, Ignition then tried to access: injectables\head\_top.txt injectables\head\_bottom.txt injectables\body\_top.txt injectables\body\_bottom.txt And what do you know, any HTML you put in these files will be directly loaded in the DOM of all Perspective sessions man_shrugging . [java\_x…

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [February 16, 2025, 7:33pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/110 "2025-02-16T19:33:06Z")

</div>

Version 2.0.2 is out, and includes the `timestack` scale.

> **[Release @embr-modules/charts@2.0.2 · mussonindustrial/embr](https://github.com/mussonindustrial/embr/releases/tag/%40embr-modules%2Fcharts%402.0.2)**
>
> Patch Changes
> 
> 
> 39d5957: Add 'timestack' scale by @jkmnt (https://github.com/jkmnt/chartjs-scale-timestack).
> Update License.
> 
> 
> Updated dependencies \[39d5957\]
> 
> @embr-modules/charts-web@2.0.2

> **Demo View**
>
> ```python
> {
> "custom": {
> "format": {
> "month": "short"
> }
> },
> "params": {},
> "propConfig": {
> "custom.data": {
> "access": "PRIVATE",
> "binding": {
> "config": {
> "expression": "now(0)"
> },
> "transforms": [
> {
> "code": "\t\t\n\timport random\n\t\t\n\tnow \u003d value\n\tend \u003d system.date.toMillis(now)\n\tstart \u003d system.date.toMillis(system.date.addMonths(now, -1))\n\tsteps \u003d (end - start) / 2048\n\t\n\tpoints \u003d []\n\ty \u003d 50\n\t\n\tmoves \u003d [-1, 1]\n\t\n\tfor ts in range(start, end, steps):\n\t\ty +\u003d random.choice(moves)\n\t\tpoints.append({ \u0027x\u0027: ts, \u0027y\u0027: y })\n\t\t\t\n\treturn points",
> "type": "script"
> }
> ],
> "type": "expr"
> },
> "persistent": false
> },
> "custom.format": {
> "persistent": true
> }
> },
> "props": {
> "defaultSize": {
> "height": 477
> }
> },
> "root": {
> "children": [
> {
> "meta": {
> "name": "Chartjs"
> },
> "position": {
> "basis": "300px",
> "grow": 1,
> "shrink": 0
> },
> "propConfig": {
> "props.data.datasets[0].data": {
> "binding": {
> "config": {
> "path": "view.custom.data"
> },
> "type": "property"
> }
> },
> "props.options.scales.x.timestack.format_style.month": {
> "binding": {
> "config": {
> "path": "view.custom.format.month"
> },
> "type": "property"
> }
> }
> },
> "props": {
> "data": {
> "datasets": [
> {
> "label": "Dataset 1"
> }
> ]
> },
> "options": {
> "elements": {
> "line": {
> "borderWidth": 1
> },
> "point": {
> "radius": 0
> }
> },
> "parsing": false,
> "plugins": {
> "legend": {
> "display": false
> }
> },
> "scales": {
> "x": {
> "timestack": {
> "format_style": {}
> },
> "type": "timestack"
> },
> "y": {
> "type": "linear"
> }
> }
> },
> "redraw": true
> },
> "type": "embr.chart.chart-js"
> },
> {
> "children": [
> {
> "events": {
> "component": {
> "onActionPerformed": {
> "config": {
> "script": "\tchart \u003d self.parent.parent.getChild(\"Chartjs\")\n\tproxy \u003d chart.getJavaScriptProxy(\u0027chart\u0027)\n\tproxy.runAsync(\u0027\u0027\u0027\n\t\t() \u003d\u003e this.resetZoom()\n\t\u0027\u0027\u0027)\n\t"
> },
> "scope": "G",
> "type": "script"
> }
> }
> },
> "meta": {
> "name": "ResetZoom"
> },
> "position": {
> "shrink": 0
> },
> "props": {
> "image": {
> "icon": {
> "path": "material/zoom_out_map"
> }
> },
> "style": {
> "padding": "0.25rem"
> },
> "text": "Reset Zoom"
> },
> "type": "ia.input.button"
> },
> {
> "children": [
> {
> "meta": {
> "name": "Label"
> },
> "position": {
> "shrink": 0
> },
> "props": {
> "text": "Month Style"
> },
> "type": "ia.display.label"
> },
> {
> "meta": {
> "name": "Dropdown"
> },
> "position": {
> "basis": "150px",
> "shrink": 0
> },
> "propConfig": {
> "props.value": {
> "binding": {
> "config": {
> "bidirectional": true,
> "path": "view.custom.format.month"
> },
> "type": "property"
> }
> }
> },
> "props": {
> "options": [
> {
> "label": "short",
> "value": "short"
> },
> {
> "label": "long",
> "value": "long"
> },
> {
> "label": "numeric",
> "value": "numeric"
> },
> {
> "label": "2-digit",
> "value": "2-digit"
> }
> ],
> "style": {
> "minWidth": "150px"
> }
> },
> "type": "ia.input.dropdown"
> }
> ],
> "meta": {
> "name": "MonthStyle"
> },
> "position": {
> "shrink": 0
> },
> "props": {
> "style": {
> "gap": "0.5rem",
> "marginLeft": "auto"
> }
> },
> "type": "ia.container.flex"
> }
> ],
> "meta": {
> "name": "Interaction"
> },
> "position": {
> "shrink": 0
> },
> "props": {
> "style": {
> "padding": "0.5rem"
> }
> },
> "type": "ia.container.flex"
> }
> ],
> "meta": {
> "name": "root"
> },
> "props": {
> "direction": "column",
> "style": {
> "gap": "1rem",
> "padding": "0.25rem"
> }
> },
> "type": "ia.container.flex"
> }
> }
> 
> ```

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [February 18, 2025, 10:58pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/111 "2025-02-18T22:58:05Z")

</div>

How safe (or not safe) is grabbing a javascript proxy from a chart on view load in and stuffing it into `parVarMap` or similar?

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [February 18, 2025, 11:57pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/112 "2025-02-18T23:57:12Z")

</div>

The proxy object holds a reference to the backing `Component`; I’d be comfortable storing a proxy in a `viewVarMap` but not in a `pageVarMap`.

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [February 19, 2025, 12:00am UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/113 "2025-02-19T00:00:53Z")

</div>

I'm using a proxy object to adjust span/zoom instead of relying on a binding to the component property and the responsiveness is worlds better.

It also means no accidental thrashing from binding/value change script execution order.

Completely unrelated, I can't seem to find/configure the smoothie chart you had previewed a few versions back. Is that a completely separate module?

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [February 19, 2025, 12:36am UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/114 "2025-02-19T00:36:06Z")

</div>

> [@ryan.white](#):
>
> I'm using a proxy object to adjust span/zoom instead of relying on a binding to the component property and the responsiveness is worlds better.

❤🎉

Also worth noting if you talking about storing a proxy object in a `pageVarMap`:

It’s more correct to think about it as _the_ proxy object. It’s created once on first access, then all other calls to `getJavaScriptProxy(name)` return the same object over and over again.

> [@ryan.white](#):
>
> Completely unrelated, I can't seem to find/configure the smoothie chart you had previewed a few versions back.

It hasn’t been merged yet, I decided I didn’t love the `onChartUpdate` component method and wanted a more general solution for scheduling tasks inside of Perspective sessions.

But now that I’ve built it (Periscope’s `system.perspective.queueSubmit` and `system.perspective.queueCancel`, also not merged) I discovered it doesn’t actually replace the `onChartUpdate` functionality 😂.

I can probably post a build including the latest Chart.js components and the beta Smoothie Charts component tomorrow if you’re interested.

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [February 19, 2025, 10:22pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/115 "2025-02-19T22:22:31Z")

</div>

~~How would you go about passing in values of other properties (like custom properties) of the chart component into the inline javascript functions you can define?~~

 

I'm guessing there isn't a way to provide additional arguments other than what is provided by the default action you are scripting on.

~~Edit: trying to abuse a binding to do string concat does not seem to work, as the chart seems to rebuild entirely on a inline function string change.~~

Wow i'm dumb I can just grab `this.store.custom` 🙃 🌴

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [February 20, 2025, 1:25am UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/116 "2025-02-20T01:25:02Z")

</div>

> [@ryan.white](#):
>
> I can just grab `this.store.custom` 🙃 🌴

Yup, you’ve got it. You also have access the global object `perspective.context` for a quick way at the session, page, and view too (obviously you can transverse to them from the `this` component reference, but it gets old fast).

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [February 20, 2025, 1:47am UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/117 "2025-02-20T01:47:02Z")

</div>

> [@bmusson](#):
>
> `perspective.context`

Didn't know about this one. I also don't know how I forgot about the `this.store` I'm using it in about 6 of my scripts already. 🤷

---

<div class="post-metadata">

**Author:** ![louagejasper](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/louagejasper/32/86311_2.png) [@louagejasper](https://forum.inductiveautomation.com/u/louagejasper)\
**Post date:** [February 20, 2025, 1:18pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/118 "2025-02-20T13:18:52Z")

</div>

For anyone using this module,how does it compare to ApexCharts?

---

<div class="post-metadata">

**Author:** ![Benjamin\_Furlani](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/benjamin_furlani/32/66084_2.png) [@Benjamin\_Furlani](https://forum.inductiveautomation.com/u/Benjamin_Furlani)\
**Post date:** [February 20, 2025, 2:26pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/119 "2025-02-20T14:26:05Z")

</div>

I have used both, and found it easier to use. It is technically heavier on the configuration side, but I think it fits the Ignition model well for sandboxiness.

I always felt like ApexCharts were slower for large datasets, but I may not have had to most optimal settings for them. ChartJS just handles a ton of data pretty seamlessly. I can load a few hundred thousand datapoints on a ChartJS and don't feel much lag.

Both have pretty good documentation.

Also, @bmusson has a pretty cool name, but might be biased 🤷‍♂️

**Disclaimer** : I have a lot of experience using ChartJS with normal React applications, and not as much ApexCharts experience so I am sure optimization could have been done for my attempts to implement the ApexCharts.

---

<div class="post-metadata">

**Author:** ![louagejasper](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/louagejasper/32/86311_2.png) [@louagejasper](https://forum.inductiveautomation.com/u/louagejasper)\
**Post date:** [February 20, 2025, 2:39pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/120 "2025-02-20T14:39:58Z")

</div>

Thanks, that is useful input!

[Previous page](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618.md?page=5)

[Next page](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618.md?page=7)
