halogen
0.2.0indexedTurns natural-language prompts into full Material 3 themes at runtime — generating colors, typography, and shapes from LLM seeds, expanding via HCT, with per-key caching and presets.
Turns natural-language prompts into full Material 3 themes at runtime — generating colors, typography, and shapes from LLM seeds, expanding via HCT, with per-key caching and presets.
Runtime theme generation for Compose Multiplatform
Halogen turns natural language into complete Material 3 themes at runtime. Give it a prompt like "warm coffee shop" or "neon cyberpunk" and it generates colors, typography, and shapes, all cached so the LLM only runs once per prompt.
On Android, it can run entirely on-device with Gemini Nano. On iOS, Desktop, and Web, plug in any cloud LLM.
Halogen makes all of this possible with a single resolve() call.
// 1. Build the engine
val halogen = Halogen.Builder()
.provider(GeminiNanoProvider())
.cache(HalogenCache.memory())
.build()
// 2. Generate a theme
halogen.resolve(key = "coffee", hint = "warm coffee shop vibes")
// 3. Apply it
HalogenTheme { App() }
That's three lines to go from a text prompt to a full Material 3 theme. See the Quick Start guide for setup details.
Most apps need three modules:
implementation("me.mmckenna.halogen:halogen-core:0.2.0")
implementation("me.mmckenna.halogen:halogen-compose:0.2.0")
implementation("me.mmckenna.halogen:halogen-engine:0.2.0")
Then add a provider and optionally a persistent cache:
// Gemini Nano on-device provider (Android only, min SDK 26)
implementation("me.mmckenna.halogen:halogen-provider-nano:0.2.0")
// Room KMP persistent cache (Android, iOS, JVM - not wasmJs)
implementation("me.mmckenna.halogen:halogen-cache-room:0.2.0")
// Image-to-theme color extraction (all platforms)
implementation("me.mmckenna.halogen:halogen-image:0.2.0")
The LLM generates 6 seed colors + typography/shape hints. Halogen expands those into 49 M3 color roles, 15 text styles, and 5 shape sizes using pure-Kotlin HCT color science. One call produces both light and dark schemes - toggling dark mode is instant, no second LLM call.
resolve("coffee", "warm coffee shop")
→ cache HIT? → return instantly
→ cache MISS? → LLM generates seeds → expand to full M3 theme → cache → apply
Results are cached by key, so the LLM is never called twice for the same prompt.
Control how seed colors get expanded into palettes:
Halogen.Builder()
.provider(myProvider)
.config(HalogenConfig.Vibrant)
.build()
All presets are in HalogenConfig.presets if you want to build a UI picker.
All platforms get in-memory LRU caching and full Compose Material 3 support.
Halogen wraps MaterialTheme by default, but the core types are just numbers: ARGB ints, font weights, dp values. Map them to anything:
HalogenTheme(
spec = currentSpec,
themeWrapper = { expanded, isDark, content ->
CompositionLocalProvider(LocalMyTheme provides expanded.toMyTheme(isDark)) {
content()
}
},
) { App() }
| Sample | Platform | Description |
|---|---|---|
sample/ |
Full documentation at halogen.mmckenna.me:
Quick Start · Architecture · Provider Guide · Custom Extensions · Custom Theme Systems · API Reference ·
| Dependency | Version |
|---|---|
| Kotlin |
See CONTRIBUTING.md for development setup, code style, and PR guidelines.
Copyright 2025-2026 Matt McKenna
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this except compliance the License.
You may obtain a copy of the License at
http:
Unless applicable law agreed to writing, software
distributed under the License distributed an BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied.
See the License the specific language governing permissions
limitations under the License.
resolveImage() call extracts colors and generates a matching theme| Preset | Style |
|---|
Default | Balanced M3 tonal spot - good for most apps |
Vibrant | Bolder, more saturated |
Muted | Desaturated, calm |
Monochrome | Single-hue variations |
Punchy | High-energy, high-contrast |
Pastel | Soft and airy |
Editorial | Strong primary, neutral everything else |
Expressive | Colorful - even the neutrals are tinted |
| Platform | LLM Provider | Persistent Cache |
|---|
| Android | Gemini Nano (on-device) or cloud | Room |
| iOS | Cloud providers | Room |
| Desktop (JVM) | Cloud providers | Room |
| Web (WasmJs) | Cloud providers | - |
| Android |
| Playground, weather themes, test harness, settings |
sample-shared/ | Android, iOS, Desktop, Web | KMP sample with cloud LLM providers |
| 2.2.20 |
| Compose Multiplatform | 1.10.1 |
| Android Gradle Plugin | 8.13.2 |
| Min Android SDK | 24 (Nano: 26) |
| JVM Target | 17 |
Surfaced from shared tags and platforms — no rankings paid for.