Guides
The Kotlin port
The same reciprocal field, native on Android. It is a port of the JS engine, not a reinterpretation: the package layout, the API surface, and the physics mirror the npm packages — and the Swift port — one-to-one. The two planes are held to the same shared cross-plane conformance golden (JS / Swift / Kotlin). Where they diverge, it is a bug or a documented platform difference.
Install
Nothing is on Maven Central yet — consume it from source. The modules live in the repo's
android/
directory; the recommended door is :fundamental-compose.
// settings.gradle.kts — depend on the modules from source.
// Preview: nothing is published to Maven Central yet, so include the repo's
// android/ build as a composite (a maven coordinate door comes later).
includeBuild("../fundamental-engine/android") {
dependencySubstitution {
substitute(module("com.fundamental:fundamental-compose"))
.using(project(":fundamental-compose"))
}
}
// build.gradle.kts (your app module)
dependencies {
implementation("com.fundamental:fundamental-compose")
}
To see it run before wiring it into your own app, build the bundled desktop Field Lab:
./gradlew :lab:run from
android/ — every engine pillar as a live, interactive scene.
Modules — one-to-one with npm
Four libraries, each the mirror of an npm package (and of a Swift target).
fundamental-core imports zero platform code, exactly as
@fundamental-engine/core imports zero DOM.
| Gradle module | npm equivalent | What it is |
|---|---|---|
:fundamental-core | @fundamental-engine/core | The pure physics — particles, bodies, the 36-force catalog, the integrator, formations, reactions. Zero platform imports. |
:fundamental-platform | @fundamental-engine/dom | The six-phase frame scheduler (discover → read → compute → state → write → render) and the six registries. |
:fundamental-android | @fundamental-engine/vanilla | The imperative host — FieldFieldView, a plain Android View / Canvas with no Compose dependency. |
:fundamental-compose | @fundamental-engine/react | The declarative adapter — FieldView, Modifier.fieldBody(), the FieldHandle exposed to descendants. |
Compose — FieldView + Modifier.fieldBody()
The declarative door. FieldView mounts the field surface; any composable inside
becomes a body with the Modifier.fieldBody(...) modifier — the Kotlin analogue of the
data-body attribute (and of SwiftUI .fieldBody()). It accepts the same
token vocabulary and tuning as the web.
import com.fundamental.compose.FieldView
import com.fundamental.compose.fieldBody
@Composable
fun ContentScreen() {
Box {
// the field surface — accepts the same tuning as the web / Swift hosts
FieldView(accent = Color(0xFF4DA3FF), renderMode = RenderMode.DOTS)
// any composable becomes a body — the Kotlin analogue of [data-body]
Text(
"pull me",
modifier = Modifier.fieldBody(tokens = listOf("attract"), strength = 1.2f, range = 150f),
)
}
} View / Canvas — the imperative host
For non-Compose apps, FieldFieldView is a plain Android View that runs the
field on a Canvas and drives its own frame loop — the analogue of
new FieldField() / mountField() from
the vanilla JS API.
import com.fundamental.android.FieldFieldView
// imperative host — a plain Android View / Canvas, no Compose required.
val field = FieldFieldView(context)
parent.addView(field) // mounts and starts the frame loop
field.addBody(BodySpec(tokens = listOf("attract"), strength = 1.2f, range = 150f))
field.burst(tap.x, tap.y) // a CGPoint analogue from a MotionEvent
// the loop stops automatically on detach from the window Parity
The Kotlin port ships 44/44 FieldHandle methods — full parity with the JS engine
as of #823.
The core force set, the scheduler and registries, formations, the FieldHandle public
API (relationship edges included), and all six advanced methods (addAgent,
sample, on, readParticleIds,
readParticleChannels, registerOverlay) are implemented and pass the
cross-plane conformance gate.
The full method-by-method breakdown is on the parity matrix page.
Some methods behave differently by platform design: Compose and the View/Canvas host own the draw
loop, so setRender and setOverlay are read by the host rather than
driving a native canvas directly. scrollV is fed by the platform via
feedScrollV(). These platform differences are documented in the parity matrix notes.
The deeper architectural story — why the same engine can run on the web, in WebGL, headless, and natively on Apple platforms and Android behind one host seam — is the same one the Swift port tells. The seam itself is the platform layer; the runtime handle is the FieldHandle.