The Long Static Void

Solving the Mouselook Problem in JavaScript With Scroll Events

The inability to lock the mouse position in the browser in a JavaScript-only app poses a bit of a problem for implementing mouselook, for things like 3D (WebGL) First-Person games. The two workarounds I’ve seen are click-and-drag, which is tiring, and what I’ll call mouse-swing, which is just nauseating. Now, it looks like the next generation of browsers are working on a “Mouse Lock” API, but until that time, I’ve found that with the special events “mousewheel” (in Chrome and Safari) and “MozMousePixelScroll” (in Firefox) you can actually achieve free-range mouselook, with, erm … a few caveats. Here is a demo.

  • It requires a trackpad or “trackpad-like” surface that has two-axis motion, like Apple trackpads or the Magic Mouse. Fortunately, the Magic Mouse can be bought as an accessory and runs on Windows too.
  • It requires a browser that supports two-dimensional mouse-scroll events (Chrome, Safari, Firefox. IE has “onmousewheel”, but it’s only vertical), and supports WebGL, which currently just leaves Chrome and Firefox.

Anyway, the trick is that you can cancel mouse-scroll events, resulting in no motion of the cursor or the page, all the while getting (technology permitting) two-dimensional motions. For the demo, I built on the absolutely fantastic CopperLicht WebGL engine. The gist is that CL lets you give the cameras a custom mouse tracker, so you can essentially implement “virtual coordinates” based on, in this case, scroll offsets. That code ends up looking like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var engine = startCopperLichtFromFile('theCanvas', 'copperlictdata/room.ccbjs');

// ...

var _Cursor = function () {},
    cursor;

_Cursor.prototype = engine;
cursor = new _Cursor;
cursor.wheelSensitivity = 0.75;

function onMouseWheel(e) {
    if ('wheelDeltaX' in e) {
        cursor.MouseX += e.wheelDeltaX * cursor.wheelSensitivity;
        cursor.MouseY += e.wheelDeltaY * cursor.wheelSensitivity;

    } else if ('axis' in e) {
        if (e.axis === e.HORIZONTAL_AXIS) {
            cursor.MouseX += e.detail * cursor.wheelSensitivity;
        } else {
            cursor.MouseY += e.detail * cursor.wheelSensitivity;
        }
    }
    e.preventDefault();
}
engine.MainElement.addEventListener('mousewheel', onMouseWheel, false);
engine.MainElement.addEventListener('MozMousePixelScroll', onMouseWheel, false);


engine.getScene().getActiveCamera().Animators[0].CursorControl = cursor;

The actual code is up on GitHub.

The fact that this all requires specific tech is unfortunate, but not unheard of in gaming (racing wheels, joysticks), and every MacBook has it built-in. But here’s hoping the browser vendors hurry up and get Mouse Lock implemented and make all of this irrelevant.