
Smooth, pressure-aware signature capture offering UNDO/REDO, velocity-based stroke widths, Catmull-Rom Bézier smoothing, presets, tunable parameters, SVG/bitmap export and smart auto-crop.
A modern Kotlin Multiplatform library for capturing smooth signature drawings with UNDO/REDO functionality, built with Compose Multiplatform.
✅ Fully Supported: Android • Desktop (JVM) • iOS • Web (Wasm/JS)
Enhanced fork of gcacace/android-signaturepad, completely rewritten in Kotlin for KMP with Compose Multiplatform.
-Xexplicit-api=strict
dependencies {
implementation("com.tuppersoft:signature-pad:$lastVersion")
}@Composable
fun SignatureScreen() {
val state = rememberSignaturePadState()
Column(modifier = Modifier.fillMaxSize()) {
SignaturePad(
state = state,
config = SignaturePadConfig.fountainPen(),
modifier = Modifier.fillMaxWidth()
)
}
}Three carefully tuned presets for different signature styles:
// 🖋️ Fountain Pen (1-4dp) - Elegant, variable width
SignaturePad(
config = SignaturePadConfig.fountainPen(),
modifier = Modifier.fillMaxWidth()
)
// 🖊️ BIC Pen (1.8-2.8dp) - Nearly uniform with subtle ink accumulation
SignaturePad(
config = SignaturePadConfig.pen(),
modifier = Modifier.fillMaxWidth()
)
// 🖍️ Marker (5-7dp) - Bold, thick strokes
SignaturePad(
config = SignaturePadConfig.marker(),
modifier = Modifier.fillMaxWidth()
)Full control over drawing behavior:
SignaturePad(
config = SignaturePadConfig(
penMinWidth = 2.dp, // Thinnest line (fast drawing)
penMaxWidth = 8.dp, // Thickest line (slow drawing)
penColor = Color.Blue, // Stroke color
velocitySmoothness = 0.8f, // Drawing smoothness (0.0-1.0)
widthSmoothness = 0.7f, // Width transition smoothness (0.0-1.0)
minVelocity = 0f, // Speed for max width (px/ms)
maxVelocity = 10f, // Speed for min width (px/ms)
widthVariation = 1.5f, // Thickness contrast (1.0 = linear)
inputNoiseThreshold = 1.0f // Hand shake filter (pixels)
),
modifier = Modifier.fillMaxWidth().height(400.dp)
)val state = rememberSignaturePadState()
// Check state
if (state.isEmpty) {
Text("Please sign above")
}
// UNDO/REDO capabilities
val canUndo: Boolean = state.canUndo()
val canRedo: Boolean = state.canRedo()
// Actions
val undoSuccess: Boolean = state.undo() // Returns true if successful
val redoSuccess: Boolean = state.redo() // Returns true if successful
state.clear() // Clear all strokesval svg: String = state.toSvg()
// Save or transmit the SVG string// Bitmap with white background
val bitmap = state.toBitmap()
// Bitmap with transparent background
val transparentBitmap = state.toTransparentBitmap()
// Auto-crop to signature bounds with padding
val croppedBitmap = state.toBitmap(
crop = true,
paddingCrop = 16 // pixels of padding around signature
)
// Combine options
val finalBitmap = state.toTransparentBitmap(
crop = true,
paddingCrop = 20
)React to user interactions:
SignaturePad(
state = state,
config = SignaturePadConfig.fountainPen(),
onStartSign = {
// User started drawing - disable submit button, etc.
println("User started signing")
},
onSign = {
// Signature updated - enable submit, show preview, etc.
println("Signature updated")
},
onClear = {
// Signature cleared - reset form state, etc.
println("Signature cleared")
},
modifier = Modifier.fillMaxWidth()
)Complete reference for all configuration options:
| Parameter | Type | Range | Default | User Question | Description |
|---|---|---|---|---|---|
penMinWidth |
Dp | > 0 | 1.0.dp | "What's the thinnest my line can be?" | Minimum stroke width (fast drawing) |
penMaxWidth |
Dp | > 0 | 4.0.dp | "What's the thickest my line can be?" | Maximum stroke width (slow drawing) |
penColor |
Color | - | Color(0xFF003D82) | "What color is my pen?" | Stroke color |
velocitySmoothness |
Float | 0.0-1.0 | 0.85 | "How smooth should the drawing feel?" | Stroke smoothness (0.0 = jumpy, 1.0 = very smooth) |
widthSmoothness |
Float | 0.0-1.0 | 0.7 | "How gradual should thickness changes be?" | Width transition smoothness (0.0 = abrupt, 1.0 = gradual) |
minVelocity |
Float | ≥ 0 | 0.0 | "When does the line stop getting thicker?" | Velocity for maximum width (px/ms) |
maxVelocity |
Float | ≥ 0 | 8.0 | "When does the line stop getting thinner?" | Velocity for minimum width (px/ms) |
widthVariation |
Float | 0.5-3.0 | 1.5 | "How much should thickness vary with speed?" | Width contrast (1.0 = linear, >1.0 = more contrast, <1.0 = less) |
inputNoiseThreshold |
Float | ≥ 0 | 0.8 | "How much should I filter hand shake?" | Min distance between points to filter tremor (px) |
signature-pad/
├── commonMain/ # Shared code (100% of library)
│ ├── compose/ # Compose UI components
│ ├── geometry/ # Bézier math, points
│ ├── rendering/ # Canvas rendering
│ └── export/ # Bitmap/SVG exporters
├── androidMain/ # Android manifest only
└── desktopMain/ # Desktop-specific (currently empty)
| Platform | Status | Version | Notes |
|---|---|---|---|
| Android | ✅ Stable | API 21+ | Full support |
| Desktop (JVM) | ✅ Stable | Java 17+ | Windows, macOS, Linux |
| iOS | ✅ Stable | iOS 15+ | arm64, x64, simulator |
| Web (Wasm) | ✅ Stable | Modern browsers | Chrome, Firefox, Safari (experimental support) |
| Web (JS) | ✅ Stable | Modern browsers | Chrome, Firefox, Safari, Edge |
Note: All platforms share 100% of the code through
commonMain. Platform-specific folders (iosMain,jsMain,wasmJsMain) are intentionally empty as the library uses pure Compose Multiplatform APIs that work across all targets.
Contributions are welcome! Please follow these guidelines:
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)# Clone the repository
git clone https://github.com/rulogarcillan/signature-pad.git
cd signature-pad
# Build the library
./gradlew :signature-pad:build
# Run the sample app (Android)
./gradlew :app:installDebug
# Run the sample app (Desktop)
./gradlew :app:run
# For iOS, use Xcode or Kotlin Multiplatform Mobile plugin in Android StudioCopyright 2025 Tuppersoft by Rulo Garcillan
Copyright 2014-2016 Gianluca Cacace
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This is a derivative work based on android-signaturepad by Gianluca Cacace.
⭐ If you find this library useful, please star the repo!
A modern Kotlin Multiplatform library for capturing smooth signature drawings with UNDO/REDO functionality, built with Compose Multiplatform.
✅ Fully Supported: Android • Desktop (JVM) • iOS • Web (Wasm/JS)
Enhanced fork of gcacace/android-signaturepad, completely rewritten in Kotlin for KMP with Compose Multiplatform.
-Xexplicit-api=strict
dependencies {
implementation("com.tuppersoft:signature-pad:$lastVersion")
}@Composable
fun SignatureScreen() {
val state = rememberSignaturePadState()
Column(modifier = Modifier.fillMaxSize()) {
SignaturePad(
state = state,
config = SignaturePadConfig.fountainPen(),
modifier = Modifier.fillMaxWidth()
)
}
}Three carefully tuned presets for different signature styles:
// 🖋️ Fountain Pen (1-4dp) - Elegant, variable width
SignaturePad(
config = SignaturePadConfig.fountainPen(),
modifier = Modifier.fillMaxWidth()
)
// 🖊️ BIC Pen (1.8-2.8dp) - Nearly uniform with subtle ink accumulation
SignaturePad(
config = SignaturePadConfig.pen(),
modifier = Modifier.fillMaxWidth()
)
// 🖍️ Marker (5-7dp) - Bold, thick strokes
SignaturePad(
config = SignaturePadConfig.marker(),
modifier = Modifier.fillMaxWidth()
)Full control over drawing behavior:
SignaturePad(
config = SignaturePadConfig(
penMinWidth = 2.dp, // Thinnest line (fast drawing)
penMaxWidth = 8.dp, // Thickest line (slow drawing)
penColor = Color.Blue, // Stroke color
velocitySmoothness = 0.8f, // Drawing smoothness (0.0-1.0)
widthSmoothness = 0.7f, // Width transition smoothness (0.0-1.0)
minVelocity = 0f, // Speed for max width (px/ms)
maxVelocity = 10f, // Speed for min width (px/ms)
widthVariation = 1.5f, // Thickness contrast (1.0 = linear)
inputNoiseThreshold = 1.0f // Hand shake filter (pixels)
),
modifier = Modifier.fillMaxWidth().height(400.dp)
)val state = rememberSignaturePadState()
// Check state
if (state.isEmpty) {
Text("Please sign above")
}
// UNDO/REDO capabilities
val canUndo: Boolean = state.canUndo()
val canRedo: Boolean = state.canRedo()
// Actions
val undoSuccess: Boolean = state.undo() // Returns true if successful
val redoSuccess: Boolean = state.redo() // Returns true if successful
state.clear() // Clear all strokesval svg: String = state.toSvg()
// Save or transmit the SVG string// Bitmap with white background
val bitmap = state.toBitmap()
// Bitmap with transparent background
val transparentBitmap = state.toTransparentBitmap()
// Auto-crop to signature bounds with padding
val croppedBitmap = state.toBitmap(
crop = true,
paddingCrop = 16 // pixels of padding around signature
)
// Combine options
val finalBitmap = state.toTransparentBitmap(
crop = true,
paddingCrop = 20
)React to user interactions:
SignaturePad(
state = state,
config = SignaturePadConfig.fountainPen(),
onStartSign = {
// User started drawing - disable submit button, etc.
println("User started signing")
},
onSign = {
// Signature updated - enable submit, show preview, etc.
println("Signature updated")
},
onClear = {
// Signature cleared - reset form state, etc.
println("Signature cleared")
},
modifier = Modifier.fillMaxWidth()
)Complete reference for all configuration options:
| Parameter | Type | Range | Default | User Question | Description |
|---|---|---|---|---|---|
penMinWidth |
Dp | > 0 | 1.0.dp | "What's the thinnest my line can be?" | Minimum stroke width (fast drawing) |
penMaxWidth |
Dp | > 0 | 4.0.dp | "What's the thickest my line can be?" | Maximum stroke width (slow drawing) |
penColor |
Color | - | Color(0xFF003D82) | "What color is my pen?" | Stroke color |
velocitySmoothness |
Float | 0.0-1.0 | 0.85 | "How smooth should the drawing feel?" | Stroke smoothness (0.0 = jumpy, 1.0 = very smooth) |
widthSmoothness |
Float | 0.0-1.0 | 0.7 | "How gradual should thickness changes be?" | Width transition smoothness (0.0 = abrupt, 1.0 = gradual) |
minVelocity |
Float | ≥ 0 | 0.0 | "When does the line stop getting thicker?" | Velocity for maximum width (px/ms) |
maxVelocity |
Float | ≥ 0 | 8.0 | "When does the line stop getting thinner?" | Velocity for minimum width (px/ms) |
widthVariation |
Float | 0.5-3.0 | 1.5 | "How much should thickness vary with speed?" | Width contrast (1.0 = linear, >1.0 = more contrast, <1.0 = less) |
inputNoiseThreshold |
Float | ≥ 0 | 0.8 | "How much should I filter hand shake?" | Min distance between points to filter tremor (px) |
signature-pad/
├── commonMain/ # Shared code (100% of library)
│ ├── compose/ # Compose UI components
│ ├── geometry/ # Bézier math, points
│ ├── rendering/ # Canvas rendering
│ └── export/ # Bitmap/SVG exporters
├── androidMain/ # Android manifest only
└── desktopMain/ # Desktop-specific (currently empty)
| Platform | Status | Version | Notes |
|---|---|---|---|
| Android | ✅ Stable | API 21+ | Full support |
| Desktop (JVM) | ✅ Stable | Java 17+ | Windows, macOS, Linux |
| iOS | ✅ Stable | iOS 15+ | arm64, x64, simulator |
| Web (Wasm) | ✅ Stable | Modern browsers | Chrome, Firefox, Safari (experimental support) |
| Web (JS) | ✅ Stable | Modern browsers | Chrome, Firefox, Safari, Edge |
Note: All platforms share 100% of the code through
commonMain. Platform-specific folders (iosMain,jsMain,wasmJsMain) are intentionally empty as the library uses pure Compose Multiplatform APIs that work across all targets.
Contributions are welcome! Please follow these guidelines:
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)# Clone the repository
git clone https://github.com/rulogarcillan/signature-pad.git
cd signature-pad
# Build the library
./gradlew :signature-pad:build
# Run the sample app (Android)
./gradlew :app:installDebug
# Run the sample app (Desktop)
./gradlew :app:run
# For iOS, use Xcode or Kotlin Multiplatform Mobile plugin in Android StudioCopyright 2025 Tuppersoft by Rulo Garcillan
Copyright 2014-2016 Gianluca Cacace
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This is a derivative work based on android-signaturepad by Gianluca Cacace.
⭐ If you find this library useful, please star the repo!