| 1 | export 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 | */ |
| 14 | export 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 | } |