Hi there !
Just sharing a small flow that allows to display P5.js graphics in a widget. The trick is to use iframe. Controls here are possible thru mqtt.
Code based on the three.js demo found in https://flows.nodered.org/
P5.js sample code grabbed from https://p5js.org/examples
[{"id":"26b3c373.d3c1ac","type":"comment","z":"714d61bd.6125f","name":"P5.js example","info":"","x":130,"y":200,"wires":[]},{"id":"d1f80fe9.20ab5","type":"http in","z":"714d61bd.6125f","name":"","url":"/p5js","method":"get","upload":false,"swaggerDoc":"","x":120,"y":240,"wires":[["7e285f13.49a7d"]]},{"id":"7e285f13.49a7d","type":"template","z":"714d61bd.6125f","name":"Color","field":"payload.p5js","fieldType":"msg","format":"javascript","syntax":"plain","template":"// Write pure P5.js code here -----------------------------\n\nlet rSlider, gSlider, bSlider, mqttRadius;\n\nfunction setup() {\n // create canvas\n width = window.innerWidth;\n height = window.innerHeight;\n createCanvas(width, height); \n textSize(15);\n noStroke();\n\n // create sliders\n rSlider = createSlider(0, 255, 100);\n rSlider.position(20, 20);\n gSlider = createSlider(0, 255, 0);\n gSlider.position(20, 50);\n bSlider = createSlider(0, 255, 255);\n bSlider.position(20, 80);\n}\n\nfunction draw() {\n const r = rSlider.value();\n const g = gSlider.value();\n const b = bSlider.value();\n background(r, g, b);\n text('red', rSlider.x * 2 + rSlider.width, 35);\n text('green', gSlider.x * 2 + gSlider.width, 65);\n text('blue', bSlider.x * 2 + bSlider.width, 95);\n \n ellipse(width/2,height/2,mqttRadius,mqttRadius);\n}\n\n\n//----------------------------------------\n","output":"str","x":290,"y":240,"wires":[["b817762f.d6cac8"]]},{"id":"b817762f.d6cac8","type":"template","z":"714d61bd.6125f","name":"MQTT","field":"payload.mqtt","fieldType":"msg","format":"javascript","syntax":"plain","template":"// Write MQTT code here ----------------------------------------\n\nvar client = mqtt.connect('mqtt://192.168.1.68:8080');\n\nclient.on('connect', function () {\n client.subscribe('p5js/#', function (err) {\n if (!err) {\n //client.publish('p5js', 'P5.js has subscribed to p5js/# topic')\n }\n })\n})\n\nclient.on('message', function (topic, payload) {\n // payload is Buffer\n console.log(\"MQTT:[\"+topic.toString()+\"/\"+payload.toString()+\"]\")\n \n myText = payload.toString();\n mqttRadius = parseInt(myText)\n \n //redraw();\n})\n","output":"str","x":450,"y":240,"wires":[["a2223ec8.98c74"]]},{"id":"a2223ec8.98c74","type":"template","z":"714d61bd.6125f","name":"HTML","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<html>\n<head>\n<meta charset=\"utf-8\">\n\n<script type=\"text/javascript\" src=\"https://cdn.jsdelivr.net/npm/p5@1.0.0/lib/p5.js\"></script>\n<script type=\"text/javascript\" src=\"https://cdn.jsdelivr.net/npm/mqtt@3.0.0/dist/mqtt.min.js\"></script>\n \n\n\n<script>{{{payload.p5js}}}</script>\n<script>{{{payload.mqtt}}}</script>\n\n\n</head>\n</html>\n\n","output":"str","x":610,"y":240,"wires":[["5cc4b28e.8729fc"]]},{"id":"5cc4b28e.8729fc","type":"change","z":"714d61bd.6125f","name":"Set Headers","rules":[{"t":"set","p":"headers","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"headers.content-type","pt":"msg","to":"text/html","tot":"str"},{"t":"set","p":"headers.Access-Control-Allow-Origin","pt":"msg","to":"*","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":240,"wires":[["ba902e89.332b2"]]},{"id":"ba902e89.332b2","type":"http response","z":"714d61bd.6125f","name":"","statusCode":"","headers":{},"x":970,"y":240,"wires":[]},{"id":"3a7288f3.9f5fc8","type":"ui_template","z":"714d61bd.6125f","group":"52c0f781.971f08","name":"P5.js display in iFrame","order":0,"width":"6","height":"6","format":"\n<iframe width=\"100%\" height=\"100%\" frameborder=\"0\" scrolling=\"no\" src=\"http://192.168.1.68:1880/p5js\"></iframe>\n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":false,"templateScope":"local","x":160,"y":280,"wires":[[]]},{"id":"af1079b6.031d48","type":"ui_slider","z":"714d61bd.6125f","name":"","label":"Radius","tooltip":"","group":"52c0f781.971f08","order":1,"width":0,"height":0,"passthru":true,"outs":"end","topic":"p5js","min":"10","max":"100","step":1,"x":120,"y":340,"wires":[["f202ee4f.41ad"]]},{"id":"f202ee4f.41ad","type":"mqtt out","z":"714d61bd.6125f","name":"","topic":"","qos":"0","retain":"false","broker":"2bfc0288.2ed06e","x":290,"y":340,"wires":[]},{"id":"7987b649.b09648","type":"mqtt in","z":"714d61bd.6125f","name":"","topic":"p5js/#","qos":"2","datatype":"auto","broker":"2bfc0288.2ed06e","x":290,"y":400,"wires":[["4aea3d45.ed3aa4"]]},{"id":"4aea3d45.ed3aa4","type":"debug","z":"714d61bd.6125f","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":470,"y":400,"wires":[]},{"id":"186ae793.1f6ec8","type":"template","z":"714d61bd.6125f","name":"Game of life","field":"payload.p5js","fieldType":"msg","format":"javascript","syntax":"plain","template":"\n// Write P5.js code here -----------------------------\n\nlet w;\nlet columns;\nlet rows;\nlet board;\nlet next;\n\n \n \nfunction setup() {\n width = window.innerWidth;\n height = window.innerHeight;\n\n createCanvas(width, height); \n w = 10;\n // Calculate columns and rows\n columns = floor(width / w);\n rows = floor(height / w);\n // Wacky way to make a 2D array is JS\n board = new Array(columns);\n for (let i = 0; i < columns; i++) {\n board[i] = new Array(rows);\n }\n // Going to use multiple 2D arrays and swap them\n next = new Array(columns);\n for (i = 0; i < columns; i++) {\n next[i] = new Array(rows);\n }\n init();\n}\n\nfunction draw() {\n background(255);\n generate();\n for ( let i = 0; i < columns;i++) {\n for ( let j = 0; j < rows;j++) {\n if ((board[i][j] == 1)) fill(0);\n else fill(255);\n stroke(0);\n rect(i * w, j * w, w-1, w-1);\n }\n }\n\n}\n\n// reset board when mouse is pressed\nfunction mousePressed() {\n init();\n}\n\n// Fill board randomly\nfunction init() {\n for (let i = 0; i < columns; i++) {\n for (let j = 0; j < rows; j++) {\n // Lining the edges with 0s\n if (i == 0 || j == 0 || i == columns-1 || j == rows-1) board[i][j] = 0;\n // Filling the rest randomly\n else board[i][j] = floor(random(2));\n next[i][j] = 0;\n }\n }\n}\n\n// The process of creating the new generation\nfunction generate() {\n\n // Loop through every spot in our 2D array and check spots neighbors\n for (let x = 1; x < columns - 1; x++) {\n for (let y = 1; y < rows - 1; y++) {\n // Add up all the states in a 3x3 surrounding grid\n let neighbors = 0;\n for (let i = -1; i <= 1; i++) {\n for (let j = -1; j <= 1; j++) {\n neighbors += board[x+i][y+j];\n }\n }\n\n // A little trick to subtract the current cell's state since\n // we added it in the above loop\n neighbors -= board[x][y];\n // Rules of Life\n if ((board[x][y] == 1) && (neighbors < 2)) next[x][y] = 0; // Loneliness\n else if ((board[x][y] == 1) && (neighbors > 3)) next[x][y] = 0; // Overpopulation\n else if ((board[x][y] == 0) && (neighbors == 3)) next[x][y] = 1; // Reproduction\n else next[x][y] = board[x][y]; // Stasis\n }\n }\n\n // Swap!\n let temp = board;\n board = next;\n next = temp;\n}\n","output":"str","x":310,"y":40,"wires":[["b817762f.d6cac8"]]},{"id":"34e6c43e.58ab9c","type":"template","z":"714d61bd.6125f","name":"Flock","field":"payload.p5js","fieldType":"msg","format":"javascript","syntax":"plain","template":"// Write P5.js code here -----------------------------\n\nlet flock;\n\nfunction setup() {\n createCanvas(300, 300);\n createP(\"Drag the mouse to generate new boids.\");\n\n flock = new Flock();\n // Add an initial set of boids into the system\n for (let i = 0; i < 100; i++) {\n let b = new Boid(width / 2,height / 2);\n flock.addBoid(b);\n }\n}\n\nfunction draw() {\n background(51);\n flock.run();\n}\n\n// Add a new boid into the System\nfunction mouseDragged() {\n flock.addBoid(new Boid(mouseX, mouseY));\n}\n\n// The Nature of Code\n// Daniel Shiffman\n// http://natureofcode.com\n\n// Flock object\n// Does very little, simply manages the array of all the boids\n\nfunction Flock() {\n // An array for all the boids\n this.boids = []; // Initialize the array\n}\n\nFlock.prototype.run = function() {\n for (let i = 0; i < this.boids.length; i++) {\n this.boids[i].run(this.boids); // Passing the entire list of boids to each boid individually\n }\n}\n\nFlock.prototype.addBoid = function(b) {\n this.boids.push(b);\n}\n\n// The Nature of Code\n// Daniel Shiffman\n// http://natureofcode.com\n\n// Boid class\n// Methods for Separation, Cohesion, Alignment added\n\nfunction Boid(x, y) {\n this.acceleration = createVector(0, 0);\n this.velocity = createVector(random(-1, 1), random(-1, 1));\n this.position = createVector(x, y);\n this.r = 3.0;\n this.maxspeed = 3; // Maximum speed\n this.maxforce = 0.05; // Maximum steering force\n}\n\nBoid.prototype.run = function(boids) {\n this.flock(boids);\n this.update();\n this.borders();\n this.render();\n}\n\nBoid.prototype.applyForce = function(force) {\n // We could add mass here if we want A = F / M\n this.acceleration.add(force);\n}\n\n// We accumulate a new acceleration each time based on three rules\nBoid.prototype.flock = function(boids) {\n let sep = this.separate(boids); // Separation\n let ali = this.align(boids); // Alignment\n let coh = this.cohesion(boids); // Cohesion\n // Arbitrarily weight these forces\n sep.mult(1.5);\n ali.mult(1.0);\n coh.mult(1.0);\n // Add the force vectors to acceleration\n this.applyForce(sep);\n this.applyForce(ali);\n this.applyForce(coh);\n}\n\n// Method to update location\nBoid.prototype.update = function() {\n // Update velocity\n this.velocity.add(this.acceleration);\n // Limit speed\n this.velocity.limit(this.maxspeed);\n this.position.add(this.velocity);\n // Reset accelertion to 0 each cycle\n this.acceleration.mult(0);\n}\n\n// A method that calculates and applies a steering force towards a target\n// STEER = DESIRED MINUS VELOCITY\nBoid.prototype.seek = function(target) {\n let desired = p5.Vector.sub(target,this.position); // A vector pointing from the location to the target\n // Normalize desired and scale to maximum speed\n desired.normalize();\n desired.mult(this.maxspeed);\n // Steering = Desired minus Velocity\n let steer = p5.Vector.sub(desired,this.velocity);\n steer.limit(this.maxforce); // Limit to maximum steering force\n return steer;\n}\n\nBoid.prototype.render = function() {\n // Draw a triangle rotated in the direction of velocity\n let theta = this.velocity.heading() + radians(90);\n fill(127);\n stroke(200);\n push();\n translate(this.position.x, this.position.y);\n rotate(theta);\n beginShape();\n vertex(0, -this.r * 2);\n vertex(-this.r, this.r * 2);\n vertex(this.r, this.r * 2);\n endShape(CLOSE);\n pop();\n}\n\n// Wraparound\nBoid.prototype.borders = function() {\n if (this.position.x < -this.r) this.position.x = width + this.r;\n if (this.position.y < -this.r) this.position.y = height + this.r;\n if (this.position.x > width + this.r) this.position.x = -this.r;\n if (this.position.y > height + this.r) this.position.y = -this.r;\n}\n\n// Separation\n// Method checks for nearby boids and steers away\nBoid.prototype.separate = function(boids) {\n let desiredseparation = 25.0;\n let steer = createVector(0, 0);\n let count = 0;\n // For every boid in the system, check if it's too close\n for (let i = 0; i < boids.length; i++) {\n let d = p5.Vector.dist(this.position,boids[i].position);\n // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)\n if ((d > 0) && (d < desiredseparation)) {\n // Calculate vector pointing away from neighbor\n let diff = p5.Vector.sub(this.position, boids[i].position);\n diff.normalize();\n diff.div(d); // Weight by distance\n steer.add(diff);\n count++; // Keep track of how many\n }\n }\n // Average -- divide by how many\n if (count > 0) {\n steer.div(count);\n }\n\n // As long as the vector is greater than 0\n if (steer.mag() > 0) {\n // Implement Reynolds: Steering = Desired - Velocity\n steer.normalize();\n steer.mult(this.maxspeed);\n steer.sub(this.velocity);\n steer.limit(this.maxforce);\n }\n return steer;\n}\n\n// Alignment\n// For every nearby boid in the system, calculate the average velocity\nBoid.prototype.align = function(boids) {\n let neighbordist = 50;\n let sum = createVector(0,0);\n let count = 0;\n for (let i = 0; i < boids.length; i++) {\n let d = p5.Vector.dist(this.position,boids[i].position);\n if ((d > 0) && (d < neighbordist)) {\n sum.add(boids[i].velocity);\n count++;\n }\n }\n if (count > 0) {\n sum.div(count);\n sum.normalize();\n sum.mult(this.maxspeed);\n let steer = p5.Vector.sub(sum, this.velocity);\n steer.limit(this.maxforce);\n return steer;\n } else {\n return createVector(0, 0);\n }\n}\n\n// Cohesion\n// For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location\nBoid.prototype.cohesion = function(boids) {\n let neighbordist = 50;\n let sum = createVector(0, 0); // Start with empty vector to accumulate all locations\n let count = 0;\n for (let i = 0; i < boids.length; i++) {\n let d = p5.Vector.dist(this.position,boids[i].position);\n if ((d > 0) && (d < neighbordist)) {\n sum.add(boids[i].position); // Add location\n count++;\n }\n }\n if (count > 0) {\n sum.div(count);\n return this.seek(sum); // Steer towards the location\n } else {\n return createVector(0, 0);\n }\n}\n","output":"str","x":290,"y":80,"wires":[["b817762f.d6cac8"]]},{"id":"faea71ac.d19e2","type":"template","z":"714d61bd.6125f","name":"Clock","field":"payload.p5js","fieldType":"msg","format":"javascript","syntax":"plain","template":"// Write P5.js code here -----------------------------\n\nlet cx, cy;\nlet secondsRadius;\nlet minutesRadius;\nlet hoursRadius;\nlet clockDiameter;\n\nfunction setup() {\n width = window.innerWidth;\n height = window.innerHeight;\n \n width = width-((width*10)/100);\n height = height - ((height*10)/100);\n \n createCanvas(width, height); stroke(255);\n\n let radius = min(width, height) / 2;\n secondsRadius = radius * 0.71;\n minutesRadius = radius * 0.6;\n hoursRadius = radius * 0.5;\n clockDiameter = radius * 1.7;\n\n cx = width / 2;\n cy = height / 2;\n}\n\nfunction draw() {\n background(230);\n\n // Draw the clock background\n noStroke();\n fill(244, 122, 158);\n ellipse(cx, cy, clockDiameter + 25, clockDiameter + 25);\n fill(237, 34, 93);\n ellipse(cx, cy, clockDiameter, clockDiameter);\n\n // Angles for sin() and cos() start at 3 o'clock;\n // subtract HALF_PI to make them start at the top\n let s = map(second(), 0, 60, 0, TWO_PI) - HALF_PI;\n let m = map(minute() + norm(second(), 0, 60), 0, 60, 0, TWO_PI) - HALF_PI;\n let h = map(hour() + norm(minute(), 0, 60), 0, 24, 0, TWO_PI * 2) - HALF_PI;\n\n // Draw the hands of the clock\n stroke(255);\n strokeWeight(1);\n line(cx, cy, cx + cos(s) * secondsRadius, cy + sin(s) * secondsRadius);\n strokeWeight(2);\n line(cx, cy, cx + cos(m) * minutesRadius, cy + sin(m) * minutesRadius);\n strokeWeight(4);\n line(cx, cy, cx + cos(h) * hoursRadius, cy + sin(h) * hoursRadius);\n\n // Draw the minute ticks\n strokeWeight(2);\n beginShape(POINTS);\n for (let a = 0; a < 360; a += 6) {\n let angle = radians(a);\n let x = cx + cos(angle) * secondsRadius;\n let y = cy + sin(angle) * secondsRadius;\n vertex(x, y);\n }\n endShape();\n}\n\n\n","output":"str","x":290,"y":120,"wires":[["b817762f.d6cac8"]]},{"id":"d3c3b87.5496148","type":"template","z":"714d61bd.6125f","name":"Mobile cube","field":"payload.p5js","fieldType":"msg","format":"javascript","syntax":"plain","template":"// Write P5.js code here -----------------------------\n\nx = 0;\n\nfunction setup() {\n width = window.innerWidth;\n height = window.innerHeight;\n \n // Add some borders\n width = width-((width*10)/100);\n height = height - ((height*10)/100);\n \n createCanvas(width, height, WEBGL); \n stroke(255);\n}\n\nfunction draw() {\n x = x + 0.5;\n background(250);\n normalMaterial();\n rotateX(accelerationX+x * 0.01);\n rotateY(accelerationY * 0.01);\n box(100, 100, 100);\n}\n\n//----------------------------------------\n\n\n","output":"str","x":310,"y":160,"wires":[["b817762f.d6cac8"]]},{"id":"52c0f781.971f08","type":"ui_group","z":"","name":"iFrame","tab":"200f07c6.f6ba98","order":1,"disp":true,"width":"6","collapse":true},{"id":"2bfc0288.2ed06e","type":"mqtt-broker","z":"","name":"","broker":"192.168.1.68","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"200f07c6.f6ba98","type":"ui_tab","z":"","name":"P5js","icon":"dashboard","disabled":false,"hidden":false}]
Be sure to modify the mqtt server ip address in the code according to your config
Please comment and help to enhance this piece of code. Once done, I expect to publish this on https://flows.nodered.org/
All the best !