web stats

Web Sensor API: raw and uncut

Sensors found in smartphones define the mobile experience. GPS and the
magnetometer enable the fluid experience of maps; motion sensing enables
activity recognition and games, and of course the camera and microphone
allow whole categories of rich media applications. Beyond these now
obvious examples, sensors can also enable clever inventions, such as
Cycloramic, which used the vibrator motor in iPhones (4 and 5) to
rotate the phone and take a panorama, pushup counters which
use the proximity sensor to count repetitions, and Send Me To
Heaven
, which uses the accelerometer to determine flight time of
a phone thrown vertically as high as possible. I’ve had some experience
using and abusing sensors too, most recently for the Cardboard magnet
button
.

However, over the last couple of years, I’ve had to step away from the
web as a development platform, in part because of the poor state of
sensor APIs. In this post, I will describe some of the problems, take a
look at sensor APIs on iOS and Android, and suggest a solution in the
spirit of the extensible web manifesto.

Existing sensor APIs are underspecified

One of the most popular sensor APIs on the web is the DeviceMotion event
API
, which is basically always just an opaque abstraction around the
accelerometer. The web, as always, tries to solve the problem in the
most general way possible:

This specification provides several new DOM events for obtaining
information about the physical orientation and movement of the hosting
device. The information provided by the events is not raw sensor data,
but rather high-level data which is agnostic to the underlying source
of information. Common sources of information include gyroscopes,
compasses and accelerometers.

This could be fine in theory, except the specs end up being so vague in
their attempt to please everybody, that they under-specify the behavior
of events such as DeviceOrientation. Throw in some rogue implementers,
and you end up with huge discrepancies in browsers, as Pete found back
in 2011
:

For most browsers, alpha returns the compass heading, so when the
device is pointed north, alpha is zero. With Mobile Safari, alpha is
based on the direction the device was pointing when device orientation
was first requested. The compass heading is available in the
webkitCompassHeading parameter.

A useful sensor abstraction would be to build a compass on top of the
magnetometer (and maybe gyro) sensors, and then expose that as a high
level Compass API. Unfortunately many web sensor APIs give us a
mid-level of abstraction. They don’t map reliably to particular hardware
sensors, nor do they provide much use. Sensors allow many applications
that were not originally envisioned by the spec writers. By choosing
poorly specified ivory-tower abstractions, the web limits what can be
done on the platform.

Low level sensor APIs don’t exist

While you can work around the insanity of Device* style events on the
web with platform-specific shims, you cannot work around missing sensor
APIs. Magnetometers, pressure sensors, proximity, light, temperature,
battery, etc. These are mostly missing, and the ones that are specified
are specified in a very narrow way that does not generalize across to
other types of sensors (eg. DeviceLightEvent).

Unfortunately it seems that previous attempts to push for a general low
level sensor API haven’t really gotten much traction. In
fact, it’s a bit unclear whether or not the Device API working
group
, is even the right place for sensor APIs, since their
mandate is supposedly more about services than sensors:

[To] enable the development of Web Applications and Web Widgets that
interact with devices services such as Calendar, Contacts, Camera,
etc.

Except here’s a sensor API from the same group, which
seems to be abandoned… I don’t even

There are more recent voices (circa 2014) that seem to be pushing in a
generic sensor API direction, from folks like Rick Waldron
and Tim Volodine. Many of these ideas are still working within
the confines of a sensor API for each type of sensor. This does not
scale well for the web, which tends to take a long time for any new web
standard, but this renewed interest is very exciting and promising!

Sensors on other platforms

The web is woefully behind native platforms in almost every regard (with
possibly the exception of audio). Sensors on iOS and Android have a rich
history, and ended up in a pretty similar place as the two platforms
have scrambled to converge. Let’s take a look.

iOS started off with a UIAccelerometer API, which was
replaced by CoreMotion in iOS 5. Rather than providing a
series of specific APIs for each type of sensor API as it had before,
CoreMotion provides a unified framework for sensor events. Each data
type inherits from a common base class CMLogItem, and most of the API is
encapsulated in CMMotionManager, which explicitly lists accelerometer,
gyroscope and magnetometer-related APIs. iOS went from specific to
generic, which makes it super easy to add new types of sensor data. That
said, the API is generic only for motion sensors, which excludes a bunch
of sensors not directly related to motion like temperature, humidity,
etc.

