Sensor Apis Dom Vs Generic

The browser has two sensor APIs from different vendors. The old DOM events work on iOS. The new Generic Sensor API does not. Here is how both work and when to use each.

June 10, 20265 min read2 / 17

There are two JavaScript APIs for reading phone sensors. One was built by Apple around 2009. The other was built by Chrome in 2017. They read the same hardware, they do not replace each other, and which one works depends on which device your user has.

The iOS question determines everything. If your users are on iPhone, only the old API works. If they're on Android or desktop Chrome, either works. The sensible strategy is to prefer the new API and fall back to the old one.

The DOM Events API (The Original)

Apple created this one. Chrome adopted it. Firefox and others followed. It works everywhere -- including iOS Safari, which is the part that matters.

The pattern is a simple event listener on the window object:

JavaScript
window.addEventListener('devicemotion', event => { const { x, y, z } = event.acceleration; const withGravity = event.accelerationIncludingGravity; const { alpha, beta, gamma } = event.rotationRate; }); window.addEventListener('deviceorientation', event => { const { alpha, beta, gamma } = event; // compass and tilt angles });

There's no setup, no constructor, no start() call. The event fires continuously once you add the listener. You cannot stop it and you cannot control the frequency. The sensor reports at its default rate and your handler receives every update.

This was fine when it shipped -- web developers were just happy to have the data. The limitations became more visible as use cases grew more complex.

The Generic Sensor API (The Modern One)

Chrome introduced the Generic Sensor API in 2017 as a cleaner alternative. It's promise-aware, constructor-based, and gives you control that the DOM events API never had.

JavaScript
const sensor = new Accelerometer({ frequency: 60 }); sensor.addEventListener('reading', () => { const { x, y, z } = sensor; updateUI(x, y, z); }); sensor.addEventListener('error', event => { console.error(event.error.name, event.error.message); }); sensor.start(); // begin reading // later: sensor.stop(); // stop reading

The frequency option sets how many readings per second you receive -- 60 means roughly 60 Hz, matching a typical display refresh. You can call sensor.stop() to pause and sensor.start() again to resume. This is the first time web developers could control sensor polling rate or stop it on demand.

The tradeoff: it is not available on iOS or Firefox. Safari has not implemented it. The light-green API tier applies here -- Chromium-only.

Two sensor APIs: DOM Events (everywhere) vs Generic Sensor API (Chromium only) ExpandTwo sensor APIs: DOM Events (everywhere) vs Generic Sensor API (Chromium only)

Available Generic Sensor Constructors

The Generic Sensor API ships with a set of concrete sensor classes:

JavaScript
new Accelerometer({ frequency: 60 }) // raw force, X/Y/Z new LinearAccelerationSensor({ frequency: 60 }) // force minus gravity new Gyroscope({ frequency: 60 }) // rotation, alpha/beta/gamma new Magnetometer({ frequency: 60 }) // compass direction new AbsoluteOrientationSensor({ frequency: 60 }) // device orientation in space new RelativeOrientationSensor({ frequency: 60 }) // orientation relative to start new AmbientLightSensor({ frequency: 1 }) // lux -- behind a flag

LinearAccelerationSensor is worth noting specifically. The DOM events API lets you request acceleration with or without gravity via event.acceleration vs event.accelerationIncludingGravity. The Generic Sensor API handles this with separate constructors.

Controlling Sensors with Permissions-Policy

Both APIs respect the Permissions-Policy HTTP header. If you want to block sensor access on your page or in embedded iframes -- which is worth doing if your page doesn't use sensors -- the header syntax looks like:

HTTP
Permissions-Policy: accelerometer=(), gyroscope=(), magnetometer=()

This is independent of the permission dialog question. The header controls whether your JavaScript can even attempt to read the sensor. The permission model governs the user-facing dialog -- for sensors on Android, there typically isn't one, but the Permissions-Policy layer still applies.

Which API to Use

The pragmatic approach for cross-browser sensor code:

JavaScript
function startSensorReading(onReading) { if ('Accelerometer' in window) { // Generic Sensor API -- Android Chrome, Edge const sensor = new Accelerometer({ frequency: 60 }); sensor.addEventListener('reading', () => onReading(sensor.x, sensor.y, sensor.z)); sensor.start(); } else if ('DeviceMotionEvent' in window) { // DOM Events fallback -- iOS Safari, Firefox window.addEventListener('devicemotion', event => { const { x, y, z } = event.acceleration ?? event.accelerationIncludingGravity; onReading(x, y, z); }); } }

Check for the Generic Sensor constructor first. Fall back to the DOM event. This gets you coverage across Android Chrome, iOS Safari, and Firefox.

One wrinkle remains: on iOS 13 and later, the DOM events fallback requires explicit permission before it works. That permission model -- and the story of how it broke thousands of web experiences overnight -- is what the next post covers.

The Essentials

  1. The DOM Events API (devicemotion, deviceorientation) works on iOS, Android, and Firefox. It always streams, has no stop(), and no frequency control.
  2. The Generic Sensor API uses constructors per sensor type. It supports frequency control, start() / stop(), and is Chromium-only -- not available on iOS or Firefox.
  3. Check for 'Accelerometer' in window first, fall back to 'DeviceMotionEvent' in window to cover both platforms in a single code path.
  4. Use Permissions-Policy: accelerometer=() in HTTP headers to block sensor access on pages that don't need it.

Further Reading and Watching