Stand Alone Mode — E-Learning Lab
Stand Alone Mode is an optional authoring workflow that replaces Vizard Inspector with Blender and targets WebXR / standalone VR headsets (e.g. Meta Quest) instead of a tethered Vizard runtime. All existing Vizard-based workflows are completely unchanged when the flag is False.

Enabling Stand Alone Mode
Open global_config/globalConfig.py and set:
STAND_ALONE_MODE = True
Set it back to False at any time to return to the standard Vizard Inspector workflow.
What Changes When Enabled
1. Blender replaces Vizard Inspector
Any action that would normally open Vizard Inspector now opens Blender instead (note: with BLENDER_EMBEDDED = True (the default) Blender will open embedded in E-Learning Lab next to the main Preview window):
| Action | Normal mode | Stand Alone mode |
|---|---|---|
| Double-click environment | Opens Inspector | Opens in Blender |
| Double-click scene object | Opens Inspector | Opens in Blender |
| Double-click avatar | Opens Inspector | Opens in Blender |
| Right-click → Edit Model | Opens Inspector | Opens in Blender |
| Preview window Edit button | Opens Inspector | Opens in Blender |

2. .glb / .gltf files are kept as primary assets
When a .glb or .gltf file is dropped into the Environments, Objects, or Avatars asset bins:
- The
.glb/.gltfis kept as the primary asset file. - A temporary
.osgbis created only to capture the thumbnail screenshot. - The
.osgbis automatically deleted ~4 seconds after the thumbnail is saved.
In normal mode the original non-OSGB file is deleted after conversion. In Stand Alone mode the original is always preserved.
3. _tag node convention for interactive objects
The Scene Objects panel (the checkboxes for Grabbable, Animate, Visible, etc.) normally scans all group nodes in the loaded Vizard scene. In Stand Alone mode it filters to only nodes whose name ends with _tag. This will also now carry over to non-stand alone mode, so scenes edited in Blender will work in the non-stand alone workflow as well.
When authoring a model in Blender, name any object you want to be interactive with a _tag suffix:
Chair_tag ← will appear in the panel as "Chair_tag", can be marked Grabbable
Lever_tag ← will appear as "Lever_tag", can be marked Animate
BackgroundWall ← will NOT appear (no _tag suffix)
Can then check "Grabbable", "Hidden" or "Animate" in the E-Learning Lab GUI

Multi-User
For the current Multi-User functionality see the file WebXR Multi-User Testing Guide below.
Authoring Workflow
Step 1 — Enable the flag
# global_config/globalConfig.py
STAND_ALONE_MODE = True
Step 2 — Prepare your .glb models in Blender
- Drag a .glb environment to the preview window
- Click "Edit" to edit your model
- Drag in additional objects directly to the embedded Blender window.
- Name any interactive objects with a
_tagsuffix (e.g.Door_tag,Button_tag). - To set a starting point, drag in the
START_POINT_SINGLE_start.glbobject from the objects bin and then use Blender's translate and rotate tools to place where you want the user to start.

- Export as
.glb(File → Export → glTF 2.0, format = GLB).
Step 3 — Import models into the E-Learning Lab
Drag .glb files from Windows Explorer onto the Environments or Objects asset bins. The app will:
1. Copy the file to Assets/Environments/ (or Assets/Objects/).
2. Capture a thumbnail (via a temporary .osgb — deleted automatically).
3. Display the model in the gallery with its thumbnail.
Step 4 — Build your sequence
Drag environments from the Environments panel, 360 Media, Videos, or Images, slideshows onto either:
- The Scenes list (left panel) — adds as a new slide, or replaces the currently-selected blank slide.
- The Preview image (center area) — same behavior.

The scene appears in the list and loads in the Vizard preview window. You can then:
- Add Audio, Prompts, Images, Video, or 3d object files to the scene as normal.
- Right-click a scene → Edit in Blender to open the .glb for further editing.
- Use the Scene Objects panel to mark _tag nodes as Grabbable, Animate, or Visible.
Step 5 — Run in WebXR
Click "Run Session" and Choose "WebXR"

