Documentation
Everything you need to use bzzz.
Installation
Install the package from your command line.
npm install bzzz
Basic usage
Import the haptics singleton and call a named pattern. Always trigger from a user interaction — browsers may reject playback outside click, tap, or keyboard handlers.
Patterns
Five built-in patterns for common interaction feedback. Each returns a PlaybackResult with the output mode.
Rising 3-pulse. Use after save, submit, or successful completion.
haptics.success()
// → { mode: "haptics" | "audio" | "none" }play()
Play a raw PatternBlock[] array when you need direct control. Each block is a pulse (vibration) or gap (pause). Intensity ranges from 0 to 1 (default: 1.0) — lower values produce softer feedback.
Capabilities & fallbacks
The runtime uses native haptics when available and falls back to audio. Every call returns a PlaybackResult reporting what actually happened.
When the user has prefers-reduced-motion: reduce enabled, all feedback is automatically suppressed and methods return { mode: "none" }. Browsers may also reject playback outside user-triggered interactions — that is why mode reporting matters.
Configuration
Globally enable or disable haptic feedback. When disabled, all methods return { mode: "none" }.
haptics.setEnabled(false); // mute all feedback haptics.isEnabled(); // → false haptics.setEnabled(true); // resume haptics.success(); // → { mode: "haptics" }
Output mode
Control which output channels are used. The default "auto" mode uses haptics when available and falls back to audio — never both at once.
haptics.setOutput("audio"); // audio only, never vibrate haptics.setOutput("haptics"); // vibration only, never play audio haptics.setOutput("both"); // always fire both when available haptics.setOutput("auto"); // default: haptics if available, else audio haptics.getOutput(); // → "audio"
Pass output to createHaptics() to set the mode per-instance from creation.
Call dispose() to clean up the AudioContext and any internal DOM elements. Safe to call multiple times.
haptics.dispose();
createHaptics()
Create isolated instances with custom pattern registries. Each instance has its own enabled state and does not affect the global singleton.
Add patterns after creation with register(). Call dispose() when the instance is no longer needed.
appHaptics.register("delete", [ { type: "pulse", duration: 40 }, { type: "gap", duration: 18 }, { type: "pulse", duration: 44 } ]); appHaptics.play("delete"); appHaptics.dispose();
React
Hooks for React 18+. Available from bzzz/react — no extra packages or providers needed.
useHaptics
Wraps the global singleton with stable callbacks safe for dependency arrays. Does not cause re-renders.
import { useHaptics } from "bzzz/react"; function SaveButton() { const { success, error } = useHaptics(); async function handleSave() { try { await save(); success(); } catch { error(); } } return <button onClick={handleSave}>Save</button>; }
useCreateHaptics
Creates an isolated instance scoped to the component. Automatically disposes on unmount.
import { useCreateHaptics } from "bzzz/react"; function GameController() { const haptics = useCreateHaptics({ patterns: { hit: [ { type: "pulse", duration: 30, intensity: 1.0 }, { type: "gap", duration: 20 }, { type: "pulse", duration: 50, intensity: 0.8 }, ], }, }); return <button onClick={() => haptics.play("hit")}>Attack</button>; }
Enable / Disable
Both hooks expose isEnabled() and setEnabled() for building settings UIs.
const { isEnabled, setEnabled } = useHaptics(); setEnabled(false); // mute setEnabled(true); // resume isEnabled(); // → boolean
Browser support
bzzz adapts to each platform automatically. Native vibration is preferred, with audio click fallback when unavailable.
| Platform | Haptics | Audio | How |
|---|---|---|---|
| Android Chrome / Edge / Opera | Yes | Yes | Vibration API |
| Android Samsung Internet | Yes | Yes | Vibration API |
| Android Firefox | No | Yes | Audio only (Vibration API removed v129) |
| iOS 18+ (all browsers) | Yes | Yes | Taptic Engine (switch hack) |
| iOS < 18 (all browsers) | No | Yes | Audio only |
| Desktop browsers | No | Yes | Audio only |
| SSR / Node.js | No | No | Silent (none) |
iOS Taptic Engine (input switch hack)
iOS doesn't support the Vibration API. Instead, bzzz uses a hidden <input type="checkbox" switch> element. Toggling it triggers the Taptic Engine at the WebKit level — works in all iOS browsers.
| Browser | navigator.vibrate | Switch hack | bzzz mode |
|---|---|---|---|
| Android Chrome | Yes | No | haptics |
| Android Firefox | No (removed v129) | No | audio |
| iOS Safari | No | Yes (iOS 18+) | haptics |
| iOS Chrome | No | Yes (iOS 18+) | haptics |
| iOS Firefox | No | Yes (iOS 18+) | haptics |
On desktop, demos return { mode: "audio" } or { mode: "none" }. For the full haptic experience, try on a mobile device.
API Reference
haptics
Global singleton. Import from bzzz.
| Method | Returns | Description |
|---|---|---|
selection() | PlaybackResult | Light 2-pulse tap |
success() | PlaybackResult | Rising 3-pulse |
error() | PlaybackResult | Urgent 4-pulse buzz |
toggle() | PlaybackResult | Symmetric 3-pulse |
snap() | PlaybackResult | Escalating 5-pulse ramp |
play(pattern) | PlaybackResult | Play a PatternBlock[] |
getCapabilities() | CapabilityState | Check device support |
setEnabled(bool) | void | Enable or disable globally |
isEnabled() | boolean | Check enabled state |
setOutput(mode) | void | Set output channel routing |
getOutput() | OutputMode | Current output mode |
dispose() | void | Clean up AudioContext and DOM |
createHaptics(options?)
Factory for isolated instances. Import from bzzz.
| Option | Type | Default |
|---|---|---|
patterns | Record<string, PatternBlock[]> | {} |
output | OutputMode | "auto" |
Returns a HapticsInstance with play(name | pattern), register(name, pattern), getCapabilities(), setEnabled(), isEnabled(), and dispose().
OutputMode
| Value | Description |
|---|---|
"auto" | Haptics if available, audio fallback — never both (default) |
"haptics" | Vibration / Taptic Engine only — never plays audio |
"audio" | Audio clicks only — never vibrates |
"both" | Fire both channels simultaneously when available |
PlaybackResult
| Prop | Type | Description |
|---|---|---|
mode | "haptics" | "audio" | "none" | Primary output channel used |
haptics | boolean | Whether vibration / Taptic Engine fired |
audio | boolean | Whether audio click fired |
CapabilityState
| Prop | Type | Description |
|---|---|---|
haptics | boolean | Vibration API available |
audio | boolean | Web Audio API available |
ios | boolean | iOS / iPadOS device |
reducedMotion | boolean | prefers-reduced-motion active |
PatternBlock
| Type | Fields | Description |
|---|---|---|
pulse | duration: number, intensity?: number | Vibration. Intensity 0–1 (default: 1.0) |
gap | duration: number | Pause between pulses |
Pattern Editor
Draw a pattern or start from a preset. Fine-tune the blocks, preview and copy the code.