100% AI-generated library — humans just held the keyboard

The AI-native Compose layout for token-maxxers.

Describe the layout you want in plain English. Laygent writes the algorithm at runtime via an LLM, validates it against your prompt, and caches the result so your frames stay fast.

Jetpack Compose Compose Multiplatform AI-native
Example.kt
val config = LaygentConfig.defaultOpenAi(openAiApiKey = myOpenAiKey)

@Composable
fun Example() {
  val state = rememberLaygentState(config) {
    "Arrange items in a 3-column grid with 8 dp of spacing between them."
  }

  Laygent(state) {
    repeat(12) {
      Box(Modifier.size(50.dp).background(Color.Blue))
    }
  }
}

Why Laygent

A drop-in Layout for Compose that you program with prose.

Prompt → layout

Skip the math. Describe alignment, spacing, wrapping, conditional behavior — Laygent generates the measure/place algorithm to match.

Self-validating

An evaluator model checks the produced layout against your prompt and feeds errors back to the codegen model until it’s right.

Cached & fast

The validated JS algorithm is reused on every frame and survives configuration changes via rememberSaveable.

Multiplatform-ready

Built on Compose’s Layout + intrinsic measurement APIs. Works wherever Compose Desktop / Multiplatform runs.

Tag-aware

Attach prose-friendly labels via Modifier.laygentTag() and reference them in your prompt to drive per-item rules.

Bring your own model

Powered by Koog. Mix providers and models per stage — fast for codegen, strong for evaluation.

How it works

Laygent slots into Compose’s layout pipeline. The model writes JavaScript that drives the same measure/place primitives you already know.

You describe

Pass a prompt to rememberLaygentState. Read any state you like inside the prompt lambda — changes trigger a regen, not a recomposition.

The model generates

The codegen LLM emits a doLayout() JavaScript function with access to a Compose-shaped layout API: measurables, constraints, intrinsics, measure, place, setSize.

The runtime validates

QuickJS executes the code under real constraints. Errors round-trip back to the model. An evaluator LLM then judges whether the output matches your prompt.

Compose runs it

The validated algorithm becomes the MeasurePolicy for a normal Compose Layout. Subsequent frames just execute it — no LLM round-trip.

Quickstart

Add the dependency, hand Laygent an OpenAI key (or any Koog-supported model), and use it like any other Compose layout. Laygent is published to Maven Central.

build.gradle.kts
repositories {
  mavenCentral()
}

dependencies {
  implementation("com.zachklipp:laygent:0.1.0")
  implementation(compose.desktop.currentOs)
}
YourScreen.kt
val config = LaygentConfig.defaultOpenAi(openAiApiKey = BuildConfig.OPENAI_API_KEY)

@Composable
fun Gallery(photos: List<Photo>) {
  val state = rememberLaygentState(config) {
    "Arrange items in a 3-column grid with 8 dp of spacing between them."
  }

  Laygent(state) {
    photos.forEach { photo ->
      AsyncImage(
        model = photo.url,
        contentDescription = null,
        modifier = Modifier
          .size(120.dp)
          .laygentTag(photo.kind), // optional: prompt-addressable
      )
    }
  }
}

Mixing providers per stage

Want a cheap model writing code and a strong model validating it? Compose two LlmConfigs.

val openAi = OpenAILLMClient(apiKey = openAiKey)

val config = LaygentConfig(
  codeGenLlm = LlmConfig(client = openAi, model = OpenAIModels.Chat.GPT5_4Mini),
  evalLlm    = LlmConfig(client = openAi, model = OpenAIModels.Chat.GPT5_4),
)

Tracking spend

Every token used during generation and evaluation is reported through onTokensUsed.

var tokensUsed by remember { mutableLongStateOf(0L) }

val state = rememberLaygentState(
  config = config,
  layoutPrompt = { prompt },
  onTokensUsed = { tokensUsed += it },
)

Public API

A small surface — most of the magic happens inside the runtime.

SymbolWhat it does
Laygent(state, modifier, content) Composable that lays out content using the algorithm currently in state. Drop-in for any other Compose layout.
rememberLaygentState { prompt } Holds the validated algorithm. State read inside the prompt lambda triggers regeneration, not recomposition. Survives recreation via rememberSaveable.
LaygentConfig Bundles the codegen and evaluation LLM configs. Use LaygentConfig.defaultOpenAi(apiKey) for sane defaults.
LlmConfig(client, model, params) Pairs a Koog LLMClient with the LLModel and LLMParams to invoke it under.
LocalLaygentConfig Composition local for providing a default LaygentConfig to a subtree.
LaygentStatus GeneratingLayout, ValidatingLayout, or Idle. Read state.status to drive a spinner / skeleton UI.
state.forceUpdateLayout(prompt) Suspending. Re-runs codegen even if the prompt hasn’t changed (e.g. for a “regenerate” button).
Modifier.laygentTag(tag) Attaches a string tag to a child. The model sees these tags and can reference them in its algorithm.

Prompts that work

Pulled from the bundled demo. Anything you can describe with concrete rules — sizes, spacing, conditions, tag-based dispatch — Laygent can usually realize.

GridLayout components in a grid with 3 columns with 10 dp of space between items.
ResponsiveLay out the items in a grid with four columns as long as there is at least 600 dp of available width. Below that, one column. 10 dp gap, items grow to fill.
CircularArrange items in a circle so they fill the available space but don’t extend past the minimum dimension. Stretch to an oval if it helps spacing.
SpiralArrange items in a golden spiral starting from the center, evenly spaced, filling the available space.
Tag-drivenTwo circles. Items with tags go on the left half; untagged items go on the right half.
Equal-width stackStack items vertically with 8 dp gaps. Every item must be the same width: the smallest width that fits every item without truncation.

Try it in your browser

An interactive playground for editing prompts, swapping models, randomizing items, and watching token spend — running right here via Compose Multiplatform on wasmJs. Paste an OpenAI API key to drive the LLM.

Laygent web demo

Honest caveats

  • The first frame of any new prompt costs at least one round-trip to your model provider. Cache hits after that.
  • Layout code is JavaScript executed in QuickJS — fast, but more overhead than native Kotlin MeasurePolicy code.
  • You are sending prompt text and tag strings to your LLM provider. Don’t paste secrets into your prompt.
  • Models occasionally need a couple of correction rounds to satisfy weird prompts. Expect more tokens for ambitious instructions.
  • This library is a 100% AI-generated proof-of-concept. Treat the API as experimental.