Dennis Hackethal
@dennis.hackethal·Joined Jun 2024·Ideas
Founder Veritula. Author. Software engineer. I study the mind and build tools for thinkers. Ex Apple. Translator of The Beginning of Infinity.
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:
1
<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:

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:
123javascriptconst 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:
1javascriptthis.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:
1234javascripttrigger([{ 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:
123456
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:

Usage
Importing the package is easy. In Rails, for example:
12
# config/importmap.rbpin "web-haptics", to: "https://esm.sh/web-haptics@0.0.6"
123456789
import { WebHaptics } from 'web-haptics';// In a Stimulus controller somewherelet haptics = new WebHaptics();haptics.trigger('success'); // or 'error', 'warning', etc for built-in hapticshaptics.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:
1
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.
web-haptics: Haptic Feedback Finally Comes to iOS Safari
lochie just published a package bringing haptic feedback to the web on iOS: 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:
1
<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:

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:
123javascriptconst 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:
1javascriptthis.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:
1234javascripttrigger([{ 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:
123456
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:

Usage
Importing the package is easy. In Rails, for example:
12
# config/importmap.rbpin "web-haptics", to: "https://esm.sh/web-haptics@0.0.6"
123456789
import { WebHaptics } from 'web-haptics';// In a Stimulus controller somewherelet haptics = new WebHaptics();haptics.trigger('success'); // or 'error', 'warning', etc for built-in hapticshaptics.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:
1
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.
Users can now pick a custom @username.
I just changed mine to @dennis.hackethal. (Period instead of hyphen.) Old profile links are automatically redirected. Old @mentions are automatically updated.
@usernames used to be assigned automatically based on a user’s first and last name. Now, users have more choice.
To change your @username, go to Settings.
Users can now pick a custom @username.
I just changed mine from @dennis-hackethal* to @dennis.hackethal. (Period instead of hyphen.) Old profile links are automatically redirected. Old @mentions have an asterisk and explain the change on hover while linking to the updated URL.
@usernames used to be assigned automatically based on a user’s first and last name. Now, users can choose.
To change your @username, go to Settings.
Can there be such a thing as too much fun?
Can there be such a thing as too much profit?
In both cases, I think ‘no’. And I wonder if the fear of ‘too much’ fun and ‘too much’ profit is fundamentally the same thing.
Like, when parents worry that their kids are having too much fun, and when socialists are suspicious of companies turning a profit… is that an expression of the same fear?
Maybe the role of profit in the economy is the same as that of fun in a single mind: it signals successful discovery of common preferences.
Link directly to user-bio field for convenience
This was a big week for Veritula. Users can now:
- ✍️ Post ideas to their own profile, and others’ profiles, outside of discussions. (Beta)
- 🔄 Repost ideas.
- 🙋♂️ Set a profile description under Settings. Tell others about yourself!
- 💻 Embed discussions on third-party sites. Similar feature to Disqus and Giscus. Ideal for comments on blogs, say. See the embed code under Settings. (Early beta)
- 🌉 Show images while preserving viewers’ privacy.1
To render an image, use this markdown syntax:

This was a big week for Veritula. Users can now:
- ✍️ Post ideas to their own profile, and others’ profiles, outside of discussions. (Beta)
- 🔄 Repost ideas.
- 🙋♂️ Set a profile description under Settings. Tell others about yourself!
- 💻 Embed discussions on third-party sites. Similar feature to Disqus and Giscus. Ideal for comments on blogs, say. See the embed code under Settings. (Early beta)
- 🌉 Show images while preserving viewers’ privacy.1
To render an image, use this markdown syntax:

#2844·Dennis HackethalOP, 4 months agoA Life Guided by Reason
In #2281, I explain how Veritula helps you make rational decisions – in other words, how to live rationally, ie, a life guided by reason. (I use the words ‘reason’ and ‘rationality’ synonymously. The same goes for ‘unreason’ and ‘irrationality’.)
A life guided by reason defies the dominant, Kantian philosophy of our age. Ayn Rand summarized that philosophy as, “Be rational, except when you don’t feel like it.”1 In other words, it says to mix reason and unreason; to stray from rationality arbitrarily; to be rational only sometimes. It claims that there is a necessary clash between reason and emotion. It is an attack on reason, an attempt to do the impossible – and it leads to dissatisfaction with yourself and conflict with others.
If you are rational only sometimes, if you stray from rationality arbitrarily, then you are irrational. There is no third option. This conclusion can be proven easily: if you tried to stray from rationality non-arbitrarily, ie, if you tried to come up with a considered argument for straying from rationality, you could only do so by following the steps in #2281. And those steps are the application of rationality again.
So it’s impossible to stray from rationality rationally. There is no gray area between reason and unreason. Rationality has an all-or-nothing character. This does not mean that reason has to snuff out all emotion. On the contrary: there is no necessary clash between rationality on the one hand and emotion on the other. Rationality means finding unanimous consent between emotion, explicit thought, inexplicit thought, and any other kind of idea.
If you follow the steps in #2281 consistently, then you are always rational. A life worth living is one guided exclusively by reason. Consistent application of rationality may be difficult at first, but with practice, it will get easier. Master it, and you will have a fighting chance of becoming what David Deutsch calls a beginning of infinity.
Ayn Rand. Philosophy: Who Needs It. ‘From the Horse’s Mouth’ (p. 110). 1975. Kindle Edition. As quoted previously.
#2281·Dennis HackethalOP revised 5 months agoRational Decision-Making
Expanding on #2112…
If an idea, as written, has no pending criticisms, it’s rational to adopt it and irrational to reject it. What reason could you have to reject it? If it has no pending criticisms, then either 1) no reasons to reject it (ie, criticisms) have been suggested or 2) all suggested reasons have been addressed already.
If an idea, as written, does have pending criticisms, it’s irrational to adopt it and rational to reject it – by reference to those criticisms. What reason could you have to ignore the pending criticisms and adopt it anyway?
#554·Tom Nassis, over 1 year agoVeritula deserves to scale to the size of Wikipedia.
But it never will, unless its users innovate.
How can the global success of Wikipedia inspire Veritula?
This is the first idea posted straight to my profile, outside of discussions.