Student starter code (30% baseline)
index.html- Main HTML pagescript.js- JavaScript logicstyles.css- Styling and layoutpackage.json- Dependenciessetup.sh- Setup scriptREADME.md- Instructions (below)💡 Download the ZIP, extract it, and follow the instructions below to get started!
By completing this activity, you will:
# Install dependencies
npm install --legacy-peer-deps
# Start the development server
npx expo start
What You'll Experience:
This activity teaches you to add professional tactile feedback that makes apps feel premium and responsive.
✅ Fully Functional:
⚠️ Your Mission (Discovery TODOs):
Goal: Transform this demo into an interactive haptic toolkit with custom patterns and real-world applications!
File: App.js (lines 15-25)
Try each impact button and notice the differences:
// Light Impact - Gentle tap (for subtle interactions)
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
// Medium Impact - Standard press (for buttons)
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
// Heavy Impact - Strong press (for confirmations/deletes)
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);
Discovery Challenge:
Why: Different intensities communicate different levels of importance and action consequences.
File: App.js (lines 28-38)
Try the three notification types:
// Success - Task completed (positive feedback)
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
// Warning - Proceed with caution (neutral alert)
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning);
// Error - Something went wrong (negative feedback)
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
Discovery Challenge:
Why: Notification haptics have distinct patterns that communicate meaning without looking at the screen.
File: App.js (lines 41-43)
Tap the Selection button repeatedly (10 times fast):
// Selection - Subtle tick for scrolling/picking
Haptics.selectionAsync();
Discovery Challenge:
Why: Selection feedback provides subtle confirmation without being overwhelming during rapid interactions.
New Feature: Add a slider that triggers haptics based on position
Create a new section in App.js after line 131:
// TODO: Add slider imports at top
import { Slider } from '@react-native-community/slider';
// TODO: Add state for slider value
const [intensity, setIntensity] = useState(50);
// TODO: Create custom intensity handler
const triggerCustomIntensity = (value) => {
setIntensity(value);
// Trigger haptic based on slider position
if (value < 33) {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
} else if (value < 66) {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
} else {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);
}
};
// TODO: Add slider UI
<View style={styles.section}>
<Text style={styles.sectionTitle}>Custom Intensity</Text>
<Text style={styles.description}>
Drag the slider to feel haptic intensity change dynamically
</Text>
<Slider
style={{ width: '100%', height: 40 }}
minimumValue={0}
maximumValue={100}
value={intensity}
onValueChange={triggerCustomIntensity}
minimumTrackTintColor="#3b82f6"
maximumTrackTintColor="#334155"
/>
<Text style={styles.sliderValue}>{Math.round(intensity)}% Intensity</Text>
</View>
Success Criteria:
Why: Dynamic haptics make interactions feel more responsive and give users a sense of control.
New Feature: Create multi-step haptic patterns for complex actions
Add after the Custom Intensity section:
// TODO: Create combo pattern function
const triggerComboPattern = async (pattern) => {
if (pattern === 'success-celebration') {
// Celebrate success with a pattern: tick-tick-boom!
await Haptics.selectionAsync();
setTimeout(() => Haptics.selectionAsync(), 100);
setTimeout(() => Haptics.notificationAsync(
Haptics.NotificationFeedbackType.Success
), 200);
} else if (pattern === 'error-emphasis') {
// Emphasize error with double impact
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);
setTimeout(() => Haptics.notificationAsync(
Haptics.NotificationFeedbackType.Error
), 150);
} else if (pattern === 'countdown') {
// 3-2-1 countdown pattern
await Haptics.selectionAsync();
setTimeout(() => Haptics.selectionAsync(), 500);
setTimeout(() => Haptics.selectionAsync(), 1000);
setTimeout(() => Haptics.impactAsync(
Haptics.ImpactFeedbackStyle.Heavy
), 1500);
}
};
// TODO: Add combo pattern UI
<View style={styles.section}>
<Text style={styles.sectionTitle}>Combo Patterns</Text>
<Text style={styles.description}>
Multi-step haptic sequences for complex interactions
</Text>
<TouchableOpacity
style={[styles.button, styles.successButton]}
onPress={() => triggerComboPattern('success-celebration')}
>
<Text style={styles.buttonText}>Success Celebration</Text>
<Text style={styles.buttonSubtext}>Tick-tick-boom pattern</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.errorButton]}
onPress={() => triggerComboPattern('error-emphasis')}
>
<Text style={styles.buttonText}>Error Emphasis</Text>
<Text style={styles.buttonSubtext}>Double impact alert</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.selectionButton]}
onPress={() => triggerComboPattern('countdown')}
>
<Text style={styles.buttonText}>Countdown Timer</Text>
<Text style={styles.buttonSubtext}>3-2-1-Go!</Text>
</TouchableOpacity>
</View>
Success Criteria:
Why: Combo patterns can convey complex information and make apps feel more sophisticated.
New Feature: Add smart haptics that adapt to user behavior
Add a context-aware haptic system:
// TODO: Add state to track interaction count
const [tapCount, setTapCount] = useState(0);
const [lastTapTime, setLastTapTime] = useState(0);
// TODO: Create context-aware haptic function
const triggerSmartHaptic = () => {
const now = Date.now();
const timeSinceLastTap = now - lastTapTime;
setLastTapTime(now);
setTapCount(prev => prev + 1);
// Reduce haptic intensity if user is tapping rapidly (avoid annoyance)
if (timeSinceLastTap < 500) {
// Rapid tapping - use subtle selection
Haptics.selectionAsync();
} else if (tapCount % 5 === 0) {
// Every 5th tap - give stronger confirmation
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);
} else {
// Normal tapping - medium impact
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
}
};
// TODO: Add smart haptic UI
<View style={styles.section}>
<Text style={styles.sectionTitle}>Smart Haptics</Text>
<Text style={styles.description}>
Adapts intensity based on your tapping speed
</Text>
<TouchableOpacity
style={[styles.button, styles.lightButton]}
onPress={triggerSmartHaptic}
>
<Text style={styles.buttonText}>Smart Feedback</Text>
<Text style={styles.buttonSubtext}>
Tap rapidly vs slowly - feel the difference!
</Text>
</TouchableOpacity>
<Text style={styles.tipText}>Taps: {tapCount}</Text>
</View>
Success Criteria:
Why: Context-aware haptics prevent overuse and make apps feel intelligent and responsive to user behavior.
New Feature: Create reusable haptic patterns for common scenarios
Create a haptic presets object:
// TODO: Define preset patterns
const HAPTIC_PRESETS = {
'button-press': () => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium),
'delete-action': () => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy),
'form-submit': () => Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success),
'form-error': () => Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error),
'picker-scroll': () => Haptics.selectionAsync(),
'toggle-switch': () => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light),
};
// TODO: Add preset selector UI
<View style={styles.section}>
<Text style={styles.sectionTitle}>Haptic Presets</Text>
<Text style={styles.description}>
Ready-to-use patterns for common app interactions
</Text>
{Object.keys(HAPTIC_PRESETS).map((preset) => (
<TouchableOpacity
key={preset}
style={[styles.button, styles.mediumButton]}
onPress={HAPTIC_PRESETS[preset]}
>
<Text style={styles.buttonText}>{preset}</Text>
</TouchableOpacity>
))}
</View>
Success Criteria:
Why: Presets help maintain consistency across your app and speed up development.
For students who want to go beyond:
Create a Simon Says style game:
Show in Portfolio: Video demo showing haptic feedback synced with visual cues, explaining how haptics enhance gameplay.
Build an accessibility tool:
Show in Portfolio: Document accessibility testing results and user feedback on haptic navigation.
Create a short interactive story:
Show in Portfolio: "Haptic Design Document" explaining how each pattern was chosen to match story emotions.
When to Use Haptics:
Intensity Guidelines:
UX Considerations:
When showcasing this project:
Record Video Demo
Explain Design Decisions
Connect to Real Projects
Discuss Trade-offs
# Clear Expo cache
npx expo start --clear
After completing this activity:
Before submitting, ensure:
Add haptics when:
Add haptics when:
Consider haptics for:
When complete:
Tech Stack: React 18.3.1 | React Native 0.76.1 | Expo SDK ~54.0.0 | expo-haptics ~14.0.0
Need Help? Review Concept 17: Haptic Feedback & Vibration, or ask in the course discussion forum!