Hi everyone! ![]()
I’d like to share Viewer3D, a real-time 3D SCADA visualization for warehouse / factory automation layouts — roller & chain conveyors, racking, twin-column stacker cranes, ring-shuttles, AGVs and forklifts. It renders a live warehouse floor and animates the full pallet lifecycle: pallets flow along a conveyor graph, a shuttle hands them off to a stacker crane, the crane puts them away into storage cells and retrieves them back out onto the outfeed lines.
Live demo: Viewer 3D - Scada3D
(toggle 2D/3D and the light/dark theme from the top-right; nav-cube to orbit; E for edit mode)
What’s interesting (to me, anyway)
It’s built with plain three.js (r184) and vanilla ES modules — no React / R3F, no TypeScript, no bundler in development. The browser loads the modules natively; three.js comes straight from the jsdelivr CDN via an import map. All geometry is procedural — no Blender, no imported models — every mesh is generated in code from real warehouse dimensions (mm).
The whole thing is engineered to scale to tens of thousands of stock pallets and ~1,000 conveyor modules at 60 fps. On the full scene above it sits around ~90 fps with ~670 draw calls / ~2.6M triangles.
Tech / approach:
- Custom minimal ECS drives the simulation systems (conveyor transport, crane put-away & retrieval, shuttle transfer, cradle lifts, owner-follow), with memoized queries so most frames are cache hits.
- Draw-call reduction is the core strategy:
InstancedMeshpools for repeated pallets / conveyor modules (one draw call per pool, instances stay raycast-pickable), static composites merged into single meshes with baked vertex colors, and edge outlines batched into oneLineSegmentsper opacity group. - Render-on-demand + FPS cap: the RAF loop skips the render entirely when nothing moved, so an idle scene drops the GPU to ~0.
- Smooth 2D ⇄ 3D camera: orthographic ↔ perspective via an element-wise projection-matrix morph (instead of a hard camera swap that pops), plus nav-cube rotations and fly-to/home tweens.
- In-browser edit mode: move/rotate/resize/retype equipment, marquee multi-select with AutoCAD window/crossing semantics, magnet-snap conveyor connections, and layout save.
- Backend is ASP.NET Core serving static files only — all logic is on the frontend.
The pallet movement you see is currently simulated; next step is feeding live positions from the backend via SignalR.
Happy to answer questions — especially about the instancing / draw-call batching or the 2D⇄3D projection morph. Feedback very welcome! ![]()



