- Room DB (ArtLegendDatabase, SessionDao, SessionEntity) wired up - SessionRepository saves scores and queries last 7 sessions per module - ProgressViewModel exposes sessions as StateFlow - ProgressScreen replaced with real bar chart + session history list - MainActivity now persists each game result to Room on return - build.gradle.kts + gradle.properties: KSP and Room 2.6.1 added Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ArtLegend
S-Pen mastery training for digital artists — gamified, modular, precision-first.
ArtLegend is an Android training app that turns S-Pen skill-building into a structured game. Each module isolates one drawing skill (pressure control, line confidence, curve accuracy, vector precision) and drills it with real-time feedback, scoring, and progressive difficulty.
Vision
Most artists know what they want to draw but lack the muscle memory to execute it. ArtLegend bridges that gap by making practice deliberate, measurable, and satisfying.
Each module isolates a specific physical skill. The first is Corridor Scroll — a Guitar Hero-style scrolling corridor the player must track with their stylus, training spatial accuracy and confident stroke coverage across all angles and speeds. Later modules build toward exercises where the player's accumulated strokes form actual drawings, so the skills trained are directly transferable to real artwork.
The long-term goal: a modular curriculum where artists buy skill packs, track their growth over weeks, and build real hand-eye coordination for professional digital work.
Module List
See MODULES.md for full gameplay design, scoring rules, and difficulty variables for each module.
| Module | Skill Trained | Status |
|---|---|---|
| Corridor Scroll | Sustained stroke control inside a scrolling, direction-changing corridor | In Progress |
| Stroke-to-Drawing | Guided strokes that compose a picture; teaches deliberate mark-making | Planned |
| Pressure Ramp | Dynamic stroke weight control against a target pressure curve | Planned |
| Curve Sculptor | Smooth bezier curve shaping | Planned |
| Vector Snap | Precision endpoint targeting | Planned |
Architecture
MainActivity (ComponentActivity + Compose)
└── Navigation: Hub → ModuleDetail → Progress → Settings
|
| Intent (puts ModuleType enum name)
v
GameActivity (AndroidApplication / LibGDX)
└── GameHost : Game() → routes to correct Screen by ModuleType
└── LineCorridorScreen (first real module)
└── StylusInputBridge (AtomicReference — bridges UI thread → GL thread)
Key Design Decisions
- Compose for navigation/UI — Hub, progress, settings, and module-detail screens benefit from standard Android UI patterns (adaptive layout, accessibility, back stack).
- LibGDX for game rendering — Frame-accurate rendering, direct OpenGL access, and fine-grained input timing for each training module.
- Separate Activities — Clean boundary:
MainActivityowns Compose state;GameActivityowns the LibGDX lifecycle. Intent extras carry theModuleType. shared/module — Zero-dependency pure-Kotlin module that holds theModuleTypeenum. Both Compose and LibGDX layers depend on it; neither depends on the other.- StylusInputBridge —
AtomicReference<StylusFrame?>lets the UI thread post stylus data (pressure, tilt, orientation) without locking the GL thread.
Tech Stack
| Layer | Technology |
|---|---|
| UI / Navigation | Compose Multiplatform, Material3, Navigation Compose |
| Game rendering | LibGDX 1.12.1 (OpenGL ES) |
| Language | Kotlin 1.9.23 |
| Build | Gradle 8.x (Kotlin DSL) |
| Min SDK | 26 (Android 8.0) |
| Target SDK | 34 (Android 14) |
| Primary device | Samsung Galaxy Tab S-series with S-Pen |
Build Instructions
Prerequisites
- Android Studio Hedgehog or later
- Android SDK 34
- JDK 17
Build debug APK
./gradlew :android:assembleDebug
Install on device
./gradlew :android:installDebug
Verify compilation
./gradlew :shared:compileKotlin
./gradlew :core:compileKotlin
./gradlew :android:compileDebugKotlin
Stylus Setup
ArtLegend reads S-Pen input via Android's MotionEvent API in GameActivity:
// Captured per frame in GameActivity.handleStylusEvent()
val pressure = event.pressure // 0.0 – 1.0
val tiltRad = event.getAxisValue(MotionEvent.AXIS_TILT)
val orientRad = event.getAxisValue(MotionEvent.AXIS_ORIENTATION)
val isStylus = event.getToolType(0) == TOOL_TYPE_STYLUS
The StylusInputBridge (an AtomicReference<StylusFrame?>) carries this data thread-safely into the LibGDX GL thread where each module's render() can consume it without locking.
The bridge posts every touch event but does not queue them — only the latest frame is visible to the GL thread. This matches the rendering cadence (60 fps) without unbounded buffering.
How to Add a New Module
- Add an entry to
ModuleTypeinshared/src/main/kotlin/com/artlegend/shared/ModuleType.kt - Create
core/src/main/kotlin/com/artlegend/modules/YourModuleScreen.ktimplementingScreen - Add a
whenbranch inGameHost.create()routing to your new screen - Add a
ModuleCardentry inHubScreen.kt(title + description) - Add a description string in
ModuleDetailScreen.kt'swhenblock
Business / Licensing Model
ArtLegend is designed around a modular content model:
- Free core: Line Corridor + basic progress tracking
- Skill packs (one-time purchase): Additional modules sold individually or as bundles (e.g., "Ink Fundamentals Pack" = Pressure Ramp + Curve Sculptor)
- Pro subscription: Unlocks all current + future modules, cloud sync, detailed analytics
- Institutional licensing: Studio / school licenses for group training dashboards
The ModuleType enum is the extension point — new modules ship as app updates and are unlocked via in-app purchase entitlements checked before launching GameActivity.