haptic/0.1-draft · an open format for touch
Touch,
written down.
haptics.md is an open, portable haptic format and registry. One readable
.hap.md file compiles to the best feedback each platform can deliver,
degrades honestly when it can't, and is legible enough for an AI agent to author
without guessing.
Spikes are transients. Blocks are continuous events.
A haptic you can read.
A .hap.md is a Markdown file with three parts: YAML frontmatter for the
registry, prose that describes the feel in plain language, and exactly one fenced
haptic block: the JSON that is the compilable source of truth. Humans
review it in a pull request. Agents author it without guessing.
---
id: pull-to-refresh
intent: impact.light
platforms: [ios, android, web:best-effort]
feel: A light tick the instant the threshold is crossed.
--- Fires once, at the moment the user pulls past the refresh
threshold. A single light tick that says "released, we've
got it." Not a confirmation of success; that comes later. ```haptic
{
"haptic": "0.1",
"intent": "impact.light",
"track": [{ "at": 0, "type": "transient", "intensity": 0.6, "sharpness": 0.7 }],
"fallbacks": { "web": [10], "reduced": "impact.light" }
}
``` Compiles high. Degrades honestly.
Every target resolves top-down, deterministically, and stops at the first rung it can deliver. A device with no actuator is a valid target that produces nothing. No runtime ever reports that intensity worked when it was flattened away.
- 1
intentA native semantic primitive. The best feel the platform ships.
- 2
trackThe synthesized timeline of transient and continuous events.
- 3
fallbacks.webDuration-only milliseconds. Crude, by design.
- 4
silent no-opNothing plays. Still a valid outcome.
.sensoryFeedback(.impact(weight: .light),
trigger: didCrossThreshold) vib.vibrate(startComposition()
.addPrimitive(PRIMITIVE_LOW_TICK, 0.6f)
.compose()) navigator.vibrate?.([10]) Agents build the screens. Now they can build the feel.
AI agents scaffold entire UIs all day and never add a line of touch feedback, because there was nothing for them to reach for. haptics.md ships authored Skills, an llms.txt a model can load directly, and a registry legible enough to author against. A compiler-backed MCP server comes next.
$ npx haptics add pull-to-refresh --platform ios
✓ resolved pull-to-refresh from the vendored registry
✓ copied into your project
you own the file. no runtime dependency. works offline. Haptic braille, held honestly.
The reason this exists is accessibility: a standardized, hardware-free way for any phone with a vibration motor to convey a braille cell, a digit, a wayfinding cue, a silent confirmation. Documented once, so every app stops inventing its own incompatible buzz.
≈ one cell per second. That is the physics.
A complement, never a replacement
Blind and low-vision people already use phones fluently with screen readers and refreshable braille displays. This adds one channel. It does not "enable" anyone.
Slow, by physics
One motor means roughly one braille cell per second: letters, digits, short codes, wayfinding cues. Not paragraphs. Any demo implying otherwise is wrong.
Web-limited
On the web this is Android-Chrome-only and a silent no-op on iPhone Safari and desktop. Rich braille needs a native app.
A draft, until braille readers shape it
No default, timing, or claim is "recommended" until blind braille readers and the braille authorities have shaped it. It is published in the open to invite exactly that.
36 patterns, ready to feel.
The interactions that deserve feedback: likes, holds, thresholds, detents, outcomes. Each pattern ships ready-to-paste SwiftUI, UIKit, or Core Haptics code, plus a Markdown block built to paste straight into an LLM prompt.
Tapping a heart / like button
User taps a heart to like a post; the icon pops and a soft tap confirms it.
Pull-to-refresh threshold
The moment a pull gesture crosses the "release to refresh" threshold, plus a success on completion.
Hold-to-confirm with rising tension
A "hold 1.5 s to confirm" button: vibration ramps up while holding, success fires at completion.
Open by design.
Apache-2.0 code. CC0 patterns. A draft spec published in the open to be argued with, and a registry anyone can add a pattern to in ten minutes.