START DOCUMENTATION

3D Table Configurator With TypeScript

What you will learn

In this tutorial you will learn how to create a 3D website by using JavaScript, HTML and CSS by exporting code from our TypeScript web CAD editor. In the video we set up 3D table model configurator logic, introducing you to some basic 3D programming concepts. We use OpenCascade OCCT geometry kernel algorithms and render shapes via BabylonJS game engine through bitbybit-runner.js library. You will create a skybox and directional light that will also create shadows. This lecture is free and is great for people who want to learn parametric design, coding and 3D web development. It is tuned for beginners and experts who want to integrate our open-source technologies in their products and applications.

Final result

You can either visit StackBlitz project page or copy these code snippets into your website to make the result from this tutorial appear on your website.

TypeScript code

In the following visual script you will find the final code that was exported to JavaScript via "Export to Runner" popover.

TypeScript

3D Table

Complete Code

You can also download this static index.html file and open it in your browser to see the result. It contains all of the styling, html and script inside it.

<!DOCTYPE html> <html lang="en"> <head> <title>Home</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@0.19.1/runner/bitbybit-runner-babylonjs.js"></script> <script> const runner = window.bitbybitRunner.getRunnerInstance(); let bitbybit; let Bit; const model = { width: 1, length: 1, height: 0.5, thickness: 0.05, }; setTimeout(async () => { const runnerOptions = { canvasId: 'myCanvas', canvasZoneClass: 'myCanvasZone', enablePhysics: false, enableOCCT: true, enableJSCAD: false, enableKeyEventListeners: false, backgroundColor: '#0000ff', loadFonts: [], }; const res = await runner.run(runnerOptions); bitbybit = res.bitbybit; Bit = res.Bit; changeModel(); }, 0); let previousMesh; async function changeModel(value, name) { if (value === undefined || model[name] !== value) { if (value !== undefined) { model[name] = value; } const inputs = model; const { solid, compound, face } = bitbybit.occt.shapes; const legHeight = inputs.height - inputs.thickness; const halfLegHeight = legHeight / 2; const halfThickness = inputs.thickness / 2; const widthOffset = inputs.width / 2 - halfThickness; const lengthOffset = inputs.length / 2 - halfThickness; const start = async () => { const skyboxOptions = new Bit.Inputs.BabylonScene.SkyboxDto(); skyboxOptions.skybox = Bit.Inputs.Base.skyboxEnum.clearSky; bitbybit.babylon.scene.enableSkybox(skyboxOptions); const lightOptions = new Bit.Inputs.BabylonScene.DirectionalLightDto(); lightOptions.intensity = 3; bitbybit.babylon.scene.drawDirectionalLight(lightOptions); const tableTopShape = await solid.createBox({ width: inputs.width, length: inputs.length, height: inputs.thickness, center: [0, inputs.height - halfThickness, 0], }); const leg1Shape = await solid.createBox({ width: inputs.thickness, length: inputs.thickness, height: legHeight, center: [widthOffset, halfLegHeight, lengthOffset], }); const leg2Shape = await solid.createBox({ width: inputs.thickness, length: inputs.thickness, height: legHeight, center: [-widthOffset, halfLegHeight, lengthOffset], }); const leg3Shape = await solid.createBox({ width: inputs.thickness, length: inputs.thickness, height: legHeight, center: [widthOffset, halfLegHeight, -lengthOffset], }); const leg4Shape = await solid.createBox({ width: inputs.thickness, length: inputs.thickness, height: legHeight, center: [-widthOffset, halfLegHeight, -lengthOffset], }); const groundShape = await face.createCircleFace({ radius: 2, center: [0, 0, 0], direction: [0, 1, 0], }); const compoundShape = await compound.makeCompound({ shapes: [ tableTopShape, leg1Shape, leg2Shape, leg3Shape, leg4Shape, groundShape, ], }); const drawOptions = new Bit.Inputs.Draw.DrawOcctShapeSimpleOptions(); drawOptions.faceColour = '#555577'; drawOptions.edgeWidth = 1; const table = await bitbybit.draw.drawAnyAsync({ entity: compoundShape, options: drawOptions, }); return { table }; }; const res = await start(); if (previousMesh) { previousMesh.dispose(); } previousMesh = res.table; } } window.changeModel = changeModel; </script> <style> body { background-color: #1a1c1f; color: white; font-weight: 400; font-family: 'IBM Plex Sans', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; padding: 50px; } #myCanvas { outline: none; border: 1px solid white; border-radius: 5px; width: 100%; } </style> </head> <body> <h1>3D Table Configurator</h1> <div class="myCanvasZone"> <canvas id="myCanvas"></canvas> </div> <div> <input id="width" type="range" value="1" min="0.3" max="3" step="0.1" onchange="changeModel(+event.target.value, 'width')" /> <label for="width">Width</label> </div> <div> <input id="length" type="range" value="1" min="0.3" max="3" step="0.1" onchange="changeModel(+event.target.value, 'length')" /> <label for="length">Length</label> </div> <div> <input id="height" type="range" value="0.5" min="0.3" max="1.5" step="0.1" onchange="changeModel(+event.target.value, 'height')" /> <label for="height">Height</label> </div> <div> <input id="thickness" type="range" value="0.05" min="0.01" max="0.25" step="0.01" onchange="changeModel(+event.target.value, 'thickness')" /> <label for="thickness">Thickness</label> </div> </body> </html>