A web-based 3D scene editor that assembles scenes from GLB/GLTF models, syncs with a JSON representation, and integrates with Metaversal Fabric for 6DoF access via Metaverse Browser.
Scene Assembler lets you:
- Add 3D objects from a library (GLB/GLTF models) into a scene
- Move, rotate, and scale objects with an interactive gizmo
- Edit the scene as JSON in a code editor (changes sync both ways)
- Export your scene for use in Metaversal Fabric or other tools
- Collaborate with others when connected to a Fabric
-
Serve the app locally (required for proper loading):
cd site python3 -m http.server 8080Or with Node.js:
npx serve site -p 8080
-
Open in your browser:
http://localhost:8080 -
Log in (if using Fabric): Enter your Fabric URL and admin key in the login modal.
-
Or use without Fabric: You can still use the JSON editor and object library to build scenes locally.
| Step | Action |
|---|---|
| Add objects | Click the Object Library icon (bottom bar) → click a model to add it to the scene |
| Select | Click an object in the 3D viewport or in the Scene Outliner (left sidebar) |
| Move / Rotate / Scale | Use the W / E / R buttons (or toolbar) and drag the gizmo handles |
| Edit JSON | Open the Code Editor panel to edit the scene structure directly |
| Undo / Redo | Use the toolbar buttons or keyboard shortcuts |
| Export | Export menu → Export Backup (keeps DB links) or Export for External (for new scenes/databases) |
When you first open the app click on the Help, a Getting Started tour guides you through the editor's main features. You can restart it anytime from the help menu or settings.
Use Ctrl or ⌘ (Cmd) where Ctrl is shown below. Shortcuts are disabled while the JSON editor is focused (except where the editor handles its own keys).
| Key | Action |
|---|---|
W |
Translate (move) mode |
E |
Rotate mode |
R |
Scale mode |
Delete |
Delete selected object(s) |
Ctrl+Z / Ctrl+Shift+Z |
Undo / Redo |
Ctrl+D |
Duplicate selected |
F |
Frame camera on selected |
Q / Shift+Q |
Detach / re-attach gizmo |
Escape |
Deselect |
Arrow keys |
Move camera |
Space (hold) + Arrow keys |
Pan camera |
| Context menu | Select all / Deselect all (right-click the 3D viewport or scene outliner) |
| Issue | Solution |
|---|---|
| Blank screen | Serve the app over HTTP (don’t open file://). Use python3 -m http.server or npx serve. |
| Models not loading | Check the browser console for CORS or 404 errors. Ensure model URLs are accessible. Editor currently only accepts .glb and .gltf files. |
| Login modal stuck | Fabric requires a valid URL and key (passcode). A valid Fabric URL and corresponding MVSADMINKEY must be entered correctly to connect to a Fabric. For local-only use, you can work with the JSON editor without logging in. |
| Gizmo not visible | Make sure an object is selected (highlighted in the outliner). |
The app is split into sa-* modules. Each file has a single responsibility. Classic scripts load in dependency order at the bottom of site/index.html. Global state lives in sa-core.js (scene, camera, selection) and sa-bootstrap.js (DOM refs).
The CodeMirror 6 JSON editor is initialized in a <script type="module"> block in index.html; it imports sa-json-lint.js (schema linting) and sa-json-autocomplete.js (completions) as ESM alongside packages from esm.sh.
site/index.html
├── Head: Bootstrap, Driver.js, jQuery, Socket.io, Fabric (vendor/mv/*), maputil.js, rp1.js, Three.js
├── Body: <script type="module"> → CodeMirror + sa-json-lint.js, sa-json-autocomplete.js
└── Foot (order matters):
sa-config.js
sa-core.js
sa-bootstrap.js
sa-models.js
sa-properties.js
sa-transforms.js
sa-json-sync.js
sa-selection.js
sa-groups.js
sa-sidebar.js
sa-ui.js
sa-object-library.js
sa-main.js
sa-shell.js
sa-tutorial.js
Key concepts:
- Object Root – A
THREE.Groupthat holds all scene objects.userData.isCanvasRoot === true. - Editor Group – A group that can contain multiple models.
userData.isEditorGroup === true. - Selection –
selectedObject(single),selectedObjects(array). Updated byselectObjectinsa-selection.js. - JSON ↔ Scene –
sa-json-sync.jskeeps the document and the 3D scene in sync when changes are applied; the editor also runs lint and autocomplete via the ESM modules above.
| Layer | Technology |
|---|---|
| 3D | Three.js 0.128 (OrbitControls, TransformControls, GLTFLoader) |
| UI | Bootstrap 5.3.8, Font Awesome 7, Outfit (Google Fonts) |
| Code editor | CodeMirror 6 (via window.jsonEditorAPI; ESM from esm.sh; sa-json-lint.js, sa-json-autocomplete.js) |
| Tutorial | Driver.js 1.4 (Getting Started walkthrough) |
| Multi-user | Metaversal Fabric (vendor/mv/*), jQuery 3.7.1, Socket.io 4.8 |
| Entry | maputil.js, rp1.js (Fabric integration, scene load) |
Edit site/js/sa-config.js to change behavior:
| Category | Constants |
|---|---|
| Scene | SCENE_BACKGROUND, GRID_COLOR, GRID_COLOR_MINOR, RULER_COLOR, HUMAN_GUIDE_COLOR |
| Selection | BOX_COLORS, BOX_HELPER_COLOR, OBJECT_ROOT_BOX_COLOR, ERROR_OBJECT_COLOR |
| Camera | ORBIT_MAX_DISTANCE_MULTIPLIER, PAN_SPEED, FREE_MOVE_SPEED |
| Lighting | LIGHT_HEMISPHERE_SKY, LIGHT_HEMISPHERE_GROUND, LIGHT_HEMISPHERE_INTENSITY |
| Preview | PREVIEW_SIZE, PREVIEW_ROTATION_SPEED, PREVIEW_BACKGROUND, PREVIEW_SCALE_FIT |
| Limits | TRI_SCENE_MAX, TEX_SCENE_MAX_MP |
| Undo | MAX_UNDO_HISTORY |
| UI | MIN_CODE_EDITOR_WIDTH, MIN_OBJECT_LIBRARY_HEIGHT |
| Module | Purpose | Depends on |
|---|---|---|
| sa-config.js | Shared constants. No dependencies. | — |
| sa-core.js | Three.js scene, camera, renderer, lights, grid, ruler, human guide. | sa-config |
| sa-bootstrap.js | DOM refs, getJSONEditorText/setJSONEditorText, init on DOMContentLoaded. |
sa-config, sa-core |
| sa-models.js | Model cache, loadModelFromReference, URL utilities. |
sa-config |
| sa-properties.js | Bounds, clamping, texture info, properties panel updates. | sa-core, sa-bootstrap |
| sa-transforms.js | Transform gizmo, box helpers, undo/redo, canvas size, transform events. | sa-config, sa-core |
| sa-json-sync.js | Syncs JSON editor ↔ 3D scene; buildNode, parseJSONAndUpdateScene, export. |
sa-core, sa-models |
| sa-json-lint.js | CodeMirror lint source for Scene Assembler JSON schema (ESM; loaded from index.html). |
@codemirror/lang-json, @codemirror/lint |
| sa-json-autocomplete.js | Scene JSON completions; optional reformat when parse succeeds (skips huge docs). | @codemirror/lang-json, @codemirror/language, @codemirror/state |
| sa-selection.js | selectObject, selectFromSidebar, selectFromCanvas, select all, deselect. |
sa-core |
| sa-groups.js | Group/ungroup, drag-drop attach, duplicate, delete. | sa-core |
| sa-sidebar.js | Builds outliner; createSidebarItem, addGroupToList, addModelToList, makeLabelEditable. |
sa-core |
| sa-ui.js | Keyboard shortcuts, buttons, context menu, hover/selection, camera helpers. | sa-core |
| sa-object-library.js | Loads object library, preview thumbnails, add objects to scene. | sa-core |
| sa-main.js | loadScene, refreshScene; window exports for rp1.js. |
sa-* |
| sa-shell.js | Bootstrap tooltips, code editor panel fade/resize, object library panel resize. | sa-config |
| sa-tutorial.js | Getting Started walkthrough using driver.js; sets sceneAssemblerTutorialActive. |
sa-core, sa-bootstrap, sa-config, driver.js |
Exposed API (for rp1.js / Fabric):
| Function | Module | Purpose |
|---|---|---|
loadScene(sJSONScene, clearHistory) |
sa-main.js | Load a scene from Fabric/URL |
loadObjectLibrary(sRootUrl) |
sa-main.js | Load object library from URL |
generateSceneJSONEx() |
sa-main.js | Export scene as JSON |
Important DOM IDs (see site/js/README.md for full list):
modelList– Outliner (scene hierarchy)properties/propertiesPanel– Properties paneljsonEditor– Code editor containerobjLibPanel/objLibGrid/objLibToggle– Object librarysnap,canvasSize,setCanvasSize– Scene settingstranslate,rotate,scale– Transform mode buttonsundo,redo,delete,resetCamera– Toolbar actionsexportJson,exportJsonExt– Export Backup / Export for External
Debugging: Use browser DevTools. Key globals: scene, camera, selectedObjects, canvasRoot.
- Add a feature: Identify the right module and extend it. Add a new
sa-*.jsonly if the responsibility is clearly separate. - Change config: Edit
sa-config.js; avoid hardcoding values elsewhere. - Test: Run a local server; open
index.html. Fabric requires auth; JSON editor can be used without it.
For more detail on modules, conventions, and vendor code, see site/js/README.md.
Apache-2.0. See LICENSE and file headers.