Android started off right, and hasn’t had to change much, providing a
generic API for sensors since API level 3. Android’s API
is accessed through a SensorManager, which provides a somewhat overly
abstract API, because of its support for multiple sensors of one type
(eg. two accelerometers) in the same device. Still, the idea is good,
and all of the low level sensor data are well specified (per sensor
type)so the hardware/firmware vendor knows what data format their sensor
should stream. Of course there are still rogue implementations that
don’t follow the spec, but that is a perennial problem for any open-ish
ecosystem.

Android also has a distinction between software-based sensors and
hardware-based ones. The idea is that the same framework can provide
both the low level data coming directly from the hardware, as well as
useful higher level data obtained through sensor fusion. As of
API level 19, Android also provides batch mode for sensor data, which
is very useful for conserving battery and CPU for applications where
some delay is acceptable.

One nice advantage of an iOS style API is that each sensor type has its
own structure (rather than just an amorphous array of floats, as in
Android), which is quite a bit easier to parse. The downside is that
adding new sensor types introduces more overhead, since each one
requires a new structure to be defined and agreed upon. Since we are
talking about web standards, which evolve at a glacial pace, we should
err on a simple API that works well without spec modifications.

Great artists steal

There is no need for the web to reinvent the wheel. The wheel has
already been invented by iOS and Android. All we need to do is take the
good parts from these successful sensor platforms, and integrate them
into the web in a way that makes sense. The web is not the place for
innovation, but for standardization.

Conceptually, a sensor provides a stream of data. The developer should
be able to configure the rate at which new data comes in, as well as
batching the data in windows of sensor data (as is customarily done with
audio data, for example). In Android, because of a plurality of devices,
it’s important to be able to check if a particular sensor is available.
The same concept maps well to the web.

Toward A Web Sensor API

In general, here are the requirements for a Web Sensor API that works:

  • A specification defining the format of the data, similar to
    Android.
  • A way to feature detect for the existence of a particular sensor.
  • A way to request (and revoke) a stream of sensor data.
  • A way to specify how often to poll the sensor.
  • Bonus: A way to request sensor data in batch form.

While bringing an API like this to the web is a huge undertaking, there
is a silver lining. The sensors we’re talking about are all considered
(at least for now) low-security, in the sense that on native platforms,
there is no extra permission required to access them. This makes it
possible to simply propose an API, convince everybody of it’s worth, and
then have it implemented across the web!

I don’t have a strong opinion about how the API itself looks like as
long as it fulfils the above requirements. Here’s a simple strawman
which should satisfy them:

// Check for magnetometer support.
if (sensors.Magnetometer === undefined) {
  console.error('No magnetometer found');
}

// Start listening for changes to the sensor.
var magnetometer = sensors.Magnetometer;
magnetometer.addEventListener('changed', onMagnetometer, {
  sample_rate: sensors.POLL_FAST, // In hertz, eg. POLL_FAST == 100
  batch: 1 // Number of data points to provide in a single poll.
});

// Handle sensor events.
function onMagnetometer(event) {
  var data = event.data[0];
  // Get the timestamp (in millis).
  var t = data.timestamp;
  // Get the data (in this case µT, as per spec).
  var x = data.values[0];
  var y = data.values[1];
  var z = data.values[2];
  // Process the data.
  superAdvancedSensorFusionThing.addData(t, x, y, z);
}

// Stop listening.
magnetometer.removeEventListener('changed', onMagnetometer);

Conclusion

If you aren’t yet convinced that we need access to low level sensors on
the web, recall web developers scoffing at device pixel ratio (DPR),
really questioning the need for to ever go above 2x. Now that some
screens
are ending up 5cm from your face, the current
generation of 4x displays isn’t enough. The same exact thing applies to
sensors. The need is there, but it is not seen as enough of a priority
by the web community.

By enabling low level sensor access, we can allow new experiences never
before possible on the web. Pushup rep counters, the magnet
button
in Cardboard, and myriads more applications
yet to be concieved could all be built on the web platform, eliminating
a big reason why the web is increasingly losing its relevance on mobile
devices. Providing low level sensor access is critical and aligns
perfectly with the extensible web vision.

Update (Nov 14, 2014): There was a W3C call about this very
topic yesterday! Kicking off efforts in this github repo.
Join us!

Powered by WPeMatico