Skip to content

Get started

Add the dependencies

Use Java 25+, Kotlin 2.4.20 and Compose Multiplatform 1.12.1 for the tested development setup. Add the repository to the consuming project's settings.gradle.kts:

dependencyResolutionManagement {
    repositories {
        maven {
            url = uri("../byteink/build/repo")
            content { includeGroup("com.vivenotes.byteink") }
        }
        google()
        mavenCentral()
    }
}

Publish the checkout locally once; its native build outputs must be present:

# In ByteInk; omit the native builds when their pinned outputs already exist.
native/build-linux.sh
native/build-windows.sh
./gradlew publishAllPublicationsToBuildRepository

In the consumer's JVM dependencies:

kotlin {
    jvm()
    sourceSets.jvmMain.dependencies {
        implementation("com.vivenotes.byteink:byteink-compose:0.1.0-SNAPSHOT")
        implementation("com.vivenotes.byteink:byteink-kit:0.1.0-SNAPSHOT")
        implementation(compose.desktop.currentOs)
    }
}
compose.desktop {
    application {
        mainClass = "your.package.MainKt"
        jvmArgs += "--enable-native-access=ALL-UNNAMED"
    }
}

Apply the Kotlin Compose compiler and Compose Multiplatform plugins in your application. The application supplies its OS-specific Compose/Skiko runtime. byteink-compose and byteink-kit bring in core, the pinned AndroidX modules and the native loader.

For a plain JVM project, put the same dependencies in dependencies { ... }. The example build uses a standalone JVM consumer.

Other repository choices

Mode Setup
Source/composite Add includeBuild("../byteink") to settings; keep the normal dependency declarations. Native outputs are still required.
Maven local Run ./gradlew publishToMavenLocal in ByteInk; configure mavenLocal { content { includeGroup("com.vivenotes.byteink") } }.
GitHub Packages Use the registry below and a published version. A remote package must already exist.
maven {
    url = uri("https://maven.pkg.github.com/crownbyte0b/byteink")
    content { includeGroup("com.vivenotes.byteink") }
    credentials {
        username = providers.environmentVariable("GITHUB_ACTOR").orNull
        password = providers.environmentVariable("GITHUB_TOKEN").orNull
    }
}

GitHub's Maven registry requires authentication, including for public packages. Use a classic token with read:packages; keep it outside source control. Registry documentation.

Avoid adding Google's androidx.ink:ink-nativeloader or ink-nativeloader-jvm separately: they conflict with ByteInk's loader. See troubleshooting.

Create, store and rebuild a stroke

This uses the same input/brush types as AndroidX Ink. elapsedTimeMillis starts at zero for each stroke.

package wiki

import androidx.ink.brush.InputToolType
import androidx.ink.strokes.MutableStrokeInputBatch
import androidx.ink.strokes.Stroke
import com.vivenotes.byteink.core.InkRuntime
import com.vivenotes.byteink.kit.AuthoredViveStroke
import com.vivenotes.byteink.kit.ViveBrushes
import com.vivenotes.byteink.kit.ViveInkTool

fun firstStroke(): AuthoredViveStroke {
    InkRuntime.load()
    val tool = ViveInkTool(
        familyId = ViveBrushes.MARKER,
        sizeDp = 6f,
        colorArgb = 0xff202020.toInt(),
        colorFollowsTheme = false,
    )
    val inputs = MutableStrokeInputBatch().apply {
        add(type = InputToolType.MOUSE, x = 10f, y = 50f, elapsedTimeMillis = 0L)
        add(type = InputToolType.MOUSE, x = 90f, y = 50f, elapsedTimeMillis = 80L)
    }
    return tool.complete(
        stroke = Stroke(tool.brush, inputs),
        id = "stroke-1",
        pageId = "page-1",
        seq = 1,
        createdAt = 42L,
    )
}

ViveInkTool.complete returns the finished stroke and a new stored row. canonicalStroke rebuilds that row through the codec, giving the geometry a reload will use. Persist row with a unique ID, correct page ID, and repository-allocated seq.

For live drawing, continue to the Compose surface. All tool parameters.