JavaScript Device Motion & Orientation Table
| Piece | What it does | Field note |
|---|---|---|
devicemotion | The motion feed | acceleration (gravity out), accelerationIncludingGravity, rotationRate, interval - physics in device Hz |
deviceorientation | The tilt feed | alpha (compass), beta (front-back), gamma (left-right) in degrees - the fused pose |
iOS requestPermission | The gate | Safari 13+: must call inside a user gesture, then attach listeners on granted - silent no-events otherwise |
calibration | The honesty | Compass drifts, tilt needs a flat reference - the 'set level' button is yours to build; no API exists |
screen orientation | The twist | Values are device-frame relative; landscape without transposing axes reads a level sideways |
throttle + interval | The flood | 60Hz+ hardware rate - accumulate, sample on rAF, low-pass before anything user-facing |
https + policy | The precondition | Secure context only; iframes need allow="accelerometer; gyroscope" - silent nothing on all three failures |
desktop reality | The honest scope | No accelerometer means nulls or frozen constants - feature-detect per event, never assume motion |
The device orientation events family is the phone-as-instrument API: devicemotion streams raw accelerometer and gyroscope data (acceleration, accelerationIncludingGravity, rotationRate), while deviceorientation streams the fused pose - alpha around the z axis (compass heading), beta front-to-back tilt, gamma left-to-right tilt. Together they power level tools, steering games, AR prefixes and shake gestures, at whatever rate the device's hardware runs, which is usually faster than your app can render.
Bottom line: iOS Safari is the API's center of gravity since version 13 - motion and orientation are OFF by default, and DeviceMotionEvent.requestPermission() must be called inside a user gesture (a tap) to trigger the platform prompt; the exact same pattern gates DeviceOrientationEvent.requestPermission. No gesture, no prompt, and the events silently never fire - the number one 'works on Android, dead on iPhone' bug in this family. Check typeof DeviceMotionEvent.requestPermission === 'function' to know you are on the gated path.
The honest part: the sensors are cheap, and the numbers admit it. Compass-based alpha drifts with local magnetic fields and never recalibrates itself; beta and gamma are only trustworthy relative to a known reference, which is why every credible level app opens with a 'place on a flat surface and calibrate' step you must build yourself - there is no calibration API. And desktops report nulls or frozen constants: this is phone hardware talking through a browser, so feature-detect per event, sample the flood, and never assume motion exists because the listener attached.
How to use
- Gate iOS first: on a button tap, call DeviceMotionEvent.requestPermission() (and the orientation twin), branch on the 'granted' string, and only then attach your event listeners - both the prompt and the listeners belong inside the gesture, or iOS denies without ever asking the user.
- Pick the right event: devicemotion for physics (shake detection, motion-triggered actions - use acceleration, which excludes gravity, or accelerationIncludingGravity when you want the tilt baked in), deviceorientation for pose (levels, steering, pointing - alpha/beta/gamma in degrees).
- Calibrate by convention: capture beta/gamma offsets when the user taps 'set level on this surface', subtract them from every reading, and re-ask when the values look pinned. Compass alpha needs the same honesty - it drifts near magnets and cars, so treat heading as soft guidance, not navigation truth.
- Mind the screen: sensor values are device-frame relative, and screen.orientation.angle (0/90/180/270) tells you how the OS has rotated the mapping. Landscape mode without rotating your axes makes a level read sideways - transpose beta/gamma per the angle, or lock the interaction to portrait.
- Throttle the flood: events arrive at hardware rate (often 60Hz+); accumulate readings and sample on a rAF cadence, use event.interval as the device's own claim, and smooth with a low-pass filter (value = 0.8 * old + 0.2 * new) before anything user-facing - raw sensor data is noise wearing a decimal point.
Frequently asked questions
Why do my motion events never fire on iPhone?
Because iOS 13+ ships the sensors OFF: both devicemotion and deviceorientation require the user to grant permission through DeviceMotionEvent.requestPermission() / DeviceOrientationEvent.requestPermission(), and that call itself must run inside a real user gesture - a tap on a button - or it rejects without ever showing the prompt. The failure is silent: listeners attach fine, no events arrive, no console error. The working pattern: feature-detect (typeof DeviceMotionEvent.requestPermission === 'function'), show a 'enable motion' button, request inside its click handler, attach listeners only on 'granted', and keep a visible fallback for 'denied'. Android Chrome needs none of this - which is exactly why the bug ships.
What is the difference between acceleration and accelerationIncludingGravity?
acceleration is the device's proper acceleration - gravity subtracted out - so a phone sitting on a table reads near zero and a phone in free fall reads near zero too. accelerationIncludingGravity is what the accelerometer physically measures: the reaction to gravity baked in, so the same table phone reads roughly 9.8 on its resting axis. Practical split: shake and gesture detection wants proper acceleration (movement, not orientation); tilt-from-rest detection wants the including-gravity variant (orientation, not movement). Reading the wrong one produces the classic 'my shake detector fires when I rotate the phone' bug - rotation changes the gravity projection, which is movement in the wrong sensor's worldview.
Do alpha, beta and gamma match the angles my phone shows in a level app?
Only after you do the work the level app is doing. beta and gamma are rotations around the device's own axes in degrees, trustworthy relative to wherever the device was when it started - which is why calibration against a known-flat surface is a user step, not an API. alpha is rotation around the vertical axis derived from the magnetometer, which makes it a compass heading with compass problems: drift near magnets, steel and speakers, jumps when the OS re-calibrates, and a different zero than the compass app. And screen rotation rotates the mapping - landscape without transposing axes gives a level that reads sideways. The sensors report a pose; your app defines 'flat'.
Do these events work in iframes and on http?
No and no. Motion sensors require a secure context - on plain http the listeners attach and receive nothing - and inside an iframe the parent's Permissions Policy governs: without allow="accelerometer; gyroscope; magnetometer" on the iframe tag, the embedded page gets the dead-silent treatment iPhone users know from permissions. Desktops are their own failure mode: no accelerometer means null values or frozen constants. The complete support check is therefore three-part - secure context, permissions policy (or top-level page), and the iOS permission grant - and the honest app surfaces which link of that chain is missing instead of shipping a button that does nothing.