Equo Chromium: Embedded Browser as a JavaFX Node

Axel Orsingher on september 25th 2026

JavaFX gives you a clean scene graph, hardware-accelerated rendering, and a coherent component model — but until now, embedding a real browser inside it required awkward workarounds: spawning a separate process, marshaling frames over sockets, or dropping down to OS-level APIs to paste a native window into the JavaFX hierarchy. None of those approaches actually integrate with the scene graph. They don't obey layout, they don't clip, and they certainly don't handle HiDPI correctly.

Equo Chromium now ships a JavaFX mode that changes that. The browser runs offscreen (OSR) inside CEF, delivers BGRA pixel frames to a WritableImage, and shows up in your scene graph as a plain javafx.scene.Node. Layout, clipping, and HiDPI all work because the browser really is a node — not a foreign window pasted on top.




One Call to Embed Chromium

The public API is a single static factory on ChromiumBrowser:


StackPane browserHost = new StackPane();

ChromiumBrowser browser = ChromiumBrowser.javafx(browserHost, "https://equo.dev");

Pass any javafx.scene.layout.Pane and a URL. That's it. The node is added to the pane immediately and starts rendering as soon as CEF delivers the first frame. From that point forward, the browser is just another child node — you can put it in a BorderPane, stack things on top of it, animate it, or bind its size to anything in your scene.

The same one-liner works in Kotlin:


val browserHost = StackPane()

val browser = ChromiumBrowser.javafx(browserHost, "https://equo.dev")



The Scene Graph Stays in Charge

Because the browser is a real node, layout works exactly as you'd expect. The sample app puts the browser in the center of a BorderPane, under a toolbar made of plain JavaFX controls:


BorderPane root = new BorderPane();

root.setTop(toolbar);         // a real JavaFX HBox

root.setCenter(browserHost);  // the browser node

The browser listens to its own layout bounds. When the pane resizes, it reports the new logical size back to CEF, which re-renders at that exact size. Device scale factor is read from Window.getRenderScaleX(), so HiDPI screens get native-resolution frames automatically; no manual scaling, no blurry rendering on 4K displays.

Screen position is tracked the same way: localToScreen(0, 0) keeps CEF informed, which matters for OSR popups (like <select> dropdowns) that need absolute coordinates to appear in the right place.




Input Feels Native

Mouse clicks, drags, scrolls, and keystrokes are translated from JavaFX events and forwarded directly to the offscreen CEF browser. All the modifiers you'd expect — Shift, Ctrl, Alt, Meta — come through correctly. Scroll follows the JavaFX delta directly rather than forcing a fixed notch-per-event model.

Focus is wired to JavaFX focus: when the node gains focus, CEF activates; when it loses focus, CEF deactivates. Right-click opens a proper JavaFX ContextMenu (since OSR can't draw CEF's native popup menu).




Popups Open as JavaFX Stages

window.open() and target=_blank links work out of the box. CEF's native popup mechanism can't create a visible window in OSR mode, so Equo Chromium intercepts the before-popup event and opens a new javafx.stage.Stage with its own browser inside:


subscribe().onBeforePopup(event -> {
    event.prevent();
    Platform.runLater(() -> openPopupWindow(event.getUrl()));
});

The popup window tracks the page title via onTitleChanged, so it behaves the way users expect, just like Chrome opening a new tab in a new window.




The Full Equo Chromium API, Unchanged

JavaFX mode supports the same ChromiumBrowser API you'd use in SWT, Swing, or Standalone:


browser.setUrl("https://example.com");
browser.executeJavaScript("document.title = 'hello'");
browser.showDevTools();
browser.subscribe().onTitleChanged(e -> stage.setTitle(e.getTitle()));
browser.printToPdf("/tmp/page.pdf", settings -> {});

Every event subscription, every navigation method, DevTools, PDF printing, all available without modification. The toolkit abstraction stays intact; only the rendering surface changes.




Get Started

Maven (Java):

<dependency>
    <groupId>com.equo</groupId>
    <artifactId>chromium</artifactId>
    <version><!-- latest --></version>
</dependency>

//mvn verify          # runs the sample; override with -Durl=https://...

Gradle (Kotlin):

./gradlew :app:run  # override with -Durl=https://...

Both sample projects are in the chromium.samples repository under maven-samples/javafx and gradle-samples/javafx.

Why This Matters

Embedding a real browser into a native UI toolkit has always meant fighting the windowing system. SWT's Browser widget hosts a native CEF window as a child of the SWT composite. Swing's mode wraps the CEF component in an AWT hierarchy. Both approaches depend on the OS window manager to handle painting, sizing, and Z-order.

JavaFX mode flips that: CEF never creates a window. Instead, it renders into a pixel buffer, and the JavaFX scene graph decides what to do with those pixels. The result is a browser that behaves like a node, because it is one.

If you're building a JavaFX application that needs to display web content, instrument it with Java logic, or expose a web-based UI alongside native controls, Equo Chromium's JavaFX mode is the missing piece.




To learn more about how Equo Chromium can provide modern web integrations for your application, visit our GitHub repository or contact us to discuss a modernization prototype.

Equo

© 2026 Equo Tech, Inc.