The MirroredSpace
2025
The MirroredSpace is a VR classroom prototype for the Meta Quest 3, built as a standalone application on a source build of Meta's Unreal Engine 5 fork. I was the only person working on it: engine and gameplay C++, shaders, environment modelling and texturing, and the packaged APK.
It was built to answer one question before anything else got built. A remote student in a hybrid class is a second-class participant — a flat window into a room they cannot act in. The obvious fix is to put them in the room, but that only works if the core interaction is good enough to be worth being there live for. So the prototype targets the hardest part first: writing on a whiteboard with a tracked controller at quality high enough to justify a collaborative system around.
Whiteboard rendering
The whiteboards are rendered from signed distance fields rather than as painted textures, so strokes stay sharp at any viewing distance — which matters in VR, where a student can walk up to within a few inches of the board.
- Strokes are stamped into a distance field in a compute shader, then evaluated in a pixel shader at render time. Both are hand-written HLSL.
- Stroke data is stored as coordinates rather than pixels, which keeps boards cheap to transmit and lets a late-joining participant reconstruct the full board state.
Interaction and haptics
Drawing on a virtual surface with a tracked controller is harder than it looks, because there is no physical stop when the pen meets the board.
- Pen-down and pen-up thresholds are decoupled with hysteresis, so tracking jitter near the board surface cannot produce a rapid stutter of broken strokes.
- Haptic intensity is scaled by pen depth into the board surface, giving a usable substitute for the missing physical resistance.
- Stroke drawing is interpolated between controller positions to create smoother continuous lines.
Performance work on Quest 3
Standalone Quest is a fixed hardware budget. Profiling used Unreal Insights, RenderDoc, and ovrgpuprofiler.
- Traced a persistent frame cost to a synchronous GPU completion fence wait — the CPU blocking on GPU work that did not need to be waited on inline.
- Traced a periodic hitch to garbage collection running inside the world tick, which showed up as a regular stall rather than a steady cost.
- Environment lighting is fully baked, keeping per-frame lighting cost off the mobile GPU entirely.
Environment and locomotion
- The classroom is modelled, textured, and UV-mapped in Maya. I based it on a real chemistry teaching classroom, down to the table layout and board placement and other measurements. Familiarity was deliberate: the room should read as somewhere a student has already been.
- Locomotion uses teleport movement and snap turning, converted from Unreal's Blueprint VR template into C++.
Networking architecture (designed, not implemented)
The multi-user layer was specified but never built — development stopped first. Recording the design here to show my reasoning.
The plan was to transmit intent rather than imagery: head and hand pose transforms, whiteboard stroke coordinates, and voice, with no video encoding anywhere in the pipeline. Because strokes are already stored as coordinates, the whiteboard is nearly free to replicate, and a late joiner can rebuild full board state from the stroke list rather than needing a keyframe.
The estimated saving was roughly an order of magnitude against video conferencing for a thirty-participant session. The more important property was not bandwidth: transmitting poses means every participant sees correct relative geometry — where someone is looking and what they are pointing at — which a grid of video tiles cannot represent at any bitrate.
Outcome
I put the prototype in front of instructors and students through a university design-thinking course. The feedback contradicted the premise: the students who were supposed to benefit most preferred the flat video call, largely because a headset is a barrier to casual attendance in a way a browser tab is not. I also found that for remote learners asynchronous learning is preferred, not being asked to attend a live class at a fixed time. I stopped development rather than continue building the collaborative layer against that finding.
The rendering and interaction work stands on its own.