Tech
Laptop Tap Detection: Why One Accelerometer Threshold Isn't Enough
Falcon, Founding Member at Miller

We set three conditions for what we think psychological presence requires in a product. It has to answer every time you call. It has to be the same every time. And it must not answer when you didn't call.
Those conditions turned into engineering constraints almost immediately. This is what they look like from inside.
Why knock
We were trying to raise presence and kept running into the same wall. Miller is software, and software has no physical body. You can make it faster, smarter, more accurate, and it still doesn't feel like something beside you, because there is nothing to touch.
That led to a simple question. How do you get the attention of someone who isn't looking at you? There are only two ways. Make a sound, or knock. Say their name, or tap something and let the contact carry it.
Sound has been done. Hey Siri, Hi Alexa, OK Google. Every voice assistant starts by asking you to say a name out loud. Knocking on a laptop hasn't been done. Phones have precedent (iOS Back Tap detects a double-tap on the back of an iPhone using the accelerometer), but on a laptop chassis, with a tap quiet enough to use in a meeting, nobody has shipped that. We assumed that was because it's hard, and we wanted it to be hard, because difficulty that keeps others out is a good reason to go in.
Not slap
There's a product that went semi-viral by letting you slap a laptop to trigger a response. That's a different problem entirely. A slap is loud, large in motion, and unmistakable to a sensor. You don't need an upper bound on impact magnitude because the gesture itself is extreme. You also can't do it in a meeting, at a café, or in an open-plan office without turning heads.
What we needed was a tap. Two fingers, quiet enough that the person next to you doesn't notice, small enough in motion that it looks like a habit rather than a command. That constraint changes everything downstream. The sensor signal is small, it overlaps with dozens of things that aren't taps, and the margin between "real tap" and "not a tap" is narrow on both sides.
One threshold
The first version was one line of logic. Read the accelerometer, compute how much it changed, check if the change is above a cutoff.
The accelerometer gives three axes. Rather than using the absolute reading, we take the delta between the current sample and the previous one on each axis, then compute the Euclidean norm.

magScale is a per-device calibration divisor, clamped to a bounded range so that miscalibration doesn't push the measurement into unusable territory. At 1.0, the raw delta is used as-is. Above 1.0, the measured mag shrinks. Below 1.0, it grows.
A candidate tap must land within a band. Below a mode-specific minimum, the impact is too weak to be a finger tap. Above a global maximum, the impact is too strong; it's a drop, a collision, or the laptop being set down. The mode-specific minimum also differs by position in the sequence.
position | double-tap mode | triple-tap mode |
|---|---|---|
first tap | τ₁ | τ₁ |
second tap | τ₂,D | τ₂,T |
third tap | n/a | τ₃,T |

We ran about fifty tests. It was chaos. Two problems showed up immediately, both about the surface the laptop sits on.
On a dense, rigid surface (thick hardwood, metal), the tap transmits very little energy into the laptop body. The delta barely registers. On a lighter or more resonant surface, the energy gets in, but the waveform rings. Instead of a clean spike, you get a gradually decaying oscillation, and the threshold fires on the echoes too.
Then a third problem. Tap the laptop, and the impact travels through the chassis into the desk, bounces off the desk surface, and comes back. The sensor sees two events from one tap.
Fifty tests was enough to know that a single threshold on raw magnitude solves nothing.
Reading the waveform
We started looking at what the raw signal actually looks like. Not just the magnitude, but the shape over time.
A finger tap on the laptop body produces a short, sharp spike. A desk vibration produces a longer, lower-energy undulation. A return shock (the bounce-back from the surface) looks like a smaller echo of the original spike, arriving tens of milliseconds later. These are visually distinct when you plot them, but overlapping enough in magnitude that a threshold alone can't tell them apart.
We spent a lot of time hitting the laptop and hitting the desk, comparing the two side by side, repeatedly. The honest answer is that we never found a single feature that separates them with 100% reliability. What we did find was enough structure to filter most of the noise, and we settled on a direction: it is more important to not fire when it shouldn't than to catch every legitimate tap.
Two ideas came out of this stage. Both survive in the final system.
Stability gate
The first idea: if the laptop has been shaking for the past second, the current spike is probably not an intentional tap. We needed a way to measure how much the device moved recently, without letting a single outlier sample distort the picture.
We compute the trimmed range of each axis over a one-second window. Trimming means sorting the samples and discarding the top and bottom 2% before taking the range.

Given a sorted array X of the last one second of samples on the x-axis:

yR and zR are computed identically on their respective axes. If the sample count is below a minimum, trimming is skipped and raw range is used.
The stability gate does two things at once. It checks upper bounds on each axis to reject environments that are too unstable (desk vibration, being carried, sliding). And it checks lower bounds on certain axes to confirm that the impact had enough axis-specific energy to be a finger tap. If the Z-axis range over the last second is too small, the candidate probably wasn't a direct physical tap on the body. The lower bound is a tap-energy check disguised as a stability condition.
The per-axis bounds differ between double-tap and triple-tap modes.
Normal double-tap stability:

