Skip to main content

Interactive 3D Models with Runner & Inputs

In the previous tutorial, we learned how to run a static Rete script on a webpage using the Bitbybit Runner. Now, let's take it a step further and explore how to make your embedded 3D content interactive by:

  1. Passing inputs from your webpage to the Rete script.
  2. Receiving outputs (like created meshes) back from the script.
  3. Updating the 3D scene dynamically by re-executing the script with new inputs and disposing of old geometry.

This allows you to build powerful 3D product configurators, interactive data visualizations, or any application where users can modify parameters and see the 3D model update in real time.

The Rete Script: A Parametric Industrial Part

For this tutorial, we'll use a Rete script that generates a parametric industrial-style 3D part. This script is designed to accept inputs such as radius1, radius2, and distance, which will control its geometry.

Below is an interactive preview of the Rete visual program. Notice how this script might have specific "Input" nodes designed to receive data from the runner.

Bitbybit Platform

Visual Program: Parametric Industrial Part

rete logoRete
Script Source (rete)
{
"id": "rete-v2-json",
"nodes": {
"aeb75413e5faf4a9": {
"id": "aeb75413e5faf4a9",
"name": "bitbybit.occt.shapes.wire.createCircleWire",
"customName": "circle wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 3,
"center": [
0,
0,
0
],
"direction": [
0,
1,
0
]
},
"inputs": {
"radius": {
"connections": [
{
"node": "a0e02b9e66e7f6d6",
"output": "result",
"data": {}
}
]
}
},
"position": [
665.9391849750841,
133.35929588138666
]
},
"9dcc0046a89dd771": {
"id": "9dcc0046a89dd771",
"name": "bitbybit.occt.shapes.wire.createCircleWire",
"customName": "circle wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 1,
"center": [
0,
0,
0
],
"direction": [
0,
1,
0
]
},
"inputs": {
"center": {
"connections": [
{
"node": "46509f29de5c3339",
"output": "result",
"data": {}
}
]
},
"radius": {
"connections": [
{
"node": "23e2605879d4d83e",
"output": "result",
"data": {}
}
]
}
},
"position": [
638.6830591039859,
459.6335741820354
]
},
"46509f29de5c3339": {
"id": "46509f29de5c3339",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 24,
"y": 0,
"z": 0
},
"inputs": {
"x": {
"connections": [
{
"node": "829ba5cff7cd0b88",
"output": "result",
"data": {}
}
]
}
},
"position": [
119.4404235226572,
892.369411977663
]
},
"b35c3fee4944b357": {
"id": "b35c3fee4944b357",
"name": "bitbybit.occt.shapes.wire.createWireFromTwoCirclesTan",
"customName": "wire from two circles tan",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"keepLines": "outside",
"circleRemainders": "inside",
"tolerance": 1e-7
},
"inputs": {
"circle1": {
"connections": [
{
"node": "aeb75413e5faf4a9",
"output": "result",
"data": {}
}
]
},
"circle2": {
"connections": [
{
"node": "9dcc0046a89dd771",
"output": "result",
"data": {}
}
]
}
},
"position": [
1349.6676303711936,
189.2601423416136
]
},
"8c5c70d697301471": {
"id": "8c5c70d697301471",
"name": "bitbybit.occt.operations.offset",
"customName": "offset",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"distance": -0.5,
"tolerance": 0.1
},
"inputs": {
"shape": {
"connections": [
{
"node": "b35c3fee4944b357",
"output": "result",
"data": {}
}
]
}
},
"position": [
1825.380102136211,
-214.45160289076006
]
},
"cf2ca13211e16c80": {
"id": "cf2ca13211e16c80",
"name": "bitbybit.occt.shapes.face.createFaceFromWires",
"customName": "face from wires",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"planar": false
},
"inputs": {
"shapes": {
"connections": [
{
"node": "4896486e51c017ac",
"output": "list",
"data": {}
}
]
}
},
"position": [
3715.0477213383656,
105.27994105359419
]
},
"4896486e51c017ac": {
"id": "4896486e51c017ac",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "b35c3fee4944b357",
"output": "result",
"data": {}
},
{
"node": "3c8e79b7b2ac191e",
"output": "result",
"data": {}
}
]
}
},
"position": [
3432.8615947206135,
140.51713458776825
]
},
"3c8e79b7b2ac191e": {
"id": "3c8e79b7b2ac191e",
"name": "bitbybit.occt.shapes.wire.reversedWire",
"customName": "reversed wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"shape": {
"connections": [
{
"node": "cea3459514435abe",
"output": "result",
"data": {}
}
]
}
},
"position": [
2881.569297123601,
-111.71749084143329
]
},
"0005686a90cd1614": {
"id": "0005686a90cd1614",
"name": "bitbybit.occt.operations.extrude",
"customName": "extrude",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"direction": [
0,
1,
0
]
},
"inputs": {
"shape": {
"connections": [
{
"node": "cf2ca13211e16c80",
"output": "result",
"data": {}
}
]
},
"direction": {
"connections": [
{
"node": "4ec4ba6b1abdad01",
"output": "result",
"data": {}
}
]
}
},
"position": [
4206.222825598234,
327.3206994836024
]
},
"cea3459514435abe": {
"id": "cea3459514435abe",
"name": "bitbybit.occt.fillets.fillet2d",
"customName": "fillet 2d",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 0.3
},
"inputs": {
"shape": {
"connections": [
{
"node": "8c5c70d697301471",
"output": "result",
"data": {}
}
]
}
},
"position": [
2171.632610327837,
-99.45054875803254
]
},
"4ab45856eea5ffaa": {
"id": "4ab45856eea5ffaa",
"name": "bitbybit.math.numberSlider",
"customName": "number slider",
"data": {
"options": {
"min": 1.5,
"max": 7,
"step": 0.1,
"width": 350
},
"number": 4.6
},
"inputs": {},
"position": [
-1437.9488180364556,
104.20470159762908
]
},
"2fe63d7e5a48f271": {
"id": "2fe63d7e5a48f271",
"name": "bitbybit.occt.shapes.wire.reversedWire",
"customName": "reversed wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"shape": {
"connections": [
{
"node": "0d82d8b2def523d0",
"output": "result",
"data": {}
}
]
}
},
"position": [
1740.8804815238311,
961.3909812429428
]
},
"0d82d8b2def523d0": {
"id": "0d82d8b2def523d0",
"name": "bitbybit.occt.shapes.wire.createCircleWire",
"customName": "circle wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 1,
"center": [
0,
0,
0
],
"direction": [
0,
1,
0
]
},
"inputs": {
"radius": {
"connections": [
{
"node": "939a4b96f003138a",
"output": "result",
"data": {}
}
]
}
},
"position": [
1292.5028022264862,
983.0458732393794
]
},
"ac9bbb6869048812": {
"id": "ac9bbb6869048812",
"name": "bitbybit.occt.shapes.face.createFaceFromWires",
"customName": "face from wires",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"planar": true
},
"inputs": {
"shapes": {
"connections": [
{
"node": "1e9698c8e6e0cfef",
"output": "list",
"data": {}
}
]
}
},
"position": [
2512.5687464258817,
588.4414254196271
]
},
"1e9698c8e6e0cfef": {
"id": "1e9698c8e6e0cfef",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "aeb75413e5faf4a9",
"output": "result",
"data": {}
},
{
"node": "2fe63d7e5a48f271",
"output": "result",
"data": {}
}
]
}
},
"position": [
2143.039683497029,
766.2218724544525
]
},
"e877a5de5cb17f97": {
"id": "e877a5de5cb17f97",
"name": "bitbybit.occt.shapes.face.createFaceFromWires",
"customName": "face from wires",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"planar": true
},
"inputs": {
"shapes": {
"connections": [
{
"node": "f3a0226b5ba00430",
"output": "list",
"data": {}
}
]
}
},
"position": [
2624.485427828371,
1283.786591534435
]
},
"bce4da91d74dbcaa": {
"id": "bce4da91d74dbcaa",
"name": "bitbybit.math.numberSlider",
"customName": "number slider",
"data": {
"options": {
"min": 1.5,
"max": 7,
"step": 0.1,
"width": 350
},
"number": 2.9
},
"inputs": {},
"position": [
-1458.3892149323854,
578.6595517247349
]
},
"245b7d814ba4587f": {
"id": "245b7d814ba4587f",
"name": "bitbybit.occt.shapes.wire.createCircleWire",
"customName": "circle wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 1,
"center": [
0,
0,
0
],
"direction": [
0,
1,
0
]
},
"inputs": {
"center": {
"connections": [
{
"node": "46509f29de5c3339",
"output": "result",
"data": {}
}
]
},
"radius": {
"connections": [
{
"node": "4be26000a10d20be",
"output": "result",
"data": {}
}
]
}
},
"position": [
1275.8814421068166,
1428.5941765537818
]
},
"9b38658756f0d0be": {
"id": "9b38658756f0d0be",
"name": "bitbybit.occt.shapes.wire.reversedWire",
"customName": "reversed wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"shape": {
"connections": [
{
"node": "245b7d814ba4587f",
"output": "result",
"data": {}
}
]
}
},
"position": [
1748.835638066051,
1475.1404775474389
]
},
"f3a0226b5ba00430": {
"id": "f3a0226b5ba00430",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "9dcc0046a89dd771",
"output": "result",
"data": {}
},
{
"node": "9b38658756f0d0be",
"output": "result",
"data": {}
}
]
}
},
"position": [
2208.1254794320785,
1326.362027250052
]
},
"ceca4a3825ffbc90": {
"id": "ceca4a3825ffbc90",
"name": "bitbybit.occt.operations.extrude",
"customName": "extrude",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"direction": [
0,
1,
0
]
},
"inputs": {
"shape": {
"connections": [
{
"node": "ac9bbb6869048812",
"output": "result",
"data": {}
},
{
"node": "e877a5de5cb17f97",
"output": "result",
"data": {}
}
]
},
"direction": {
"connections": [
{
"node": "eb092f0c677cc514",
"output": "result",
"data": {}
}
]
}
},
"position": [
3567.0495771803926,
1012.3724098767043
]
},
"eb092f0c677cc514": {
"id": "eb092f0c677cc514",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 7,
"z": 0
},
"inputs": {},
"position": [
3226.3887292384293,
1377.2117757682631
]
},
"939a4b96f003138a": {
"id": "939a4b96f003138a",
"name": "bitbybit.math.twoNrOperation",
"customName": "two nr operation",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"first": 1,
"second": 1,
"operation": "subtract"
},
"inputs": {
"first": {
"connections": [
{
"node": "a0e02b9e66e7f6d6",
"output": "result",
"data": {}
}
]
}
},
"position": [
558.8427077394689,
810.1272903513453
]
},
"4be26000a10d20be": {
"id": "4be26000a10d20be",
"name": "bitbybit.math.twoNrOperation",
"customName": "two nr operation",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"first": 1,
"second": 1,
"operation": "subtract"
},
"inputs": {
"first": {
"connections": [
{
"node": "23e2605879d4d83e",
"output": "result",
"data": {}
}
]
}
},
"position": [
615.8480411775889,
1280.5032349360806
]
},
"24d33634a3bd83ba": {
"id": "24d33634a3bd83ba",
"name": "bitbybit.math.numberSlider",
"customName": "number slider",
"data": {
"options": {
"min": 15,
"max": 30,
"step": 0.1,
"width": 350
},
"number": 19.5
},
"inputs": {},
"position": [
-1460.6780163340518,
1099.070556428186
]
},
"4ec4ba6b1abdad01": {
"id": "4ec4ba6b1abdad01",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 3,
"z": 0
},
"inputs": {},
"position": [
3767.40337004187,
503.4437814659145
]
},
"9d3fe9ceb38277cb": {
"id": "9d3fe9ceb38277cb",
"name": "bitbybit.occt.fillets.filletEdges",
"customName": "fillet edges",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 0.3
},
"inputs": {
"shape": {
"connections": [
{
"node": "ceca4a3825ffbc90",
"output": "result",
"data": {}
}
]
}
},
"position": [
3981.7749388049233,
1031.3125789633891
]
},
"d70ae4d66736ebc5": {
"id": "d70ae4d66736ebc5",
"name": "bitbybit.occt.transforms.translate",
"customName": "translate",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"translation": [
0,
0,
0
]
},
"inputs": {
"shape": {
"connections": [
{
"node": "9d3fe9ceb38277cb",
"output": "result",
"data": {}
}
]
},
"translation": {
"connections": [
{
"node": "c3c264215e094e1d",
"output": "result",
"data": {}
}
]
}
},
"position": [
4411.41722640604,
1251.7287566123932
]
},
"c3c264215e094e1d": {
"id": "c3c264215e094e1d",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": -2,
"z": 0
},
"inputs": {},
"position": [
3923.126185111321,
1435.3150259366798
]
},
"e9929290c4a7dbd1": {
"id": "e9929290c4a7dbd1",
"name": "bitbybit.occt.shapes.wire.createWireFromTwoCirclesTan",
"customName": "wire from two circles tan",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"keepLines": "outside",
"circleRemainders": "outside",
"tolerance": 1e-7
},
"inputs": {
"circle1": {
"connections": [
{
"node": "aeb75413e5faf4a9",
"output": "result",
"data": {}
}
]
},
"circle2": {
"connections": [
{
"node": "9dcc0046a89dd771",
"output": "result",
"data": {}
}
]
}
},
"position": [
1292.4703320113213,
-784.5287635659598
]
},
"4d06a31963826b24": {
"id": "4d06a31963826b24",
"name": "bitbybit.occt.operations.offset",
"customName": "offset",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"distance": 2,
"tolerance": 0.1
},
"inputs": {
"shape": {
"connections": [
{
"node": "e9929290c4a7dbd1",
"output": "result",
"data": {}
}
]
}
},
"position": [
1906.199028508239,
-1029.2604367143058
]
},
"f95f7ffba78b66de": {
"id": "f95f7ffba78b66de",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "4d06a31963826b24",
"output": "result",
"data": {}
},
{
"node": "a9a26851c9000a06",
"output": "result",
"data": {}
}
]
}
},
"position": [
2501.1181549967487,
-817.3106126908197
]
},
"560b53068b4839bd": {
"id": "560b53068b4839bd",
"name": "bitbybit.occt.shapes.face.createFaceFromWires",
"customName": "face from wires",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"planar": true
},
"inputs": {
"shapes": {
"connections": [
{
"node": "f95f7ffba78b66de",
"output": "list",
"data": {}
}
]
}
},
"position": [
2861.864703723829,
-878.5851750760701
]
},
"a9a26851c9000a06": {
"id": "a9a26851c9000a06",
"name": "bitbybit.occt.shapes.wire.reversedWire",
"customName": "reversed wire",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"shape": {
"connections": [
{
"node": "e9929290c4a7dbd1",
"output": "result",
"data": {}
}
]
}
},
"position": [
1967.4855118956514,
-634.0865287164468
]
},
"074db2e6fa47860a": {
"id": "074db2e6fa47860a",
"name": "bitbybit.occt.operations.extrude",
"customName": "extrude",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"direction": [
0,
1,
0
]
},
"inputs": {
"shape": {
"connections": [
{
"node": "560b53068b4839bd",
"output": "result",
"data": {}
}
]
},
"direction": {
"connections": [
{
"node": "76c0f014777e7b16",
"output": "result",
"data": {}
}
]
}
},
"position": [
3365.1213476056246,
-889.0716969404066
]
},
"b54654b3f81e2142": {
"id": "b54654b3f81e2142",
"name": "bitbybit.occt.transforms.translate",
"customName": "translate",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"translation": [
0,
0,
0
]
},
"inputs": {
"translation": {
"connections": [
{
"node": "c18d76502bf7cc74",
"output": "result",
"data": {}
}
]
},
"shape": {
"connections": [
{
"node": "36c0e6370a3135f7",
"output": "result",
"data": {}
}
]
}
},
"position": [
4405.044912133333,
-862.4295973353054
]
},
"c18d76502bf7cc74": {
"id": "c18d76502bf7cc74",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 1,
"z": 0
},
"inputs": {},
"position": [
3513.5161651135536,
-473.05804385461227
]
},
"8d9afacc319453fb": {
"id": "8d9afacc319453fb",
"name": "bitbybit.occt.shapes.edge.getEdges",
"customName": "get edges",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"shape": {
"connections": [
{
"node": "4d06a31963826b24",
"output": "result",
"data": {}
}
]
}
},
"position": [
3174.135738058122,
-1204.4187488749792
]
},
"36c0e6370a3135f7": {
"id": "36c0e6370a3135f7",
"name": "bitbybit.occt.fillets.filletEdgesListOneRadius",
"customName": "fillet edges list one radius",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"radius": 0.4
},
"inputs": {
"shape": {
"connections": [
{
"node": "074db2e6fa47860a",
"output": "result",
"data": {}
}
]
},
"edges": {
"connections": [
{
"node": "8d9afacc319453fb",
"output": "result",
"data": {}
}
]
}
},
"position": [
3910.7460919068053,
-1169.1905610536687
]
},
"76c0f014777e7b16": {
"id": "76c0f014777e7b16",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 0.5,
"z": 0
},
"inputs": {},
"position": [
2993.8703217269817,
-610.3216367287403
]
},
"6e348b6dc08b750d": {
"id": "6e348b6dc08b750d",
"name": "bitbybit.occt.shapes.face.getFaces",
"customName": "get faces",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"shape": {
"connections": [
{
"node": "b54654b3f81e2142",
"output": "result",
"data": {}
}
]
}
},
"position": [
4764.459880838269,
-703.1915971356891
]
},
"050e8cde698b9089": {
"id": "050e8cde698b9089",
"name": "bitbybit.lists.removeItemAtIndex",
"customName": "remove item at index",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"index": 3,
"clone": true
},
"inputs": {
"list": {
"connections": [
{
"node": "6e348b6dc08b750d",
"output": "result",
"data": {}
}
]
}
},
"position": [
5159.311512624683,
-874.9400432214468
]
},
"0fd0a0c57656132a": {
"id": "0fd0a0c57656132a",
"name": "bitbybit.occt.transforms.mirrorAlongNormal",
"customName": "mirror along normal",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"origin": [
0,
0,
0
],
"normal": [
0,
0,
1
]
},
"inputs": {
"shape": {
"connections": [
{
"node": "e2f99a71243aeed8",
"output": "result",
"data": {}
}
]
},
"normal": {
"connections": [
{
"node": "55b0ffffa94542e1",
"output": "result",
"data": {}
}
]
},
"origin": {
"connections": [
{
"node": "b3046e3d5b9ea8c6",
"output": "result",
"data": {}
}
]
}
},
"position": [
6102.71693807506,
-573.7660215650886
]
},
"e2f99a71243aeed8": {
"id": "e2f99a71243aeed8",
"name": "bitbybit.occt.shapes.shell.sewFaces",
"customName": "sew faces",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"tolerance": 1e-7
},
"inputs": {
"shapes": {
"connections": [
{
"node": "050e8cde698b9089",
"output": "result",
"data": {}
}
]
}
},
"position": [
5576.561899937931,
-859.680923154312
]
},
"55b0ffffa94542e1": {
"id": "55b0ffffa94542e1",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 1,
"z": 0
},
"inputs": {},
"position": [
5191.089993851374,
-215.19168500585442
]
},
"b3046e3d5b9ea8c6": {
"id": "b3046e3d5b9ea8c6",
"name": "bitbybit.vector.vectorXYZ",
"customName": "vector xyz",
"async": false,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"x": 0,
"y": 1.5,
"z": 0
},
"inputs": {},
"position": [
5180.356403615972,
-522.6279997627405
]
},
"70ed51745404298f": {
"id": "70ed51745404298f",
"name": "bitbybit.occt.shapes.shell.sewFaces",
"customName": "sew faces",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"tolerance": 1e-7
},
"inputs": {
"shapes": {
"connections": [
{
"node": "6058d79a2e1618cf",
"output": "list",
"data": {}
}
]
}
},
"position": [
6884.767282311176,
-798.1487157884476
]
},
"6058d79a2e1618cf": {
"id": "6058d79a2e1618cf",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "e2f99a71243aeed8",
"output": "result",
"data": {}
},
{
"node": "0fd0a0c57656132a",
"output": "result",
"data": {}
}
]
}
},
"position": [
6548.557950183183,
-679.1803257961602
]
},
"664044503380f055": {
"id": "664044503380f055",
"name": "bitbybit.draw.drawAnyAsync",
"customName": "draw any async",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"entity": {
"connections": [
{
"node": "70ed51745404298f",
"output": "result",
"data": {}
}
]
},
"options": {
"connections": [
{
"node": "f42918b0adb44263",
"output": "result",
"data": {}
}
]
}
},
"position": [
7375.940133508733,
-575.3702061418084
]
},
"f42918b0adb44263": {
"id": "f42918b0adb44263",
"name": "bitbybit.draw.optionsOcctShapeMaterial",
"customName": "options occt shape material",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"precision": 0.01,
"drawEdges": true,
"edgeColour": "#474747",
"edgeWidth": 2
},
"inputs": {
"faceMaterial": {
"connections": [
{
"node": "b095de9485da2da4",
"output": "result",
"data": {}
}
]
}
},
"position": [
6803.07667079093,
-257.84353244700446
]
},
"b095de9485da2da4": {
"id": "b095de9485da2da4",
"name": "bitbybit.babylon.material.pbrMetallicRoughness.create",
"customName": "pbr metallic roughness",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"name": "Custom Material",
"baseColor": "#474eff",
"emissiveColor": "#000000",
"metallic": 0.1,
"roughness": 0.9,
"alpha": 1,
"backFaceCulling": false,
"zOffset": 2
},
"inputs": {},
"position": [
6440.862982340747,
-136.28746828913185
]
},
"3ddada174f21c7ff": {
"id": "3ddada174f21c7ff",
"name": "bitbybit.babylon.scene.enableSkybox",
"customName": "enable skybox",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"skybox": "clearSky",
"size": 1000,
"blur": 0.6,
"environmentIntensity": 0.7
},
"inputs": {},
"position": [
-52.931589545080556,
1554.506524900003
]
},
"2de1c33883ea2cce": {
"id": "2de1c33883ea2cce",
"name": "bitbybit.babylon.scene.drawDirectionalLight",
"customName": "draw directional light",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"direction": [
-100,
-100,
-100
],
"intensity": 3,
"diffuse": "#ffffff",
"specular": "#ffffff",
"shadowGeneratorMapSize": 1024,
"enableShadows": true,
"shadowDarkness": 0,
"shadowUsePercentageCloserFiltering": true,
"shadowContactHardeningLightSizeUVRatio": 0.2,
"shadowBias": 0.0001,
"shadowNormalBias": 0.002,
"shadowMaxZ": 1000,
"shadowMinZ": 0
},
"inputs": {},
"position": [
-546.6658452417059,
1525.133375396519
]
},
"6da9ef0b5518644b": {
"id": "6da9ef0b5518644b",
"name": "bitbybit.draw.drawAnyAsync",
"customName": "draw any async",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"entity": {
"connections": [
{
"node": "5f154d4ff5a249ee",
"output": "result",
"data": {}
}
]
},
"options": {
"connections": [
{
"node": "d12942e2bd5c3ced",
"output": "result",
"data": {}
}
]
}
},
"position": [
7049.929610536694,
713.8803221630949
]
},
"5f154d4ff5a249ee": {
"id": "5f154d4ff5a249ee",
"name": "bitbybit.occt.shapes.compound.makeCompound",
"customName": "make compound",
"async": true,
"drawable": true,
"data": {
"genericNodeData": {
"hide": true,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"shapes": {
"connections": [
{
"node": "d2ccedecef5f2d73",
"output": "list",
"data": {}
}
]
}
},
"position": [
5035.975336103436,
700.952975045815
]
},
"d2ccedecef5f2d73": {
"id": "d2ccedecef5f2d73",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "0005686a90cd1614",
"output": "result",
"data": {}
},
{
"node": "d70ae4d66736ebc5",
"output": "result",
"data": {}
}
]
}
},
"position": [
4722.254054471903,
696.965152460524
]
},
"9fff1b4f49d981eb": {
"id": "9fff1b4f49d981eb",
"name": "bitbybit.babylon.material.pbrMetallicRoughness.create",
"customName": "pbr metallic roughness",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"name": "Custom Material",
"baseColor": "#212121",
"emissiveColor": "#000000",
"metallic": 0.95,
"roughness": 0.235,
"alpha": 1,
"backFaceCulling": false,
"zOffset": 2
},
"inputs": {},
"position": [
5628.201101780516,
1083.7868997974167
]
},
"d12942e2bd5c3ced": {
"id": "d12942e2bd5c3ced",
"name": "bitbybit.draw.optionsOcctShapeMaterial",
"customName": "options occt shape material",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
},
"precision": 0.01,
"drawEdges": true,
"edgeColour": "#6e6e6e",
"edgeWidth": 2
},
"inputs": {
"faceMaterial": {
"connections": [
{
"node": "9fff1b4f49d981eb",
"output": "result",
"data": {}
}
]
}
},
"position": [
6226.179700514807,
931.5108132152701
]
},
"f4d52f2a80f089ba": {
"id": "f4d52f2a80f089ba",
"name": "bitbybit.runner.getRunnerInputValue",
"customName": "get runner input value",
"data": {
"property": "radius1"
},
"inputs": {},
"position": [
-1382.47258275571,
-59.430973467150814
]
},
"68eaae1eb8590bf9": {
"id": "68eaae1eb8590bf9",
"name": "bitbybit.runner.getRunnerInputValue",
"customName": "get runner input value",
"data": {
"property": "radius2"
},
"inputs": {},
"position": [
-1395.7916723821993,
430.5923836799792
]
},
"a0e02b9e66e7f6d6": {
"id": "a0e02b9e66e7f6d6",
"name": "bitbybit.logic.firstDefinedValueGate",
"customName": "first defined value gate",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"value2": {
"connections": [
{
"node": "4ab45856eea5ffaa",
"output": "result",
"data": {}
}
]
},
"value1": {
"connections": [
{
"node": "f4d52f2a80f089ba",
"output": "result",
"data": {}
}
]
}
},
"position": [
-693.4906229363137,
-37.469011274900396
]
},
"23e2605879d4d83e": {
"id": "23e2605879d4d83e",
"name": "bitbybit.logic.firstDefinedValueGate",
"customName": "first defined value gate",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"value1": {
"connections": [
{
"node": "68eaae1eb8590bf9",
"output": "result",
"data": {}
}
]
},
"value2": {
"connections": [
{
"node": "bce4da91d74dbcaa",
"output": "result",
"data": {}
}
]
}
},
"position": [
-700.033687476092,
442.3571307208691
]
},
"60256bc11178e009": {
"id": "60256bc11178e009",
"name": "bitbybit.runner.getRunnerInputValue",
"customName": "get runner input value",
"data": {
"property": "distance"
},
"inputs": {},
"position": [
-1401.8505202022602,
923.5916504395701
]
},
"829ba5cff7cd0b88": {
"id": "829ba5cff7cd0b88",
"name": "bitbybit.logic.firstDefinedValueGate",
"customName": "first defined value gate",
"async": false,
"drawable": false,
"data": {
"genericNodeData": {
"hide": false,
"oneOnOne": false,
"flatten": 0,
"forceExecution": false
}
},
"inputs": {
"value1": {
"connections": [
{
"node": "60256bc11178e009",
"output": "result",
"data": {}
}
]
},
"value2": {
"connections": [
{
"node": "24d33634a3bd83ba",
"output": "result",
"data": {}
}
]
}
},
"position": [
-708.6734021910144,
925.768055455432
]
},
"fce98756ef52d24d": {
"id": "fce98756ef52d24d",
"name": "bitbybit.runner.setRunnerResultValue",
"customName": "set runner result value",
"data": {
"property": "meshes"
},
"inputs": {
"value": {
"connections": [
{
"node": "155ee584199d2ae5",
"output": "list",
"data": {}
}
]
}
},
"position": [
8393.868496862571,
-11.080208856355476
]
},
"155ee584199d2ae5": {
"id": "155ee584199d2ae5",
"name": "bitbybit.lists.createList",
"customName": "create list",
"data": {},
"inputs": {
"listElements": {
"connections": [
{
"node": "664044503380f055",
"output": "result",
"data": {}
},
{
"node": "6da9ef0b5518644b",
"output": "result",
"data": {}
}
]
}
},
"position": [
8006.614818310852,
-0.8772432710706539
]
}
}
}

