Modern Chromium Embedded Framework for Compose Multiplatform Desktop & Universal Java (Swing, AWT, SWT, JavaFX) Applications
Kromium is an open-source, production-grade Chromium Embedded Framework (CEF) library built with first-class multi-toolkit ergonomics for Compose Multiplatform Desktop and the entire Universal Java Desktop ecosystem (Swing, AWT, Eclipse SWT, JavaFX) across Windows, macOS, and Linux.
Backed by CEF 150 and the battle-tested JetBrains JCEF runtime, Kromium delivers on-demand bootstrapping, hardware-accelerated rendering, pure Java2D lightweight off-screen rendering (OSR), dynamic HiDPI scaling, virtual asset streaming via custom protocols (app://), asynchronous PDF export, declarative context menus, WebRTC permission interception, two-way JavaScript IPC bridges, and enterprise proxying.
Tip
Explore the Interactive Documentation Portal: Visit kromium.daviante.dev for interactive guides, API search, and live demos. Full technical documentation is also maintained in the docs/ directory.
- π Universal Java Desktop Ecosystem: Native declarative
@Composable KromiumViewfor Compose Desktop, alongside first-class support for Java Swing, Standard AWT, Eclipse SWT, and JavaFX with zero Kotlin runtime dependencies required for JVM callers. - π¨ Pure Java2D Lightweight OSR (Zero JOGL/OpenGL): Built-in
KromiumOSRPaneldraws Chromium byte buffers directly into Java2D double-buffered images, completely resolving Swing/JavaFX airspace and Z-ordering conflicts without external OpenGL native libraries. True-color little-endian memory mapping eliminates the classic inverted red/blue "Smurf" bug with zero CPU overhead. - π±οΈ Continuous Sub-Pixel Trackpad & Wheel Scrolling: Full
getPreciseWheelRotation()sub-pixel event translation and floating-point accumulation eliminate dead-zones, lag, and sudden jumps on macOS Trackpads and Windows Precision Touchpads. - β¨οΈ Native macOS Command (β) Shortcuts in OSR: Seamless execution of Cmd+C, Cmd+V, Cmd+X, Cmd+A, Cmd+Z, redo, reload, zoom, and history navigation even without a native Cocoa window or menu bar.
- π₯οΈ Automatic HiDPI / Retina Scaling: Dynamic scale factor detection (
AffineTransform.getScaleX()) and real-time screen info synchronization guarantee razor-sharp web rendering across mixed 125%, 150%, and 200% displays on Windows, macOS, and Linux. Fully configurable via developer APIs (scaleFactor,scrollMultiplier,RenderingHints). - π¦ Zero-Bloat On-Demand Bootstrapping: Ship ultra-compact 15β30 MB desktop installers. Kromium downloads, verifies with SHA-256, extracts, and caches the native platform JCEF runtime on first launch.
- π Virtual Asset Streaming (
app://): Stream local bundled HTML, CSS, JS, and WebAssembly directly from classpath resources or local directories via custom protocols without running a local HTTP server. - π Async Vector PDF Export & Native Print Dialog: Non-blocking document printing via Kotlin Coroutines (
printToPdf) or JavaCompletableFuture(printToPdfAsync), plus native OS print dialogs (print()). - π±οΈ Declarative Context Menu DSL: Full control over right-click menus with turnkey shortcuts (
inspectElement(),copyLink(),searchWeb()) and direct lambda callbacks without JCEF integer command bookkeeping. - ποΈ WebRTC Media & Device Permissions: Granular, origin-aware permission interception for Microphone, Camera, Screen Sharing, and Desktop Audio with in-memory session caching and strict security defaults.
- β‘ Two-Way JavaScript Bridge: Coroutine and
CompletableFutureJS evaluation, DOM extraction, and secure type-safe bidirectional IPC routing using@JavascriptInterface. - π’ Enterprise Proxy & Network Management: Dynamic runtime proxy switching (PAC, WPAD, HTTPS TLS tunnels, SOCKS5 remote DNS), NTLM/Kerberos SSO, SSL error policies, and host locking.
- β Zero JVM Module Configuration: Automatic runtime module opening eliminates manual
--add-opensflags on Java 17, 21, and 23+. - π€ Enterprise Web Automation & Headless Normalization: Rich, auto-waiting DOM automation DSL (
click,fill,waitForSelector,selectOption), in-flight network synchronization (waitForNetworkIdle), and headless desktop environment normalization for reliable E2E testing and server-side data extraction without external drivers or fragile Selenium/Playwright processes.
Add Kromium to your project dependencies:
repositories {
mavenCentral()
google()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
}
dependencies {
// For Compose Multiplatform Desktop:
implementation("dev.daviante:kromium-compose:3.0.150-b11")
// Or for Pure Java / Swing / Headless JVM:
implementation("dev.daviante:kromium-core:3.0.150-b11")
}repositories {
mavenCentral()
google()
maven { url 'https://maven.pkg.jetbrains.space/public/p/compose/dev' }
}
dependencies {
implementation 'dev.daviante:kromium-compose:3.0.150-b11'
// or: implementation 'dev.daviante:kromium-core:3.0.150-b11'
}<dependency>
<groupId>dev.daviante</groupId>
<artifactId>kromium-core</artifactId>
<version>3.0.150-b11</version>
</dependency>import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import dev.daviante.kromium.compose.KromiumView
import dev.daviante.kromium.compose.rememberKromiumViewState
import dev.daviante.kromium.presentation.browser.Kromium
fun main() = application {
LaunchedEffect(Unit) {
if (!Kromium.isReady) Kromium.initialize()
}
Window(onCloseRequest = ::exitApplication, title = "Kromium Compose Browser") {
MaterialTheme {
val state = rememberKromiumViewState(initialUrl = "https://github.com/daviante/kromium")
KromiumView(state = state, modifier = Modifier.fillMaxSize())
}
}
}import dev.daviante.kromium.domain.config.KromiumConfig;
import dev.daviante.kromium.presentation.browser.Kromium;
import dev.daviante.kromium.presentation.browser.KromiumBrowser;
import dev.daviante.kromium.presentation.browser.KromiumClient;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class QuickstartJava {
public static void main(String[] args) {
// 1. Initialize engine
Kromium.initialize(KromiumConfig.builder().build());
// 2. Create client and browser
KromiumClient client = Kromium.newClient();
KromiumBrowser browser = client.createBrowser("https://github.com/daviante/kromium");
// 3. Mount UI component into standard JFrame
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Kromium Java Browser");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1280, 800);
frame.add(browser.getUiComponent());
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}Kromium is engineered to run seamlessly across every major JVM graphical toolkit. Choose the optimal rendering mode and container for your architecture:
| Framework / Toolkit | Recommended Mode | Underlying Technology | Primary Container | Sample Project |
|---|---|---|---|---|
| Compose Multiplatform | Windowed (GPU) (or OSR) | Skia Hole-Punching via SwingPanel |
@Composable KromiumView |
:kromium-sample-compose |
| Java Swing | Lightweight OSR (Default) | Pure Java2D KromiumOSRPanel |
JFrame / JPanel |
:kromium-sample-swing |
| Standard AWT | Heavyweight Windowed | Native OS Window embedding | java.awt.Frame / Panel |
:kromium-sample-awt |
| Eclipse SWT | Heavyweight Windowed | Bridged AWT Composite (SWT_AWT) |
org.eclipse.swt.widgets.Shell |
:kromium-sample-swt |
| JavaFX | Lightweight OSR | Embedded Swing wrapper (SwingNode) |
javafx.scene.Scene / SwingNode |
:kromium-sample-javafx |
Tip
For complete setup instructions and code examples for each framework, see the Universal Java Desktop Quickstart.
Serve local bundled web applications (HTML, CSS, JavaScript, React/Vue bundles, WebAssembly) securely via custom protocols without opening local HTTP ports or battling port-clash and CORS issues.
import dev.daviante.kromium.presentation.browser.Kromium
import dev.daviante.kromium.presentation.scheme.KromiumCustomScheme
import dev.daviante.kromium.presentation.scheme.KromiumSchemeHandler
// Register 'app://' before initialize:
Kromium.registerCustomScheme(
KromiumCustomScheme.create(
schemeName = "app",
domain = "myapp",
handler = KromiumSchemeHandler.fromClasspath(
resourcePathPrefix = "/web-app",
spaFallback = "index.html"
)
)
)
Kromium.initialize()
// In Compose UI:
state.loadUrl("app://myapp/index.html")import dev.daviante.kromium.presentation.browser.Kromium;
import dev.daviante.kromium.presentation.scheme.KromiumCustomScheme;
import dev.daviante.kromium.presentation.scheme.KromiumSchemeHandler;
// Register 'app://' protocol backed by classpath bundle:
Kromium.registerCustomScheme(
KromiumCustomScheme.builder("app")
.domain("myapp")
.handler(KromiumSchemeHandler.fromClasspath("/web-app", "index.html"))
.build()
);
Kromium.initialize();
browser.loadUrl("app://myapp/index.html");π Deep dive: docs/guides/asset-filtering-and-security.md
Export web pages into high-resolution, vector-crisp PDF documents asynchronously with precise layout control, or trigger the operating system's native print preview dialog.
import dev.daviante.kromium.domain.model.KromiumPaperSize
import dev.daviante.kromium.domain.model.KromiumPdfMargins
import dev.daviante.kromium.domain.model.KromiumPdfSettings
import java.io.File
// Non-blocking suspending export:
val pdf: File = browser.printToPdf(
targetFile = File("exports/invoice.pdf"),
settings = KromiumPdfSettings(
paperSize = KromiumPaperSize.A4,
landscape = false,
printBackground = true,
margins = KromiumPdfMargins.fromMillimeters(10.0, 10.0, 10.0, 10.0),
displayHeaderFooter = true,
headerTemplate = "<span class=\"title\"></span>",
footerTemplate = "<span class=\"pageNumber\"></span> of <span class=\"totalPages\"></span>"
)
)
// Or open the native OS print dialog:
state.print() // in Compose
// or: browser.print()import dev.daviante.kromium.domain.model.KromiumPaperSize;
import dev.daviante.kromium.domain.model.KromiumPdfMargins;
import dev.daviante.kromium.domain.model.KromiumPdfSettings;
import java.io.File;
KromiumPdfSettings settings = KromiumPdfSettings.builder()
.paperSize(KromiumPaperSize.Letter)
.printBackground(true)
.margins(KromiumPdfMargins.None.INSTANCE)
.build();
// Non-blocking async generation returning CompletableFuture<File>:
browser.printToPdfAsync(new File("exports/report.pdf"), settings)
.thenAccept(file -> System.out.println("Generated PDF: " + file.getAbsolutePath()))
.exceptionally(ex -> {
System.err.println("PDF generation failed: " + ex.getMessage());
return null;
});
// Interactive native print dialog:
browser.print();π Deep dive: docs/reference/browser-and-client-api.md
Customize, add, or replace right-click context menus with high-level actions, custom callbacks, and built-in shortcuts without manual integer command ID plumbing.
import dev.daviante.kromium.presentation.menu.KromiumContextMenuHandler
state.setContextMenu { ctx ->
clear() // Remove default browser items (View Source, etc.)
if (ctx.params.isLink()) {
copyLink("Copy Target Link")
separator()
}
if (ctx.params.hasSelection()) {
copy("Copy")
searchWeb() // Turnkey: "Search Google for '%s'"
separator()
}
item("Custom App Action") { context ->
println("User clicked on: ${context.params.pageUrl}")
}
subMenu("Developer") {
inspectElement() // Opens DevTools inspecting clicked coordinates
viewSource()
}
}
// Or use ready-made turnkey presets:
state.contextMenuHandler = KromiumContextMenuHandler.minimalEditing(includeInspectElement = true)
state.contextMenuHandler = KromiumContextMenuHandler.devToolsOnly()
state.contextMenuHandler = KromiumContextMenuHandler.disabled()import dev.daviante.kromium.presentation.menu.KromiumContextMenuHandler;
client.setContextMenuHandler((builder, ctx) -> {
builder.clearDefaults();
if (ctx.getParams().hasSelection()) {
builder.copy();
builder.searchWeb();
builder.addSeparator();
}
builder.inspectElement();
builder.addItem("Export Row", c -> exportSelection(c.getParams().getSelectionText()));
});
// Turnkey presets:
client.setContextMenuHandler(KromiumContextMenuHandler.minimalEditing(true));
client.setContextMenuHandler(KromiumContextMenuHandler.devToolsOnly());π Deep dive: docs/reference/handlers-and-events.md
Intercept and evaluate web permission requests (Microphone, Camera, Screen Sharing, Desktop Audio) requested by web apps (e.g. Google Meet, Zoom, WebRTC video calling). Unhandled requests default strictly to Deny.
import dev.daviante.kromium.presentation.handler.KromiumPermissionDecision
import dev.daviante.kromium.presentation.handler.KromiumPermissionHandler
import dev.daviante.kromium.presentation.handler.KromiumPermissionType
// Fine-grained selective permission filtering:
state.permissionHandler = KromiumPermissionHandler { request ->
when {
// Whitelist corporate domain for audio only
request.origin == "https://meet.corp.internal" -> {
KromiumPermissionDecision.grant(KromiumPermissionType.AUDIO_CAPTURE)
}
// Grant all permissions for trusted apps
request.origin.startsWith("app://") -> KromiumPermissionDecision.GRANT
// Securely deny all untrusted sites
else -> KromiumPermissionDecision.DENY
}
}
// Or use ready-made turnkey domain presets:
state.permissionHandler = KromiumPermissionHandler.forOrigins("meet.google.com", "zoom.us")
// Programmatically revoke remembered session permissions:
state.clearPermissionCache()import dev.daviante.kromium.presentation.handler.KromiumPermissionDecision;
import dev.daviante.kromium.presentation.handler.KromiumPermissionHandler;
import dev.daviante.kromium.presentation.handler.KromiumPermissionType;
client.setPermissionHandler(request -> {
if (request.hasAudio() && "https://meet.company.com".equals(request.getOrigin())) {
return KromiumPermissionDecision.grant(KromiumPermissionType.AUDIO_CAPTURE);
}
return KromiumPermissionDecision.DENY;
});
// Turnkey origin allowlist:
client.setPermissionHandler(KromiumPermissionHandler.forOrigins("meet.google.com", "zoom.us"));
// Revoke session cache:
client.clearPermissionCache();π Deep dive: docs/reference/handlers-and-events.md
Execute arbitrary JavaScript with coroutine and CompletableFuture return values, or bind native JVM objects to JavaScript's window object for bidirectional communication.
import dev.daviante.kromium.presentation.js.JavascriptInterface
// 1. Evaluate JavaScript asynchronously:
val headingText: String? = browser.evaluateJavascript("document.querySelector('h1').innerText")
// 2. Register native object into JavaScript window context:
class NativeBridge {
@JavascriptInterface
fun onUserAction(payload: String) {
println("Received from web page: $payload")
}
}
browser.registerJsInterface(NativeBridge(), "desktopApp")
// In web page: window.desktopApp.onUserAction("Hello from JS!")import dev.daviante.kromium.presentation.js.JavascriptInterface;
// 1. Evaluate JavaScript returning CompletableFuture<String>:
browser.evaluateJavascript("document.title")
.thenAccept(title -> System.out.println("Page title is: " + title));
// 2. Register native Java bridge object:
public class NativeBridge {
@JavascriptInterface
public void notify(String message) {
System.out.println("Message from JS: " + message);
}
}
browser.registerJsInterface(new NativeBridge(), "desktopApp");π Deep dive: docs/guides/javascript-and-dom.md
Perform server-side web automation, E2E workflow testing, structured DOM extraction, and automated screenshot capture in offscreen headless environments with full desktop environment normalization and network idle synchronization.
// 1. Initialize headless browser with desktop normalization:
val config = KromiumConfig().apply {
windowlessRendering = true
emulateDesktopEnvironment = true
}
Kromium.initialize(config)
val browser = Kromium.newClient().createBrowser("https://example.com/login")
// 2. Automate user interaction with auto-waiting DSL:
browser.waitForSelector("#username")
browser.fill("#username", "service-account")
browser.fill("#password", "SecureKey456!")
browser.click("button[type='submit']")
// 3. Synchronize with background network activity:
browser.waitForNetworkIdle(idleTimeMs = 500)
// 4. Extract data or capture raster screenshot:
val headline = browser.getTextContent(".welcome-title")
val screenshot: java.awt.image.BufferedImage? = browser.takeScreenshot()KromiumBrowser browser = client.createBrowser("https://example.com/portal");
// Chain interaction steps asynchronously:
browser.waitForSelectorAsync("#search", 5_000)
.thenCompose(ok -> browser.fillAsync("#search", "Enterprise Chromium", 5_000))
.thenCompose(ok -> browser.clickAsync("#submit", 5_000))
.thenCompose(ok -> browser.waitForNetworkIdleAsync(500, 10_000))
.thenCompose(ok -> browser.getTextContentAsync(".result-text", 5_000))
.thenAccept(result -> System.out.println("Extracted Result: " + result));π Deep dive: docs/guides/headless-and-automation.md
Optional, flag-enabled helper utilities for integrating custom tab strips with native OS titlebars and macOS traffic lights without imposing rigid defaults or altering standard window behavior.
import dev.daviante.kromium.compose.chrome.macTrafficLightsPadding
import dev.daviante.kromium.compose.chrome.MacTrafficLightsSpacer
// In your custom TabStrip / Header composable:
Row(
modifier = Modifier
.fillMaxWidth()
// Automatically applies padding on macOS only when enabled; 0.dp otherwise
.macTrafficLightsPadding(enabled = true, width = 76.dp)
) {
// Custom tab items here...
}
// Or use a platform-adaptive spacer:
Row(modifier = Modifier.fillMaxWidth()) {
MacTrafficLightsSpacer(enabled = true, width = 76.dp)
// Custom tabs...
}import dev.daviante.kromium.presentation.chrome.KromiumChromeConfig;
import dev.daviante.kromium.presentation.chrome.KromiumWindowChrome;
// 1. Configure macOS full window content (merging tab strip with titlebar)
KromiumChromeConfig config = KromiumChromeConfig.builder()
.enabled(true)
.macTrafficLightsWidth(80)
.transparentTitleBar(true)
.hideWindowTitle(true)
.build();
KromiumWindowChrome.apply(frame, config);
// 2. Enable window dragging & double-click maximize on custom tab strip
KromiumWindowChrome.installWindowDragger(tabStripPanel, frame);Comprehensive, in-depth documentation organized according to the DiΓ‘taxis framework is maintained in the docs/ directory:
| Category | Guides & References | Key Topics |
|---|---|---|
| π Getting Started | Installation Guide Compose Quickstart Swing JVM Quickstart |
Repository setup, multi-module configurations, Gradle dependencies, first browser window. |
| ποΈ Core Concepts | Architecture State & Lifecycle Security & Privacy |
Bootstrap pipeline, multi-process Chromium architecture, KromiumState flow, and telemetry suppression. |
| π Guides | Compose UI Integration Navigation & History JavaScript & DOM Network & Proxies |
Multi-tab UI, navigation controls, coroutine JS execution, IPC routers, and enterprise proxy configuration. |
| π Guides (Cont.) | Asset Filtering & Schemes Cookie Management Downloads & Dialogs Headless & Automation |
Virtual asset streaming, ad blocking, host locking, async cookie store, modal dialogs, and off-screen data extraction. |
| π API Reference | Configuration (KromiumConfig)Browser & Client API Handlers & Events Exceptions & Logging |
Complete property catalogs, method signatures, composite multiplexers, and pluggable logging. |
| π¦ Deployment | Packaging & Distribution Platform Considerations Troubleshooting & FAQ |
Desktop installers (MSI, DMG, DEB), ProGuard/R8 rules, macOS framework symlinks, and Linux dependencies. |
Visit the master sitemap at docs/README.md.
| Operating System | Architectures | Minimum Version | Prerequisites |
|---|---|---|---|
| Windows | x64 (64-bit) | Windows 10 / 11, Server 2019+ | Visual C++ 2015β2022 Redistributable |
| macOS | ARM64 (Apple Silicon) & Intel x64 | macOS 11.0 (Big Sur)+ | None (Universal binaries resolved automatically) |
| Linux | x64 & ARM64 | Ubuntu 20.04+, Debian 11+, Fedora 36+ | Standard desktop X11/GTK libraries (libnss3, libasound2, libdrm2) |
Minimum Java Runtime: Java 17 LTS (Java 21 LTS Recommended).
The repository includes ready-to-run desktop browser applications demonstrating every framework integration:
./gradlew :kromium-sample-compose:runFeatures multi-tab browsing, DevTools REPL, live canvas animation, and visible text extraction.
./gradlew :kromium-sample-swing:runFeatures lightweight pure Java2D OSR rendering, modern FlatLaf dark theme, download manager dialog, proxy switching, and native print preview.
./gradlew :kromium-sample-awt:runDemonstrates native heavyweight java.awt.Frame embedding with functional navigation toolbar and clean lifecycle disposal.
./gradlew :kromium-sample-swt:runDemonstrates embedding CEF inside an Eclipse SWT application via SWT_AWT.new_Frame with full toolbar controls and SWT event loop handling.
./gradlew :kromium-sample-javafx:runDemonstrates embedding lightweight OSR Chromium inside a JavaFX Scene via SwingNode with dynamic HiDPI scaling, scene resize synchronization, and navigation controls.
Kromium is open-source software licensed under the Apache License, Version 2.0. Commercial friendly, royalty-free, and compliant with standard patent and distribution requirements.
All community contributions are welcomed and governed by our Contributor License Agreement (CLA).