Normal triple-tap stability:

Triple-tap uses a wider X-axis ceiling and a wider Z-axis band, because the three-tap cadence is itself a strong signal that compensates for looser stability requirements. The Y-axis lower bound is also lower in triple-tap mode. In labelled data, finger taps and desk taps sit close together on the Y-axis, so in double-tap mode the floor is set at the separation boundary. In triple-tap mode, the cadence does enough filtering that the boundary can be relaxed without increasing false positives.
Crest gate
The second idea from waveform analysis. A finger tap is sharp. A desk vibration is round. We measure how pointed the peak is relative to its surroundings.
First, compute the jerk (sample-to-sample delta magnitude) in a window around the candidate peak.

The window spans from shortly before the peak to a longer interval after it, capturing the impulse and its immediate decay. Then:

A high crest means the peak stands alone: sharp impulse, quiet surroundings. A low crest means the energy is spread out: vibration, ringing, or the tail of a surface shock transmitted through a medium.
For double-tap, the crest condition checks whether either the first or the second tap is sufficiently sharp, with slightly different thresholds for each position.

For triple-tap, only the second and third taps are checked. The first tap is excluded because the window at the start of a sequence tends to be unstable, and users often hit the first tap lighter as a kind of windup. The second and third taps, already validated by being inside a timing sequence, are more representative of deliberate intent.

But near-misses appeared in logs. Intentional triple-taps where the best crest among taps two and three fell just below the threshold. Lowering the threshold uniformly would let ringing through. So we added a recovery path: if the crest is below the primary threshold but above a lower floor, and the STA/LTA ratio (described below) confirms that the spike was clearly distinct from the baseline, the candidate is rescued.

This pattern (strict primary gate, guarded recovery for near-misses) recurs throughout the system. Every rescue opens a door, and every door has a second lock.
Borrowing from seismology
The word "accelerometer" made one of us think of seismographs. That association turned out to be directly useful. Seismology has a well-known technique for distinguishing a real seismic event from background noise: the STA/LTA ratio. We brought it over.
High-pass filter
Before computing the ratio, the raw signal needs to be cleaned. Slow changes (tilting the laptop, shifting gravity distribution as you adjust your posture) show up as low-frequency drift in the accelerometer. These aren't impacts, but they move the baseline and distort the ratio. A high-pass filter removes them.

where f_c is the cutoff frequency. The sample-to-sample time interval is clamped to a bounded range so the filter doesn't blow up on irregular timing.

The filter is applied per axis:

The HPF magnitude across all three axes becomes the input signal for the baseline comparison.

The HPF does not replace the raw mag. In the current architecture, raw mag handles the basic candidate gating, and the HPF signal feeds the baseline system.
STA/LTA ratio
STA is the short-term average: the mean of hpfMag over a brief recent window, capturing the energy of the current moment.

LTA is the long-term average: the mean over a longer window representing the ambient baseline. One critical detail: the most recent interval is excluded from the LTA calculation, so that the current spike doesn't contaminate its own baseline.

The baseline is only trusted once enough samples have accumulated.

The ratio

The epsilon floor prevents the ratio from exploding when the environment is perfectly still.
This gave us something the raw magnitude couldn't: context. A moderate spike in a quiet room is a clear event. The same spike on a vibrating train is background noise. And a weak tap that barely misses the magnitude threshold can still be rescued if the baseline was unusually quiet, because the ratio is high even though the absolute number is low.
Candidate energy gate
This is where mag and STA/LTA combine into a single pass/fail decision for each tap candidate. The logic branches depending on whether the candidate is the first tap in a sequence or a subsequent one.
The basic path checks whether the raw mag exceeds the mode-specific minimum.

A strong bypass lets through candidates whose mag is well above the minimum, regardless of the STA/LTA ratio. If the impact is clearly large, we don't need baseline confirmation.

The adaptive rescue handles near-misses: candidates whose mag falls just below the minimum but whose STA/LTA ratio is very high, confirming the spike was clearly distinct from the baseline.

The first tap in a sequence has no cadence protecting it. There is no prior tap to validate against, so the false positive risk is highest. When the baseline is ready, the first tap must either show a sufficient STA/LTA ratio or be a strong bypass.

Subsequent taps (second in a double, second and third in a triple) are already inside a timing sequence. The cadence itself is a powerful filter, so the energy gate is simpler.

Every threshold in this gate was chosen by the same method: find the cases where legitimate taps fail, measure how far below the line they fall, open a rescue path exactly that wide, and put a second condition on it so the rescue doesn't let noise through.
Typing
We built all of the above, started using it as a working demo, and hit a problem we hadn't thought about at all.
Every keystroke sends a small impulse through the laptop body. Most keystrokes are individually below the magnitude threshold. But aggressive typing on certain keyboards produces deltas that overlap with light taps.
The fix is blunt. If a key was pressed within a guard period, all tap candidates are suppressed.

