webdevpuneet.com

Shake to Undo — Free DeviceMotion API Shake Gesture Snippet

Shake to Undo · Buttons · Plain HTML, CSS & JS · Live preview

CategoryButtons

Copy & paste

Source Code

<section class="stu-wrap">
  <span class="stu-tag">devicemotion api · shake gesture</span>
  <h1>Note deleted</h1>
  <p id="stuStatus">Shake your phone to undo, or use the button below \u2014 both trigger the exact same undo.</p>

  <div class="stu-stage">
    <div class="stu-motion-meter" id="stuMeter"><div class="stu-motion-fill" id="stuFill"></div></div>
    <p class="stu-meter-label" id="stuMeterLabel">Motion energy</p>
  </div>

  <div class="stu-actions">
    <button class="stu-btn primary" id="stuBtn">Shake (or click here) to undo</button>
    <button class="stu-btn" id="stuPermBtn" hidden>Enable motion access</button>
  </div>
  <p class="stu-note">On iOS Safari, motion sensors require a permission prompt triggered by a tap \u2014 the "Enable motion access" button appears automatically when that is needed. If DeviceMotion is unsupported, unavailable, or denied, the button is the full undo path, not a lesser fallback.</p>
</section>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Real acceleration deltas
Shake detection compares consecutive readings, not a timer.
Three feature-detected paths
No API, iOS permission-gated, or ungated.
iOS permission handling
requestPermission() called from a real tap gesture.
Equal-weight fallback
The tap button runs the exact same undo function.
Live motion meter
Visualizes accelerometer delta in real time.
Shake cooldown
A 1s window stops one shake firing repeatedly.
Named status messages
Explains exactly which path is active and why.
No dependency
Pure DeviceMotion API, no motion library.

About this UI Snippet

Shake to Undo — Real Motion Sensing With an Honest Fallback

Screenshot of the Shake to Undo snippet rendered live

Shake-to-undo is the classic mobile gesture — popularized by iOS's system-wide "shake to undo" — where physically shaking the device reverses the last action. This snippet wires up the real devicemotion event and a genuine acceleration-delta shake detector, following the same honesty pattern as canvas audio frequency bars: feature-detect the real API, use it when available, and fail into a fallback that is fully functional rather than a degraded placeholder.

Real acceleration-delta shake detection

Every devicemotion event carries accelerationIncludingGravity, an {x, y, z} reading in m/s². A single reading can't detect a shake — shaking means the *change* between consecutive readings is large. The handler computes delta = |x - lastX| + |y - lastY| + |z - lastZ| between the current and previous reading, and only counts it as a shake when delta exceeds SHAKE_THRESHOLD — genuine motion-derived detection, not a timer or a fake trigger. A 1-second cooldown (lastShakeTime) stops one violent shake from firing the undo repeatedly.

Why three different code paths exist

DeviceMotion has three distinct real-world states this snippet handles explicitly: (1) the API doesn't exist at all — most desktop browsers — detected with typeof DeviceMotionEvent === 'undefined'; (2) it exists but iOS 13+ gates it behind DeviceMotionEvent.requestPermission(), which can only be called from a real user gesture like a tap, never on page load; (3) it exists and needs no explicit permission (most Android browsers), so listening can start immediately. Each path is feature-detected rather than assumed, and each fails toward the same visible, working fallback.

The fallback is not a lesser path

The tap button calls the exact same doUndo(source) function the real shake gesture calls — it isn't a "sorry, no shake for you" message, it's the complete undo interaction, just triggered by a tap instead of an accelerometer reading. This matters because DeviceMotion is denied constantly in practice: desktop has no sensor, many browsers block it by default for privacy, and users often decline the iOS permission prompt. A demo (or a real feature) that goes dead in those cases looks broken; one where the fallback *is* the feature does not.

A live motion meter for feedback

Rather than a shake being invisible until it fires, meterFill's width is driven by the same delta value every event, scaled against the threshold — so on a supporting device you can see motion energy building toward the shake threshold in real time, turning an otherwise invisible sensor reading into visible feedback.

Customizing it

Tune SHAKE_THRESHOLD and the cooldown window for stricter or looser detection, swap doUndo for your real undo logic, or pair this with a notification permission prompt pattern for other gated-permission APIs.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA status line explains what to do based on your device.
  2. 2
    On a supporting phoneGrant motion access if prompted, then physically shake it.
  3. 3
    Watch the motion meterIt fills with real accelerometer delta as you move the device.
  4. 4
    On desktop or unsupported devicesThe status explains why, and the tap button is the full path.
  5. 5
    Tap the undo button anytimeIt runs the identical doUndo() the real shake gesture calls.
  6. 6
    Tune the sensitivityChange SHAKE_THRESHOLD and the cooldown window.

Real-world uses

Common Use Cases

Note and email apps
Undo a delete or send with a physical shake, comparing consecutive acceleration readings from the real devicemotion event.
Form and draft recovery
Revert an accidental clear by shaking, with an equal-weight tap button running the exact same undo function.
Mobile game shortcuts
Use shake gestures for resets, re-rolls or power-ups, handling the three paths of unsupported, iOS permission-gated and ungated devices.
Accessible undo
Always pair the gesture with a tappable control, since not everyone can shake a device reliably or at all.
Permission-gated API learning
Study how iOS requires requestPermission() to be called from a real tap, as a reference for any permission-gated sensor API in a progressive web app.

Got questions?

Frequently Asked Questions

Every devicemotion event provides an x/y/z acceleration reading. The handler computes the absolute difference between the current and previous reading on each axis and sums them into a single delta value. Only when that delta exceeds SHAKE_THRESHOLD does it count as a shake — a single still or slowly-moving reading never triggers it, since a shake is specifically a large change between consecutive samples, not a large single reading.

iOS 13 and later gate DeviceMotionEvent behind an explicit permission check exposed as DeviceMotionEvent.requestPermission(), which the browser only allows to be called from inside a real user gesture like a tap — never automatically on page load. The snippet checks whether that method exists and, if so, shows a visible "Enable motion access" button rather than trying (and failing) to request it silently. Most Android browsers expose devicemotion without any such gate.

The code checks typeof DeviceMotionEvent === 'undefined' first for browsers with no motion API at all, and handles a denied or failed permission request as a separate case. Both routes call the same fallbackNoMotion() function, which updates the status text to explain why and leaves the tap button as the complete, fully working undo path — not a disabled or lesser option.

Both the shake gesture and the button call the identical doUndo(source) function with only the source label differing. There is no separate, reduced code path for "no motion available" — the same undo logic runs either way, so a user on desktop or with motion denied gets exactly the same outcome as a user who successfully shakes their phone.

Do the feature detection and event wiring inside a mount effect, storing the last acceleration reading and last-shake timestamp in refs (not state, since they update on every motion event). Call your real undo action from inside doUndo, and remove the devicemotion listener in the effect's cleanup function so it doesn't keep firing after unmount.