Exporting the Script and Designing for Inputs/Outputs

When you design a Rete script intended for use with the runner and external inputs/outputs:

  • Input Nodes: Use specific "Input" nodes within your Rete graph (e.g., "Runner Number Input," "Runner Vector Input"). These nodes will receive values passed via the inputs object in runner.executeScript(exportedScript(), inputs).
  • Output Nodes: Similarly, use "Output" nodes (e.g., "Runner Geometry Output") to define what data or geometry the script should return. The runner.executeScript() method returns a promise that resolves with these outputs.

The Bitbybit "Export to Runner" feature will generate a JavaScript function string that accommodates these input and output mechanisms.

Live Demo: Interactive Control

Before diving into the code, let's see the final result in action with a live StackBlitz demo. This environment simulates how you can integrate HTML controls (buttons, sliders, etc.) to modify parameters and dynamically update the 3D model rendered by the Bitbybit Runner.

Why is this interactive demo important?

  • Dynamic Interaction: It showcases the core concept of this tutorial – modifying a 3D model in real-time based on user input from the webpage.
  • Input/Output Flow: You can observe how changes made via HTML controls are passed to the Rete script, and how the script's output (the 3D meshes) is used to update the scene.
  • Practical Use Case: This example is a simplified version of what you might build for a product configurator or an interactive educational tool.
