Skip to content

Repository files navigation

Fall Detector

Live demo License: MIT Vanilla JS No build step Tests

English | Español

A phone-based fall detector that runs entirely in the browser: DeviceMotion API, a four-phase detection state machine, and (new in this version) real-data validation and a QR-paired remote alert. The app itself has no backend, no build step, and no npm dependencies.

Demo: simulating a fall on desktop, the chart drawing the free-fall-then-impact pattern, the alert firing, and the event log updating

Contents

  1. What it does
  2. Algorithm
  3. Real-data validation
  4. Pairing & alerts
  5. Limitations
  6. How to run

What it does

The page reads the device's accelerometer (DeviceMotion), computes the acceleration magnitude |a| = √(x² + y² + z²), and runs that stream through a state machine that looks for the specific signature of a fall: a brief near-weightless phase, followed within a second by a sharp impact, followed by the device staying still. When all three happen in sequence, it logs the event and fires an alert. A "Simulate fall" button injects a synthetic sequence through the same pipeline, so the algorithm can be demoed on a desktop with no phone at all.

The UI itself has a built-in ES/EN toggle (top right), independent of this README's language.

Algorithm

Four states: WATCHING → FREEFALL → IMPACT_WAIT → CONFIRMING → WATCHING.

  1. WATCHING: waits for |a| to drop below freeFallThreshold (near-weightlessness, a sign of free fall).
  2. FREEFALL: if |a| then spikes above impactThreshold within impactWindowMs, that's read as an impact.
  3. IMPACT_WAIT / CONFIRMING: after the impact, the signal has to settle back near gravity (9.8 m/s²) for stillnessWindowMs before the alert fires. Someone who trips and catches themselves never produces this stillness phase, so it gets filtered out.

The whole algorithm lives in detector.js, a small module with no DOM dependency. The browser UI, the unit tests, and the validation harness below all import and run that exact same module, so there is only one implementation of the state machine to trust.

Real-data validation

Ran the algorithm against UMAFall (Universidad de Málaga), a public dataset of 746 real accelerometer recordings: 208 acted falls (backward, forward, lateral) and 538 activities of daily living, smartphone accelerometer at 200 Hz. Full method, per-activity breakdown, and a 27-combination threshold sweep are in results/validation.md (written in Spanish); the harness that produced them is validate.js, and it imports detector.js directly rather than reimplementing the algorithm.

With the thresholds this repo currently ships:

  • Sensitivity (falls correctly detected): 69.7% (145/208)
  • False positives on normal activity: 15.1% (81/538)

Sensitivity by fall type

Sensitivity varies a lot by fall type: 75.3% for backward falls, 74.6% forward, 57.8% lateral. The biggest source of false alarms is MakingACall (58.7%), likely because bringing a phone up to the ear and back down can look like a brief free-fall-then-impact if it's fast enough. The threshold sweep in the full report shows a config that pushes sensitivity to 98.1% at the cost of a 21.6% false-positive rate; whether that trade is worth it depends entirely on how annoying a false alarm is versus a missed fall for the specific person using this.

Read this as an upper bound, not a clinical claim. UMAFall's falls are acted by young/adult volunteers falling onto a mattress in a single lab session, not real falls by an elderly person in their own home. The full report names the specific failure mode: slow or "soft" falls (sitting down hard, sliding down rather than dropping) never produce the free-fall phase the algorithm looks for, so they're missed regardless of threshold tuning. That follows from what the algorithm looks for, which is free-fall-then-impact. Catching soft falls would need a different approach, and I don't know what that would be yet.

Pairing & alerts

A "faller" phone (the one doing the sensing) can pair with a "caregiver" phone so that a real fall alert rings on someone else's device. Pairing starts with a single "Who are you?" prompt instead of a generic create/join screen: tapping "I'm the caregiver" immediately generates a room and shows the QR code plus a 6-digit code; tapping "I'm being monitored" immediately opens the camera to scan it (typing the code is the fallback). Either way it's one tap, and the tap itself sets the role, no separate picker step after. Both devices remember the pairing in localStorage, so reloads reconnect automatically without repeating any of this.

The QR encodes a real URL (?join=<id>), not just the raw code, so it also works scanned with the phone's own camera app or Google Lens instead of this page's built-in scanner: opening the link pairs automatically, no tap required on that end at all.

It runs over MQTT (a public broker, HiveMQ, via WebSockets) so there's no backend to host. On a fall, the faller's phone publishes an alert; the caregiver's phone responds with a full-screen red overlay, a Web Audio siren, and vibration, plus an acknowledge button that lets the faller's phone show "someone's coming."

Caregiver's phone showing the full-screen fall alert: room id, timestamp, and an acknowledge button

Verified end to end over the real public broker: two browser tabs, one paired as faller and one as caregiver, a simulated fall triggering the overlay and siren on the caregiver's tab within seconds, and the acknowledge button round-tripping back to the faller's tab.

This only works if both phones are online at the same moment. There's no queue and no retry beyond what MQTT's own "retained message" mechanism gives for free. It also runs on a shared public broker, so the room code is the only thing separating this session from anyone else's, which is fine for a demo and not something to rely on for a real deployment.

Limitations

Browser motion sensors have real limits:

  • iOS Safari (and, for background use, Android Chrome too) only delivers accelerometer data while the tab is in the foreground with the screen on.
  • Background notifications on iOS require installing the page as a PWA.
  • A phone in a pocket moves differently than a wearable on a wrist or chest; the signal geometry changes.
  • This is, by design, a foreground demo system, not passive always-on monitoring of someone at home (that's native-app or dedicated-wearable territory).
  • The remote MQTT alert also needs both phones online at the same time.

How to run

No build step. Any static file server works:

npx serve .
# or
python -m http.server

Open the printed URL. On a phone, tap "Enable sensors" (iOS asks for explicit permission), then "Start monitoring." On a desktop with no motion sensors, use "Simulate fall" to run a synthetic fall sequence through the same pipeline.

Tests: node --test (Node's built-in test runner, no dependencies). Covers a clean fall, a table-drop with no free-fall phase, walking, a trip-and-recover, and the desktop simulator sequence.

Re-run the validation: download the UMAFall dataset (78 MB, not included in this repo), extract it, then node validate.js --data <path-to-extracted-csvs>. Regenerates results/validation.md and the chart.

Deploy: this is a static site. On GitHub, Settings → Pages → deploy from main, root. All script/style paths are relative, so it works from a project subpath (username.github.io/fall-detector-validated/) without any changes.


English | Español

About

Browser-based fall detection with a QR pairing system designed for caregivers.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages