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.
FieldHandle members any plane declares, against JS's
52 — generated from the port source, not typed here. The big
catch-up landed in #823. See
Implementations for the full surface-by-surface status,
and Parity below for platform-specific notes.
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 37-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 59 callable
FieldHandle members against the JS engine's 52 — the widest of the
three, because it adds the host-feed methods a Compose/View host needs (attach,
tick, feedScrollV, feedReducedMotion, pause,
resume) on top of the shared surface. It is still missing the focus/attention
substrate (focus / focusState), a batched follow-up. 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.