Implementing Feature Discovery (New Feature Highlighting) in Mobile Apps
User updated the app. New tab appeared in Tab Bar. They don't notice it — because they look where they're used to. Feature discovery solves this: visually attracts attention to new element the moment user first sees updated screen.
What Feature Discovery Is Technically
Animated visual effect above or around target UI element: pulsing badge, expanding ring, flickering border. Fundamentally differs from coach marks by not covering interface — user can interact with other elements. Attention-grabber, not blocker.
Google implemented this in Material Design via FeatureHighlight component in older versions. In current Material3 it's not explicitly present, but pattern remains.
Pulsing Badge and Ring Animation
iOS. Pulsing circle — CALayer with CABasicAnimation on transform.scale and opacity. Key detail: expanding ring created via two layers — inner stays, outer animates from scale 1.0 to 2.5 with opacity 1.0 to 0.0 in infinite loop (repeatCount = .infinity). CAAnimationGroup synchronizes both animations.
let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
scaleAnimation.fromValue = 1.0
scaleAnimation.toValue = 2.5
let opacityAnimation = CABasicAnimation(keyPath: "opacity")
opacityAnimation.fromValue = 0.8
opacityAnimation.toValue = 0.0
let group = CAAnimationGroup()
group.animations = [scaleAnimation, opacityAnimation]
group.duration = 1.5
group.repeatCount = .infinity
pulseLayer.add(group, forKey: "pulse")
Layer added above target view via targetView.layer.addSublayer(pulseLayer) or in superview.layer if effect needed beyond bounds.
In SwiftUI — withAnimation(.easeOut(duration: 1.5).repeatForever(autoreverses: false)) on Circle().scale() in overlay. Cleaner and declarative, but repeatForever without autoreverses: false gives "there-and-back" effect — not what we need for pulse.
Android Compose. InfiniteTransition + animateFloat:
val infiniteTransition = rememberInfiniteTransition()
val scale by infiniteTransition.animateFloat(
initialValue = 1f, targetValue = 2.5f,
animationSpec = infiniteRepeatable(tween(1500, easing = EaseOut), RepeatMode.Restart)
)
Material Design 3 Feature Highlight
Material3 offers FeatureHighlight as part of extended components (not in main library). For full implementation in Google style — material-components-android-compose-theme-adapter library or custom component.
Standard Material3 approach: round FloatingActionButton with extended description appears next to new feature on first screen show, disappears after 4 seconds or on tap. Implemented via AnimatedVisibility with slideInVertically + fadeIn and LaunchedEffect(Unit) with delay(4000L).
Managing Visibility and Persistence
Feature discovery shown once — on first screen appearance after update. Logic:
let key = "feature_new_tab_v2_3_0_shown"
if !UserDefaults.standard.bool(forKey: key) {
showFeatureDiscovery(for: newTabButton)
UserDefaults.standard.set(true, forKey: key)
}
Key contains version (v2_3_0) — on next update with new feature change key, users see discovery again. Storing list of shown discoveries in array [String] more convenient than separate key for each feature.
Display delay: 800–1000 ms after viewDidAppear / onAppear. Don't show instantly — user should first see screen as whole.
Combining with Other Mechanics
Feature discovery often paired with badge counter on tab icon: red circle "NEW" until user visits new section. On iOS — UITabBarItem.badgeValue = "NEW", remove on tabBarController(_:didSelect:). In Compose — custom BadgedBox from Material3.
If new feature not in navigation but inside screen — combine feature discovery with Snackbar / Toast with action: "Try new filter →". On tap — scroll to element + pulse animation. UICollectionView.scrollToItem(at:at:animated:) then with 300 ms delay — CALayer pulse.
Timeline: 1–3 days. Single pulse effect on specific element — 1 day. System with discovery queue, version-based config, multiple animation types and show analytics — 3 days.







