Skip to content

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.

  • React Native 0.71+
  • New Architecture enabled

Latest available version: 1.4.0

Terminal window
npx expo install react-native-pulsar

Then run prebuild to generate the native project files:

Terminal window
npx expo prebuild

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';
MethodDescription
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();

Platform-specific haptic feedback styles, common across iOS and Android.

MethodDescription
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();
});

Additional system haptic feedback styles that are only available on Android.

MethodDescription
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();

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);

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.

Plays the parsed pattern.

Stops the active pattern.

Parses and replaces the current pattern.

Returns true if a pattern has been parsed and is ready to play.

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>;
}

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();

useRealtimeComposer doesn’t take any parameters.

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.

Stops the active haptic.

Returns true if a haptic is currently playing.

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>;
}

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);

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;
};

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.

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" />
);
}

Global configuration for the Pulsar SDK. All methods are available on the Settings object.

import { Settings } from 'react-native-pulsar';
MethodDescription
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. Intended for testing/debugging fallback behavior.
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
import { Settings } from 'react-native-pulsar';
// Preload frequently used presets
Settings.preloadPresets(['Fanfare', 'Explosion', 'Heartbeat']);
// Disable haptics temporarily
Settings.enableHaptics(false);
// Check device support
const support = Settings.getHapticsSupportLevel();

The haptic capability level of the current device, returned by Settings.getHapticsSupportLevel().

import { HapticSupport } from 'react-native-pulsar';
enum HapticSupport {
NO_SUPPORT = 0,
LIMITED_SUPPORT = 1,
STANDARD_SUPPORT = 2,
ADVANCED_SUPPORT = 3,
}

On Android, Settings.forceHapticsSupportLevel(level) uses these exact numeric enum values when selecting the fallback path. If you force a mode while validating custom patterns, make sure you pass the exported HapticSupport enum rather than app-local aliases.

import { Settings, HapticSupport, Presets } from 'react-native-pulsar';
const support = Settings.getHapticsSupportLevel();
if (support >= HapticSupport.STANDARD_SUPPORT) {
Presets.dogBark();
}

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.

enum HapticSupport {
NO_SUPPORT = 0,
LIMITED_SUPPORT = 1,
STANDARD_SUPPORT = 2,
ADVANCED_SUPPORT = 3,
}

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,
}
ValueDescription
ENVELOPEApproximation 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_TICKApproximation 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_COMPLEXSimilar to PRIMITIVE_TICK, but uses multiple primitives depending on the requested frequency.
ENVELOPE_WITH_DISCRETE_PRIMITIVESDefault. 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);