Bitbybit Platform

StackBlitz - Interactive Rete Script with IO

Setting up Your HTML Page with Controls

The HTML structure will be similar to the previous tutorial but will now include UI elements (buttons and spans) to control the Rete script's parameters.

index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Runner Example - IO from Rete editor</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="styles.css" />
<script src="https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@latest/runner/bitbybit-runner-babylonjs.js"></script> {/* Ensure version is current */}
<script defer type="module" src="script.js"></script>
</head>
<body>
<div class="example">
<a class="logo" href="https://bitbybit.dev" target="_blank" rel="noopener noreferrer">
<img alt="Logo of Bit by bit developers company" src="https://bitbybit.dev/assets/logo-gold-small.png" />
<div>bitbybit.dev</div>
</a>
<h1>Runner Example - Interactive Rete Script</h1>
<div class="myCanvasZone">
<canvas id="myCanvas"></canvas>
</div>

{/* Controls for script parameters */}
<div class="actions">
<div>
<button onclick="updateProp('radius1', false)" title="Decrease radius1">-</button>
<span class="label" id="radius1">Radius1 is 4</span>
<button onclick="updateProp('radius1', true)" title="Increase radius1">+</button>
</div>
<div>
<button onclick="updateProp('radius2', false)" title="Decrease radius2">-</button>
<span class="label" id="radius2">Radius2 is 2</span>
<button onclick="updateProp('radius2', true)" title="Increase radius2">+</button>
</div>
<div>
<button onclick="updateProp('distance', false)" title="Decrease distance">-</button>
<span class="label" id="distance">Distance is 24</span>
<button onclick="updateProp('distance', true)" title="Increase distance">+</button>
</div>
</div>

