React Native SDK
The Pulsar React Native SDK (react-native-pulsar) exposes haptic feedback through three building blocks: Presets, usePatternComposer, and useRealtimeComposer. All preset functions and hook methods are worklet-compatible for use with Reanimated.
Requirements
Section titled “Requirements”- React Native 0.71+
- New Architecture enabled
Installation
Section titled “Installation”npx expo install react-native-pulsarThen run prebuild to generate the native project files:
npx expo prebuildyarn add react-native-pulsar react-native-workletsnpm install react-native-pulsar react-native-workletspnpm install react-native-pulsar react-native-workletsPresets
Section titled “Presets”A collection of ready-to-use haptic patterns. All preset functions are worklet-compatible and can be called directly inside Reanimated worklets.
import { Presets } from 'react-native-pulsar';Built-in presets
Section titled “Built-in presets”| Method | Description |
|---|---|
afterglow() | A three-beat phrase that dissolves gently, ideal for soft endings or gradually quieting feedback. |
aftershock() | A firm opening that settles calmly, ideal for transitions needing a strong start and a gentle finish. |
alarm() | Relentless and urgent, best for critical errors or emergencies that require immediate attention. |
anvil() | The full weight of a massive collision, conveys sheer physical force and momentum. |
applause() | A growing wave of appreciation, ideal for celebratory moments or social approval. |
Example
import { Presets } from 'react-native-pulsar';
Presets.hammer();System presets
Section titled “System presets”Common system presets
Section titled “Common system presets”Platform-specific haptic feedback styles, common across iOS and Android.
| Method | Description |
|---|---|
impactHeavy() | UIImpactFeedbackGenerator.heavy |
impactLight() | UIImpactFeedbackGenerator.light |
impactMedium() | UIImpactFeedbackGenerator.medium |
impactRigid() | UIImpactFeedbackGenerator.rigid |
impactSoft() | UIImpactFeedbackGenerator.soft |
Example
import { Presets } from 'react-native-pulsar';import { Gesture } from 'react-native-gesture-handler';
const tap = Gesture.Tap().onEnd(() => { Presets.System.impactMedium();});Android-specific system presets
Section titled “Android-specific system presets”Additional system haptic feedback styles that are only available on Android.
| Method | Description |
|---|---|
calendarDate() | HapticFeedbackConstants.CALENDAR_DATE |
clockTick() | HapticFeedbackConstants.CLOCK_TICK |
confirm() | HapticFeedbackConstants.CONFIRM |
contextClick() | HapticFeedbackConstants.CONTEXT_CLICK |
dragCrossing() | HapticFeedbackConstants.DRAG_CROSSING |
Example
import { Presets } from 'react-native-pulsar';
Presets.System.Android.primitiveLowTick();usePatternComposer
Section titled “usePatternComposer”A React hook for composing and playing a custom Pattern. The pattern is parsed on mount and whenever it changes. Resources are released automatically on unmount.
const { play, parse, isParsed } = usePatternComposer(pattern);Parameters
Section titled “Parameters”pattern (optional)
Section titled “pattern (optional)”Type: Pattern
A haptic pattern to parse on mount. When provided, the pattern is parsed automatically and re-parsed whenever the value changes. Resources are released on unmount.
Returns
Section titled “Returns”play()
Section titled “play()”Plays the parsed pattern.
stop()
Section titled “stop()”Stops the active pattern.
parse(pattern: Pattern)
Section titled “parse(pattern: Pattern)”Parses and replaces the current pattern.
isParsed()
Section titled “isParsed()”Returns true if a pattern has been parsed and is ready to play.
Example
Section titled “Example”import { usePatternComposer } from 'react-native-pulsar';import { Gesture } from 'react-native-gesture-handler';
const pattern = { discretePattern: [ { time: 0, amplitude: 1, frequency: 0.5 }, { time: 100, amplitude: 0.5, frequency: 0.5 }, ], continuousPattern: { amplitude: [ { time: 0, value: 0 }, { time: 200, value: 1 }, { time: 400, value: 0 }, ], frequency: [ { time: 0, value: 0.3 }, { time: 400, value: 0.8 }, ], },};
function MyComponent() { const composer = usePatternComposer(pattern);
const tap = Gesture.Tap().onEnd(() => { composer.play(); });
return <GestureDetector gesture={tap}>...</GestureDetector>;}useRealtimeComposer
Section titled “useRealtimeComposer”A React hook for real-time haptic control. Provides live amplitude and frequency modulation, useful for gesture-driven or continuously evolving haptic experiences. Haptics stop automatically on unmount.
const { set, playDiscrete, stop, isActive } = useRealtimeComposer();Parameters
Section titled “Parameters”useRealtimeComposer doesn’t take any parameters.
Returns
Section titled “Returns”set(amplitude: number, frequency: number)
Section titled “set(amplitude: number, frequency: number)”Updates the ongoing haptic with new amplitude and frequency values.
playDiscrete(amplitude: number, frequency: number)
Section titled “playDiscrete(amplitude: number, frequency: number)”Plays a single discrete haptic event.
stop()
Section titled “stop()”Stops the active haptic.
isActive()
Section titled “isActive()”Returns true if a haptic is currently playing.
Example
Section titled “Example”import { useRealtimeComposer } from 'react-native-pulsar';import { Gesture } from 'react-native-gesture-handler';
function MyComponent() { const realtime = useRealtimeComposer();
const pan = Gesture.Pan() .onUpdate((e) => { const amplitude = Math.min(Math.abs(e.velocityY) / 1000, 1); realtime.set(amplitude, 0.5); }) .onEnd(() => { realtime.stop(); });
return <GestureDetector gesture={pan}>...</GestureDetector>;}useAdaptiveHaptics
Section titled “useAdaptiveHaptics”A React hook that plays haptics from a cross-platform AdaptivePreset. It automatically selects the correct configuration for the current platform (iOS or Android). Each platform can provide either a custom Pattern or a native preset function.
const { play } = useAdaptiveHaptics(preset: AdaptivePreset);Parameters
Section titled “Parameters”preset: AdaptivePreset
Section titled “preset: AdaptivePreset”An object with ios and android keys. Each value is either a Pattern object or a function that triggers a native preset directly.
type AdaptivePresetConfig = (() => void) | Pattern;
type AdaptivePreset = { ios: AdaptivePresetConfig; android: AdaptivePresetConfig;};Returns
Section titled “Returns”play()
Section titled “play()”Plays the haptic for the current platform. If the platform config is a function, it is called directly. If it is a Pattern, it is played via the pattern composer.
Example
Section titled “Example”import { useAdaptiveHaptics, Presets } from 'react-native-pulsar';
const adaptivePreset = { ios: Presets.Success, // native iOS preset function android: { // custom pattern for Android discretePattern: [ { time: 0, amplitude: 1, frequency: 0.5 }, { time: 150, amplitude: 0.6, frequency: 0.4 }, ], continuousPattern: { amplitude: [], frequency: [], }, },};
function MyComponent() { const haptics = useAdaptiveHaptics(adaptivePreset);
return ( <Button onPress={haptics.play} title="Tap me" /> );}Settings
Section titled “Settings”Global configuration for the Pulsar SDK. All methods are available on the Settings object.
import { Settings } from 'react-native-pulsar';| Method | Description |
|---|---|
Settings.enableHaptics(state: boolean) | Enable or disable all haptic feedback |
Settings.enableSound(state: boolean) | Enable or disable audio simulation |
Settings.enableCache(state: boolean) | Enable or disable preset caching |
Settings.clearCache() | Clear the preset cache |
Settings.preloadPresets(presetNames: string[]) | Preload presets by name for faster playback |
Settings.stopHaptics() | Stop all currently playing haptics |
Settings.shutDownEngine() | Shut down the haptic engine |
Settings.getHapticsSupportLevel() | Returns the device’s HapticSupport level |
Settings.forceHapticsSupportLevel(level) | (Android only) Override the detected support level |
Settings.enableImpulseCompositionMode(state: boolean) | (Android only) Enable or disable impulse composition mode |
Settings.setRealtimeComposerStrategy(strategy) | (Android only) Set the strategy used by the realtime composer |
Example
Section titled “Example”import { Settings } from 'react-native-pulsar';
// Preload frequently used presetsSettings.preloadPresets(['Fanfare', 'Explosion', 'Heartbeat']);
// Disable haptics temporarilySettings.enableHaptics(false);
// Check device supportconst support = Settings.getHapticsSupportLevel();HapticSupport
Section titled “HapticSupport”The haptic capability level of the current device, returned by Settings.getHapticsSupportLevel().
import { HapticSupport } from 'react-native-pulsar';enum HapticSupport { NO_SUPPORT = 0, MINIMAL_SUPPORT = 1, LIMITED_SUPPORT = 2, STANDARD_SUPPORT = 3, ADVANCED_SUPPORT = 4,}Example
Section titled “Example”import { Settings, HapticSupport, Presets } from 'react-native-pulsar';
const support = Settings.getHapticsSupportLevel();
if (support >= HapticSupport.STANDARD_SUPPORT) { Presets.dogBark();}Pattern
Section titled “Pattern”Describes a complete haptic pattern with discrete pulses and continuous envelope curves.
type Pattern = { discretePattern: Array<{ time: number; // Milliseconds from pattern start amplitude: number; // Intensity (0-1) frequency: number; // Sharpness (0-1) }>; continuousPattern: { amplitude: Array<{ time: number; // Milliseconds from pattern start value: number; // Amplitude value (0-1) }>; frequency: Array<{ time: number; // Milliseconds from pattern start value: number; // Frequency value (0-1) }>; };};Use discretePattern for distinct taps and impacts. Use continuousPattern envelopes to shape a sustained haptic over time.
HapticSupport
Section titled “HapticSupport”enum HapticSupport { NO_SUPPORT = 0, MINIMAL_SUPPORT = 1, LIMITED_SUPPORT = 2, STANDARD_SUPPORT = 3, ADVANCED_SUPPORT = 4,}RealtimeComposerStrategy
Section titled “RealtimeComposerStrategy”Android-only. Controls how useRealtimeComposer simulates continuous haptics, since Android has no native continuous haptic API. Pass one of these values to Settings.setRealtimeComposerStrategy().
import { RealtimeComposerStrategy } from 'react-native-pulsar';enum RealtimeComposerStrategy { ENVELOPE = 0, PRIMITIVE_TICK = 1, PRIMITIVE_COMPLEX = 2, ENVELOPE_WITH_DISCRETE_PRIMITIVES = 3,}| Value | Description |
|---|---|
ENVELOPE | Approximation based on the Envelope API. Allows control over amplitude and frequency, but the signal oscillates and can be unstable. Available on Android API 36+. |
PRIMITIVE_TICK | Approximation using the Composition API TICK primitive at varying intervals. Amplitude is controllable; frequency is simulated by the timing between ticks. Signal is discrete rather than continuous. |
PRIMITIVE_COMPLEX | Similar to PRIMITIVE_TICK, but uses multiple primitives depending on the requested frequency. |
ENVELOPE_WITH_DISCRETE_PRIMITIVES | Default. Hybrid strategy. Uses the Envelope API for continuous events (API 36+) and composition primitives for discrete events (API 33+). Best of both worlds when both event types are used. |
Example
import { Settings, RealtimeComposerStrategy } from 'react-native-pulsar';
Settings.setRealtimeComposerStrategy(RealtimeComposerStrategy.PRIMITIVE_COMPLEX);