Skip to content

Overview

This page covers concepts that apply across all Pulsar SDKs: types of haptics, preloading, and caching. Read this page before the platform-specific guides for iOS, Android, or React Native.

Download the Pulsar companion app to feel haptic presets directly on your phone: App Store or Google Play

Haptic pattern in Pulsar combine two primitives: discrete and continuous events.

A discrete haptic produces a short, single burst of feedback at one point in time, such as a tap, a click. Three properties define each event:

PropertyRangeDescription
timemsWhen the event fires, relative to the pattern start
amplitude0 – 1Intensity of the haptic
frequency0 – 1Sharpness of the haptic (low values feel round and soft, high feel crisp)

For example, three quick taps might fire at 0 ms, 80 ms, and 160 ms, each with decreasing amplitude to create a fading effect.

Apple’s Core Haptics framework calls discrete haptics transient events. Pulsar uses the term “discrete” across all platforms so you can work with one mental model regardless of target device.

A continuous haptic produces a sustained vibration whose intensity and sharpness change over time. You define two curves instead of individual events: an amplitude envelope and a frequency envelope. The haptic engine interpolates between the control points in each curve.

Each control point has two properties:

PropertyRangeDescription
timemsWhen this control point occurs, relative to the start
value0 – 1Amplitude or frequency value at this point

For example, a 400 ms swell might ramp amplitude from 0 to 1 over the first 200 ms, then fade back to 0. Meanwhile, frequency shifts gradually from 0.3 to 0.8 over the full duration.

Apple’s Core Haptics framework calls these continuous events, shaped by parameter curves that control intensity and sharpness over time.

A Pulsar pattern contains both a discrete array and a continuous envelope. The haptic engine plays them simultaneously, letting you layer sharp taps on top of a sustained rumble. See the platform-specific SDK pages for data structures and code examples.

iOS and Android simulators do not support haptic hardware, which makes it difficult to verify that your haptic patterns feel correct during development. Pulsar provides a more convenient approach: it generates audio based on the haptic pattern description and plays it instead of vibration. This works for all built-in presets and for custom patterns - Pulsar generates the sound from the pattern parameters, so you can hear the shape of any haptic you define.

Because of this, Pulsar plays sound by default in debug builds. This lets you test and iterate on haptic patterns on a simulator without needing a physical device. Each haptic event produces an audible tone that reflects its amplitude, frequency, and duration, giving you an immediate sense of how the pattern will feel.

You can disable audio output at any time through the SDK configuration if you prefer a silent debug experience.

A preset is a ready-to-use haptic pattern bundled with the SDK. Playing a preset for the first time requires allocating resources, creating a pattern player, and preparing the haptic engine. These steps add latency to the first playback.

Preloading moves that cost to an earlier point, typically app startup or screen entry. When you preload a preset, the SDK initializes the player immediately and stores the result in the cache. Later play calls skip initialization and fire with minimal delay.

Preloading automatically enables caching. A preloaded preset stays in memory until you clear the cache or deallocate the SDK.

Preload presets when haptic feedback must feel instantaneous:

  • Button interactions that need a tap preset to fire without perceptible delay
  • Game events that require multiple presets to be ready at any moment
  • Onboarding flows that use a known set of presets

For presets used infrequently or unpredictably, lazy initialization with caching (the default) is sufficient.

The Pulsar SDK caches preset instances by default. The first time you play a preset, the SDK creates a player and stores the player in an in-memory dictionary. Every subsequent play call reuses the cached player, avoiding repeated initialization.

When you play a preset, the SDK follows these steps:

  1. Check the cache for an existing player.
  2. On a cache hit, return the stored player and play it immediately.
  3. On a cache miss, create a new player, store the player in the cache, then play it.

Each platform SDK provides methods to enable, disable, and clear the cache. Disabling the cache forces the SDK to create a fresh player on every play call. Fresh creation adds latency but can help with debugging or memory-constrained scenarios.

The following table compares the two strategies:

CachingPreloading
When initialization happensOn first playBefore first play
First-play latencyHigher (player created on demand)Minimal (player already ready)
Default stateEnabledOpt-in
Memory usageGrows as presets are playedAllocates upfront for specified presets

Caching and preloading work together. Preloading populates the cache ahead of time, and caching keeps players in memory for reuse. For most apps, default caching provides a good balance between performance and memory usage. Add preloading for presets where first-play latency matters.