macslap
v1.0.0
Published
Detect physical slaps/taps on Apple Silicon MacBooks via the accelerometer. Run any function on slap.
Downloads
24
Maintainers
Readme
macslap
Run any function when your MacBook gets slapped. 🖐️💻
A Node.js package that reads the Apple Silicon accelerometer (Bosch BMI286 IMU) via IOKit HID to detect physical taps, slaps, and hits on your MacBook. Register any callback — deploy code, trigger webhooks, control music, or whatever you want.
Requirements
- Apple Silicon MacBook (M1 Pro, M2, M3, M4+)
- macOS 14+ (Sonoma)
- Node.js 16+
- Must run with
sudo(IOKit HID requires root)
Check if your Mac supports it:
ioreg -l -w0 | grep -A5 AppleSPUHIDDeviceInstall
npm install macslapQuick Start
const { onSlap } = require('macslap');
onSlap((event) => {
console.log(`Slapped! Force: ${event.force.toFixed(3)}g`);
console.log(`Type: ${event.type}`); // "tap", "slap", "hit", or "punch"
});sudo node my-script.jsAPI
Simple API
const { onSlap, onTap, onHit, onPunch, onForce, isSupported } = require('macslap');
// Any impact
onSlap((event) => { /* ... */ });
// Gentle taps only (< 0.15g)
onTap((event) => { /* ... */ });
// Hard hits (0.5 - 1.0g)
onHit((event) => { /* ... */ });
// Very hard impacts (> 1.0g)
onPunch((event) => { /* ... */ });
// Custom force range
onForce(0.1, 0.3, (event) => { /* ... */ });
// Check hardware support
if (isSupported()) { /* good to go */ }All functions return an unsubscribe function:
const unsub = onSlap((event) => { /* ... */ });
// Later:
unsub(); // stop listeningOptions
onSlap(callback, {
cooldown: 750, // ms between events (default: 750)
threshold: 0.05, // min force in g to trigger (default: 0.05)
});Event Object
{
x: -0.012, // X-axis acceleration in g
y: 0.003, // Y-axis acceleration in g
z: -1.024, // Z-axis acceleration in g
force: 0.234, // magnitude of impact in g
type: "slap", // "tap" | "slap" | "hit" | "punch"
timestamp: 1711500000000
}Force Classification
| Type | Force Range | Description |
|------|-------------|-------------|
| tap | < 0.15g | Gentle touch, light tap |
| slap | 0.15 - 0.5g | Normal slap |
| hit | 0.5 - 1.0g | Hard hit |
| punch | > 1.0g | Full send |
Advanced API: SlapDetector
For more control, use the SlapDetector class directly:
const { SlapDetector } = require('macslap');
const detector = new SlapDetector({
threshold: 0.02, // ultra sensitive
cooldown: 300,
});
// Event emitter pattern
detector.on('slap', (event) => { /* any impact */ });
detector.on('tap', (event) => { /* gentle */ });
detector.on('hit', (event) => { /* hard */ });
detector.on('ready', () => console.log('Listening...'));
// Conditional handlers
detector.when('punch', (event) => {
console.log('That was violent');
});
// Custom conditions
detector.when(
(event) => event.force > 0.3 && event.x > 0,
(event) => console.log('Slapped from the right!')
);
// Read raw values
const reading = detector.read();
// { x, y, z, running, calibrated }
detector.start();
// Later:
detector.stop();Examples
Deploy on Slap
const { onSlap } = require('macslap');
const { exec } = require('child_process');
onSlap((event) => {
console.log('🚀 SHIPPING IT');
exec('git push origin main');
});Webhook Trigger
const { onSlap } = require('macslap');
onSlap(async (event) => {
await fetch('https://your-webhook.com', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
event: 'slapped',
force: event.force,
timestamp: new Date().toISOString(),
}),
});
});Tamper Detection
const { SlapDetector } = require('macslap');
const detector = new SlapDetector({
threshold: 0.02, // ultra sensitive
cooldown: 2000,
});
detector.on('slap', (event) => {
console.log('🚨 SOMEONE TOUCHED YOUR LAPTOP');
// Take photo, send notification, sound alarm, etc.
});
detector.start();Music Control with Tap Patterns
const { SlapDetector } = require('macslap');
const { exec } = require('child_process');
const detector = new SlapDetector({ threshold: 0.03, cooldown: 300 });
let taps = [], timer;
detector.on('slap', () => {
taps.push(Date.now());
clearTimeout(timer);
timer = setTimeout(() => {
const count = taps.length;
taps = [];
if (count === 1) exec(`osascript -e 'tell app "Spotify" to playpause'`);
if (count === 2) exec(`osascript -e 'tell app "Spotify" to next track'`);
if (count === 3) exec(`osascript -e 'tell app "Spotify" to previous track'`);
}, 500);
});
detector.start();How It Works
The package uses a native C++ addon (compiled via node-gyp) that:
- Opens the
AppleSPUHIDDevicein the IOKit registry (vendor usage page0xFF00, usage3) - Registers an async HID input report callback at ~100Hz
- Parses 22-byte reports: X/Y/Z as int32 LE at byte offsets 6, 10, 14 (divided by 65536 for g values)
- Computes a rolling baseline and detects deviations (slaps)
- Fires events back to JavaScript via N-API thread-safe functions
Troubleshooting
"Accelerometer not found"
- Run with
sudo:sudo node script.js - Check hardware:
ioreg -l -w0 | grep AppleSPUHIDDevice - M1 (base) may not work — M1 Pro, M2+ confirmed
"Failed to load native module"
- Make sure Xcode Command Line Tools are installed:
xcode-select --install - Rebuild:
npm rebuild - Check Node.js version: needs 16+
Triggers during typing
- Increase threshold:
{ threshold: 0.1 }or higher - Increase cooldown:
{ cooldown: 1500 }
License
MIT