<div>
<a
href="https://bitbybit.dev/projects/public/doiV1zjIIyBcQx7EeGY5/script/79CweyK6FQ4U0ToBQDsL/script-runner-example-with-inputs-and-outputs-in-project-cylindric-industrial-3d-part-by-author-bitbybit"
target="_blank"
rel="noopener noreferrer"
>
Link to original Rete script for this example.
</a>
</div>
</div>
</body>
</html>

Key additions to index.html:

  • Control Elements: We've added divs containing button elements (for incrementing/decrementing values) and span elements (to display the current parameter values).
  • onclick Attributes: The buttons call a JavaScript function updateProp() (which we'll define in script.js) to modify the input parameters.

Implementing the Interactive Logic (script.js)

The script.js file now becomes more involved as it needs to handle initial values, update these values based on user interaction, re-execute the Rete script, and manage the previously created 3D objects.

script.js
// Initial input values for the Rete script
window.inputs = {
radius1: 4,
radius2: 2,
distance: 24,
};

// Bounds for the input values to keep them within a reasonable range
const bounds = {
radius1Min: 2, radius1Max: 10,
radius2Min: 2, radius2Max: 10,
distanceMin: 5, distanceMax: 40,
};

// Store previously created meshes to dispose of them before re-rendering
window.previousMeshes = [];
updateLabels(); // Initialize labels on the page

const runner = window.bitbybitRunner.getRunnerInstance();

const runnerOptions = {
canvasId: 'myCanvas',
canvasZoneClass: 'myCanvasZone',
enablePhysics: false, // Assuming no physics for this example
enableOCCT: true, // Script uses OCCT
enableJSCAD: false,
enableManifold: false,
enableKeyEventListeners: false,
loadFonts: ['Roboto'], // If your script uses specific fonts for 3D text
};

// Initialize the runner and get access to bitbybit API and Bit (Inputs) objects
const { bitbybit, Bit } = await runner.run(runnerOptions);

// Optional: Adjust camera after runner initialization if needed
// This prevents the Rete script from resetting the camera on each execution if it also has camera controls.
const cameraOpt = new Bit.Inputs.BabylonScene.CameraConfigurationDto();
cameraOpt.position = [-30, 25, -35]; // Adjusted for better view of the industrial part
cameraOpt.lookAt = [0, 0, 0];
bitbybit.babylon.scene.adjustActiveArcRotateCamera(cameraOpt);

// Initial execution of the Rete script
let scriptResult = await runner.executeScript(exportedScript(), inputs);
previousMeshes = scriptResult.meshes || []; // Store the output meshes

// Function to update a property, re-run the script, and update the scene
async function updateProp(propName, increase) {
let currentValue = inputs[propName];
if (increase) {
currentValue++;
} else {
currentValue--;
}

// Check against bounds
if (currentValue >= bounds[propName + 'Min'] && currentValue <= bounds[propName + 'Max']) {
buttonActivation(true); // Disable buttons during processing

inputs[propName] = currentValue;
updateLabels(); // Update the displayed value on the page

// Dispose of previously created meshes
if (previousMeshes && previousMeshes.length > 0) {
previousMeshes.forEach((mesh) => {
if (mesh && typeof mesh.dispose === 'function') {
mesh.dispose();
}
});
}
previousMeshes = []; // Clear the array

// Re-execute the script with new inputs
const newResult = await runner.executeScript(exportedScript(), inputs);
previousMeshes = newResult.meshes || []; // Store new meshes

buttonActivation(false); // Re-enable buttons
}
}

// Function to update the labels on the HTML page
function updateLabels() {
document.getElementById('radius1').innerHTML = 'Radius 1 is ' + inputs.radius1;
document.getElementById('radius2').innerHTML = 'Radius 2 is ' + inputs.radius2;
document.getElementById('distance').innerHTML = 'Distance is ' + inputs.distance;
}

// Function to disable/enable control buttons during script execution
function buttonActivation(disabled) {
const buttons = document.getElementsByTagName('button');
for (let i = 0; i < buttons.length; i++) {
buttons.item(i).disabled = disabled;
}
}

// Expose updateProp to the global window object so HTML onclick can find it
window.updateProp = updateProp;

// Placeholder for your exported Rete script string
function exportedScript() {
// Replace this with the actual JavaScript string generated from your Rete editor
// for the parametric industrial part.
return '{"type":"rete","version":"0.X.X","script":"!async function(BitByBit,bitbybit,bitbybitRunnerResult,bitbybitRunnerInputs,Bit){ /* ... your very long exported Rete script ... */ }"}';
}

Explanation of script.js:

  1. window.inputs Object: Stores the current values for the parameters (radius1, radius2, distance) that will be passed to the Rete script. Making it a window property is a simple way to make it accessible globally within this script.
  2. bounds Object: Defines minimum and maximum limits for each input parameter.
  3. window.previousMeshes Array: This is crucial. It will store references to the 3D meshes created by the Rete script. Before re-running the script with new parameters, we'll dispose of these old meshes to prevent objects from overlapping and to manage memory.
  4. updateLabels(): A helper function to update the text content of the <span> elements in the HTML, reflecting the current parameter values.
  5. Camera Adjustment (Optional but Recommended):
    • const { bitbybit, Bit } = await runner.run(runnerOptions);
    • After runner.run(), we now also destructure bitbybit (for API calls) and Bit (for accessing Inputs DTOs).
    • We then adjust the active BabylonJS camera's position and target. This is done once after initializing the runner. If your Rete script also controls the camera, this step might be overridden, or you might choose to remove camera controls from the Rete script and manage it solely from your script.js.
  6. Initial Script Execution: The Rete script is executed once with the initial inputs. The scriptResult.meshes (assuming your Rete script outputs meshes under a key named "meshes") are stored in previousMeshes.
  7. async function updateProp(propName, increase):
    • This function is called when the + or - buttons in the HTML are clicked.
    • It updates the corresponding value in the inputs object, checking against bounds.
    • It calls buttonActivation(true) to temporarily disable all buttons, preventing rapid clicks while the script is processing.
    • Crucially, it iterates through previousMeshes and calls mesh.dispose() on each to remove them from the BabylonJS scene.
    • It then calls await runner.executeScript(exportedScript(), inputs) again with the new input values.
    • The newly created meshes from this execution are stored in previousMeshes.
    • buttonActivation(false) re-enables the buttons.
  8. buttonActivation(disabled): A utility to enable/disable all buttons on the page.
  9. window.updateProp = updateProp;: Exposes the updateProp function to the global scope so it can be called by the onclick attributes in the HTML.
  10. exportedScript(): You must replace the placeholder string here with the actual, complete JavaScript string generated by the "Export to Runner" feature in your Bitbybit Rete editor for the parametric industrial part.

Styling (styles.css)

The CSS provided in the initial problem description can be used to style the page, buttons, and layout.

styles.css
body {
margin: 0;
background-color: #1a1c1f;
color: white;
font-weight: 400;
font-family: 'IBM Plex Sans', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
width: 100%;
height: 100%;
}

button {
background-color: #1a1c1f; /* Match body or choose a button color */
color: white;
border: 1px solid white;
border-radius: 5px;
padding: 5px 10px; /* Added padding for better click area */
margin: 0 5px; /* Added margin for spacing */
cursor: pointer;
}

button:disabled {
opacity: 0.5;
cursor: not-allowed;
}

.example {
margin-top: 50px;
margin-bottom: 50px;
margin-left: 300px;
margin-right: 300px;
display: flex; /* Added for centering content */
flex-direction: column;
align-items: center; /* Center content horizontally */
}

@media (max-width: 1400px) {
.example {
margin-left: 100px;
margin-right: 100px;
}
}

@media (max-width: 769px) {
.example {
margin-left: 20px;
margin-right: 20px;
}
}

#myCanvas {
display: block;
outline: none;
border: 1px solid white;
border-radius: 5px;
width: 100%; /* Ensure canvas takes full width of its container */
max-width: 800px; /* Optional: max width for very large screens */
aspect-ratio: 16 / 9; /* Optional: maintain aspect ratio */
}

