1export interface AccelerationSample {
2 /** Acceleration per axis in g, gravity included. */
3 readonly x: number;
4 readonly y: number;
5 readonly z: number;
6 /** Milliseconds. */
7 readonly timestamp: number;
8}
9
10/**
11 * Reports a shake after two strong jolts within a short window. A single bump,
12 * setting the phone down, or walking stays below the threshold or the count.
13 */
14export function createShakeDetector({
15 threshold = 1.8,
16 window = 600,
17 cooldown = 1_000,
18}: { threshold?: number; window?: number; cooldown?: number } = {}) {
19 let jolts: number[] = [];
20 let lastShake = Number.NEGATIVE_INFINITY;
21 return (sample: AccelerationSample) => {
22 if (sample.timestamp - lastShake < cooldown) return false;
23 if (Math.hypot(sample.x, sample.y, sample.z) < threshold) return false;
24 jolts = [...jolts.filter((time) => sample.timestamp - time <= window), sample.timestamp];
25 if (jolts.length < 2) return false;
26 jolts = [];
27 lastShake = sample.timestamp;
28 return true;
29 };
30}