import { useEffect, useMemo, useRef } from "react"; import * as THREE from "three"; import { DataNet } from "@sjs/sdk"; const CHANNEL = "demo.array.trail"; export function SceneSyncDemo({ apiKey, mount }: { apiKey: string; mount: HTMLDivElement }) { const clientRef = useRef(null); const meshes = useMemo(() => [] as THREE.Mesh[], []); useEffect(() => { const scene = new THREE.Scene(); scene.background = new THREE.Color("#0b1118"); const camera = new THREE.PerspectiveCamera(50, mount.clientWidth / mount.clientHeight, 0.1, 100); camera.position.z = 6; const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(mount.clientWidth, mount.clientHeight); mount.appendChild(renderer.domElement); const light = new THREE.PointLight(0xffffff, 2); light.position.set(2, 3, 4); scene.add(light); for (let index = 0; index < 8; index += 1) { const mesh = new THREE.Mesh( new THREE.SphereGeometry(0.18, 18, 18), new THREE.MeshStandardMaterial({ color: 0xa9d8ff }), ); mesh.position.x = index * 0.5 - 1.75; scene.add(mesh); meshes.push(mesh); } const client = new DataNet({ apiKey, clientId: "three-scene-demo", deviceName: "three.js Scene Demo", }); client.subscribe(CHANNEL, (data) => { const samples = Array.isArray((data as { samples?: unknown[] }).samples) ? ((data as { samples: number[] }).samples) : []; meshes.forEach((mesh, index) => { const value = typeof samples[index] === "number" ? samples[index] : 0; mesh.position.y = value * 2 - 1; mesh.scale.setScalar(0.8 + value * 0.8); }); }); void client.connect(); clientRef.current = client; let frame = 0; const tick = () => { frame = requestAnimationFrame(tick); renderer.render(scene, camera); }; tick(); return () => { cancelAnimationFrame(frame); client.disconnect(); renderer.dispose(); mount.removeChild(renderer.domElement); }; }, [apiKey, meshes, mount]); return null; }