Skip to content

Figma → code

Designers bind haptic presets to components with the Pulsar Haptics Figma plugin. This page is for the developer who then implements that design in code and wants the haptics implemented too.

Driving the implementation with a coding agent and the Figma MCP server? Follow Figma MCP instead — same data, written up as an agent workflow.

A coding agent using Figma’s Dev Mode MCP (get_design_context / get_metadata) receives only visuals, layout, and text — never haptics. So Pulsar publishes each binding two ways that a developer can read:

  1. Shared plugin data on each bound node — the source of truth, readable by any tool with file access (the Figma MCP’s use_figma, or the REST API with plugin_data=shared). No Pulsar plugin install required on your side.
  2. A frame-level annotation — a Dev Mode note on every screen that has haptics, pointing here. It reaches a vanilla Figma MCP automatically, so an agent gets a hint to come looking even with nothing installed.

Give your coding agent the pulsar-figma-haptics skill and it will do the read + wire-up for you whenever you implement a Figma design:

/plugin marketplace add software-mansion-labs/skills
/plugin install skills@swmansion
/reload-plugins

Then, after generating UI from a Figma screen, the agent reads each node’s Pulsar binding and adds the matching preset call.

Each bound node carries, in shared plugin data:

NamespaceKeyValue
pulsarbindingJSON: { "v": 1, "presetId": string, "presetName": string, "customPattern"?: object }
pulsarbinding-negatednon-empty ⇒ node is explicitly unbound; skip it

presetName (e.g. "DogBark") is what becomes an SDK call. Skip any node whose binding-negated is set.

Via the Figma MCP use_figma (no token) — list every bound node on the page:

const NS = 'pulsar';
return figma.currentPage
.findAllWithCriteria({ sharedPluginData: { namespace: NS, keys: ['binding'] } })
.filter((n) => !n.getSharedPluginData(NS, 'binding-negated'))
.map((n) => ({ nodeId: n.id, nodeName: n.name, ...JSON.parse(n.getSharedPluginData(NS, 'binding')) }));

Via the REST API (needs a Figma token):

Terminal window
curl -s -H "X-Figma-Token: $FIGMA_TOKEN" \
"https://api.figma.com/v1/files/$FILE_KEY/nodes?ids=$NODE_IDS&plugin_data=shared" \
| jq '.. | .sharedPluginData? // empty'

Built-in presets are called by name; the method is presetName in camelCase ("DogBark"dogBark).

PlatformCall for dogBark
React Nativeimport { Presets } from 'react-native-pulsar';Presets.dogBark();
iOS (Swift)pulsar.getPresets().dogBark()
Android (Kotlin)pulsar.getPresets().dogBark()
Flutterawait pulsar.getPresets().dogBark();
Kotlin Multiplatformpulsar.getPresets().play("DogBark")
Webawait pulsar.getPresets().play('dogBark');

Fire it on the element’s natural interaction (press, toggle, submit). For install and full APIs, see the SDK overview and the per-platform pages.

A binding may carry a customPattern object instead of a built-in preset (legacy custom patterns). Play it as a raw Pattern — in React Native via usePatternComposer — rather than a named preset call.