
2D runtime for Compose apps offering scene/entity/system architecture, camera-aware rendering, normalized input events, asset registry, and optional physics including Barnes–Hut gravity.
Kanvas is a Kotlin Multiplatform 2D runtime for Compose applications. It provides a small engine layer for scenes, entities, systems, rendering, camera control, assets, and normalized input events.
The engine stays domain-neutral. Game rules, simulations, AI, economy, and app-specific behavior are expected to live in your own SceneSystem implementations or in optional extension modules.
Current version: 0.1.0-alpha.1
Supported targets:
| Module | Artifact | Purpose |
|---|---|---|
:engine |
io.github.maximdvinov:engine |
Core runtime, scene model, renderer API, assets, input, and Compose host. |
:enginePhysics |
io.github.maximdvinov:enginePhysics |
Optional 2D physics primitives, broadphase, collision, and physics DSL. |
:engineGravityBarnesHut |
io.github.maximdvinov:engineGravityBarnesHut |
Optional Barnes-Hut n-body gravity simulation. |
:engineWorldObjectsKit |
io.github.maximdvinov:engineWorldObjectsKit |
Reusable templates for common 2D world objects. |
After the first Maven Central release, consumers only need mavenCentral():
repositories {
google()
mavenCentral()
}
dependencies {
implementation("io.github.maximdvinov:engine:0.1.0-alpha.1")
}Optional extensions:
dependencies {
implementation("io.github.maximdvinov:enginePhysics:0.1.0-alpha.1")
implementation("io.github.maximdvinov:engineGravityBarnesHut:0.1.0-alpha.1")
implementation("io.github.maximdvinov:engineWorldObjectsKit:0.1.0-alpha.1")
}GitHub Packages is also configured as a secondary publishing target:
repositories {
maven("https://maven.pkg.github.com/MaximDvinov/Kanvas")
}GitHub Packages may require GitHub credentials for dependency resolution. Maven Central is the recommended public distribution channel.
Use GameHost when building a Compose application:
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import com.kanvas.fx.core.Engine
import com.kanvas.fx.game.Entity
import com.kanvas.fx.game.GameHost
import com.kanvas.fx.game.Render
import com.kanvas.fx.game.Scene
import com.kanvas.fx.game.System
import com.kanvas.fx.game.Transform
@Composable
fun App() {
val engine = remember { Engine() }
GameHost(
engine = engine,
modifier = Modifier.fillMaxSize()
) {
Scene("main", setCurrent = true) {
System(id = "movement") { frame ->
// Update simulation state here.
}
Entity("player") {
Transform(position = Offset(120f, 180f))
Render(zIndex = 10) { entity ->
circle(
center = entity.position,
radius = 24f,
color = Color(0xFF4EA1FF)
)
}
}
}
}
}For lower-level or advanced use cases, you can create an Engine directly and host it with EngineCanvas:
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import com.kanvas.fx.compose.EngineCanvas
import com.kanvas.fx.dsl.engine
@Composable
fun App() {
val game = remember {
engine {
scene("main", setCurrent = true) {
onUpdate { frame ->
// Imperative update logic.
}
entities {
entity("player") {
// Configure update, render, and input callbacks.
}
}
}
}
}
EngineCanvas(engine = game)
}Engine is the runtime container. It owns registered scenes, tracks the active scene, dispatches input, and advances simulation through tick(deltaSeconds).
Scene is an isolated 2D space. It owns entities, scene systems, camera state, lifecycle callbacks, update callbacks, and input handlers.
Entity represents an object in a scene. Entities can render, receive input, and participate in update logic. They are intentionally lightweight so you can model game objects, particles, UI world objects, or simulation bodies.
SceneSystem is the recommended extension point for domain logic. A physics step, gravity simulation, enemy AI, procedural spawning, or animation controller can all be implemented as systems.
Renderer2D exposes primitives such as circles, lines, rectangles, polygons, textures, and materials. Rendering is camera-aware when hosted through EngineCanvas.
Camera2D controls world position, zoom, and zoom limits. Pointer input is mapped from screen-space to world-space through the active scene camera.
AssetRegistry stores platform-neutral asset IDs for textures and shaders. Assets can be backed by paths, bitmaps, shader text, or external shader DSL entries.
EngineInputEvent normalizes pointer, drag, zoom, scroll, and keyboard events across supported platforms:
PointerDown, PointerMove, PointerUp, Tap
DragStart, Drag, DragEnd
PinchZoom, Scroll
KeyDown, KeyUp
Input handlers receive coordinates already mapped into the engine model where applicable.
Add the physics module:
dependencies {
implementation("io.github.maximdvinov:enginePhysics:0.1.0-alpha.1")
}Then attach physics as scene-level logic:
Scene("level", setCurrent = true) {
System(id = "physics") { frame ->
// Step your physics world with frame.deltaSeconds.
}
Entity("ball") {
Transform(position = Offset(100f, 100f))
Render { entity ->
circle(
center = entity.position,
radius = 16f,
color = Color(0xFF67D391)
)
}
}
}The repository includes several sample applications:
samples/planetSample (:samples:planetSample:*) - multiplatform planet scene sample.samples/flappySample (:samples:flappySample:*) - Compose game sample.samples/planetMergeSample (:samples:planetMergeSample:*) - multiplatform merge-style game sample.:docsSite and :docsShowcase - documentation site and focused browser showcase examples.Useful commands:
./gradlew :samples:planetSample:desktop:run
./gradlew :engine:jvmTest :enginePhysics:jvmTest
./gradlew publishAllPublicationsToLocalBuildRepositoryLibrary modules are configured for:
build/local-maven.See docs/RELEASING.md for the release checklist, required secrets, and publishing commands.
Kanvas is available under the MIT License.
Kanvas is a Kotlin Multiplatform 2D runtime for Compose applications. It provides a small engine layer for scenes, entities, systems, rendering, camera control, assets, and normalized input events.
The engine stays domain-neutral. Game rules, simulations, AI, economy, and app-specific behavior are expected to live in your own SceneSystem implementations or in optional extension modules.
Current version: 0.1.0-alpha.1
Supported targets:
| Module | Artifact | Purpose |
|---|---|---|
:engine |
io.github.maximdvinov:engine |
Core runtime, scene model, renderer API, assets, input, and Compose host. |
:enginePhysics |
io.github.maximdvinov:enginePhysics |
Optional 2D physics primitives, broadphase, collision, and physics DSL. |
:engineGravityBarnesHut |
io.github.maximdvinov:engineGravityBarnesHut |
Optional Barnes-Hut n-body gravity simulation. |
:engineWorldObjectsKit |
io.github.maximdvinov:engineWorldObjectsKit |
Reusable templates for common 2D world objects. |
After the first Maven Central release, consumers only need mavenCentral():
repositories {
google()
mavenCentral()
}
dependencies {
implementation("io.github.maximdvinov:engine:0.1.0-alpha.1")
}Optional extensions:
dependencies {
implementation("io.github.maximdvinov:enginePhysics:0.1.0-alpha.1")
implementation("io.github.maximdvinov:engineGravityBarnesHut:0.1.0-alpha.1")
implementation("io.github.maximdvinov:engineWorldObjectsKit:0.1.0-alpha.1")
}GitHub Packages is also configured as a secondary publishing target:
repositories {
maven("https://maven.pkg.github.com/MaximDvinov/Kanvas")
}GitHub Packages may require GitHub credentials for dependency resolution. Maven Central is the recommended public distribution channel.
Use GameHost when building a Compose application:
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import com.kanvas.fx.core.Engine
import com.kanvas.fx.game.Entity
import com.kanvas.fx.game.GameHost
import com.kanvas.fx.game.Render
import com.kanvas.fx.game.Scene
import com.kanvas.fx.game.System
import com.kanvas.fx.game.Transform
@Composable
fun App() {
val engine = remember { Engine() }
GameHost(
engine = engine,
modifier = Modifier.fillMaxSize()
) {
Scene("main", setCurrent = true) {
System(id = "movement") { frame ->
// Update simulation state here.
}
Entity("player") {
Transform(position = Offset(120f, 180f))
Render(zIndex = 10) { entity ->
circle(
center = entity.position,
radius = 24f,
color = Color(0xFF4EA1FF)
)
}
}
}
}
}For lower-level or advanced use cases, you can create an Engine directly and host it with EngineCanvas:
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import com.kanvas.fx.compose.EngineCanvas
import com.kanvas.fx.dsl.engine
@Composable
fun App() {
val game = remember {
engine {
scene("main", setCurrent = true) {
onUpdate { frame ->
// Imperative update logic.
}
entities {
entity("player") {
// Configure update, render, and input callbacks.
}
}
}
}
}
EngineCanvas(engine = game)
}Engine is the runtime container. It owns registered scenes, tracks the active scene, dispatches input, and advances simulation through tick(deltaSeconds).
Scene is an isolated 2D space. It owns entities, scene systems, camera state, lifecycle callbacks, update callbacks, and input handlers.
Entity represents an object in a scene. Entities can render, receive input, and participate in update logic. They are intentionally lightweight so you can model game objects, particles, UI world objects, or simulation bodies.
SceneSystem is the recommended extension point for domain logic. A physics step, gravity simulation, enemy AI, procedural spawning, or animation controller can all be implemented as systems.
Renderer2D exposes primitives such as circles, lines, rectangles, polygons, textures, and materials. Rendering is camera-aware when hosted through EngineCanvas.
Camera2D controls world position, zoom, and zoom limits. Pointer input is mapped from screen-space to world-space through the active scene camera.
AssetRegistry stores platform-neutral asset IDs for textures and shaders. Assets can be backed by paths, bitmaps, shader text, or external shader DSL entries.
EngineInputEvent normalizes pointer, drag, zoom, scroll, and keyboard events across supported platforms:
PointerDown, PointerMove, PointerUp, Tap
DragStart, Drag, DragEnd
PinchZoom, Scroll
KeyDown, KeyUp
Input handlers receive coordinates already mapped into the engine model where applicable.
Add the physics module:
dependencies {
implementation("io.github.maximdvinov:enginePhysics:0.1.0-alpha.1")
}Then attach physics as scene-level logic:
Scene("level", setCurrent = true) {
System(id = "physics") { frame ->
// Step your physics world with frame.deltaSeconds.
}
Entity("ball") {
Transform(position = Offset(100f, 100f))
Render { entity ->
circle(
center = entity.position,
radius = 16f,
color = Color(0xFF67D391)
)
}
}
}The repository includes several sample applications:
samples/planetSample (:samples:planetSample:*) - multiplatform planet scene sample.samples/flappySample (:samples:flappySample:*) - Compose game sample.samples/planetMergeSample (:samples:planetMergeSample:*) - multiplatform merge-style game sample.:docsSite and :docsShowcase - documentation site and focused browser showcase examples.Useful commands:
./gradlew :samples:planetSample:desktop:run
./gradlew :engine:jvmTest :enginePhysics:jvmTest
./gradlew publishAllPublicationsToLocalBuildRepositoryLibrary modules are configured for:
build/local-maven.See docs/RELEASING.md for the release checklist, required secrets, and publishing commands.
Kanvas is available under the MIT License.