User Guide
Build and view 3D digital twins in the browser, drive them from live data, lay out and simulate spaces with an agent, and publish read-only links people can open without an account. No install.
SpatialWork opens the 3D spaces your team builds (models, splats, whole buildings) in the browser and lets you hang live data on them: charts, gauges and status panels that read straight from your databases and APIs, objects that show their readings when clicked or change colour with their data, and copies that move with their data. On top of that, an agent can lay out a room from a sentence, and a simulator runs people through the space to show where it queues and crowds.
| Step | What happens |
|---|---|
| 1. Build the scene | Import models into a new scene here, or publish one from Unity SpatialWork. |
| 2. Open it on the web | Sign in with your Central Platform account; your projects are waiting on the dashboard. |
| 3. Add live data | Connect a source, describe a chart in plain words, place it in the scene. It refreshes on its own. |
| 4. Share it | Publish a read-only link, or embed it in your own dashboard. |
| Person | What they get |
|---|---|
| The owner | Everything: edit, connect data, publish, share, rename, delete. |
| An editor you added | Edits the project like you, except publishing, renaming, deleting and re-sharing (§22). |
| Someone with the published link | Read-only, no account: looks around, switches viewpoints and shown versions, clicks objects. Chart data still updates live; they never see your data connections. |
A project is a 3D scene plus everything layered on it: imported models, data charts, interactive objects, heatmaps, saved camera viewpoints, and the connections to wherever your data lives. Two kinds sit side by side on the dashboard:
| Kind | What it is |
|---|---|
| SpatialWork scene | A 3D scene you build in the browser, or one published from Unity SpatialWork. Everything in this guide applies to it. |
| GeoSense | A map-based project, opened in the geo editor. Different tool, same dashboard and the same sign-in. |
Work saves as you go, and the top bar shows when it last saved. Save there saves at once. Ctrl+Z takes back an edit you did not mean (§05).
Log in with your Central Platform account, the same email and password you use elsewhere in Hiverlab tooling. You land on the dashboard, which lists every project you own or that someone has shared with you.
| Action | Control |
|---|---|
| Look around | Left-drag |
| Orbit a point | Alt + left-drag. Turns around whatever is under the cursor |
| Pan | Right-drag or middle-drag |
| Zoom | Scroll. Moves towards whatever is under the cursor, not blindly forwards |
| Fly | WASD · up E/Space · down Q · Shift for 3× speed |
| Frame the selection | F |
| Straight-on views | The view pill at the top: Top, Front, Left and the rest |
| Jump to a viewpoint | Click it in Views |
| Move / Rotate / Scale the selection | 1 2 3 |
| Undo / Redo | Ctrl+Z · Ctrl+Shift+Z or Ctrl+Y, or the arrows in the top bar |
| Find an object by name | Ctrl+F jumps to the Scene panel's search box |
Looking and flying are deliberately separate from orbiting, the same way Blender and Unity separate them, so a fly never fights an orbit mid-drag. Any movement cancels a viewpoint fly-to. Move speed and look sensitivity are in Settings → Controls, and every key is listed under Settings → Key bindings.
The rail on the left holds Scene, Data, Agent, Simulate, Views and Assets, with Settings at the foot. Scene opens first: a tree of everything in the scene, sorted by kind. Each row carries three switches, always in the same columns:
Select a row to get the gizmo; 1, 2 and 3 switch it between Move, Rotate and Scale. Clicking an object in the 3D view opens its row in the tree. Double-click a name to rename. Drag a row onto a group to put it inside. New group and Collapse all are in the panel header. Groups start collapsed, and they are the main way to keep a scene legible. They matter later when you publish (§21).
Type in Search objects and the tree shows only the matches, plus the groups leading to them. Every word you type must appear in the name, in any order. Enter selects the first match, Shift+Enter selects all of them, F then frames them, and Esc clears the search.
Every edit to the scene can be undone, up to 50 steps per tab and version: moves, deletes, renames, new charts, a layout the agent applied. A whole drag counts as one step. Deleting asks you to confirm, and the confirmation reminds you that Ctrl+Z brings it back.
Open Assets in the rail to browse the Central Platform library: your own uploads, folders you are a member of, and the shared libraries. Pick a model or splat and it drops into the scene. Compressed GLBs (Draco or Meshopt) load as they are; there is no need to export an uncompressed copy.
The picker also shows In this project: everything the open scene already uses. That is the only listing that works on a project shared with you, since the folders belong to whoever uploaded into them.
Data → Data source. A source is a connection, defined once and reused by every chart, interactive object and dynamic set in the project. Credentials are stored encrypted on the server and never sent back to the browser.
Host, port, database, user, password. Queries are read-only SELECTs.
The URL, then as much of the rest as the API needs:
| Field | Use it for |
|---|---|
| Method | GET, or POST for a read endpoint that takes its filter in the body. |
| Request body | Appears for POST. Must be valid JSON; it is checked before saving. |
| Auth | None, an API key in a header, a bearer token, a username and password, or Sign in for a token. |
| Extra headers | Name: value, one per line. For a tenant id, an API version pin, anything that is not itself a credential. |
| Read by | Our server, or each viewer's browser. Left on the first choice, it is decided when you save: a source with a login or key is always read by our server; one with none is read by viewers' browsers if this browser can reach it. |
An API on a client's own network (an intranet) cannot be reached from our server, but it can be reached by the browsers of people on that network. A source read by each viewer's browser works that way: every viewer's browser asks the API itself, in the editor, the published link and an embedded dashboard alike. Every window shows the same data, and nothing passes through our server.
Press ⚡ on a source to test it. A failure says which half broke: bad credentials, a token that is not where expected, or the data call itself being refused.
Data → Chart adds a card into the scene. Open its ✎ for the style editor.
Pick the source, say what you want in plain language, for example "chiller flow rate for device 100001". Choose a type or leave it on Auto, and press Generate with AI. It fills in the query, and you press Test query to see what actually comes back before anything reaches the scene. Nothing is applied behind your back.
| Type | Shows | Needs |
|---|---|---|
| Value | One big number | A query returning a single value |
| Gauge | A dial, with its threshold colours drawn on the dial's own arc | A single value, plus min and max |
| Table | Rows and columns | A query returning rows |
| Status panel | A place's state at a glance: its name in large type, a coloured headline, an explanation line, KPI tiles and a footer | A query returning one row |
| Bar, Line | A plotted series | Written by the assistant. These are the two the dropdown cannot offer, because their shape lives in a generated spec |
A status panel is written as plain lines in the style editor, one per item:
Data → Interactive asset. Clicking an object in the scene opens a small panel of readings beside it. Describe what a click should show and press Ask to have the query written for you, or write it yourself.
Choose what is clickable: a whole group (everything beneath it, nested groups included), or individual objects you select and Add selected. The difference decides how the query has to look:
| Bound to | The query should return | Object id column |
|---|---|---|
| One object | Its row, or every row about it | Not needed: the binding already says which object |
| Several, or a group | One row per object | The column whose values match the object names |
Names are matched with the file extension dropped, so an object imported as pressure.glb matches a row keyed pressure. You do not need to rename anything, and your data does not need to know about .glb.
Fields to show chooses which columns the panel shows, in order. A feed that sends one row per measurement (supply temperature, return temperature, humidity for the same unit) is shown as a Measurement | Value table with units and the last update time, instead of only the last row.
Colour objects by takes a column and a list of rules: a number range or an exact text value, each with a colour. Each object takes the colour of the first rule its own value meets, and the colours are reapplied on every refresh. An object whose row stops matching goes back to its own look. Dark models show the rule's colour too, since the tint mixes with the model's own colour instead of darkening it.
Data → Dynamic objects spawns and moves copies of library models from a data API: pallets in racks, AMRs on a floor, people and equipment in a hospital. Every item in the data (a person, a pallet, a wheelchair) becomes a copy of one of the models. On each refresh the copies glide to their new places, new items appear and items that are gone disappear. The panel is laid out in the order you answer it: Data, Models, Position & motion, Colour and On click.
The quickest way: choose the data API, describe what to show, for example "staff as people, a model per role, coloured by state with SOS pulsing red", and press Ask AI. It reads a sample of the data, the scene's place names and your library's model names, and fills in the whole panel: the list, the ID, where each item goes, a model per value, status colours, the label and the click fields. Check what it chose (the preview and the line under Refresh help) and press Apply; nothing is applied on its own. Anything it got wrong is just a field to change.
Choose the data API. With a REST source every list in its data is offered with how many items it holds and their columns; choose the one that holds the things to show (the JSONPath behind it is under Edit as JSON). A database source takes a SQL query instead. The data is read straight away: a preview shows the first few items, every column question below is a list of the real columns, and the line under Refresh says how many items there are and how many would actually be placed in this scene. Nested values read as dotted columns, so pos: {x, y, z} gives pos.x, pos.y, pos.z. For a source read by viewers' browsers, keep to the lists the picker offers; filters need our server.
Models come from the asset library: shared folders and your own uploads. Nothing has to be placed in the scene to be copied. Leave Model by on one model for every item and choose that model; or choose a column (such as role) and add + Model for a value for each value that needs its own (a nurse figure for nurse, a wheelchair for wheelchair); the value box offers the values in the data. + Any other value covers the rest; without it, items with an unlisted value are not shown. Each model has a size (1 is its own size) and a turn in degrees about the vertical, which every copy of it takes.
A set made by the older editor used objects in the scene as models. It opens with those turned into the same library models at the same size and turn; Apply saves it, and can delete the scene objects that were only there to be copied.
Use one Dynamic object per kind of thing and put each in its own group, such as Staff, Patients and Assets: each then reads its own list and has its own models, status rules and label, and viewers can show or hide it. Kinds whose status values differ need no separate API for that reason. The data only says what is where; everything about the look stays here, so changing a colour or a model is a scene edit. Positions are x / y / z in scene metres, and the data supplies y where height matters, for example across floors; nothing is guessed. A tracking API on the client's own network is connected as a source read by each viewer's browser (§07).
Edit as JSON shows the whole set as configuration, to read, copy into another project or edit. The delete button is in the panel's header. The editor never puts the gizmo on a copy: the copies belong to the data, not to you.
Clicking a copy sends the host page an object-click message whose instance is the item's ID (such as S-201) and whose row is its data, so the host can open its own record for that person or asset. The host page does not send positions; the viewer reads them from the data API itself.
Data → Spatial heatmap interpolates one numeric reading across a floor, between sensors that are ordinary objects in a group. Move a sensor in the editor and its contribution moves with it.
Point it at the group, give it a query returning one row per sensor, and say which column is the name and which the value. Min and max should come from the quantity (RSSI is about −90 to −30 dBm, a percentage 0 to 100), not from whatever today's sample happens to span, or later readings fall off the scale. Viridis is the default colour scheme and the safest for colour blindness; the traffic-light scheme is the one to avoid if that matters to your audience.
Agent in the rail opens Build with Agent, which works on the whole scene. It has two tabs: Data objects, covered here, and Space layout (§13).
Pick a data source and describe what you want, e.g. "A gauge of chiller load. A Wi-Fi signal heatmap on the Access points group. Make Pump A and Pump B clickable to show flow and status." Press Build. The agent reads the source and the scene's own names. It proposes charts (placed in front of the camera), status panels standing in each place, heatmaps on a named group, clickable objects, colour rules and dynamic sets. You see the list first and choose what goes in.
Agent → Space layout rearranges a place using the furniture the scene already has. It never generates a 3D model: every desk, chair or table it places is a copy of one already in the scene or in your library, with the same materials, so nothing new is uploaded.
| Kind of request | Example |
|---|---|
| Turn a place into something else | "Make the workshop a standard office with desks and chairs" |
| A number, in rows | "200 chairs facing the stage, 10 per row" |
| Several blocks | "200 at the front, 200 at the back" |
| Fill a whole room | "Fill the space with meeting tables". Sets go in rows, single pieces along the walls |
| Put things somewhere | "Sofas in the corners", "a row of lockers along the north wall" |
| Grow what is there | "Another 50 chairs with the existing ones" |
| Edit what is there | "Remove the sofas", "rotate the desks to face the window", "move the reception desk 2 m to the left", "desks toward the window" |
| Name one object | "Chairs facing the south entrance". The agent reads every object's name, group and position, so it can aim at one exact thing |
Simulate in the rail opens this scene's simulator in a tab of its own. It runs people through the real space: they walk the floor of your scene, queue, are served, sit, wander and leave, and you see where it gets crowded and how long people wait. Pressing Simulate again brings that tab forward without losing a run, and the simulator's Editor link goes back to the editor tab. It needs a saved project.
The page has a steps graph (what people do, in order), a floor plan with the places, a 3D view of the scene, and side tabs: Properties, People, Timing, Checks and Results. Drag the dividers to resize the panes.
| Step | What people do |
|---|---|
| Arrive | Come in at an entrance: how many, between which times, evenly, randomly or at a peak |
| Service | Queue and are served: desks per place, seconds per person. Several places doing the same job share the load, and people take the quickest line |
| Occupy | Take seats or stand in an area until a clock time or for a duration; when full they stand or skip it |
| Visit | Drop by an area for a while (a share of people, a dwell time), optionally spread over several places |
| Free time | Choose where to go by what they need, until a clock time |
| Split | Branch the flow by share (70 % here, 30 % there) or by who (VIPs one way) |
| Alarm | An evacuation at a set time; everyone heads for the exits |
| Exit | Leave by one of the exit points |
Each step happens at a place on the floor. Places from the scene makes them from the scene's objects by their names: a "Registration" desk becomes the registration place. You can also select objects in 3D and Add selected. On the plan, drag a place's arrows to Move it, its corner handles to Size it, and its ring to Turn it. Add another area copies a place so one job runs in several spots. Roof shows or hides the building above head height in 3D.
Checks lists anything the setup is missing (a service with no desks, a step with two next steps and no Split) before you run. Run plays the run while it is worked out, as fast as possible or at a speed from 1× to 240×. People move on the plan and in 3D, each step shows how many people are at it, and quiet stretches are skipped. Colour people by activity or by who they are.
Results shows queues and waits per service (and per kind of person), who gave up, how long the evacuation took and whether anyone was still inside, and crowd density. Density uses Fruin's walkway levels of service: D crowded (over 0.72 people/m²), E very crowded (over 1.08), F crush risk (over 2.17). The plan can show Peak density or Time over band D for every spot on the floor. People seated or standing in a line are not counted, since those are dense by design. Afterwards, Play replays the run.
Save keeps the setup with the project (one simulation per project) and shows the revision it saved. Export downloads the setup as a file and Import loads one, to move a setup between projects.
Data → Spatial analytics slices a building model into walls and analyses the space. Wi-Fi coverage is the first analysis: point it at the floor model and the group of access points, slice the walls (or draw, select and fix them by hand), then press Run solver directly to compute coverage in the browser, or write an instruction and press Ask Copilot. The result is a coverage map scored only where there is building. It needs a saved project.
Settings, at the foot of the rail, has three tabs:
The scene's look (sun, tone mapping, exposure, bloom, background) is saved with the scene, so the published link looks the same as the editor. Shadows land on the grid, the ground you see, even under a model that hangs above it.
Views in the rail saves camera positions. The first is the scene's opening view, what anyone sees when they open the project or the published link. Viewers can jump between them; they cannot change them.
A version is an authored variant ("ground floor", "night lighting", "client cut A"), not an undo history. Each has its own objects, charts and viewpoints, and each is independently shown or hidden to viewers.
One version opens first, in the editor and on the published link. Choose it with the home button beside a version. If that version is hidden, the link opens on the first shown version instead, and the panel says so.
Save a restore point in the Versions panel keeps the whole project as it is now, every version included, under a name. Roll back returns the whole project to a restore point. Nothing is lost by doing so: save the state you are leaving as a restore point of its own first, and you can come back to it. The panel says when you have edited since the last restore point.
Publish gives the project one link. Anyone with it can look around, jump between viewpoints, click interactive objects, and switch between the versions you marked shown. Nothing else. They cannot edit, and they do not need an account.
A published scene can let whoever opens it switch whole groups off. A floor of sensors is one idea, not forty, and hiding them is easier than explaining them.
What a viewer switches off is theirs for that visit only. It is never saved back, and it never affects anyone else opening the same link. A group inside a group gets its own switch.
People on a project card adds an editor by email address. They get the project on their own dashboard with a SHARED chip, and can do everything you can except publish, rename, delete or re-share it. Those stay with the owner.
A published scene can sit in an <iframe> and talk to the page around it: your dashboard can highlight an object when someone clicks a table row, and hear about clicks in the 3D view. Embed in the Versions panel copies a ready-made iframe snippet. A click reports the object, and the asset id and other fields its binding filters on, so the host can look it up in its own data. See the embedding guide for the full message list.
One behaviour worth knowing here: selecting an object from the host works even when the viewer has switched its group off. The host asked about an object, not a group, so the group is not revealed. Expect the readout, and the camera move, for something not currently on screen.
| What you see | Usually means |
|---|---|
| A chart says no data | The query returned nothing. Press Test query in the style editor to see the raw result. |
| A chart stopped updating | A short-lived token expired. Test the source; if it is a sign-in source, that renewal is automatic and the failure is elsewhere. |
| No reading matched "…" on a click | The object id column is wrong, or the query is wider than the binding. The panel names the column it used and the values it saw. |
| A heatmap is blank | No row's name matched an object in the group. It reports how many rows it had and what the objects are called. |
| Dynamic objects do not appear | Rows came back but did not land in this scene. The line under Refresh counts how many would be placed; check that the place column names objects or rack slots this scene actually has, and that every value has a model (or add Any other value). |
| The layout agent left something out | Read its Checks: it says which step it could not carry, or which named things it found that are not furniture. Nothing is saved until you Apply. |
| A table shows columns you did not want | Set Columns. A REST query cannot trim them itself. |
| Panels feel slow | Settings → Visuals → Chart panels → Glass blur. The blur behind them is the most GPU-expensive part of how they look. Ambient occlusion is the next one to turn off. |
| An icon shows a dashed box | Its URL no longer resolves; the asset was probably removed from the library. Clear it and choose another. |
| You deleted the wrong thing | Ctrl+Z. Further back than this session, roll back to a restore point. |
Updated 10 November 2026. Something here wrong or missing? It is maintained alongside the code. Tell the team and it gets corrected in the same place the behaviour lives.