This is a guard, not a filter. It doesn't analyze the signal. It closes the window entirely. It was the first layer added not because the signal was ambiguous, but because a completely different activity produces an overlapping signal.
Timing gate
Everything up to this point detects single impacts. But the gesture is a double-tap or triple-tap: two or three taps in quick succession. That means detecting a cadence, not just an event.
We had team members tap the desk and the laptop in pairs and triples, at whatever speed felt natural, and recorded the gaps. The distribution gave two boundaries.

For triple-tap, both gaps must satisfy this independently.

Below the minimum, two spikes aren't two taps. They're one tap's ringing, the same physical impact producing multiple peaks as the energy bounces around the chassis. Above the maximum, two taps aren't part of the same gesture. They're separate, unrelated events.
The minimum started higher. It was lowered after real triple-taps from fast hands showed inter-tap intervals that were being debounced as ringing. The final value sits just above the range where single-impact echoes cluster. That margin is thin.
This is where the three conditions start fighting each other visibly. Lowering the minimum gap to catch fast taps (condition 1: answer every time) moves the boundary closer to ringing (condition 3: don't answer uninvited). Every threshold in this system sits at a negotiated coordinate between two conditions pulling in opposite directions.
Two additional guards live at this level.
A cooldown period after a successful detection prevents the same physical gesture from emitting twice.

A settling guard suppresses new sequences immediately after the laptop was moved or slid. The transient as the device comes to rest can mimic the opening tap of a new sequence. The guard triggers only when the Z-axis range is low (superficially stable) and the gyro average is high (the device was just in motion). This combination distinguishes post-movement settling from a genuinely quiet baseline.

where lastUnstableTs is updated whenever

Grass
One more metric that exists in the system but is not used as a gate. Grass measures the average jerk in a window before the peak, representing the baseline vibration level just prior to the candidate impact.

Finger taps tend to have a quiet pre-peak baseline. Desk impacts and set-down shocks tend to have a higher grass value because the ringing starts before the peak or persists from a prior event.
Grass is not a gate because in a multi-tap sequence, the preceding taps leave residual energy that raises the grass value even for legitimate taps. Using it as a gate would penalize the very cadence we're trying to detect. It remains a logging and QA metric.
The pipeline
The final detection path for normal (desk) mode:
No single layer makes the decision. Each layer removes one class of false signal, and a candidate must survive all of them. The order is deliberate. The HPF and STA/LTA baseline must run continuously regardless of whether a candidate passes or fails, so they can't be deferred. The energy gate sits on top of them. After that, broad guards suppress known conflicts (typing, cooldown, settling), then structured validation (timing, stability, crest) makes the final pass.
We ran well over two thousand test cycles across this pipeline.
The per-candidate combined condition:

The final emit conditions:

Different desk surfaces, floor materials, postures, vibration environments. Each threshold was tuned by looking at the numbers, adjusting, and testing again. Many were adjusted dozens of times. There is no threshold in the final system that was derived analytically. Every one was found by repeatedly hitting a laptop and reading the logs.
An obvious question is why we didn't train a classifier. We had labelled data and a ten-layer pipeline of hand-tuned thresholds. The answer is debuggability. Our framework says presence is set by the worst experience. When a false positive happens, we need to know exactly which layer failed and why, so we can fix that case without breaking others. A learned model gives you a probability. A pipeline of explicit gates gives you a stack trace. With data from six testers, a model would also overfit to their tap styles and generalize poorly. The hand-tuned pipeline at least fails in ways we can read.
What the three conditions look like as engineering
Roughly two thirds of the system's complexity serves condition 3 (don't answer uninvited). The typing guard, cooldown, settling guard, stability gate, crest gate, magnitude ceiling, timing floor, and later the set-down impact guard. All exist to say no.
About one third serves condition 1 (answer every time). The adaptive energy rescue, crest recovery path, loosened thresholds for later taps in a triple sequence, and the lowered timing floor. All exist to say yes to something the stricter layers would have rejected.
Nearly every value that serves condition 1 directly threatens condition 3. Lowering the magnitude floor to catch light taps opens the gate to vibrations. Widening the crest recovery to save borderline impulses lets ringing through. Shortening the minimum gap to accept fast triple-taps moves into the zone where single-tap echoes live. Each rescue has a second gate behind it (an STA/LTA check, a baseline readiness requirement, a stricter alternative threshold) to keep condition 3 intact while condition 1 reaches further.
Condition 2 (same every time) is the quietest in the code but the most structurally demanding. It's the reason every threshold was tested not in one environment but across many, because "same every time" means same across surfaces, postures, and device models, not just same across consecutive taps on the same desk.
Everything above works. On rigid desks, resonant desks, across different flooring materials, in quiet rooms and noisy ones. The pipeline handles all of it.
Then we sat on a beanbag.



