====== MIStudio Konva Bean ======
===== TL;DR / Quick Start =====
See the end of the document for a prompt that you can use with your favorite LLM - ChatGPT, CoPilot, Claude, Gemini, etc. to create any graphics you can imagine...
**Three critical rules**
* Use **class-body method syntax**:
init(container) {
**NOT**:
function init(container) {
* Handle incoming values in:
updateValues(incomingValue) {
* Incoming values are normally JSON. Parse:
var obj = JSON.parse(incomingValue);
then, for example, get the float value:
var value = parseFloat(obj.value);
===== Small Working Example =====
Paste this directly into the **InitJavaScript** property of a Konva bean.
init(container) {
container.innerHTML = "";
container.style.overflow = "hidden";
container.style.position = "relative";
this.stageHost = document.createElement("div");
this.stageHost.style.position = "absolute";
this.stageHost.style.left = "0px";
this.stageHost.style.top = "0px";
this.stageHost.style.width = "100%";
this.stageHost.style.height = "100%";
this.stageHost.style.overflow = "hidden";
container.appendChild(this.stageHost);
var w = container.clientWidth || container.offsetWidth || 300;
var h = container.clientHeight || container.offsetHeight || 200;
this.stage = new window.Konva.Stage({
container: this.stageHost,
width: w,
height: h
});
this.layer = new window.Konva.Layer();
this.stage.add(this.layer);
this.circle = new window.Konva.Circle({
x: w / 2,
y: h / 2,
radius: 40,
fill: "steelblue"
});
this.layer.add(this.circle);
var self = this;
this.resize = function() {
if (!self.stage || !self.circle) {
return;
}
var w = container.clientWidth || container.offsetWidth || 1;
var h = container.clientHeight || container.offsetHeight || 1;
self.stage.width(w);
self.stage.height(h);
self.circle.x(w / 2);
self.circle.y(h / 2);
self.layer.draw();
};
this.resizeObserver = new ResizeObserver(function() {
self.resize();
});
this.resizeObserver.observe(container);
this.resize();
}
cleanup() {
if (this.resizeObserver) {
this.resizeObserver.disconnect();
this.resizeObserver = null;
}
if (this.stage) {
this.stage.destroy();
this.stage = null;
}
this.circle = null;
this.layer = null;
this.stageHost = null;
this.resize = null;
}
updateValues(incomingValue) {
try {
var obj = JSON.parse(incomingValue);
var value = parseFloat(obj.value);
if (this.circle) {
this.circle.fill(value > 50 ? "red" : "green");
this.layer.draw();
}
} catch (err) {
console.error(err);
}
}
===== How the Konva Bean Works =====
The Konva Bean wraps your JavaScript inside a generated JavaScript class.
Your code is inserted inside a class body. Therefore you **must** write JavaScript methods using class syntax.
Valid:
init(container) {
}
updateValues(incomingValue) {
}
cleanup() {
}
Invalid:
function init(container) {
}
const init = () => {
}
===== Bean Lifecycle =====
==== init(container) ====
Called when the component is created.
Use it to:
* Create Konva stage/layers
* Create shapes
* Start animations
* Configure resize handling
==== updateValues(incomingValue) ====
Called whenever the bean receives data.
Typically receives JSON like:
{
"value":71,
"timestamp":1779727392926,
"valueType":73,
"alwaysSend":false,
"quality":0,
"properties":{"ORDER":0}
}
Typical parsing:
var obj = JSON.parse(incomingValue);
var value = parseFloat(obj.value);
==== cleanup() ====
Optional cleanup hook called before script reload.
Use it to:
* Stop animations
* Disconnect ResizeObservers
* Destroy Konva stages
* Remove listeners
===== Full Responsive Example =====
This example:
* Respects MIStudio bean bounds
* Automatically resizes
* Clips to container
* Parses incoming JSON
* Changes ball size/color/speed
init(container) {
container.innerHTML = "";
container.style.overflow = "hidden";
container.style.position = "relative";
if (typeof window.Konva === "undefined") {
container.innerHTML = "Konva is NOT loaded";
return;
}
this.stageHost = document.createElement("div");
this.stageHost.style.position = "absolute";
this.stageHost.style.left = "0px";
this.stageHost.style.top = "0px";
this.stageHost.style.overflow = "hidden";
container.appendChild(this.stageHost);
var w = container.clientWidth || 400;
var h = container.clientHeight || 300;
this.stage = new window.Konva.Stage({
container: this.stageHost,
width: w,
height: h
});
this.layer = new window.Konva.Layer();
this.stage.add(this.layer);
this.circle = new window.Konva.Circle({
x: 60,
y: h / 2,
radius: 35,
fill: "steelblue",
stroke: "white",
strokeWidth: 2
});
this.layer.add(this.circle);
var direction = 1;
var self = this;
this.resize = function() {
var w = container.clientWidth || 1;
var h = container.clientHeight || 1;
self.stage.width(w);
self.stage.height(h);
self.circle.y(h / 2);
var r = self.circle.radius();
self.circle.x(Math.max(r, Math.min(w - r, self.circle.x())));
self.layer.draw();
};
this.resizeObserver = new ResizeObserver(function() {
self.resize();
});
this.resizeObserver.observe(container);
this.anim = new window.Konva.Animation(function(frame) {
var speed = self.speed || 100;
var dt = frame && frame.timeDiff ? frame.timeDiff : 16;
var r = self.circle.radius();
var w = self.stage.width();
var x = self.circle.x() + direction * speed * dt / 1000;
if (x > w - r) {
x = w - r;
direction = -1;
}
if (x < r) {
x = r;
direction = 1;
}
self.circle.x(x);
}, this.layer);
this.anim.start();
}
cleanup() {
if (this.resizeObserver) {
this.resizeObserver.disconnect();
this.resizeObserver = null;
}
if (this.anim) {
this.anim.stop();
this.anim = null;
}
if (this.stage) {
this.stage.destroy();
this.stage = null;
}
}
updateValues(incomingValue) {
var value = NaN;
try {
var obj = JSON.parse(incomingValue);
value = parseFloat(obj.value);
} catch (err) {
value = parseFloat(incomingValue);
}
if (!isNaN(value) && this.circle) {
this.circle.radius(Math.max(10, Math.min(100, value)));
if (value < 30) {
this.circle.fill("green");
} else if (value < 70) {
this.circle.fill("orange");
} else {
this.circle.fill("red");
}
this.speed = Math.max(20, value * 3);
if (this.resize) {
this.resize();
}
this.layer.draw();
}
}
===== Troubleshooting =====
==== Unexpected token '{' ====
You likely used:
function init(container) {
Instead of:
init(container) {
==== Cannot read properties of undefined (reading 'init') ====
Usually means the generated instance was not created or the script had a syntax error.
==== Konva is NOT loaded ====
Ensure:
scriptSources = "konva.min.js"
===== LLM Meta Prompt =====
Copy this into ChatGPT/Claude/etc. and describe the visualization you want. A more extensive prompt is available [[KonvaDetailedPrompt | Konva Detailed Prompt]]
Generate JavaScript for an MIStudio Konva Bean.
CRITICAL REQUIREMENTS:
1. Use class-body method syntax only:
init(container) { }
updateValues(incomingValue) { }
cleanup() { }
DO NOT generate:
function init() { }
top-level JavaScript
arrow functions for lifecycle methods
2. Use window.Konva.
3. Respect container bounds.
Use ResizeObserver and resize with container changes.
4. Use cleanup() to stop animations, disconnect observers and destroy Konva resources.
5. updateValues(incomingValue) receives JSON:
{
"value": number,
"timestamp": number,
"valueType": number,
"quality": number
}
Parse with:
var obj = JSON.parse(incomingValue);
var value = parseFloat(obj.value);
6. Use this. for state.
7. Graphics must stay inside the bean bounds.
8. Use Konva animations and industrial-style visuals suitable for manufacturing (robots, conveyors, tanks, gauges, wafer maps, valves, alarms, etc.).
Now generate a complete working MIStudio Konva Bean for:
[DESCRIBE YOUR GRAPHIC HERE]