See Dennis Hackethal’s full profile
  Log in or sign up to participate in this discussion.
With an account, you can revise, criticize, and comment on ideas.

Discussions can branch out indefinitely. You may need to scroll sideways.
Dennis Hackethal’s avatar
1st of 3 versions

web-haptics: Haptic Feedback Finally Comes to iOS Safari

lochie just published a package bringing haptic feedback to the web: ‘web-haptics’. I spent four years at Apple working mostly on UIs, so I pay close attention to design and UX. In terms of presentation, use case, and attention to detail, this package comes as close to perfect as I’ve seen.

The problem

On Android, haptics have been available through navigator.vibrate(), but iOS does not support this API. There is a workaround, though – the input element with the switch attribute:

html
<input type="checkbox" switch>

See this switch input in action here. On iOS, the input renders not as a regular checkbox but as a switch that can be toggled on or off:

Switch input

Open the link on your iPhone, tap the switch, and you’ll feel haptic feedback. As far as I know, this is currently the only known practical workaround to trigger haptic feedback on the web in iOS.

Implementation

lochie cleverly bases his package on this single workaround and pushes it to its limits. The key lines are here, creating a switch input on the fly:

javascript
const hapticCheckbox = document.createElement("input");
hapticCheckbox.type = "checkbox";
hapticCheckbox.setAttribute("switch", "");

The package also creates an associated label and clicks it to trigger haptic feedback on the connected input:

javascript
this.hapticLabel.click();

(My understanding is that iOS will not trigger the haptic feedback when the input is programmatically clicked – so the ‘workaround within the workaround’ is to click the associated label instead.)

Custom haptics (!)

But web-haptics can do more than just trigger a single haptic ‘pulse’. The trigger function lets you pass in your own patterns with pulses of different duration and intensity, and even delays.

Want a simple success haptic? Trigger two pulses, as shown on the package homepage:

javascript
trigger([
{ duration: 30 },
{ delay: 60, duration: 40, intensity: 1 },
])

Or simply call trigger('success') as a shortcut.

Here’s an error haptic I made based on Apple’s human interface guidelines:

javascript
trigger([
{ duration: 40, intensity: 0.7 },
{ delay: 40, duration: 40, intensity: 0.7 },
{ delay: 40, duration: 40, intensity: 0.9 },
{ delay: 40, duration: 50, intensity: 0.6 },
])

lochie’s website even gives you an editor to make custom haptics. As you click and drag, both pulses and code update in real time. This experience is developer bliss:

Haptic editor

Usage

Importing the package is easy. In Rails, for example:

ruby
# config/importmap.rb
pin "web-haptics", to: "https://esm.sh/web-haptics@0.0.6"
javascript
import { WebHaptics } from 'web-haptics';
// In a Stimulus controller somewhere
let haptics = new WebHaptics();
haptics.trigger('success'); // or 'error', 'warning', etc for built-in haptics
haptics.trigger([
{ duration: 40, intensity: 0.7 },
{ delay: 40, duration: 40, intensity: 0.7 },
])

Pass a debug option to the constructor to hear clicks in development/on desktop:

javascript
new WebHaptics({ debug: true });

Again, this package comes as close to perfect as I’ve seen. In fact, I already use it on Veritula: as you type in a text field, an error haptic plays when you exceed the max length (#4473).

I highly recommend web-haptics by lochie.