Also available in: Deutsch.
In this post I’m going to describe the development (including publication of full source code, 1 file with approx. 70 SLOC) of a low-level Interface for retrieving raw, unprocessed sensor data from a SpaceMouse / SpaceNavigator in a browser environment (via JavaScript).

Table of contents:
- Intro
- Failure of support from the vendor
- Enter Wireshark
- Enter WebUSB
- development environment
- Enter WebHID
- Coordinate system
- Hardware
- Source code
- Known limitations
Also see my follow-up post where I’m presenting (including full source code) a template 6DoF driver of type «camera control» based on Three.js v.140
Intro
Whoever may have been following this blog (probably none of you, because this is the 1st post I’m also making available in an English version), may have noticed how much I enjoy 2D/3D interactive computer graphics. See some typical examples of my passion here Interactive 3D Desmo-Animation and here Interactive 3D motorcycle rig simulator.
Both my Ducati Monster as well as my Ural sidecar have reached a close-to-final stage in terms of modifications which I deemed necessary for reasons of technical or visual enhancement. Besides, after moving here (Appenzell Innerrhoden), I was deprived of my lathing machine in the process so I’m more or less hampered in producing my own mechanical parts for any future modifications.
On the other hand this gave me leeway to turn to an old and neglected passion of mine: the development of interactive 3D graphics, even beyond the realm of biking. As a tool I’m leaning on Three.js, a JavaScript library which facilitates the creation and publishing of platform-independent interactive 3D visualisations. Regarding three.js, I have by now advanced from being e mere user of the library to being a contributor: proposals for modification, which I submitted as a «pull request», have been approved by the development team and are from now on part of all future versions of three.js. But that’s another story …
Anyhow, in this context it was somehow obvious, that I would remember my trusty 15-year old SpaceMouse (which at the time of purchase had been marketed as «SpaceNavigator») and which had slowly been collecting dust in the back end of one of the drawers of my desk. I decided, to revive this device and use it furtheron as an input device for my future animations.
This is how a quite unusual journey began.
1. Stop – failure of support from the vendor
Since SpaceNavigator and SpaceMouse are comparatively unusual computer input devices that are not supported by operating systems by default, special drivers are needed to make the data of these devices accessible to an application. The first step in my search for an up-to-date driver led me to the supplier of the now again renamed SpaceMouse input devices, the company 3Dconnexion. This company maintains its own software developer program and also its own forum. After registering for both and first downloading and viewing all SDKs for the Windows, MacOS and Linux platforms, I finally found a «Web» subdirectory in the Windows SDK.
I will be as brief as possible at this point so as not to be too unkind:
What 3Dconnexion offers here as software support, even SDK for use in a web environment under JavaScript, defies every description.
A insanely complex protocol stack, with WebSockets, its own proxy server (with its own root certificate and key), cryptography library, its own (closed source) «Navlib», which communicates with an application layer that is at least documented to a potential application programmer. The current 3DxWare, which has to be installed on each computer, occupies about 63 MB of memory and starts a full five background processes:

