
Lightweight SDK enables creation of interactive scratch cards, revealing hidden content for rewards, discounts, and gamification. Features customizable brush size, auto-reveal, and scratch event callbacks.
Scratchify is a powerful and highly customizable scratch card SDK built using Jetpack Compose Multiplatform. It enables you to create engaging interactive scratch surfaces where users can scratch off an overlay to reveal hidden content underneath. Perfect for reward systems, discount reveals, surprise elements, and gamification in your modern apps!
Perfect for:
✅ Reward reveals & lottery systems
✅ Discount coupons & promotional codes
✅ Interactive surprise elements
✅ Gamification features & mini-games
✅ Educational apps & learning activities
✅ User engagement & retention strategies
✔️ Multiplatform support (Android & iOS)
✔️ Two-layer scratch surface (Overlay & Revealed Content)
✔️ Customizable brush configuration (size, color, opacity)
✔️ Auto-reveal after threshold (configurable percentage)
✔️ Scratch event callbacks (onScratchStarted, onScratchProgress, onScratchCompleted)
✔️ Instant reveal & reset functionality
✔️ Tap-to-scratch detection alongside drag gestures
✔️ Configurable grid resolution for performance optimization
✔️ Save & restore scratch state for persistent experiences
✔️ Cross-platform haptic feedback (iOS & Android)
✔️ Animated reveal effects (fade, scale, slide, bounce, zoom)
✔️ Custom brush shapes (circle, square, star, heart, diamond, custom paths)
✔️ Transparent & colored brush modes (traditional scratch or paint effects)
Since Scratchify is a Compose Multiplatform (CMP) library, you should add it to your commonMain source set to use it across both iOS and Android.
Add the dependency in your shared module's build.gradle.kts:
dependencies {
implementation("io.github.gsrathoreniks:scratchify:<latest_version>")
}import com.gsrathoreniks.scratchify.api.Scratchify
import com.gsrathoreniks.scratchify.api.ScratchifyController
import com.gsrathoreniks.scratchify.api.config.ScratchifyConfig
@Composable
fun BasicScratchCard() {
val controller = remember { ScratchifyController() }
Scratchify(
modifier = Modifier.size(300.dp, 200.dp),
config = ScratchifyConfig(),
controller = controller,
contentToReveal = {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFFFD700)),
contentAlignment = Alignment.Center
) {
Text("🎉 You Won! 🎉", style = MaterialTheme.typography.headlineMedium)
}
},
overlayContent = {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFF8E24AA)),
contentAlignment = Alignment.Center
) {
Text("Scratch Here!", color = Color.White)
}
}
)
}@Composable
fun AdvancedScratchCard() {
val controller = remember {
ScratchifyController(
ScratchifyConfig(
// Core settings
revealFullAtPercent = 0.6f,
gridResolution = 100,
enableTapToScratch = true,
// Brush configuration
brushConfig = ScratchifyBrushConfig(
brushSize = 30.dp,
brushColor = Color.Red,
opacity = 0.8f,
brushShape = BrushShape.Star(points = 5)
),
// Haptic feedback
hapticConfig = ScratchifyHapticConfig(
isEnabled = true,
onScratchStarted = HapticFeedbackType.LIGHT,
onScratchProgress = HapticFeedbackType.LIGHT,
onScratchCompleted = HapticFeedbackType.SUCCESS,
progressHapticInterval = 0.25f
),
// Animation effects
animationConfig = ScratchifyAnimationConfig(
revealAnimationType = RevealAnimationType.BOUNCE,
animationDurationMs = 800,
enableProgressAnimation = true
)
)
)
}
// Controller API usage
Row {
Button(onClick = { controller.revealInstantly() }) {
Text("Reveal")
}
Button(onClick = { controller.resetScratch() }) {
Text("Reset")
}
}
Scratchify(
modifier = Modifier.size(300.dp, 200.dp),
config = controller.config,
controller = controller,
contentToReveal = { /* Your content */ },
overlayContent = { /* Your overlay */ }
)
}| Parameter | Type | Default | Description |
|---|---|---|---|
revealFullAtPercent |
Float |
0.75f |
Auto-reveal threshold (0.0 to 1.0) |
isScratchingEnabled |
Boolean |
true |
Enable/disable scratching |
gridResolution |
Int |
150 |
Grid resolution for performance tuning |
enableTapToScratch |
Boolean |
true |
Allow single taps to create scratches |
| Parameter | Type | Default | Description |
|---|---|---|---|
brushSize |
Dp |
4.dp |
Size of the brush stroke |
brushColor |
Color |
Color.Cyan |
Brush color (Color.Transparent for traditional scratch) |
opacity |
Float |
1f |
Brush opacity (0.0 to 1.0) |
brushShape |
BrushShape |
BrushShape.Circle |
Shape of the brush stroke |
BrushShape.Circle - Classic circular brushBrushShape.Square - Square brush strokesBrushShape.Star(points: Int = 5) - Star-shaped brushBrushShape.Heart - Heart-shaped brushBrushShape.Diamond - Diamond-shaped brushBrushShape.Custom(path: Path, size: Dp) - Custom path shapes| Parameter | Type | Default | Description |
|---|---|---|---|
isEnabled |
Boolean |
true |
Enable haptic feedback |
onScratchStarted |
HapticFeedbackType |
LIGHT |
Feedback when scratching begins |
onScratchProgress |
HapticFeedbackType |
NONE |
Feedback during scratching |
onScratchCompleted |
HapticFeedbackType |
SUCCESS |
Feedback when completed |
progressHapticInterval |
Float |
0.25f |
Progress interval for haptic feedback |
| Parameter | Type | Default | Description |
|---|---|---|---|
revealAnimationType |
RevealAnimationType |
FADE |
Type of reveal animation |
animationDurationMs |
Int |
500 |
Animation duration in milliseconds |
enableProgressAnimation |
Boolean |
true |
Enable progress animations |
NONE - No animationFADE - Fade out effectSCALE - Scale down effectSLIDE_UP - Slide up and disappearSLIDE_DOWN - Slide down and disappearSLIDE_LEFT - Slide left and disappearSLIDE_RIGHT - Slide right and disappearBOUNCE - Bouncy scale effectZOOM_OUT - Zoom out effectThe ScratchifyController provides programmatic control over the scratch card:
val controller = remember { ScratchifyController() }
// Instant actions
controller.revealInstantly() // Reveal content immediately
controller.resetScratch() // Reset to initial state
// State management
val state = controller.saveState() // Save current scratch state
controller.restoreState(state) // Restore saved state
// Progress monitoring
val progress = controller.scratchProgress // Current scratch progress (0.0 to 1.0)Color.Transparent for traditional scratch-off behavioropacity for semi-transparent effectsgridResolution (75-100) for better performance on complex layoutsgridResolution (150-200) for more precise scratch detectionScratchify(
config = ScratchifyConfig(
revealFullAtPercent = 0.8f,
brushConfig = ScratchifyBrushConfig(brushSize = 25.dp),
hapticConfig = ScratchifyHapticConfig(
onScratchCompleted = HapticFeedbackType.SUCCESS
),
animationConfig = ScratchifyAnimationConfig(
revealAnimationType = RevealAnimationType.BOUNCE
)
),
contentToReveal = { LotteryPrizeContent() },
overlayContent = { LotteryTicketOverlay() }
)Scratchify(
config = ScratchifyConfig(
brushConfig = ScratchifyBrushConfig(
brushSize = 35.dp,
brushColor = Color.Magenta,
opacity = 0.7f,
brushShape = BrushShape.Star(points = 6)
)
),
contentToReveal = { CanvasBackground() },
overlayContent = { PaintSurface() }
)Scratchify(
config = ScratchifyConfig(
enableTapToScratch = true,
gridResolution = 100, // Optimized for mobile
hapticConfig = ScratchifyHapticConfig(
progressHapticInterval = 0.3f
)
),
contentToReveal = { RewardContent() },
overlayContent = { EngagementOverlay() }
)| Platform | Haptic Feedback | Performance | Status |
|---|---|---|---|
| Android | ✅ Full Support | ⚡ Optimized | ✅ Stable |
| iOS | ✅ Full Support | ⚡ Optimized | ✅ Stable |
Android:
iOS:
// Save current scratch progress
val scratchState = controller.saveState()
// Later restore the exact same state
controller.restoreState(scratchState)val customPath = Path().apply {
// Define your custom shape
moveTo(-10f, -10f)
lineTo(10f, -10f)
lineTo(0f, 10f)
close()
}
ScratchifyBrushConfig(
brushShape = BrushShape.Custom(customPath, 30.dp)
)val controller = remember { ScratchifyController() }
// Monitor progress in real-time
LaunchedEffect(controller.scratchProgress) {
println("Scratch progress: ${(controller.scratchProgress * 100).toInt()}%")
}We welcome contributions from the community! 🚀
We appreciate your help in improving Scratchify! 🎉
This project is licensed under the MIT License.
📄 Read the full license: MIT License
Made with ❤️ for the Compose Multiplatform community
Scratchify is a powerful and highly customizable scratch card SDK built using Jetpack Compose Multiplatform. It enables you to create engaging interactive scratch surfaces where users can scratch off an overlay to reveal hidden content underneath. Perfect for reward systems, discount reveals, surprise elements, and gamification in your modern apps!
Perfect for:
✅ Reward reveals & lottery systems
✅ Discount coupons & promotional codes
✅ Interactive surprise elements
✅ Gamification features & mini-games
✅ Educational apps & learning activities
✅ User engagement & retention strategies
✔️ Multiplatform support (Android & iOS)
✔️ Two-layer scratch surface (Overlay & Revealed Content)
✔️ Customizable brush configuration (size, color, opacity)
✔️ Auto-reveal after threshold (configurable percentage)
✔️ Scratch event callbacks (onScratchStarted, onScratchProgress, onScratchCompleted)
✔️ Instant reveal & reset functionality
✔️ Tap-to-scratch detection alongside drag gestures
✔️ Configurable grid resolution for performance optimization
✔️ Save & restore scratch state for persistent experiences
✔️ Cross-platform haptic feedback (iOS & Android)
✔️ Animated reveal effects (fade, scale, slide, bounce, zoom)
✔️ Custom brush shapes (circle, square, star, heart, diamond, custom paths)
✔️ Transparent & colored brush modes (traditional scratch or paint effects)
Since Scratchify is a Compose Multiplatform (CMP) library, you should add it to your commonMain source set to use it across both iOS and Android.
Add the dependency in your shared module's build.gradle.kts:
dependencies {
implementation("io.github.gsrathoreniks:scratchify:<latest_version>")
}import com.gsrathoreniks.scratchify.api.Scratchify
import com.gsrathoreniks.scratchify.api.ScratchifyController
import com.gsrathoreniks.scratchify.api.config.ScratchifyConfig
@Composable
fun BasicScratchCard() {
val controller = remember { ScratchifyController() }
Scratchify(
modifier = Modifier.size(300.dp, 200.dp),
config = ScratchifyConfig(),
controller = controller,
contentToReveal = {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFFFD700)),
contentAlignment = Alignment.Center
) {
Text("🎉 You Won! 🎉", style = MaterialTheme.typography.headlineMedium)
}
},
overlayContent = {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFF8E24AA)),
contentAlignment = Alignment.Center
) {
Text("Scratch Here!", color = Color.White)
}
}
)
}@Composable
fun AdvancedScratchCard() {
val controller = remember {
ScratchifyController(
ScratchifyConfig(
// Core settings
revealFullAtPercent = 0.6f,
gridResolution = 100,
enableTapToScratch = true,
// Brush configuration
brushConfig = ScratchifyBrushConfig(
brushSize = 30.dp,
brushColor = Color.Red,
opacity = 0.8f,
brushShape = BrushShape.Star(points = 5)
),
// Haptic feedback
hapticConfig = ScratchifyHapticConfig(
isEnabled = true,
onScratchStarted = HapticFeedbackType.LIGHT,
onScratchProgress = HapticFeedbackType.LIGHT,
onScratchCompleted = HapticFeedbackType.SUCCESS,
progressHapticInterval = 0.25f
),
// Animation effects
animationConfig = ScratchifyAnimationConfig(
revealAnimationType = RevealAnimationType.BOUNCE,
animationDurationMs = 800,
enableProgressAnimation = true
)
)
)
}
// Controller API usage
Row {
Button(onClick = { controller.revealInstantly() }) {
Text("Reveal")
}
Button(onClick = { controller.resetScratch() }) {
Text("Reset")
}
}
Scratchify(
modifier = Modifier.size(300.dp, 200.dp),
config = controller.config,
controller = controller,
contentToReveal = { /* Your content */ },
overlayContent = { /* Your overlay */ }
)
}| Parameter | Type | Default | Description |
|---|---|---|---|
revealFullAtPercent |
Float |
0.75f |
Auto-reveal threshold (0.0 to 1.0) |
isScratchingEnabled |
Boolean |
true |
Enable/disable scratching |
gridResolution |
Int |
150 |
Grid resolution for performance tuning |
enableTapToScratch |
Boolean |
true |
Allow single taps to create scratches |
| Parameter | Type | Default | Description |
|---|---|---|---|
brushSize |
Dp |
4.dp |
Size of the brush stroke |
brushColor |
Color |
Color.Cyan |
Brush color (Color.Transparent for traditional scratch) |
opacity |
Float |
1f |
Brush opacity (0.0 to 1.0) |
brushShape |
BrushShape |
BrushShape.Circle |
Shape of the brush stroke |
BrushShape.Circle - Classic circular brushBrushShape.Square - Square brush strokesBrushShape.Star(points: Int = 5) - Star-shaped brushBrushShape.Heart - Heart-shaped brushBrushShape.Diamond - Diamond-shaped brushBrushShape.Custom(path: Path, size: Dp) - Custom path shapes| Parameter | Type | Default | Description |
|---|---|---|---|
isEnabled |
Boolean |
true |
Enable haptic feedback |
onScratchStarted |
HapticFeedbackType |
LIGHT |
Feedback when scratching begins |
onScratchProgress |
HapticFeedbackType |
NONE |
Feedback during scratching |
onScratchCompleted |
HapticFeedbackType |
SUCCESS |
Feedback when completed |
progressHapticInterval |
Float |
0.25f |
Progress interval for haptic feedback |
| Parameter | Type | Default | Description |
|---|---|---|---|
revealAnimationType |
RevealAnimationType |
FADE |
Type of reveal animation |
animationDurationMs |
Int |
500 |
Animation duration in milliseconds |
enableProgressAnimation |
Boolean |
true |
Enable progress animations |
NONE - No animationFADE - Fade out effectSCALE - Scale down effectSLIDE_UP - Slide up and disappearSLIDE_DOWN - Slide down and disappearSLIDE_LEFT - Slide left and disappearSLIDE_RIGHT - Slide right and disappearBOUNCE - Bouncy scale effectZOOM_OUT - Zoom out effectThe ScratchifyController provides programmatic control over the scratch card:
val controller = remember { ScratchifyController() }
// Instant actions
controller.revealInstantly() // Reveal content immediately
controller.resetScratch() // Reset to initial state
// State management
val state = controller.saveState() // Save current scratch state
controller.restoreState(state) // Restore saved state
// Progress monitoring
val progress = controller.scratchProgress // Current scratch progress (0.0 to 1.0)Color.Transparent for traditional scratch-off behavioropacity for semi-transparent effectsgridResolution (75-100) for better performance on complex layoutsgridResolution (150-200) for more precise scratch detectionScratchify(
config = ScratchifyConfig(
revealFullAtPercent = 0.8f,
brushConfig = ScratchifyBrushConfig(brushSize = 25.dp),
hapticConfig = ScratchifyHapticConfig(
onScratchCompleted = HapticFeedbackType.SUCCESS
),
animationConfig = ScratchifyAnimationConfig(
revealAnimationType = RevealAnimationType.BOUNCE
)
),
contentToReveal = { LotteryPrizeContent() },
overlayContent = { LotteryTicketOverlay() }
)Scratchify(
config = ScratchifyConfig(
brushConfig = ScratchifyBrushConfig(
brushSize = 35.dp,
brushColor = Color.Magenta,
opacity = 0.7f,
brushShape = BrushShape.Star(points = 6)
)
),
contentToReveal = { CanvasBackground() },
overlayContent = { PaintSurface() }
)Scratchify(
config = ScratchifyConfig(
enableTapToScratch = true,
gridResolution = 100, // Optimized for mobile
hapticConfig = ScratchifyHapticConfig(
progressHapticInterval = 0.3f
)
),
contentToReveal = { RewardContent() },
overlayContent = { EngagementOverlay() }
)| Platform | Haptic Feedback | Performance | Status |
|---|---|---|---|
| Android | ✅ Full Support | ⚡ Optimized | ✅ Stable |
| iOS | ✅ Full Support | ⚡ Optimized | ✅ Stable |
Android:
iOS:
// Save current scratch progress
val scratchState = controller.saveState()
// Later restore the exact same state
controller.restoreState(scratchState)val customPath = Path().apply {
// Define your custom shape
moveTo(-10f, -10f)
lineTo(10f, -10f)
lineTo(0f, 10f)
close()
}
ScratchifyBrushConfig(
brushShape = BrushShape.Custom(customPath, 30.dp)
)val controller = remember { ScratchifyController() }
// Monitor progress in real-time
LaunchedEffect(controller.scratchProgress) {
println("Scratch progress: ${(controller.scratchProgress * 100).toInt()}%")
}We welcome contributions from the community! 🚀
We appreciate your help in improving Scratchify! 🎉
This project is licensed under the MIT License.
📄 Read the full license: MIT License
Made with ❤️ for the Compose Multiplatform community