Quick Start¶
Add Halogen to your Compose Multiplatform project and generate your first LLM-powered theme.
1. Add Dependencies¶
// build.gradle.kts
dependencies {
implementation("me.mmckenna.halogen:halogen-core:0.1.0")
implementation("me.mmckenna.halogen:halogen-compose:0.1.0")
implementation("me.mmckenna.halogen:halogen-engine:0.1.0")
implementation("me.mmckenna.halogen:halogen-provider-nano:0.1.0")
// Optional: persistent cache that survives process death
implementation("me.mmckenna.halogen:halogen-cache-room:0.1.0")
}
// build.gradle.kts (commonMain)
commonMain.dependencies {
implementation("me.mmckenna.halogen:halogen-core:0.1.0")
implementation("me.mmckenna.halogen:halogen-compose:0.1.0")
implementation("me.mmckenna.halogen:halogen-engine:0.1.0")
// Optional: persistent cache that survives process death (Android, iOS, JVM — not wasmJs)
implementation("me.mmckenna.halogen:halogen-cache-room:0.1.0")
}
2. Initialize Halogen¶
Persistent caching across app restarts
By default, HalogenCache.memory() provides an in-memory LRU cache that is lost on process death. To persist themes across app restarts, add the optional halogen-cache-room dependency and use HalogenRoomCache:
// Android only: call once in Application.onCreate()
HalogenRoomCache.initialize(applicationContext)
val halogen = Halogen.Builder()
.provider(GeminiNanoProvider())
.cache(HalogenRoomCache.create()) // persistent cache
.build()
Room cache is available on Android, iOS, and JVM (not wasmJs). On non-Android platforms, no initialize() call is needed.
3. Wrap Your UI¶
Wrap your content with HalogenTheme, passing the active spec from the engine:
HalogenTheme wraps MaterialTheme - all standard M3 accessors (MaterialTheme.colorScheme, .typography, .shapes) work as expected. When a theme is generated, the entire tree recomposes with the new values.
Animated Transitions¶
Theme changes animate smoothly by default - all 49 M3 color roles and shape corner radii transition with a 400ms tween. No extra code needed.
To customize the animation:
// Custom spring animation
HalogenTheme(
spec = spec,
colorAnimationSpec = spring(stiffness = Spring.StiffnessLow),
shapeAnimationSpec = spring(stiffness = Spring.StiffnessMedium),
) {
MyApp()
}
// Disable animation (instant snap)
HalogenTheme(
spec = spec,
colorAnimationSpec = snap(),
shapeAnimationSpec = snap(),
) {
MyApp()
}
Typography (font family, weight, letter spacing) snaps instantly to avoid text reflow during transitions.
4. Generate a Theme¶
Trigger theme generation:
// Generate and apply a theme
val result = halogen.resolve(
key = "coffee",
hint = "warm coffee shop, cozy browns, soft shapes"
)
when (result) {
is HalogenResult.Success -> { /* Generated by LLM, cached, applied */ }
is HalogenResult.Cached -> { /* Loaded from cache, no LLM call */ }
is HalogenResult.FromServer -> { /* Fetched from server, cached */ }
is HalogenResult.ParseError -> { /* JSON parse failed, default theme active */ }
is HalogenResult.Unavailable -> { /* No provider available */ }
is HalogenResult.QuotaExceeded -> { /* Rate limited, try later */ }
}
The key is a cache key - any string you choose. The hint is the natural language prompt sent to the LLM. If the key already exists in cache, the hint is ignored and the cached theme is returned instantly.
5. Platform Notes¶
Gemini Nano is Android-only
halogen-provider-nano depends on ML Kit and only works on Android. Supported devices include Pixel 9+ and Samsung Galaxy S24+. On unsupported devices, availability() returns UNAVAILABLE.
Use a cloud provider for cross-platform
For iOS, Desktop, and Web targets, implement HalogenLlmProvider with a cloud LLM (OpenAI, Claude, Ollama, etc.). See the Provider Guide for examples.
One LLM call = light + dark
A single theme generation produces both light and dark color schemes. The system dark mode toggle switches between them instantly - no second LLM call, no cache miss.