Skip to content

iOS vs Android ​

The JavaScript API is the same on both platforms, but the drawing engines are not. iOS hands the work to Apple's PencilKit. Android uses a custom view that draws each stroke as Bezier curves whose width follows the speed of your finger. This page collects the differences in one place.

Rendering ​

iOSAndroid
EnginePKCanvasView from PencilKitCustom View drawing into an offscreen bitmap
Line widthPencilKit's own pressure and speed response. The tool width is the midpoint of penMinWidth and penMaxWidth.max(penMaxWidth / (velocity + 1), penMinWidth), velocity in dp per ms. Slow is thick, fast is thin.
velocityFilterWeightIgnoredSmooths the velocity between samples (default 0.7)
defaultInkTypepen, marker, pencil; monoline, fountainPen, watercolor, crayon on iOS 17+ (falls back to pen earlier)Ignored
StylusApple Pencil pressure and tilt affect the inkTreated like a finger, except for pencilOnly

Why Android has its own renderer ​

Android has no system ink engine comparable to PencilKit. The Android view is a native port of the well-known velocity-based approach used by signature pads: each new touch sample adds a cubic Bezier segment built from the last four points, and the width changes gradually from the start to the end of each segment. Strokes are drawn into a bitmap, so a finished stroke costs nothing to redraw while you keep writing.

You can try a JavaScript version of the same algorithm on the Props page.

Defaults ​

PropiOSAndroid
penColorblack#111111
toolbarHeight4448
toolbarTintColorThe view's tint color (system blue)Each icon's own color. See Toolbar colors.
baselineColorSystem gray at 50% opacity#80808080
baselineOffsetFromBottom816
baselineWidth with baselineStyle="dotted"1.52

Set these props explicitly if you want both platforms to look the same.

History ​

iOSAndroid
What undo() revertsThe last stroke, clear() or setStrokeData()The last stroke
clear()Can be undoneCannot be undone; also empties the redo stack
setStrokeData()Can be undoneThe previous drawing is gone; undo() removes the restored strokes one by one
New strokeEmpties the redo stackEmpties the redo stack

Exports ​

iOSAndroid
PNG backgroundAlways transparentbackgroundColor if set
saveToPhotoLibrary PNGOpaque, white if no backgroundColorbackgroundColor if set, otherwise transparent
ClipboardTransparent trimmed PNG via UIPasteboardTrimmed PNG with background via a FileProvider URI
trim margin2 pt0.6 × penMaxWidth dp + 2 px
SVG units and widthsPoints, average width per strokePhysical pixels, (penMinWidth + penMaxWidth) / 2
toFile locationTemporary directoryCache directory
saveToPhotoLibrary result{ granted }; granted: false when access is denied{ granted, uri }; granted: false when the write fails

Details are on Export formats.

Stroke data ​

iOSAndroid
CoordinatesPointsdp
tSeconds since the stroke startedMilliseconds of system uptime
Extra fieldspressure, size, azimuth, altitudeNone
Restored widthPer-point sizeCurrent penMinWidth and penMaxWidth

See Stroke data and replay.

Input ​

iOSAndroid
pencilOnlyPencilKit drawingPolicy (iOS 14+). Fingers are ignored.Only TOOL_TYPE_STYLUS draws. Fingers and the stylus eraser end are ignored.
Inside a ScrollViewNo extra handlingThe view asks its parents not to intercept the touch once a stroke starts
PressureUsed by PencilKitNot used; width comes from speed only
showToolPickerShows the PencilKit tool pickerIgnored

Toolbar ​

iOSAndroid
IconsSF SymbolsBundled vector drawables in the same style
Overflow menuNative menu with icons (iOS 14+), action sheet on older versionsPopupMenu with labels only

Events ​

iOSAndroid
onChange per strokeCan fire more than onceOnce
onChange vs onEnd orderonEnd firstonChange first
Render error messageFailed to encode imageFailed to render bitmap

Released under the MIT License.