Usage
The server opens the browser automatically. On a Quest headset, open the URL in the Meta Browser.
What the WebXR runner reads from your project
- 3D environment for each scene
- 360 video for scenes named with a 360 suffix (see below)
- 360 image for scenes named with a 360 suffix
- Standard videos and images (mono or stereo)
- Powerpoint slideshows
- Scene order and transition type (Key Press / Timer / Release Object)
- Grabbable, animate, visible object lists per scene
- Per-scene audio (
.wav,.mp3,.ogg) - Whole-sequence background audio (set via right-click on an audio file in the Audio panel → Set as Whole Sequence Audio)
- Session audio loop setting
- Immersive-augmented reality
360 media scenes

Scenes whose names end with a recognized suffix are rendered as immersive 360 spheres instead of (or alongside) a .glb model. The media file is looked up in the Videos/ subfolder first, then the project root as a fallback.
| Scene name suffix | Format | Description |
|---|---|---|
_360_stereo |
Top-Bottom stereo | Top half → left eye, bottom half → right eye |
_180_stereo |
Side-by-Side stereo | Left half → left eye, right half → right eye |
_360 |
Mono equirectangular | Single sphere, both eyes |
Supported file formats:
| Type | Extensions | Notes |
|---|---|---|
| Video | .mp4, .webm |
Browser-native only — .avi, .mkv, .mov are not supported and will be skipped |
| Image | .jpg, .jpeg, .png, .webp |
Always supported |
A 360 scene does not require a .glb file — the .osgb placeholder that Vizard creates is ignored by the WebXR runner. Scenes with no .glb and no recognised 360 media are skipped with a SKIP message in the console.
On desktop, stereo scenes show the left-eye view. In the headset, each eye receives the correct half of the image automatically via WebXR layer masking.
Accessing from a Quest 3 or other device

The server binds to all network interfaces and automatically launches a Cloudflare HTTPS tunnel if cloudflared is installed. The tunnel URL appears in the console a few seconds after startup — open it in the Meta Browser and tap Enter VR (note: it may take a few minutes to load larger scenes in VR mode, on test machine took around 1 1/2 minutes for a 1 GB scene).
One-time install (Windows 11 — winget is built in):
winget install cloudflare.cloudflared --accept-package-agreements --accept-source-agreements
After that, just run the server as normal. No second terminal, no account, no sign-in (press "Run Session" in the E-Learning Lab to start the session where users can connect)
If cloudflare is not installed the server still works locally — it just prints the install hint instead.
| Where you're accessing from | URL to use | VR works? |
|---|---|---|
| Same PC | http://localhost:8765 |
Yes |
| Same WiFi — flat preview only | http://192.168.x.x:8765 |
No (HTTP) |
| Quest 3 / any network | https://xxxxx.trycloudflare.com |
Yes (HTTPS) |
The tunnel URL changes each session (free tier limitation) but no account is required.
Interaction in the headset
| Input | Action |
|---|---|
| Left controller Y button | Next scene |
| Left controller X button | Previous scene |
Trigger near a _tag object |
Grab / release object |
| Left thumbstick | Move forward / back / strafe (relative to head direction) |
| Right thumbstick left / right | Snap-turn 45° left / right (turns in place) |
| Timer transition | Auto-advances after configured duration |
Desktop / phone / tablet preview (no headset required)
| Input | Action |
|---|---|
W / A / S / D |
Fly camera |
| Mouse drag | Look around |
| Touch drag (one finger) | Look around (phones and tablets) |
V |
Next scene |
C |
Previous scene |
Augmented Reality/Passthrough mode
Right click on a scene to set in AR Mode

