Ink Custom Brush Builder
Organizationandroid
Creates custom BrushFamily definitions for an Android app using Jetpack Ink (androidx.ink 1.1.0-alpha03 and higher). Use this skill when configuring custom BrushTip shapes, adding pressure, tilt, speed, or noise sensitivity using BrushBehavior node graphs, creating multi-coat layered brushes, applying tiling or particle stamping textures, configuring SlidingWindowModel input smoothing, or serializing brushes. Delivers complete Kotlin BrushFamily, BrushCoat, BrushTip, BrushPaint, and BrushBehavior implementations. Don't use for View-based (XML) inking, Compose drawing surface setup, or stock brush selection. For Compose drawing surface setup, InProgressStrokes wet and dry rendering, eraser, or persistence, suggest the ink-app-builder skill.
Overview
- Repository
- android/skills
- Publisher
- android
- Stars
- 10.5K
- Forks
- 695
- Bundled files
- 5
- License
- Apache-2.0
- Updated
- Nov 11, 2026
Installation
tmp=$(mktemp -d)
git clone --depth 1 https://github.com/android/skills.git "$tmp"
mkdir -p ~/.claude/skills
cp -R "$tmp/jetpack-compose/ink/ink-custom-brush-builder" ~/.claude/skills/ink-custom-brush-builder
rm -rf "$tmp"tmp=$(mktemp -d)
git clone --depth 1 https://github.com/android/skills.git "$tmp"
mkdir -p ~/.codex/skills
cp -R "$tmp/jetpack-compose/ink/ink-custom-brush-builder" ~/.codex/skills/ink-custom-brush-builder
rm -rf "$tmp"Install the Ink Custom Brush Builder agent skill from https://github.com/android/skills/tree/main/jetpack-compose/ink/ink-custom-brush-builder into this project's skills folder.Instructions
Custom brushes in the Android Ink API are built around the BrushFamily
hierarchy:
BrushFamily
├── InputModel (BrushFamily.InputModel.SlidingWindowModel or PASSTHROUGH_MODEL)
└── BrushCoat[] (one or more rendering layers)
├── BrushTip (shape: scaleX, scaleY, cornerRounding, rotationDegrees, slantDegrees, pinch)
│ └── BrushBehavior[] (dynamic response to stylus or touch input)
│ └── Tree: TargetNode(input = [Operators](input = SourceNode))
└── BrushPaint (visual appearance)
├── SelfOverlap (ANY, ACCUMULATE, or DISCARD)
├── TextureLayer[] (BrushPaint.TilingTexture or BrushPaint.StampingTexture)
└── BrushPaint.ColorFunction[] (OpacityMultiplier or ReplaceColor)
Create a Brush by combining a custom BrushFamily with a Compose Color,
size, and epsilon:
val brush = Brush.createWithComposeColor(
family = myCustomFamily,
color = Color.Black,
size = 10f,
epsilon = 0.1f,
)Version and experimental API requirements :
- Requires
androidx.ink1.1.0-alpha03and higher : In1.0.0(Stable) and1.1.0-alpha01-alpha02,BrushFamily(...),BrushCoat,BrushTip,BrushPaint,BrushBehavior, andandroidx.ink.brush.behavior.*nodes were@RestrictTo(LIBRARY_GROUP)(in1.0.0stable, custom brushes can only be loaded from binary proto streams usingBrushFamily.decode). Starting in1.1.0-alpha03and higher , programmatic custom brush constructors graduated to public API (BrushPaint.TilingTexture,BrushPaint.StampingTexture,BrushPaint.ColorFunction,BrushFamily.InputModel.SlidingWindowModel, and all behavior nodes).- No
@OptInrequired in1.1.0-alpha03and higher : All public custom brush classes andink-storageBrushFamilyserialization APIs (BrushFamily.encodeanddecode,AndroidBrushFamilySerialization,BrushFamilyDecodeCallback) are unannotated in1.1.0-alpha03and higher (ExperimentalInkCustomBrushApiis@RestrictTo(LIBRARY_GROUP)in1.1.0-alpha05and higher, and is only required onBrushFamilyserialization in1.0.0stable).
Workflow and task routing
Follow these conditional instructions based on the requested custom brush task:
- Step 1 (Dependencies) : When setting up an Android project for programmatic custom brushes, ask user permission before editing build files and verify that
androidx.ink:ink-brush,androidx.ink:ink-brush-compose,androidx.ink:ink-storage, andandroidx.ink:ink-nativeloaderuse version1.1.0-alpha03or higher ingradle/libs.versions.tomlandapp/build.gradle.kts. - Step 2 (Hierarchy, tip shape, paint, and input smoothing) : When defining
BrushFamilyand multi-coatBrushCoatlayers, configuringBrushTipgeometry (scaleX,scaleY,cornerRounding,rotationDegrees,slantDegrees,pinch), settingBrushPaintproperties (SelfOverlap,ColorFunction), or smoothing stroke input withBrushFamily.InputModel.SlidingWindowModel, you MUST follow brush-structure.md. For baselineBrushcreation or1.0.0resource loading, review Brush APIs. - Step 3 (Dynamic stylus and touch behaviors) : When making a brush respond dynamically to stylus pressure, tilt, speed, direction, or procedural noise using
BrushBehaviornode trees (SourceNode,DampingNode,NoiseNode,ResponseNode,TargetNode,PolarTargetNode), you MUST follow brush-behaviors.md. - Step 4 (Textures and serialization) : When adding
BrushPaint.TilingTextureorBrushPaint.StampingTexturelayers, implementingTextureBitmapStore, or encoding and decoding customBrushFamilydefinitions (BrushFamily.encodeanddecode,AndroidBrushFamilySerialization,BrushFamilyDecodeCallback), you MUST follow textures-and-serialization.md. Inform the user which texture image files (such asapp/src/main/res/drawable-nodpi/pencil_grain.png) to add to their project and register inTextureBitmapStore. - Step 5 (Complete brush recipes) : When building a complete pressure pen, calligraphy nib, shading pencil, watercolor wash, or multi-coat shadow brush from scratch, start from the copy-ready implementations in examples.md.
- Compose drawing surface, eraser, or persistence : For Compose drawing surface setup,
InProgressStrokeswet and dry rendering, geometry-based erasing, or Room stroke persistence, suggest theink-app-builderskill.
Antipatterns
- NEVER use raw
ink.proto.*builders to construct brushes---always use the public Kotlin constructors (BrushFamily,BrushCoat,BrushTip,BrushPaint,BrushBehaviorfromandroidx.ink.brush.*). - NEVER manually scale stroke width in a touch listener for pressure or tilt sensitivity---use
BrushBehaviormappingSourceNode.Source.NORMALIZED_PRESSUREorTILT_IN_RADIANStoTargetNode.Target.SIZE_MULTIPLIERorWIDTH_MULTIPLIER. - NEVER use manual
Canvasbitmap stamping orBitmapShaderfor textured brushes---useBrushPaint.TilingTextureorBrushPaint.StampingTexturewith aTextureBitmapStore. - NEVER use
BlendMode.SRC_OVERon the final (or only)TextureLayer---useBlendMode.MODULATE,DST_IN,DST_OUT,SRC_ATOP, orSRC_INso edge anti-aliasing andOPACITY_MULTIPLIERbehaviors aren't overridden. - NEVER serialize
BrushFamilyas JSON, Gson, Moshi, orParcelable---always useBrushFamily.encodeandBrushFamily.decodeorAndroidBrushFamilySerializationfromandroidx.ink.storage. - NEVER implement manual Bézier (
quadTo,cubicTo) smoothing ---configureBrushFamily.InputModel.SlidingWindowModelfor input smoothing andDampingNodefor behavior smoothing. - NEVER use
Random.nextFloatin draw loops for organic stroke wobble---attach aNoiseNodevaried over stroke distance inBrushBehavior.
Bundled files
FAQ
What does Ink Custom Brush Builder do?
Creates custom BrushFamily definitions for an Android app using Jetpack Ink (androidx.ink 1.1.0-alpha03 and higher). Use this skill when configuring custom BrushTip shapes, adding pressure, tilt, speed, or noise sensitivity using BrushBehavior node graphs, creating multi-coat layered brushes, applying tiling or particle stamping textures, configuring SlidingWindowModel input smoothing, or serializing brushes. Delivers complete Kotlin BrushFamily, BrushCoat, BrushTip, BrushPaint, and BrushBehavior implementations. Don't use for View-based (XML) inking, Compose drawing surface setup, or stock brush selection. For Compose drawing surface setup, InProgressStrokes wet and dry rendering, eraser, or persistence, suggest the ink-app-builder skill.
Who made Ink Custom Brush Builder?
android (an organization) publishes it in the android/skills repository on GitHub.
Is Ink Custom Brush Builder free?
Yes. Ink Custom Brush Builder is open source, published by android under the Apache-2.0 license.
What comes with Ink Custom Brush Builder?
Its instructions and 5 files the agent reads only when a task needs them, such as references, examples or scripts.
Which agents can use Ink Custom Brush Builder?
Any coding agent that reads SKILL.md files, including Claude Code, Codex, Cursor, GitHub Copilot and Gemini CLI. The agent loads the skill when a task matches its description.
What are agent skills?
An agent skill is a folder with a SKILL.md file: instructions that teach a coding agent how to do one kind of task, and optionally files it can read while it works. The agent keeps only the skill's name and description in mind and reads the rest when a task calls for it.
How are agent skills different from MCP servers?
An MCP server gives an agent new tools to call, code that runs somewhere and returns results. A skill gives it knowledge: how to approach a task with the tools it already has. Many agents use both.
How can my agent test my Android app?
Robomori lets coding agents create, start and control Android emulators and devices from one command, so they can install your app, take screenshots and check dark mode, font sizes and languages.