Three.js http 3D cube example with websockets

This flow loads the three.js library (from cdnjs.cloudflare.com) and serves an http endpoint (/cube) that displays a spinning 3D cube. The websockets are used to communicate both ways, in this example, the cube sends a message each time it changes direction, which is used in Node-Red to change the cube's color.

[{"id":"82bc22cb.914be","type":"http in","z":"c33009bf.64c1c8","name":"","url":"/cube","method":"get","upload":false,"swaggerDoc":"","x":270,"y":1020,"wires":[["ab9b0e5e.f8f8b"]]},{"id":"6752712.7f98a9","type":"http response","z":"c33009bf.64c1c8","name":"","statusCode":"","headers":{},"x":815,"y":1020,"wires":[]},{"id":"ab9b0e5e.f8f8b","type":"template","z":"c33009bf.64c1c8","name":"three.js","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<html>\n<head>\n\n    <title>Test</title>\n\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    \n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.module.js\"></script>\n    \n<script>\nvar server = window.location.href.split(\"http://\")[1].split(\"/\")[0]\nconsole.log(\"Page location is \" + server)\n\nvar socket1 = new  WebSocket(\"ws://\" + server + \"/ws/receive\");\nvar socket2 = new  WebSocket(\"ws://\" + server + \"/ws/publish\");\nvar cubeRotationSpeed = 0.02;\nlet cubeColor = \"blue\"\nvar socket1Opened = false\n\nsocket1.onopen = function() {\n    socket1Opened = true\n  var message = {\n    'payload': 'Client connected'\n  };\n  socket1.send(JSON.stringify(message));\n};\n\nsocket2.onopen = function() {\n  var message = {\n    'payload': 'Client connected'\n  };\n  socket1.send(JSON.stringify(message));\n};\n\nsocket2.onclose = function(){\n  console.log('Connection closed');\n};\n\nsocket2.onerror = function(error) {\n  console.log('Error detected: ' + JSON.stringify(error));\n};\n\nsocket2.onmessage = function(e) {\n  var server_message = e.data;\n  responseObject = JSON.parse(server_message);\n\n  //alert(JSON.stringify(responseObject));\n  //Do the required stuff\n  console.log(responseObject.payload)\n  if (responseObject.payload.cubeRotationSpeed){\n      cubeRotationSpeed = responseObject.payload.cubeRotationSpeed\n  }else if(responseObject.payload.cubeColor){\n      cubeColor = responseObject.payload.cubeColor\n  }\n}\n\nvar scene = new THREE.Scene();\n\n// Make highly-transparent plane\nvar fadeMaterial = new THREE.MeshBasicMaterial({\n    color: 0x000000,\n    transparent: true,\n    opacity: 0.02\n});\nvar fadePlane = new THREE.PlaneBufferGeometry(1, 1);\nvar fadeMesh = new THREE.Mesh(fadePlane, fadeMaterial);\n\n// Create Object3D to hold camera and transparent plane\nvar camGroup = new THREE.Object3D();\nvar camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);\ncamGroup.add(camera);\ncamGroup.add(fadeMesh);\n\n// Put plane in front of camera\nfadeMesh.position.z = -0.1;\n\n// Make plane render before particles\nfadeMesh.renderOrder = -1;\n\n// Add camGroup to scene\nscene.add(camGroup);\n\n\nrenderer = new THREE.WebGLRenderer( { preserveDrawingBuffer: true, antialias: true } );\nrenderer.autoClearColor = false;\nrenderer.setSize(window.innerWidth, window.innerHeight);\n\ndocument.addEventListener('DOMContentLoaded', function () {   \n    document.body.appendChild(renderer.domElement);\n});\n\nvar geometry = new THREE.BoxGeometry(1,1,1);\n//var color = new THREE.Color(0xff0000)\nvar material = new THREE.MeshBasicMaterial({color: \"blue\"});\nvar cube = new THREE.Mesh(geometry, material);\nscene.add(cube);\n\ncube.position.z = -5;\n\nvar step = .03;\nfunction animate(){\n\n    cube.rotation.x += cubeRotationSpeed;\n    cube.rotation.y += 0.02;\n    cube.position.x += step;\n    cube.material.color = new THREE.Color(cubeColor);\n    if(Math.abs(cube.position.x) > 5.0)\n    {\n        step = -step;\n        if (socket1Opened){\n            socket1.send(\"ping\");\n        }\n    }\n    renderer.render(scene, camera);\n\n    requestAnimationFrame(animate);\n}\n\nanimate();\n\n</script>\n\n\n","output":"str","x":480,"y":1020,"wires":[["f195cd.89a76a3"]]},{"id":"f8262338.541b2","type":"websocket out","z":"c33009bf.64c1c8","name":"","server":"c6bb1eb5.edd3d","client":"","x":1010,"y":1140,"wires":[]},{"id":"1f953c53.234544","type":"inject","z":"c33009bf.64c1c8","name":"Spin slow","topic":"","payload":"{\"cubeRotationSpeed\":0.02}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":615,"y":1140,"wires":[["f8262338.541b2"]]},{"id":"ae4b9f83.855ed","type":"inject","z":"c33009bf.64c1c8","name":"Spin fast","topic":"","payload":"{\"cubeRotationSpeed\":0.1}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":615,"y":1185,"wires":[["f8262338.541b2"]]},{"id":"f195cd.89a76a3","type":"change","z":"c33009bf.64c1c8","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":640,"y":1020,"wires":[["6752712.7f98a9"]]},{"id":"eba548fa.e65d98","type":"change","z":"c33009bf.64c1c8","name":"","rules":[{"t":"delete","p":"_session","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":805,"y":1080,"wires":[["f8262338.541b2"]]},{"id":"c1fc2e6e.bd70e","type":"websocket in","z":"c33009bf.64c1c8","name":"","server":"a5db9e65.7dd36","client":"","x":290,"y":1080,"wires":[["22fc2d8b.025e32"]]},{"id":"22fc2d8b.025e32","type":"switch","z":"c33009bf.64c1c8","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"ping","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":455,"y":1080,"wires":[["eaae5e3a.3f284"]]},{"id":"eaae5e3a.3f284","type":"function","z":"c33009bf.64c1c8","name":"change color","func":"let colors = [\"white\",\"red\",0x00ff00,0x0000ff] //both hex or string are fine\nmsg.payload =  {\"cubeColor\": colors[Math.floor(Math.random()*colors.length)]}\nreturn msg;","outputs":1,"noerr":0,"x":610,"y":1080,"wires":[["eba548fa.e65d98"]]},{"id":"94b2919c.8d227","type":"comment","z":"c33009bf.64c1c8","name":"Three.js example with websockets","info":"","x":350,"y":960,"wires":[]},{"id":"c6bb1eb5.edd3d","type":"websocket-listener","z":"","path":"/ws/publish","wholemsg":"true"},{"id":"a5db9e65.7dd36","type":"websocket-listener","z":"","path":"/ws/receive","wholemsg":"true"}]

Flow Info

Created 4 years, 11 months ago
Rating: 5 5

Owner

Actions

Rate:

Node Types

Core
  • change (x2)
  • comment (x1)
  • function (x1)
  • http in (x1)
  • http response (x1)
  • inject (x2)
  • switch (x1)
  • template (x1)
  • websocket in (x1)
  • websocket out (x1)
  • websocket-listener (x2)

Tags

  • three.js
  • websockets
  • webGL
  • http
  • 3D
  • cube
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option