diff --git a/ui/index.html b/ui/index.html
index 931609b..fecb0dd 100644
--- a/ui/index.html
+++ b/ui/index.html
@@ -86,8 +86,8 @@ import * as THREE from 'three';
// --- Three.js scene ---
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100);
-camera.position.set(0, 2, 5);
-camera.lookAt(0, 0, 0);
+camera.position.set(0, 1.2, 5);
+camera.lookAt(0, 0.8, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
@@ -107,70 +107,67 @@ scene.add(pointLight);
// Grid
scene.add(new THREE.GridHelper(10, 20, 0x222244, 0x111122));
-// FC Board model
+// SaltyBot robot model — two-wheeled self-balancing robot
+// Pivot at wheel axle (y=0); body rises upward; pitches ±pitch_deg around X axis.
const boardGroup = new THREE.Group();
-// PCB
+// Main body (vertical rectangle, ~1.2 tall, center above wheel axle)
boardGroup.add(new THREE.Mesh(
- new THREE.BoxGeometry(1.6, 0.08, 1.6),
- new THREE.MeshPhongMaterial({ color: 0x1a1a1a, specular: 0x333333 })
+ new THREE.BoxGeometry(0.7, 1.2, 0.3),
+ new THREE.MeshPhongMaterial({ color: 0x1a2a4a, specular: 0x334466 })
));
+boardGroup.children[boardGroup.children.length - 1].position.set(0, 0.7, 0);
-// Copper traces
-const traceMat = new THREE.MeshPhongMaterial({ color: 0xcc8833, specular: 0xffaa44 });
-for (let i = -0.6; i <= 0.6; i += 0.3) {
- const trace = new THREE.Mesh(new THREE.BoxGeometry(1.4, 0.005, 0.02), traceMat);
- trace.position.set(0, 0.043, i);
- boardGroup.add(trace);
-}
+// Left wheel
+const wheelGeo = new THREE.CylinderGeometry(0.4, 0.4, 0.14, 20);
+const wheelMat = new THREE.MeshPhongMaterial({ color: 0x1a1a1a, specular: 0x333333 });
+const leftWheel = new THREE.Mesh(wheelGeo, wheelMat);
+leftWheel.rotation.z = Math.PI / 2;
+leftWheel.position.set(-0.47, 0, 0);
+boardGroup.add(leftWheel);
-// MCU chip
-const mcu = new THREE.Mesh(
- new THREE.BoxGeometry(0.5, 0.06, 0.5),
- new THREE.MeshPhongMaterial({ color: 0x222222, specular: 0x444444 })
-);
-mcu.position.set(0, 0.07, 0);
-boardGroup.add(mcu);
+// Right wheel
+const rightWheel = new THREE.Mesh(wheelGeo, wheelMat);
+rightWheel.rotation.z = Math.PI / 2;
+rightWheel.position.set(0.47, 0, 0);
+boardGroup.add(rightWheel);
-// IMU chip
-const imuChip = new THREE.Mesh(
- new THREE.BoxGeometry(0.2, 0.04, 0.2),
- new THREE.MeshPhongMaterial({ color: 0x331111, specular: 0x442222 })
-);
-imuChip.position.set(-0.4, 0.06, -0.3);
-boardGroup.add(imuChip);
+// Wheel rims (blue accent)
+const rimGeo = new THREE.TorusGeometry(0.37, 0.025, 8, 20);
+const rimMat = new THREE.MeshPhongMaterial({ color: 0x0055cc, specular: 0x0088ff });
+[[-0.54], [0.54]].forEach(([x]) => {
+ const rim = new THREE.Mesh(rimGeo, rimMat);
+ rim.rotation.z = Math.PI / 2;
+ rim.position.set(x, 0, 0);
+ boardGroup.add(rim);
+});
-// USB connector
-const usb = new THREE.Mesh(
- new THREE.BoxGeometry(0.35, 0.1, 0.15),
- new THREE.MeshPhongMaterial({ color: 0x888888, specular: 0xaaaaaa })
-);
-usb.position.set(0, 0.06, -0.85);
-boardGroup.add(usb);
+// Display panel on front face
+boardGroup.add(new THREE.Mesh(
+ new THREE.BoxGeometry(0.48, 0.36, 0.02),
+ new THREE.MeshPhongMaterial({ color: 0x001133, specular: 0x003366, emissive: 0x000a1a })
+));
+boardGroup.children[boardGroup.children.length - 1].position.set(0, 0.75, 0.16);
// Status LED
const ledMat = new THREE.MeshBasicMaterial({ color: 0xff0000 });
-const led = new THREE.Mesh(new THREE.SphereGeometry(0.03, 8, 8), ledMat);
-led.position.set(0.5, 0.06, 0.5);
+const led = new THREE.Mesh(new THREE.SphereGeometry(0.04, 8, 8), ledMat);
+led.position.set(0.22, 1.1, 0.16);
boardGroup.add(led);
-// Mounting holes
-const holeMat = new THREE.MeshPhongMaterial({ color: 0x444444 });
-[[-0.55, -0.55], [-0.55, 0.55], [0.55, -0.55], [0.55, 0.55]].forEach(([x, z]) => {
- const ring = new THREE.Mesh(new THREE.TorusGeometry(0.06, 0.015, 8, 16), holeMat);
- ring.rotation.x = Math.PI / 2;
- ring.position.set(x, 0.05, z);
- boardGroup.add(ring);
-});
+// Sensor stem (thin post above body)
+boardGroup.add(new THREE.Mesh(
+ new THREE.BoxGeometry(0.1, 0.28, 0.1),
+ new THREE.MeshPhongMaterial({ color: 0x2a2a2a, specular: 0x444444 })
+));
+boardGroup.children[boardGroup.children.length - 1].position.set(0, 1.44, 0);
-// Forward arrow
-const arrow = new THREE.Mesh(
- new THREE.ConeGeometry(0.08, 0.2, 8),
- new THREE.MeshBasicMaterial({ color: 0xff4444, transparent: true, opacity: 0.8 })
-);
-arrow.rotation.x = -Math.PI / 2;
-arrow.position.set(0, 0.06, -0.65);
-boardGroup.add(arrow);
+// Sensor head (camera/LIDAR box at top)
+boardGroup.add(new THREE.Mesh(
+ new THREE.BoxGeometry(0.28, 0.16, 0.28),
+ new THREE.MeshPhongMaterial({ color: 0x111122, specular: 0x333355 })
+));
+boardGroup.children[boardGroup.children.length - 1].position.set(0, 1.66, 0);
scene.add(boardGroup);