Skip to main content

Hapbeat Python SDK

Drive Hapbeat haptic devices from Python over Wi-Fi UDP. For researchers (PsychoPy / Jupyter / ROS), media artists, and anyone prototyping haptics in Python.

📚 Docs: https://devtools.hapbeat.com/docs/sdk-integration/

A script can drive Hapbeat with a few lines. The fire side (play / stop) and the tuning side (EventMap) are kept orthogonal and linked only by event id.

Install

pip install hapbeat-python-sdk        # core (zero dependencies, stdlib socket only)
pip install "hapbeat-python-sdk[osc]"   # + generic OSC bridge (TouchOSC / Max / TD)

With pipx you get the hapbeat CLI (including the launchpad below) in an isolated environment: pipx install hapbeat-python-sdk. Note that pipx does not make import hapbeat available to your own scripts — for the examples/ use a normal pip install in a venv.

Try everything from one page (launchpad)

hapbeat launchpad          # opens http://127.0.0.1:7100 in your browser

A single local web page to fire events, run a metronome, a breathing pacer, or send Morse — start and stop them live, no per-example launching. It serves a tiny stdlib HTTP server that relays button presses to the device over UDP (browsers can't send UDP directly). Works great with pipx install hapbeat-python-sdk.

Quick start

import hapbeat

hb = hapbeat.connect(app_name="MyExperiment")  # opens the UDP socket + keep-alive
hb.play("sample-kit.sine_100hz", gain=0.3)   # fire event "sample-kit.sine_100hz" at gain 0.3
hb.play("sample-kit.sine_100hz")             # gain omitted -> kit baseline intensity
hb.play("sample-kit.sine_100hz", pan=-1)     # pan: -1 left / 0 centre / +1 right
hb.stop("sample-kit.sine_100hz")
hb.stop_all()
hb.close()

or as a context manager:

with hapbeat.connect(app_name="MyExperiment") as hb:
    hb.play("sample-kit.sine_100hz")

"sample-kit.sine_100hz" must be an event id present in the kit deployed to the device (via Hapbeat Studio). The SDK sends the instruction; the waveform lives in the kit on the device.

Discovery

for dev in hb.discover(timeout=1.5):
    print(dev.ip, dev.address, dev.firmware_version)

How packets are sent

Commands are unicast to devices that answered a PING and use broadcast only while no device is known. Clip streams are stricter: they start only on exact PONG-reported endpoints and remain Deferred while none matches; stream packets are never broadcast. Wi-Fi APs hold broadcast frames until their next DTIM beacon (100–300 ms) whenever any client on the AP is power-saving, which is heard as late haptics and stuttering streams; unicast is not batched that way. The 5-second keep-alive PING keeps that table current, so leave keepalive=True.

Pass connect(unicast=False) to always broadcast — worth it when many devices must fire in lockstep, since one broadcast reaches them all at once while N unicasts go out one after another.

EventMap — the tuning side (optional)

Keep per-event default gains in one place and let play("id") resolve them, so firing code never hard-codes intensities:

em = hapbeat.EventMap.from_manifest("my-kit/my-kit-manifest.json")
hb = hapbeat.connect(event_map=em)
hb.play("sample-kit.sine_100hz")        # uses the kit manifest's intensity for this event

EventMap reads the kit manifest (schema 2.0.0) intensity as the baseline gain. You can also build one by hand: EventMap.from_dict({"sample-kit.sine_100hz": 0.5}).

Haptic file — add targeting on top of the kit

The Studio-generated manifest holds kit content (intensity / clip), but not app-side concerns like which device/body part an event goes to. Put those in a haptic file (an EventMap overlay that references the kit), so play(id) resolves the target without the caller passing it:

// haptics.json
{
  "kit": "kits/my-kit",
  "events": {
    "sample-kit.sine_100hz": { "target": "player_1/pos_neck", "gain": 0.8 },
    "rain.loop":  { "target": "*/pos_back" }
  }
}

Targets are matched positionally: segment i of the target is compared with segment i of the device address, * stands for one whole segment (pos_* is compared literally — use player_1/*), and a short target front-matches. Device addresses are always player_<N>/<position>/group_<M>, defaulting to player_1 / group_1, so a group has to be written */*/group_2: plain group_2 is compared against the player slot and never matches.

hb = hapbeat.connect(app_name="MyApp", haptics="haptics.json")
hb.play("sample-kit.sine_100hz")     # goes to player_1/pos_neck at gain 0.8 — from the file

Two playback modes: command and clip

The same play(id) call branches on the manifest:

Manifest bucket Mode What happens
events command the SDK sends a PLAY; the device plays its installed clip
stream_events clip the SDK reads the WAV from the kit's stream-clips/ and streams it over UDP

Put the kit inside your project and call events by id — the per-event details (intensity, loop, command vs clip, which WAV) live in the kit, not your code:

my-app/
  app.py
  kits/my-kit/
    my-kit-manifest.json     # the "haptic file" -> EventMap
    install-clips/           # command clips (flashed to the device via Studio)
    stream-clips/*.wav       # clip-mode WAVs the SDK streams
import hapbeat
hb = hapbeat.connect(app_name="MyApp", kit="kits/my-kit")
hb.play("sample-kit.sine_100hz")     # command -> device plays its installed clip
rain = hb.play("rain.loop")  # clip -> returns a StreamPlayback
rain.pan = -0.5
rain.stop()                  # stops only this logical source

You can also stream an ad-hoc PCM16 buffer (e.g. a synthesized stereo cue where L/R amplitude conveys direction):

left = hb.stream_pcm(pcm_bytes, sample_rate=16000, channels=2,
                     target="player_1/left", loop=True)
right = hb.stream_pcm(other_pcm, target="player_1/right", loop=True)
left.gain = 0.5

PCM16 input is normalized to 16 kHz stereo inside the shared endpoint mixer. Sources aimed at one endpoint are mixed into one wire session; sources aimed at different endpoints run independently. A full runnable example is in examples/clip_project/.

Generic OSC bridge

Any OSC tool (TouchOSC, Max/MSP, TouchDesigner, a DAW) can drive Hapbeat without code. Run the bridge and send /hapbeat/play <event_id> [target] [time] [gain] [pan]:

hapbeat osc-bridge --listen 7702

See docs/osc.md for the address spec.

CLI

hapbeat scan                       # list devices on the LAN
hapbeat play sample-kit.sine_100hz --gain 0.3
hapbeat stop-all
hapbeat launchpad                  # browser UI for all of the above

Examples

Ready-to-run sample applications live in examples/: a psychophysics experiment, a breathing pacer, a haptic metronome, a live trigger pad, a task-completion notifier, and a Morse transmitter. Each is a single stdlib-only file — see examples/README.md.

For AI coding agents

Working with Claude / Cursor / Copilot? Hand your agent AGENTS.md — a single self-contained file with the SDK's model, API, project layout, and pitfalls. One file is enough to get the whole picture.

Use the Hapbeat Python SDK. Read AGENTS.md and follow its API and best practices.

License

MIT © Hapbeat

Metadata

Release files for hapbeat-python-sdk 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for hapbeat-python-sdk 0.3.0
File Size Uploaded Provenance
hapbeat_python_sdk-0.3.0.tar.gz 87.1 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for hapbeat-python-sdk 0.3.0
File Interpreter ABI Platform
hapbeat_python_sdk-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 149.0 kB

Release files / hapbeat_python_sdk-0.3.0.tar.gz

Download URL hapbeat_python_sdk-0.3.0.tar.gz
Size 87.1 kB
Tags Source
SHA-256 checksum
How to use checksums
a13b768a88d1cd4ef1da59a22c38febbe9fd10ede6530f818257743ac8f80976
BLAKE2b-256 checksum
How to use checksums
b0eaefb82b28511a1899ce293150bf47187082296e910b8bb3d17f4b5d845927
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release files / hapbeat_python_sdk-0.3.0-py3-none-any.whl

Download URL hapbeat_python_sdk-0.3.0-py3-none-any.whl
Size 61.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2afa87492405c1673c0f0aaa848f982b144369e2e39ef039043fb155649e4731
BLAKE2b-256 checksum
How to use checksums
63e9866f83751be191af62c8a3dbf3201897422436cd82559c837daddf023298
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Security for hapbeat-python-sdk 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for hapbeat_python_sdk-0.3.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page