This will then use the passthrough camera on any stand alone headset, or a phone or tablet to use augmented reality
Biopac
The E-Learning Lab can connect to a Biopac AcqKnowledge system to record physiological data (heart rate, GSR, EEG, etc.) in sync with WebXR sessions.
Check "Biopac" under the "Advanced Settings"
When a WebXR session is started with Biopac enabled, a companion script launches automatically in a separate window. See Session Synchronization below for how the companion controls session timing.
Note: Biopac recording works alongside Data Saving (see below). Both can be active at the same time.
Saving Data
The E-Learning Lab can save a timestamped CSV log for every WebXR session without requiring Biopac hardware.
Check "Data Saving" under the "Advanced Settings"
When a WebXR session starts with Data Saving, a companion script launches automatically. It creates a CSV file with the session date, start time, trial label, and any flag events recorded during the session. The CSV is saved to the project's data folder and can be opened in Excel or any data analysis tool.
There is also the option to set a label that will go into the data in the biopac_webxr_companion.py script on line 59
yield sightlab.startTrial(trialLabel="A")
See Session Synchronization below for how the companion controls session timing.
Session Synchronization
When Data Saving or Biopac is enabled, a companion script launches in a separate window on the instructor machine. It controls when the session starts and ends so that all participants and the data recording are synchronized.
Flow
- Waiting screen — As soon as the companion launches, all connected browser clients (Quest headsets, phones, tablets) show a black "Loading session…" screen. Users should click Enter VR from this screen so the headset enters VR mode while scenes download in the background.
- Ready — Once scenes have finished downloading, the screen updates to "✓ All scenes ready — Waiting for instructor to start". The instructor can now press Space.
- Start — The instructor presses Spacebar in the companion window (that window must be in focus). All clients simultaneously clear the waiting screen, the recording trial begins, and AcqKnowledge starts capturing (if Biopac is enabled).
- End — The instructor presses Spacebar again. All clients fade to a black "Session ended" screen, the trial ends, and the data file is saved.
Important: The companion window must be in focus (clicked/active) on the instructor machine for spacebar keypresses to register. If the E-Learning Lab or another window is in front, the keypress will be ignored.

