xeolabs bio photo


Lindsay Kay

3D software engineer / WebGL developer

Twitter LinkedIn Github

My chapter on SceneJS for OpenGL Insights is now free to download!

Creating WebGL Demos for zSpace using xeogl


zSpace is a technology firm based in Sunnyvale, California that creates mixed reality systems that combine elements of virtual and augmented reality in a specially-built computer system, which has a quad-buffered stereo display that works with IR-tracked active-shuttered glasses and a stylus.

xeogl is an open-source WebGL-based 3D engine that I’ve been working on for the past year or so.

Last year, zSpace loaned me one of their zSpace 300 displays so that I could put together some xeogl demos for its beta WebVR support. If you were at GDC 2017, you might have seen those demos running at the Khronos booth.

These demos were my first foray into WebVR and were lots of fun to make. If you happen to be using a zSpace 300 right now, go ahead and click these thumbnails to run them:

Simple geometries Gearbox loaded from glTF Saw loaded from glTF

zSpace support in xeogl

To support zSpace, I added two new component types to xeogl:

  • ZSpaceEffect - renders its scene in quad-buffered stereo while updating the viewing and projection transforms off the zSpace WebVR device events.
  • ZSpaceStylusControl - an input control to select and drag entities with the stylus.

Let’s take a quick look at how these components are used.

First, I’ll set up a 3D scene by importing a glTF model into xeogl, using a GLTFModel component:

 var model = new xeogl.GLTFModel({
    src: "models/gltf/2.0/Reciprocating_Saw/PBR-SpecGloss/Reciprocating_Saw.gltf",
    position: [1, 0, 0],
    rotation: [90, 0, 0]    

Our model is the red reciprocating saw shown in the third thumbnail above. I’ve also attached a Rotate to our GLTFModel, just to tip it upright so that we can see it more clearly.

Now, to view the model on the zSpace display, I’ll simply create a ZSpaceEffect:

 var zspaceEffect = new ZSpaceEffect();

That activates immediately, rendering whatever we have in our scene in quad-buffered stereo. At this point, we can put on our stereo glasses and see the model rendering in 3D.

Detecting zSpace support

The ZSpaceEffect will fire a “supported” event once it has determined whether or not your browser is running on a zSpace viewer:

 zspaceEffect.on("supported", function (supported) {
     if (!supported) { 
         this.error("This computer is not a ZSpace viewer! Inconceivable.");           

Stylus tracking

Now let’s track the World-space position and direction of the zSpace stylus. Also, whenever we press button 0 on the stylus while intersecting an entity, we’ll ray-pick the entity with the stylus.

 zspaceEffect.on("stylusPos", function(pos) { 
     console.log("Stylus position: " + pos);
 zspaceEffect.on("stylusDir", function(dir) { 
     console.log("Stylus direction: " + dir);
 zspaceEffect.on("stylusButton0", function(down) { 
     if (down) {
         var hit = zspaceEffect.scene.pick({
             pickSurface: true,
             origin: zspaceEffect.stylusPos,
             direction: zspaceEffect.stylusDir
         console.log("Entity selected with stylus: " + hit.entity.id);

note that we can also just poll the ZSpaceEffect at any time for the current state of the stylus:

 // Get stylus position
 var stylusPos = zspaceEffect.stylusPos;
 var stylusDir = zspaceEffect.stylusDir;
 // Get button states
 var button0 = zspaceEffect.stylusButton0; // Boolean
 var button1 = zspaceEffect.stylusButton1;
 var button2 = zspaceEffect.stylusButton2;


In xeogl we’d normally wrap your input controls in reusable components, so I added the ZSpaceStylusControl as an boilerplate/example on which you might base your own zSpace stylus control.

This component:

  • hooks into the ZSpaceEffect events we just saw,
  • renders a 3D line segment that appears to extend from the tip of the stylus,
  • grabs intersecting entities when you push button 0, and
  • drags grabbed entities around while button 0 is down.

To use it, all we need to do is add it to our scene:

 var zspaceStylusControl = new xeogl.ZSpaceStylusControl();

At this point we can see a ray extending from the tip of our stylus, with which we can select and drag entities.


zSpace’s support for WebVR is still in beta, so xeogl’s support for zSpace is also in beta. Therefore, I’ll be changing the implementation of these xeogl components moving forwards, but their API should remain the same. Stay tuned!