Attached was an example program based on Three.js in version 71 (currently Three.js is at version 139) which had obviously been derelict since several years , based on the above protocol stack and the overlying abstraction layer. This layer even requires(!) supremacy over the Three.js cameraMatrixWorld: the potential user must(!) provide information about the camera.position, the camera.lookAt(), the camera.fov, the bounding box of the geometries in the Three.js scene and other information, from which the so-called «driver» then calculates a suitable cameraMatrixWorld for Three.js. In addition, specifications for a «construction plane», a standard viewing direction, etc.. This is all information which is none of a driver’s effing business. This is unbearable, and no serious Three.js developer will let himself be forced into such a tight corset if at any cost can be avoided.
My question to one of the software clerks in the software support forum, if there is no possibility to pass the raw sensor data of the SpaceMouse unprocessed through the protocol stack and make it available to software developers, was unfortunately always declined, even after intensive and heated discussions.
Even my pointing out that Three.js now supports more than 100,000 websites, with a foreseeable multiple of visitors, and that this represented a huge reservoir of potential new customers for 3Dconnexion, but which would not be tapped into without adequate support for software developers, did not change the outcome.
Now I basically don’t care whether 3Dconnexion exploits its market potential or not. But what I do care about is that a fantastically intuitive input device should now no longer be usable to my liking in a web environment. E.g. as I had already come to know and appreciate it in my earlier Linux-based applications 15 years ago.
The inventors of the term bloatware seem to have had exactly such software packages in mind, which have a senselessly inflated range of functions and resource consumption, and no longer provide core functionality, or obfuscate required information to the maximum. To use the term «overengineering» for this would, in my opinion, cross the line into insulting real engineers.
The German version of the above linked wikipedia article on bloatware aptly states:
Bloatware entsteht in der Regel aus Marketinggründen oder – auch angeblichen – Anwenderwünschen.
Bloatware is typically generated through marketing demands or – even only alleged – customer demands. (Translation: mine)
Wikipedia
Exactly this reasoning had been given to me by the clerk in the Software Developer Forum: the SDK is so complex because otherwise, when providing the raw sensor data, a developer would have to develop «the entirety of navigation features customers have come to expect» himself.
It finally became clear to me that no help could be expected from 3Dconnexion. I decided to take matters into my own hands and help myself if possible.
2. Stop – Enter Wireshark
I wanted to start from scratch before any driver might corrupt or hide data. My SpaceNavigator is a wired USB device and I knew that this device could actually provide the data I wanted. The data I want to read and process is first fed into the Universal Serial Bus. The tool of choice for eavesdropping on data logs by a dedicated hacker is called Wireshark. Wireshark is an OpenSource program which is also available in a version for macOS. I installed it. Originally intended for log analysis of network traffic, Wireshark can now also eavesdrop on USB. You need the «interface» XHC20 under macOS.
My development machines are an iMac from 2014 and a MacBook Air from 2017, both running macOS Catalina (10.15.7). For security reasons (I should get to read this more often in the further course of my adventure…) the XHC20 interface is disabled by default in macOS Catalina. But there is a trick how to enable it again. I followed this instruction in the Apple developer forum. Following the warning, I took my MacBook Air for this experiment, which seems to have survived the procedure and its undoing without any damage so far. This was my first Wireshark session, and I was really impressed by the amount of information and its structured presentation provided by Wireshark! 😎 :