T key — Sync flag / custom event
While a session is running, pressing T in the companion window inserts a synchronization marker:
- Writes a
"T key pressed"event to the CSV data file - If Biopac is active, sends a Sync Event marker to AcqKnowledge at that exact moment
This is useful as a secondary synchronization method. For example, if a participant takes noticeably longer than others to load into a scene (VR scenes can take 1–2 minutes to become fully interactive after the session starts), the instructor can press T at the moment that participant is visibly ready — giving a precise timestamp to align against in post-processing.
Scene load times in VR
Even after the session starts and the waiting screen clears, VR users may experience a short delay (typically 1–2 minutes on large sessions) before scenes are fully interactive. This is normal — the 3D assets are large files and continue streaming in the background. The T key flag can be used to mark the exact moment a participant is ready if precise per-user alignment is needed.
Converting Projects to WebXR
An existing Vizard-based project can be converted for WebXR in one step. Right-click the project in the Projects panel and choose Convert to WebXR.
This will:
- Duplicate the project as
<name>_WebXR— the original is left completely untouched - Convert every
.osgbscene file to.glbin the copy - Convert any browser-incompatible videos (
.avi,.mkv,.mov) to.mp4if FFmpeg is available - Remove the original
.osgband old video files from the copy to save space
A progress bar shows each file as it converts. When done, the _WebXR project appears in the Projects panel and is ready to run with Single WebXR.
Note: Scenes that contain 3D text labels (
osgText::Text3D) cannot be converted automatically — remove the text labels in Vizard first, then re-export.
Converting Models and Videos
Convert to glb (individual assets)
Right-click any environment or object in the asset gallery and choose Convert to glb. This converts the asset to .glb using the built-in converter and saves it alongside the original as <name>_glb. The thumbnail is copied automatically. Supported source formats: .osgb, .fbx, .obj, .dae (and .glb/.gltf are simply copied).
This is useful when you want to make a single asset available in Stand Alone / WebXR mode without converting the whole project.
Video conversion
Browser playback requires .mp4 or .webm. If FFmpeg is installed (or placed in the utils/ folder), the Convert to WebXR project action converts incompatible videos automatically. For manual conversion:
ffmpeg -i input.avi -c:v libx264 -crf 23 -preset fast -movflags +faststart output.mp4
Filtering Assets
The Objects and Avatars galleries have a Type filter dropdown in the top bar. It automatically populates with the file extensions present in the gallery (e.g. .glb, .osgb, .fbx) and lets you show only one type at a time.
In Stand Alone mode this is particularly handy if you have both .osgb and .glb versions of assets in the gallery — select .glb from the filter to see only the WebXR-ready files.
Presentation Generator
The Presentation Generator Wizard builds a complete multi-scene lesson from a topic and a set of options — no manual scene creation needed.
When stand alone mode is active it will save the slides as .glb files instead of .osgb
WebXR Multi-User Testing Guide
Overview
The E-Learning Lab WebXR server lets one or more VR headsets (or browsers) view the same
E-Learning presentation simultaneously. All connected clients share the same scene index in
real time — navigating on any client advances all others.
Prerequisites
On the PC (server)
| Requirement | Notes |
|---|---|
| Python 3.x | 3.14+ tested |
websockets package |
pip install websockets |
cryptography package |
pip install cryptography — needed for HTTPS/WSS |
cloudflare (optional) |
Only for single-user internet access; not needed for LAN |
| Stand Alone device on the same Wi-Fi | Both devices must be on the same local network |
Blender (or a 3D editor for .glb models) |
Blender has the best integrated support — download Blender |
First run only: When the HTTPS certificate is generated, Windows will show a UAC
security dialog asking you to allowcertutilto add a trusted root certificate.
You must approve this for the PC browser to trust the connection without warnings.
This is a one-time step — the certificate is cached in%TEMP%\webxr_ssl\.
Starting the Server
From the E-Learning Lab GUI
- Open
E_Learning_Lab.py - Load a project
- Click Run Session → WebXR
- The PC browser opens automatically at
https://localhost:8765
What the server prints at startup
═══════════════════════════════════════════════════
E-Learning Lab WebXR Server
Project : MyProjectName
Local : https://localhost:8765/
LAN : https://192.168.0.100:8765/
WS sync : wss://192.168.0.100:8766
═══════════════════════════════════════════════════
The LAN URL is what the Quest 3 needs. Note it down.
Connecting a Quest 3
Network requirement
The Quest 3 must be on the same Wi-Fi network as the PC running the server.
Do not use Cloudflare for multi-user — see Limitations.
Steps
- Put on the Quest 3.
- Open Meta Browser (the built-in browser, not a sideloaded one).
- Navigate to the LAN URL printed at server startup, e.g.:
https://192.168.0.100:8765
- Meta Browser will show a certificate warning:
"Your connection is not private" /
ERR_CERT_AUTHORITY_INVALID
This is expected — the certificate is self-signed. Tap Advanced and then
"Proceed to 192.168.0.100 (unsafe)" to bypass it.
- The E-Learning Lab page should load. Tap Enter VR.
Note: The first time you connect from Quest 3, the page may not load immediately
after tapping "Proceed." If you see a blank page or it times out, do a full page
reload (reload icon in Meta Browser). In testing, the page loaded successfully after
one reload (after waiting around 5 minutes). Subsequent connections to the same server (same cert) should be faster.
Controls
PC / Desktop Browser
| Action | Control |
|---|---|
| Next scene | Click Next ▶ button, or press V |
| Prev scene | Click ◀ Prev button, or press C |
Quest 3 VR Controller
| Action | Control |
|---|---|
| Next scene | Y button (left controller) |
| Prev scene | X button (left controller) |
| Move forward/back | Left thumbstick |
| Strafe left/right | Left thumbstick |
| Snap turn | Right thumbstick left/right |
| Grab object | Right trigger (for Release Object transitions) |
Multi-User Sync Behavior
What syncs
- Scene index — every client jumps to the same scene in real time
- Release Object transitions — server-authoritative; whichever client releases the
object, all clients advance together (no double-advancing) - Session state — waiting / started / stopped overlay shown on all clients
- Video play/pause
- Audio state
What does NOT currently sync
- User presence / position (planned for Phase 3)
Connection flow
- Client opens the page → WebSocket connects to port 8766
- Server immediately sends a
state_snapshot— client jumps to the current scene - From then on, any scene change from any client broadcasts to all others in ~50–100 ms
- If the WS drops, the client auto-reconnects within 3 seconds and re-syncs
- If running with INSTRUCTOR_LED then only the Instructor can initiate scene changes
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
PC browser shows ERR_CONNECTION_RESET |
Server not running or still starting | Wait a moment (or a few minutes), refresh |
| PC browser shows cert warning | certutil step failed or was denied |
Run certutil -addstore -user Root <cert.pem> manually |
Quest 3 shows ERR_CERT_AUTHORITY_INVALID |
Expected on first connect | Tap Advanced → Proceed, then reload the page |
| Quest 3 shows "WebXR Unavailable" | HTTP (not HTTPS) URL used | Use the https:// LAN URL — plain HTTP can't use WebXR |
| Quest 3 controller changes slides locally but PC doesn't follow | WS connection dropped | Check WS port 8766 is open in Windows Firewall; wait for auto-reconnect |
ImportError: No module named 'websockets' |
Package not installed | pip install websockets |
ImportError: No module named 'cryptography' |
Package not installed | pip install cryptography |
| Firewall prompt appeared but rules weren't added | netsh needs admin rights |
Approve the UAC dialog for firewall rule creation |
Known Limitations
Cloudflare tunnel — single-user only
When USE_CLOUDFLARE = True in globalConfig.py, a trycloudflare.com HTTPS tunnel is
started for internet access. However:
- The tunnel only exposes port 8765 (HTTP/WebXR content)
- Port 8766 (WebSocket sync) is NOT reachable through the tunnel
- This means Cloudflare mode supports only one viewer at a time
For multi-user use, both headsets must be on the same LAN as the PC.
Self-signed certificate on Quest 3
- The certificate is trusted automatically on the PC (via
certutil) - On Quest 3, Meta Browser requires a manual bypass each time the server IP changes
(if you move to a different network or the PC gets a new DHCP address) - The bypass is per-server-cert, not per-session — once bypassed, Meta Browser remembers
it for subsequent visits to the samehttps://192.168.x.x:8765address
LAN-only sync
Multi-user WS sync only works on the same local network. Remote participants via
Cloudflare get single-user view only.
Session control modes
Set in globalConfig.py:
INSTRUCTOR_LED— only the instructor tab can navigate; student headsets followALL_CLIENT_CONTROL— any headset can navigate (current behavior)
Put in network address with ?role=instructor
Next Steps
Route WS through port 8765 (HTTP Upgrade) for Cloudflare multi-user support
Currently the WS server runs on a separate port 8766 that Cloudflare can't proxy.
Moving WS to share port 8765 via the HTTP Upgrade: websocket handshake would make
multi-user sync work through the Cloudflare tunnel for remote participants.
Auto-install missing packages
If websockets or cryptography aren't installed, the server currently crashes with
an ImportError. Add a subprocess.check_call([sys.executable, '-m', 'pip', 'install',
...]) bootstrap so the first run self-installs them.
Presence (Phase 3) — Show how many users are connected in the HUD; optionally
render a simple avatar sphere per remote user at their approximate head position.
Cloudflare WS keepalive — Cloudflare's tunnel has a 100s idle timeout on HTTP
connections; the 15s WS ping keeps it alive for the HTTP server but WS on 8766 goes
through a separate path. Merging ports would solve this at the same time as item 1.
AR mode multi-user — --ar flag works for single-user passthrough; multi-user
AR hasn't been tested and may need additional anchor sync.
Things worth adding in the future
- mDNS/Bonjour discovery — Auto-discovers the server on
the LAN so users never have to type an IP address. This would eliminate the manual
"note down the LAN URL" step entirely.
Limitations & Planned Next Steps
- 360 video formats — Only
.mp4and.webmplay in browsers. Existing.aviand.mkvfiles (e.g. from older Vizard projects) must be converted to.mp4(e.g. with ffmpeg) for WebXR playback. The Vizard runtime is unaffected and continues to use the original formats. .osgbscenes — Standard (non-Stand Alone) scenes in.osgbformat are not loadable by Three.js and will be skipped by the WebXR runner. Only.glb/.gltfscenes and recognised 360 media are served.- Multi-user networking — Still needs avatars and requires being on same LAN
- Quiz UI — Not yet implemented in the WebXR runner.
- AI Agents - Not yet implemented
- Screencasting - Not yet implemented
- Data Saving - Limited and currently can synchronize with biopac, add labels, time stamp and flags (no eye tracking or position data)