Serializable scene language
A space is plain data: walls, artworks, and a camera, in a single object that survives a JSON.parse(JSON.stringify(scene)) round-trip. No three.js, no functions — exactly what an editor or backend needs.
Walls as top-down paths
Draw a wall as a polyline footprint with a height and thickness. One continuous run of walls follows the path — no need to declare each side, and corners just work.
Artworks anchored to edges
Name any wall segment and hang a piece on it by position (units or %) and height (units, %, or the camera's eye level), on either face. Move the wall and the piece follows.
Free-roaming camera
A first-person camera you drive with the mouse and WASD or the arrow keys, with a subtle head-bob and boundary clamping. Ported from a real virtual gallery.
Hot reconciliation by id
Hand the engine a new scene and it diffs by id — only what changed is rebuilt. Add a wing or drop one while the visitor is walking, and the camera keeps its pose.
Framework-agnostic
The engine is vanilla three.js with a high-level ctx API; three.js stays an implementation detail. Use it directly, or with the React bindings and a declarative <Gallery> component.
A DOM overlay (HUD)
Layer controls and visual aids over the canvas with a slot-based overlay. Optional daisyUI widgets out of the box, or bring your own component with a single (ctx) => HTMLElement.
Tree-shakeable & typed
Small packages — core, engine, props, overlay, react — so you only pay for what you use. TypeScript throughout, strict mode, ESM and CJS builds.