.logo {
margin-bottom: 20px;
display: flex; /* For aligning logo image and text */
align-items: center;
text-decoration: none;
}

.logo img {
width: 50px;
height: 50px;
margin-right: 10px; /* Space between logo image and text */
}

.myCanvasZone {
margin-top: 20px;
margin-bottom: 10px;
width: 100%; /* Ensure canvas zone takes available width */
display: flex; /* For centering canvas if it has max-width */
justify-content: center;
}

a {
color: white;
vertical-align: middle;
text-decoration: underline; /* Make links more obvious */
}

.actions {
margin-top: 20px; /* Added margin-top */
margin-bottom: 20px;
display: flex; /* For laying out control groups */
flex-direction: column;
gap: 10px; /* Space between control groups */
align-items: center; /* Center control groups */
}

.actions > div { /* Style for each control group (radius1, radius2, etc.) */
display: flex;
align-items: center;
}

.label {
text-align: center;
display: inline-block;
min-width: 150px; /* Increased min-width for better spacing */
margin: 0 5px; /* Margin around label */
font-size: 1.1em; /* Slightly larger label text */
}

Conclusion

This tutorial demonstrates a significant step up in using the Bitbybit Runner: creating dynamic, interactive 3D experiences on your website. You've learned how to:

  • Design Rete scripts that accept external inputs.
  • Pass data from your HTML/JavaScript to the executed Rete script.
  • Receive outputs (like 3D meshes) back from the script.
  • Manage the lifecycle of 3D objects by disposing of old geometry before rendering new versions.
  • Set up basic HTML controls to trigger these dynamic updates.

This opens up a world of possibilities for building custom 3D tools and applications powered by your visual programs.