Here is a scatter demo using chart.js...
[{"id":"acbdb34e9e6d9720","type":"change","z":"553814a2.1248ec","name":"random x/y","rules":[{"t":"set","p":"payload","pt":"msg","to":"(\t $minimum := 1;\t $maximum := 100;\t $x := $round(($random() * ($maximum-$minimum)) + $minimum, 0);\t $y := $round(($random() * ($maximum-$minimum)) + $minimum, 0);\t {\t \"x\": $x,\t \"y\": $y\t }\t)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1030,"y":1980,"wires":[["b69c5129af8f7ec1"]]},{"id":"b69c5129af8f7ec1","type":"ui_template","z":"553814a2.1248ec","group":"dce9e7a2.d20c78","name":"scatter","order":8,"width":"9","height":"14","format":"<script>\n (function(scope) {\n const chartData = {\n datasets: [{\n label: 'Scatter Dataset',\n data: [],\n backgroundColor: 'rgb(255, 99, 132)'\n }],\n };\n\n const config = {\n type: 'scatter',\n data: chartData,\n options: {\n maintainAspectRatio: false,\n scales: {\n x: {\n type: 'linear',\n position: 'bottom'\n }\n }\n }\n };\n\n var myChart = new Chart(\n document.getElementById('myChart'),\n config\n );\n\n scope.$watch('msg', function(msg) {\n if (msg) {\n \n if(msg.topic == \"add\") {\n myChart.data.datasets[0].data.push(msg.payload);\n myChart.update();\n } \n if(msg.topic == \"set\") {\n myChart.data.datasets[0].data = [...msg.payload];\n myChart.update();\n }\n }\n });\n })(scope);\n</script>\n\n<div class=\"chart-container\" style=\"position: relative; height:100%; width:100%\">\n <canvas id=\"myChart\"></canvas>\n</div>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":1210,"y":1980,"wires":[[]]},{"id":"c06bb8a0c16fab29","type":"ui_template","z":"553814a2.1248ec","group":"dce9e7a2.d20c78","name":"head script","order":9,"width":0,"height":0,"format":"<!-- <script type=\"text/javascript\" src=\"https://canvasjs.com/assets/script/canvasjs.min.js\"></script> -->\n<script src=\"https://cdn.jsdelivr.net/npm/chart.js\"></script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"global","x":1230,"y":1940,"wires":[[]]},{"id":"295977d8a88c0c92","type":"ui_button","z":"553814a2.1248ec","name":"","group":"dce9e7a2.d20c78","order":11,"width":"3","height":"1","passthru":false,"label":"add","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"add","topicType":"str","x":990,"y":1940,"wires":[["acbdb34e9e6d9720"]]},{"id":"e29654d556555bba","type":"ui_button","z":"553814a2.1248ec","name":"","group":"dce9e7a2.d20c78","order":12,"width":"3","height":"1","passthru":false,"label":"clear","tooltip":"","color":"","bgcolor":"","icon":"","payload":"[]","payloadType":"json","topic":"set","topicType":"str","x":990,"y":2020,"wires":[["b69c5129af8f7ec1"]]},{"id":"35c0629717713a22","type":"ui_button","z":"553814a2.1248ec","name":"","group":"dce9e7a2.d20c78","order":10,"width":"3","height":"1","passthru":false,"label":"set data","tooltip":"","color":"","bgcolor":"","icon":"","payload":"[{\"x\":10,\"y\":11},{\"x\":11,\"y\":12},{\"x\":13,\"y\":12},{\"x\":15,\"y\":12},{\"x\":18,\"y\":16},{\"x\":20,\"y\":22},{\"x\":20,\"y\":22},{\"x\":21,\"y\":22},{\"x\":24,\"y\":22},{\"x\":26,\"y\":25},{\"x\":28,\"y\":30},{\"x\":30,\"y\":40},{\"x\":30,\"y\":30},{\"x\":30,\"y\":40},{\"x\":31,\"y\":40},{\"x\":31,\"y\":51},{\"x\":31,\"y\":42},{\"x\":34,\"y\":30},{\"x\":37,\"y\":30},{\"x\":40,\"y\":82},{\"x\":40,\"y\":80},{\"x\":40,\"y\":90},{\"x\":42,\"y\":83},{\"x\":44,\"y\":93},{\"x\":45,\"y\":63},{\"x\":45,\"y\":99},{\"x\":46,\"y\":42},{\"x\":48,\"y\":82}]","payloadType":"json","topic":"set","topicType":"str","x":1000,"y":1900,"wires":[["b69c5129af8f7ec1"]]},{"id":"dce9e7a2.d20c78","type":"ui_group","name":"Object detection","tab":"5132060d.4cde48","order":1,"disp":true,"width":"9","collapse":false},{"id":"5132060d.4cde48","type":"ui_tab","name":"Home","icon":"dashboard","disabled":false,"hidden":false}]