Device shake detection w/ plain JS, no libraries
An attempt at universal shake detection.
For non-iOS: Shaking the first-time will show a permission prompt to the user asking to allow use of sensor.
For iOS (or any device strict about requestPermission
API): An extra step is needed in the user's experience. The user must invoke the sensor permission prompt themselves rather than the permission prompt coming up on it's own on first shake. This is done by you providing a button somewhere in the experience, perhaps in a toolbar or a modal, where the button invokes the requestPermission
API.
In addition to the above, you need to host this on an HTTPS server (I used github-pages
). I have it working on localhost
/local-wifi too, but that's another thread. For this problem specifically, I would avoid testing this in online IDE's (like Codepen) even if they're https, requestPermission
may not work.
Recommendation: Whatever you do, in your app (or website) it would be good if you independently store state for the user, ie whether they allowed permission or not. If they hit "Cancel", then you can reliably know this and possibly periodically tell them "Hey, you're missing out on this awesome functionality!" within your experience, and offer the permission prompt again (through explicit UI control).
HTML
<button id="btn_reqPermission" style="display: none;padding: 2em">
Hey! This will be much better with sensors. Allow?
</button>
<div id="output_message"></div>
Javascript:
// PERMISSION BUTTON
var btn_reqPermission = document.getElementById("btn_reqPermission")
btn_reqPermission.addEventListener("click", () => { this.checkMotionPermission() })
// ON PAGE LOAD
this.checkMotionPermission()
// FUNCTIONS
async function checkMotionPermission() {
// Any browser using requestPermission API
if (typeof DeviceOrientationEvent.requestPermission === 'function') {
// If previously granted, user will see no prompts and listeners get setup right away.
// If error, we show special UI to the user.
// FYI, "requestPermission" acts more like "check permission" on the device.
await DeviceOrientationEvent.requestPermission()
.then(permissionState => {
if (permissionState == 'granted') {
// Hide special UI; no longer needed
btn_reqPermission.style.display = "none"
this.setMotionListeners()
}
})
.catch( (error) => {
console.log("Error getting sensor permission: %O", error)
// Show special UI to user, suggesting they should allow motion sensors. The tap-or-click on the button will invoke the permission dialog.
btn_reqPermission.style.display = "block"
})
// All other browsers
} else {
this.setMotionListeners()
}
}
async function setMotionListeners() {
// ORIENTATION LISTENER
await window.addEventListener('orientation', event => {
console.log('Device orientation event: %O', event)
})
// MOTION LISTENER
await window.addEventListener('devicemotion', event => {
console.log('Device motion event: %O', event)
// SHAKE EVENT
// Using rotationRate, which essentially is velocity,
// we check each axis (alpha, beta, gamma) whether they cross a threshold (e.g. 256).
// Lower = more sensitive, higher = less sensitive. 256 works nice, imho.
if ((event.rotationRate.alpha > 256 || event.rotationRate.beta > 256 || event.rotationRate.gamma > 256)) {
this.output_message.innerHTML = "SHAKEN!"
setTimeout(() => {
this.message.innerHTML = null
}, "2000")
}
})
}