Skip to content

Instantly share code, notes, and snippets.

@Hugobox
Created October 4, 2019 14:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Hugobox/079151b53f63261911684acc015f9166 to your computer and use it in GitHub Desktop.
Save Hugobox/079151b53f63261911684acc015f9166 to your computer and use it in GitHub Desktop.
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"}]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment