# WAI Extension: Haptics > Status: Draft. Phase 2 of the cargo roadmap. Touch as instructions: > ship a keyframed intensity/sharpness envelope, the sink reconstructs an > actuator signal. Conformance is **sample-equivalence**. Reference impl: > the `haptic` feature of `wai-rs`; corpus: `haptic-conformance/`; live > sink: wai.transaction.science/haptics. Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174. ## 1. Scope and model A `wai.haptic.signal` object is a per-channel keyframed envelope of **intensity** and **sharpness** (the Core-Haptics-shaped pair every modern actuator API maps to) that the sink reconstructs to a per-sample buffer and plays on whatever actuators it has. It is lever 3 (instructions-at-the-sink) for touch — an effect is a few hundred bytes, not a recorded waveform. In scope: the container, the deterministic reconstruct contract, the **sample-equivalence** conformance criterion, the fallback floor, the receipt profile. Out of scope: a transport; actuator hardware specifics (the channel→actuator binding is the sink's); carrier synthesis (the signal is an amplitude/sharpness envelope, not a modulated waveform — the deterministic floor stays trig-free). Relationship to the core spec: mirrors interactive-worlds / spatial audio. The reconstruct runs in the `wai.det.fixed64` floor; `media` takes the informational value `"haptic"`; `wai.haptic.signal` is authoritative. ## 2. Container (`WHAP`) ``` "WHAP" | u16 n_sections | section table (u8 kind, u32 off, u32 len) | sections ``` | kind | section | required | |---|---|---| | `0x01` | contract (canonical JSON) | REQUIRED | | `0x02` | channel signals | REQUIRED | ### 2.1 Contract ```json { "sample_rate": 1000, "numeric": "wai.det.fixed64", "channels": 1, "duration": 1500 } ``` Haptic rates are low (≈1 kHz); `duration` is in samples. **Reader limit.** A reader MUST NOT refuse a signal for its length while `duration × channels` is at most 134 217 728 (2²⁷, the audio limit of SPEC §2), and MAY refuse one past it (`duration_too_long`), when it reads the contract, before it reconstructs anything. A conforming encoder writes no signal past it. ### 2.2 Channel signals `n_channels u32`, then per channel: `n_keys u16`, then `n × ( pos_sample u64 | intensity i64 Fx | sharpness i64 Fx )`. Both fields are in `[0, 1]`. Between keyframes they linear-interpolate in `Fx`; before the first / after the last key they hold. ## 3. Reconstruct contract For each channel, for each sample `t ∈ [0, duration)`: `intensity(t)`, `sharpness(t)` = the `Fx` linear interpolation of the channel's keyframes at `t`, each clamped to `[0,1]` and quantized to a `u8` (`round(v · 255)`, half away from zero, in integer space). The canonical reconstruction is, per channel, the interleaved `[intensity, sharpness]` `u8` pairs. ## 4. Conformance — sample-equivalence > Given the same `wai.haptic.signal`, a conforming sink MUST reconstruct > the identical canonical `u8` buffer, hence the identical BLAKE3 — on > every machine, no tolerance parameter. ``` signal_hash = BLAKE3("wai:haptic-signal\x01" || per-channel u8 pairs) ``` ## 5. Fallback floor A sink with no actuators ignores the signal (haptics are supplementary); there is no perceptual substitute to fall back *to*, so `fallback` is typically `null`. A sink MAY surface the envelope visually (a waveform) — presentation, not conformance. ## 6. Receipt (JWP profile) `signal_hash` is the identity; the group's objects are per-channel reconstruction blocks, Merkle-bound; one Ed25519 signature (verified as SPEC §7 *Signatures* requires) over signal_hash + root + `work` (= duration × channels) + carried `joules_micro`; `parent_receipt_hash` chains — the worlds/audio receipt shape reused. The figure's acquisition class rides beside it as an optional signed label ([energy-measurement](energy-measurement.md) §2.8): absent, the receipt keeps its legacy bytes and the figure is unlabelled; present, it is inside the signature. The reference meter labels every figure it measures `OnChipCounter`, with its declared uncertainty. ## Appendix Sample-equivalence is replay/mixdown-equivalence with the tick replaced by the haptic sample. The envelope is trig-free by construction (amplitude + sharpness, not a modulated carrier), so the `Fx` floor reconstructs it exactly with `+,−,×,÷` alone — the simplest cargo class to make bit-exact, which is why it's the fast Phase-2 win.