portable-haptics
v0.1.0
Published
Dependency-free browser haptics helper with navigator.vibrate and conservative iOS WebKit switch fallback.
Maintainers
Readme
Portable Haptics
Small dependency-free browser haptics helper for personal prototypes, mobile web toys, and browser games.
It uses navigator.vibrate() where available, and can fall back to the iOS Safari / iOS WebKit <input type="checkbox" switch> haptic-feedback behavior described in Safari release notes and community writeups. Unsupported environments safely return false.
This package is intentionally conservative: it does not monkeypatch navigator.vibrate, does not reparent the DOM, does not install framework adapters, and does not use audio fallback. Prior art was reviewed; this package does not vendor or copy third-party code.
Package Files
portable-haptics.mjs: ESM library.portable-haptics.d.ts: TypeScript declarations.README.md: package usage notes.LICENSE: MIT license.
demo.html and RESEARCH.md are local repository aids and are not published to npm.
Install
From a packed tarball:
npm install ./portable-haptics-0.1.0.tgzFrom a local folder:
npm install ./portable-hapticsAfter publishing:
npm install portable-hapticsQuick Use
<button id="tap">Tap</button>
<script type="module">
import { haptic } from "portable-haptics";
document.querySelector("#tap").addEventListener("click", () => {
haptic("light");
});
</script>Instance API
import { createHaptics } from "portable-haptics";
const haptics = createHaptics({
cooldownMs: 32,
maxPulsesPerMinute: 180,
});
button.addEventListener("click", () => {
haptics.play("selection");
});
successButton.addEventListener("click", () => {
haptics.play("success");
});Available built-in effects:
selectionlightmediumheavysuccesswarningerror
For one-off custom effects:
haptics.play({ vibrate: [20, 30, 20], pulses: [0, 70] });
haptics.pulse(3, 60);vibrate is used by browsers that support the Vibration API. pulses is used by the iOS Safari switch fallback, where each pulse is one hidden switch click.
Diagnostics
console.table(haptics.diagnose());diagnose() reports the active capability path and warnings such as ios-switch-hack-disabled-for-version.
Design Goals
- Keep it tiny and dependency-free.
- Make haptics an enhancement, never required behavior.
- Avoid global monkeypatching and app-wide DOM tricks.
- Prefer explicit calls from user interaction handlers.
- Disable the iOS switch fallback on parsed iOS versions newer than the known working range unless explicitly forced.
Publishing Checklist
- Choose the final package name, preferably scoped, such as
@your-scope/portable-haptics. - Re-check name availability on npm immediately before publishing.
- Run
npm run check. - Run
npm pack --dry-run. - Publish with
npm publish --access publicwhen the package name and license are final.
Important Notes
- iOS Safari does not support the Vibration API.
- The iOS fallback relies on Safari 18's native haptic feedback for
<input type="checkbox" switch>. - A similar package,
ios-haptics, documents that Apple patched its programmatic label-click path in iOS 26.5. This package therefore defaultsiosSwitchMaxVersionto[26, 4]. - The fallback cannot control duration or strength like
navigator.vibrate(). - You can opt into a forced attempt with
createHaptics({ enableIOSSwitchHack: "force" }), but treat it as best-effort. - Call haptics from a user interaction such as
pointerdown,click, or a game input event. - This is an implementation quirk and may change in future Safari versions.
Recommended Pattern
Use haptics as an enhancement, never as required feedback.
const didHaptic = haptics.play("light");
if (!didHaptic) {
// Keep visual/audio feedback working here.
}