The vertically tripartite screen offers a list with key data of the recorded telegrams in the upper panel. Newer telegrams are appended at the bottom. It may be possible to configure this sorting direction …
In the middle panel details of that telegram are displayed, which is marked in the upper panel. As far as possible, the data are preceded by «speaking» labels, presumably specified names of data fields.
In the lowest panel the hex dump of the telegram marked above is displayed. This is then really «raw». That’s exactly what I wanted.
In the screenshot above I show a snippet of the traffic triggered by plugging the USB plug into my USB hub. I call this the «plug event». The highlighted line in the upper panel is the response of my SpaceNavigator to the immediately preceding request of the USB to the new device, immediately after I had plugged the USB cable of my SpaceNavigator into the socket of my USB hub: Who are you? and: What are your capabilities?
In its response, my SpaceNavigator introduces itself nicely: among other things, that it comes from the vendor with idVendor 0x046d (Logitech, Inc., marked green), and has an idProduct of 0xc626 (3Dconnexion SpaceNavigator 3D Mouse, marked blue). In the hex dump you can also see that we are dealing with a «Little Endian» device, so the least significant byte is listed before the most significant byte.
Since I didn’t want to extend or repeat the «System Integrity Protection suspended» state for my MacBook Air for an unnecessarily long time, I came up with a set of possible situations, a so-called «scenario», which covers a typical user profile of a SpaceMouse or SpaceNavigator user. My SpaceNavigator is a very basic device, which focuses on providing 6DoF data. In addition, it features two side-mounted buttons, as well as a blue illuminated ring surrounding the puck. Newer devices at the higher end of the 3Dconnexion product spectrum are almost predominantly marketed as programmable keyboards, with 6DoF virtually only as a collateral benefit. Not my kind of thing.
These are the use cases I came up with for my purist SpaceMouse:
- Device plug event (device plugged)
- Device unplug event (device unplugged)
- Key(s) pressed/released
- LED turned on/of
- Translation event
- Rotation event
For each of these use cases I logged (as purely as possible) the resulting telegram exchange and saved it as a separate file. Simultaneous telegram traffic of other devices connected to the same USB can be filtered out in Wireshark. The telegram recordings saved in separate files allow the subsequent offline analysis of the telegram traffic, with System Integrity Protection again reactivated.
So the above screenshot of one telegram out of a series of telegrams triggered by connecting the device to the bus is only one example out of several. Key presses or releases also trigger similar, though less extended, information exchange via the USB. So do deflections of the SpaceMouse’s «puck» from its static center position. Here, for the first time, I could already see the very bytes that I’m ultimately interested in whizzing across the screen . What spurred my motivation to bite through here again clearly 😎
This brings me from analysis to synthesis: how can the knowledge gained about traffic be exploited in practice?
3. Stop – Enter WebUSB
My further research soon led me to the WebUSB API. An interface for making USB devices available in a web context, i.e. using JavaScript. That sounded promising!
I took the example code of one of the developers of WebUSB as a starting point for my attempts. The first lines of this reference implementation worked fine, but quite soon you have to «claim» a selected and opened device, i.e.: claim exclusive access to it programmatically:
device.claimInterface(2)) // Request exclusive control over interface #2.
This call always triggered an error message in the JavaScript Console of my browser:
DOMException: The requested interface implements a protected class
This I puzzled me for quite a while, until I finally had the idea to enter the complete error message (enclosed in double quotation marks) into a search engine.
The answer was found, as so often, on stackoverflow.com : it turned out that the developers of WebUSB had deliberately, «for security reasons», prevented the «claiming» of devices of certain device classes. Unfortunately, this also includes the device class HID, human interface device, to which the SpaceMouse also belongs.
So, unfortunately, this hopeful approach turned out to be a dead end. After all, the developer who had propagated the withdrawn support for the HID device class referred to the dedicated API WebHID, which would in principle offer the same functionality, just limited to HID class devices.
Interlude: development environment
A closer look into the specification of the WebHID API showed me that central functions would only be available in a «secure context«, i.e. only under the https protocol.
Now I need to backtrack a bit to make the implications of this requirement understandable:
Every web publishing project involves development, i.e. writing, testing, correcting text files. In the simplest case, these are pure HTML files. These can «reference» other text files, e.g. include JavaScript files using <script> </script> tags.
After the development is completed, the developer uploads these files to the usually rented web space at his provider, from where they are played out via the web server running at the provider to the browser of a website visitor and displayed there. Often using the http://
scheme. In principle, this case would look like this in the URL input line of a website visitor’s browser (this is only a non(!) working example):

Now, the software development process is usually a highly iterative one: software changes (e.g. changes to .html or .js files) are made, then tested, then the deviations between the desired and the currently realized result are evaluated, resulting in new changes to the source files. Which heralds the next iteration in the development cycle.
Since the repeated uploading of half-finished development results to the publicly visible web space inhibits the interactive development flow and, on the other hand, one does not want to expose oneself to the whole world while bumbling around, or to be disturbed by random website visitors, developers like to resort to the simplification of testing a file from the local computer directly in their own browser during software development for the web. In the browser URL input line it looks like this, for example:

For convenience, that had been my only method of development so far.
As temptingly simple and direct as this access is, it also offers disadvantages, e.g. for only slightly more complex projects in which, for example, a main file present on the local computer wants to access resources on the Internet. Mixing file:///
and http://
schemes reliably introduces annoying CORS problems – another security mechanism of today’s browsers.
These problems can also be circumvented by installing your own web server on your local machine and then using the http://
scheme to access local files. I decided to install the lightweight web server lighttpd for this purpose, as also recommended by the Three.js project. Installation and minimal configuration worked without any problems thanks to the linked quick guides and were completed after ten minutes at most. After starting the lighttpd webserver, I could now access my local file via http://
:

This is the related and complete web server configuration file lighttpd.conf
:
server.document-root = "/Users/chris/Desktop/Vielzutun/SpaceNavigator/Web/"
server.port = 3000
mimetype.assign = (
".html" => "text/html",
".txt" => "text/plain",
".jpg" => "image/jpeg",
".png" => "image/png"
)
Whew!!! Now just take a breather and enjoy the achieved state! 😎
In the final stage of development, the aim is to be able to access a local file via the local web server using
in order to provide the «secure context» required in the further course of development.https://
There are several methods described on the Internet how to do this. I decided to use the mkcert
method.
mkcert
is a tool that simulates a Certification Authority that can be used on the local computer. Its functionality is described very clearly in the following graphic:

After correct installation of mkcert
, the following entry can be found in the local «keychain management» on macOS:

Now only the configuration of the webserver lighttpd
has to be adjusted. Here is my complete lighttpd.conf
, with which I finally have the desired «secure context»:
server.document-root = "/Users/chris/Desktop/Vielzutun/SpaceNavigator/Web/"
server.modules += ( "mod_openssl" )
$SERVER["socket"] == ":443" {
ssl.engine = "enable"
ssl.pemfile = "/Users/chris/Desktop/Vielzutun/SpaceNavigator/Web/mylocalhost.pem"
ssl.ca-file = "/Users/chris/Library/Application Support/mkcert/rootCA.pem"
}
mimetype.assign = (
".html" => "text/html",
".txt" => "text/plain",
".jpg" => "image/jpeg",
".png" => "image/png"
)
Of course, the paths must be adapted to the conditions of the respective developer machine
After a restart of lighttpd
the goal is reached, the long sought-after lock symbol as a sign of a successful access via https://
:

I have described these steps in such detail because on the one hand they were new to me (and correspondingly time-consuming to find out) and on the other hand because every developer who wants to use my results for himself will not be able to avoid following them on his computer as well. Without https://
the further progress of development will not work in any case, even if other ways for the activation of https://
can or have been taken.
Now that the backpack is completely packed, let’s go to
4. Stop – Enter WebHID
The WebHID developers also provide code snippets that demonstrate the correct use of this API. I used these as a basis for my new attempts:
let deviceFilter = { vendorId: 0x046d };
let requestParams = { filters: [deviceFilter] };
let outputReportId = 0x01;
let outputReport = new Uint8Array([42]);
function handleConnectedDevice(e) {
console.log("Device connected: " + e.device.productName);
}
function handleDisconnectedDevice(e) {
console.log("Device disconnected: " + e.device.productName);
}
function handleInputReport(e) {
console.log(e.device.productName + ": got input report " + e.reportId);
console.log(new Uint8Array(e.data.buffer));
}
navigator.hid.addEventListener("connect", handleConnectedDevice);
navigator.hid.addEventListener("disconnect", handleDisconnectedDevice);
navigator.hid.requestDevice(requestParams).then((devices) => {
if (devices.length == 0) return;
devices[0].open().then(() => {
console.log("Opened device: " + device.productName);
device.addEventListener("inputreport", handleInputReport);
device.sendReport(outputReportId, outputReport).then(() => {
console.log("Sent output report " + outputReportId);
});
});
});
Source: https://github.com/WICG/webhid/blob/main/EXPLAINER.md
First possible pitfall: the vendorID
, as a possible filter parameter when calling:
navigator.hid.requestDevice({ filters: [{ vendorId: 0x046d }] )
Even though my SpaceNavigator is clearly labeled as a «3Dconnexion» product (see introductory photo), this device identifies itself as coming from «Logitech Inc.» with a vendorID of 0x046d
. The adhesive label as a type plate replacement on the device’s connection cable, and especially the macOS «system information» provide unambiguous information here:


Since the mother of all space mice, the SpaceMouse Classic is a development of DLR and the rights to its marketing have apparently changed several times over the decades, the possibility should be kept in mind that batches of such devices could still be in circulation that could identify themselves with a different vendorID! Since I have only my own, approx. 15 years old copy of a SpaceMouse /SpaceNavigator available, I can neither confirm nor exclude this assumption.
If you have passed the correct vendorID as a filter parameter,
navigator.hid.requestDevice({ filters: [{ vendorId: 0x046d }] })
.then((devices) => {
if (devices.length == 0) return;
device = devices[0]
if (!device.opened) device.open() // avoid re-opening an already open device
.then(() => {
console.log("Opened device: " + device.productName);
device.addEventListener("inputreport", handleInputReport);
})
.catch(error => { console.error(error)
})
});
then a compatible(!) browser responds with a browser-generated selection dialog in which the user selects a device for use «for security reasons» with an active user gesture (click, touch):

By clicking on the «Connect» (Verbinden) button the device is opened and an EventListener for events of the type «inputreport» is installed.
At this point a remark on the nomenclature:
A Universal Serial Bus (USB) is a communication channel controlled by the host (the computer end). All data exchange over this bus is performed by the host. Connected devices that need communication at irregular and unpredictable intervals can «indicate» this to the bus, in the sense of «I’ve got something, can you come and get it when you get a chance?» So the device cannot «pump» data into the bus at arbitrary times, but the bus pulls in data from the device when it has time to take care of it.
Against this background, from the point of view of the bus, «inputreport» denotes a telegram from the device to the host, for example as a result of a mouse movement or a keystroke, while an «outputreport» denotes a data traffic from the host to the device, for example: «switch the LED on/off».
The installed device handler for «inputreport» is already logging data into the JavaScript console of the browser when you press keys and/or move the puck. These still have to be interpreted correctly, which I managed by a combination of «closely looking», «filter out already understood messages» and «trial&error».
Crucial for the understanding of this byte salad was the realization that translation and rotation are provided as different reports, with a report length of 6 bytes each. The assumption that for each axis a signed 2-byte value had to be considered seemed obvious. And so it was.
Key events were quickly deciphered. Because they can be produced easily and predictably in «pure» form.
For toggling the LED I had to experiment. Once, because this was the first outputReport
so far, so I could not observe it in the wild. Fortunately I had the Wireshark telegrams, which hinted at ReportId = 4 for LED (green text background):

Also there were error messages when using invalid reportIds
, and the assumption to be able to toggle the LED on or off with a transmitted value of «1» or «0» was no more than an educated guess. Which quickly proved to be true.
With this I consider my specific problem: «read raw sensor data of a SpaceNavigator / SpaceMouse using JavaScript» as completely resolved.
Coordinate system
SpaceMouse and SpaceNavigator rest on the horizontal desk surface with their heavy, rubberized base. Relative to this, the user moves the spring-mounted puck, which is slightly movable with respect to all six degrees of freedom. The deflection of the puck is evaluated by the device as translation or rotation with respect to each of the three axes and transferred to the bus as a signed integer value.
I observed the following coordinate system and associated value ranges during my tests (lettering «3Dconnexion» facing the user, USB cable leading away from the user):
- Move puck left – right: Tx = [-340 .. +430]
- Move puck away from – towards user: Ty = [-430 .. +430]
- Pull puck out of desk – push into desk: Tz = [-410 .. +430]
- Tilt puck (top edge) forward – backward: Rx = [-350 .. +370]
- Tilt puck (top edge) right – left: Ry = [-340 .. +330]
- Twist puck (as seen from above) CCW – CW: Rz = [-430 .. 370]

Hardware Availability
The described device is still available under its current name «SpaceMouse Compact» as new goods in 3Dconnexion’s current offer. Price is in the range of around CHF/€ 150.- for new, and significantly cheaper in pre-owned condition, e.g. on Ebay.
Source code
If you have a SpaceMouse or a SpaceNavigator at your disposal but are afraid of the stress of setting up your own webserver incl. secure context (https) just to try it out, I have uploaded the source code provided below to my webspace, from where it can be accessed directly via the following URL, without modification of your own computer. Requires Google Chrome in a version >= 100 . The required secure context (https) is provided directly from my website when accessing the URL below:
https://vielzutun.ch/wordpress/public/WebHID/WebHID.html
Below is the complete source code for communicating with a SpaceMouse / SpaceNavigator in a JavaScript context.
<!DOCTYPE html>
<html lang="en">
<head>
<title>WebHID Playground</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
</head>
<body>
<div id="info">
WebHID Playground by <a href="https://vielzutun.ch" target="_blank" rel="noopener">vielzutun.ch</a> <br/>
</div>
<button onclick="selectDevice()">Request HID Device</button>
<button onclick="ledOn()">LED On</button>
<button onclick="ledOff()">LED Off</button>
<script>
let device;
navigator.hid.addEventListener("connect", handleConnectedDevice);
navigator.hid.addEventListener("disconnect", handleDisconnectedDevice);
function handleConnectedDevice(e) {
console.log("Device connected: " + e.device.productName);
}
function handleDisconnectedDevice(e) {
console.log("Device disconnected: " + e.device.productName);
console.dir(e);
}
function selectDevice() {
navigator.hid.requestDevice({ filters: [{ vendorId: 0x046d }] })
.then((devices) => {
if (devices.length == 0) return;
device = devices[0]
if (!device.opened) device.open() // avoid re-opening an already open device
.then(() => {
console.log("Opened device: " + device.productName);
device.addEventListener("inputreport", handleInputReport);
})
.catch(error => { console.error(error)
})
});
}
function handleInputReport(e) {
switch ( e.reportId ) {
case 1: // translation event
const Tx = e.data.getInt16(0, true); // 'true' parameter is for little endian data
const Ty = e.data.getInt16(2, true);
const Tz = e.data.getInt16(4, true);
console.log("Tx: " + Tx + ", Ty: " + Ty + ", Tz: " + Tz);
break;
case 2: // rotation event
const Rx = e.data.getInt16(0, true);
const Ry = e.data.getInt16(2, true);
const Rz = e.data.getInt16(4, true);
console.log("Rx: " + Rx + ", Ry: " + Ry + ", Rz: " + Rz);
break;
case 3: // key press/release event
const value = e.data.getUint8(0);
/*
For my SpaceNavigator, a device having two (2) keys only:
value is a 2-bit bitmask, allowing 4 key-states:
value = 0: no keys pressed
value = 1: left key pressed
value = 2: right key pressed
value = 3: both keys pressed
*/
console.log("Left key " + ((value & 1) ? "pressed," : "released,") + " Right key " + ((value & 2) ? "pressed, " : "released;"));
break;
default: // just in case a device exhibits unexpected capabilities 8-)
console.log(e.device.productName + ": Received UNEXPECTED input report " + e.reportId);
console.log(new Uint8Array(e.data.buffer));
}
}
function ledOn() {
const outputReportId = 4;
const outputReport = Uint8Array.from([1]);
device.sendReport(outputReportId, outputReport)
.then(() => {
console.log("Sent output report " + outputReportId + ": " + outputReport);
})
.catch(error => { console.error(error)
})
}
function ledOff() {
const outputReportId = 4;
const outputReport = Uint8Array.from([0]);
device.sendReport(outputReportId, outputReport)
.then(() => {
console.log("Sent output report " + outputReportId + ": " + outputReport);
})
.catch(error => { console.error(error)
})
}
</script>
</body>
</html>
The decoded data is displayed in the following human-readable format in the JavaScript console only:
Tx: -28, Ty: 71, Tz: -14
Rx: -256, Ry: 190, Rz: -248
Left key pressed, Right key released;
Left key pressed, Right key pressed,
Left key released, Right key pressed,
Left key released, Right key released;
Sent output report 4: 1
Sent output report 4: 0
Since at the beginning I had been mocking the absurd complexity of the sample code provided by 3Dconnexion, I have kept myself as concise as possible here in order to maximize contrast. A minimal error handling is available, but does not include any higher-order protection like e.g. locking against a telegram dispatch before opening the device. For this I ask your understanding.
In a follow-up to this post, I’m presenting a template demo for processing raw SpaceMouse sensor data into an intuitive camera control in a current (rev. 0.140.0) Three.js environment.
Known limitations
The most significant limitation by far is, that the WebHID API is not supported by all browsers and is even referred to as «experimental» in some places.

- I did my development using Google Chrome version 100.0.4896.127 (Official Build) (x86_64). This success report is based on the use of Chrome.
- I have also run tests with Opera version 85.0.4341.60 (x86_64). Although Opera has the required
navigator.hid
extension according to the debugger, the identical code unfortunately already fails in recognizing the context of a «user gesture», i.e. click or touch. Which I think is a bug in Opera, so I filed a bug report with the Opera development team. - I cannot make any statement about Microsoft Edge.
I recommend to clarify if this limitation can be accepted before using it in a production environment. By giving feedback to the developers of WebHID, you can probably help this API to be further developed and possibly adapted by other browser providers.
I have made the Three.js forum aware of my findings to do my part in spreading the word.
As for my SpaceMouse / SpaceNavigator:
I have seen in the current and also earlier versions of 3DxWare a function to «Calibrate» a SpaceMouse. This used to be (SpaceMouse Classic (serial)) occasionally necessary when there was a drift of the zero point of the SpaceMouse. If that happened, the device would be constantly reporting small displacements, even if the puck was not touched at all. Using the «Calibrate» function, you could then define the current neutral position of the puck as the new zero point.
Since I could neither observe nor artificially induce this condition on my SpaceNavigator, I could not develop a workaround for it/against it. I leave this to the interested reader, and would appreciate any feedback. The Wireshark protocols seem to indicate, that an «LED On» command is being sent for calibration.