| title | Quick Start |
|---|---|
| description | Build your first haptic pattern with Jindong in minutes |
Let's build your first haptic feedback pattern with Jindong.
This guide covers the **Compose API** (`jindong-compose` module). For non-Compose usage, see [Core API Guide](/docs/api/core-api).import io.github.compose.jindong.*
import io.github.compose.jindong.dsl.*
import kotlin.time.Duration.Companion.ms
@Composable
fun App() {
var count by remember { mutableStateOf(0) }
JindongProvider {
// Haptic triggers when count changes
Jindong(count) {
Haptic(100.ms)
}
Button(onClick = { count++ }) {
Text("Tap me!")
}
}
}That's it! Every time you tap the button, a 100ms haptic pulse fires.
Provides the haptic execution context:
JindongProvider {
// Haptics work inside this scope
}The main composable for defining haptic patterns:
Jindong(key1, key2, ...) {
// Haptic pattern defined here
}- Works like
LaunchedEffect - Executes when any key changes
- Content block defines the pattern
Creates a vibration:
Haptic(duration)
Haptic(duration, intensity)Events execute one after another:
Jindong(trigger) {
Haptic(100.ms) // Starts at 0ms
Haptic(50.ms) // Starts at 100ms
Haptic(30.ms) // Starts at 150ms
}Use Delay for pauses between events:
Jindong(trigger) {
Haptic(50.ms) // 0-50ms
Delay(100.ms) // 50-150ms (silence)
Haptic(50.ms) // 150-200ms
}Control vibration strength:
Jindong(trigger) {
Haptic(100.ms, HapticIntensity.LIGHT) // 25% intensity
Haptic(100.ms, HapticIntensity.MEDIUM) // 50% intensity
Haptic(100.ms, HapticIntensity.STRONG) // 75% intensity
Haptic(100.ms, HapticIntensity.HIGH) // 100% intensity
}Repeat patterns with Repeat:
Jindong(trigger) {
Repeat(3) {
Haptic(50.ms)
Delay(30.ms)
}
// Equivalent to:
// Haptic → Delay → Haptic → Delay → Haptic → Delay
}Use Unit as key to trigger haptic immediately when entering a screen:
@Composable
fun WelcomeScreen() {
// Executes once when screen appears
Jindong(Unit) {
Haptic(100.ms, HapticIntensity.MEDIUM)
}
Text("Welcome!")
}@Composable
fun TapButton() {
var taps by remember { mutableStateOf(0) }
Jindong(taps) {
Haptic(50.ms, HapticIntensity.LIGHT)
}
Button(onClick = { taps++ }) {
Text("Tap")
}
}@Composable
fun SuccessHaptic(success: Boolean) {
Jindong(success) {
if (success) {
// Double pulse for success
Haptic(50.ms, HapticIntensity.MEDIUM)
Delay(50.ms)
Haptic(100.ms, HapticIntensity.STRONG)
}
}
}@Composable
fun ErrorHaptic(error: Boolean) {
Jindong(error) {
if (error) {
// Triple strong pulse for error
Repeat(3) {
Haptic(80.ms, HapticIntensity.HIGH)
Delay(50.ms)
}
}
}
}@Composable
fun FadeOutHaptic(trigger: Int) {
Jindong(trigger) {
RepeatWithIndex(5) { index ->
val intensity = HapticIntensity.Custom(1.0f - (index * 0.2f))
Haptic(50.ms, intensity)
Delay(30.ms)
}
}
}@Composable
fun HeartbeatHaptic(trigger: Int) {
Jindong(trigger) {
// Lub
Haptic(60.ms, HapticIntensity.STRONG)
Delay(80.ms)
// Dub
Haptic(40.ms, HapticIntensity.MEDIUM)
Delay(400.ms)
// Repeat
Haptic(60.ms, HapticIntensity.STRONG)
Delay(80.ms)
Haptic(40.ms, HapticIntensity.MEDIUM)
}
}| Pattern | Key | Code |
|---|---|---|
| On screen enter | Jindong(Unit) |
Haptic(100.ms) |
| Single tap | Jindong(count) |
Haptic(50.ms) |
| Double tap | Jindong(count) |
Repeat(2) { Haptic(40.ms); Delay(40.ms) } |
| Long press | Jindong(count) |
Haptic(200.ms, HapticIntensity.MEDIUM) |
| Success | Jindong(success) |
Haptic(50.ms); Delay(50.ms); Haptic(100.ms) |
| Error | Jindong(error) |
Repeat(3) { Haptic(80.ms); Delay(50.ms) } |
Choose keys carefully:
// Good: Specific trigger
Jindong(buttonClickCount) { ... }
// Avoid: Changes too often
Jindong(System.currentTimeMillis()) { ... }When a key changes, the content block is recompiled with the current state, so a value the block captures must also be one of the keys for the pattern to reflect it:
var level by remember { mutableStateOf(1) }
// `level` is captured AND a key, so the pattern grows as `level` changes
Jindong(level) {
RepeatWithIndex(level) { index ->
Haptic(50.ms, HapticIntensity.Custom(1f - index * 0.1f))
}
}If a captured value is not in the keys, the pattern keeps the value from the last key change instead of updating. The Reactive Contract covers when values are read and when playback fires.
- Jindong API - Deep dive into the main API
- Composable DSL - All DSL primitives
- HapticIntensity - Intensity options