English
GitHub
Theme

Desktop UI for Java

Desktop apps in Java, drawn from scratch.

Limn draws its own pixels. Widgets, layout, text, charts, media and a 3D viewport, in one dependency, with no Swing, no JavaFX and no native toolkit underneath.

JDK 17 · Windows, macOS, Linux · Apache-2.0

The demo application, rendered by Limn during this build.
The demo application, rendered by Limn during this build.
The demo application, rendered by Limn during this build.

Try it now

The kitchen sink and the theme editor, straight from Maven Central: nothing to clone, and nothing to install but jbang, which fetches a JDK if you have none. What downloads is the toolkit, the fonts and the native libraries of this machine, not the other five platforms'.

-XstartOnFirstThread is macOS-only, and a JVM given it anywhere else refuses to start. The aliases run the newest release; to pin one, name the coordinate: jbang io.github.limn-toolkit:limn-demo:0.7.0. Offline, the demo is also one file attached to every release.

jbang --java-options=-XstartOnFirstThread demo@limn-toolkit/limn-toolkit  # the kitchen sink
jbang --java-options=-XstartOnFirstThread theme-editor@limn-toolkit/limn-toolkit  # the theme editor
jbang demo@limn-toolkit/limn-toolkit  # the kitchen sink
jbang theme-editor@limn-toolkit/limn-toolkit  # the theme editor
jbang demo@limn-toolkit/limn-toolkit  # the kitchen sink
jbang theme-editor@limn-toolkit/limn-toolkit  # the theme editor

Five minutes

One dependency and a main method

No markup language, no annotation processor, no build plugin. Add the backend, which brings the toolkit with it, write plain Java, and you have a window.

Read the install guide
build.gradle.kts
dependencies {
    implementation("io.github.limn-toolkit:limn-backend-lwjgl:0.7.0")
}
Main.java
public static void main(String[] args) {
    // macOS needs -XstartOnFirstThread on the JVM command line; it is macOS-only and a
    // JVM elsewhere given it will not start.
    try (Backend backend = new LwjglBackend()) {
        NativeWindow window = backend.createWindow(
                new WindowConfig("Hello, Limn", 480, 320, true, true));

        Column column = new Column();
        column.gap(12);
        column.add(new Label("A window, drawn by Limn."));
        column.add(new Button("Close").onAction(window::requestClose));

        Scene scene = new Scene(new Padding(Insets.all(24), column));
        scene.bind(window);

        backend.runEventLoop();
    }
}

What you get

A component set you do not have to build

Buttons, fields, lists, tabs, menus, dialogs, split panes, a colour picker, bar, line and donut charts, and a virtualized list where a million rows cost what twenty do. Every one of them reads its colour, its shape and its density from the theme, so nothing is hardcoded and a compact mode is one line.

See all of them
Button
Button
ComboBox
ComboBox
Slider
Slider
SegmentedControl
SegmentedControl
Checkbox and switch
Checkbox and switch
Spinner
Spinner

Layout that fits in your head

Four widgets and one marker: a column stacks, a row spreads, a stack overlays, padding insets, and Expanded says who takes the space that is left. That is the whole vocabulary; there is no constraint solver to configure and no layout manager to install.

Read the layout guide
A window laid out with Limn
A window laid out with Limn
A window built from a column, a row, a split and one Expanded.

Forms without a framework

A field is a widget, a validation rule is a listener, and submitting is a method call. Nothing to bind, nothing to register, and validation states that recolour a field the moment the user fixes it.

Read the forms guide
A form built with Limn
A form built with Limn
Captions, validation, a choice and the actions row.

Video and 3D are widgets too

A physically-based 3D viewport and a video player, composited as ordinary widgets: a scroll view clips them, a stack draws over them, and they take part in layout like a label does.

Read the media guide
The Limn 3D viewport
The Limn 3D viewport
The 3D viewport, composited into an ordinary window.

In your users' languages

Text is measured with the same advances it is drawn with, and the font fallback runs per character, so Latin, Greek, Cyrillic and CJK mix in one string without you choosing a face. Input methods compose inside the field, and editing moves by grapheme cluster, so combining marks and multi-part emoji are never split. Right to left is not just shaping: direction is an axis a subtree inherits, so a Hebrew window mirrors — and can still hold a left-to-right code pane.

Read the text guide
The same screen captured in Japanese, Simplified Chinese, Korean and Russian, quilted into one window.
The same screen captured in Japanese, Simplified Chinese, Korean and Russian, quilted into one window.
The same screen, captured in four languages during this build.

Your identity, not the toolkit's

Your application should look like your product, not like the library it was built with. A theme is plain data (every colour, the corner radius, the size step every control inherits), and one call swaps it at runtime. Load the typeface your brand ships from a file you control, and none of the toolkit's own look survives.

How theming works
The same dense screen of controls rendered seven times side by side, each strip in a different palette, size step and typeface.
One screen, seven themes. The code behind every strip is identical.

Before you commit

What Limn does not do

Every toolkit trades something. These are the trades, stated up front, because finding them out in week three is worse than reading them now.

  • Horizontal text only

    Arabic, Hebrew, Devanagari and Thai shape wherever text is drawn, layout mirrors under an inherited right-to-left axis, and digits, ordering and case follow the language — the ar and he bundles ship, with a Bold for every script. What is not here is vertical writing: no top-to-bottom columns, no second axis through layout. Lines break; they are not justified.

  • No screen-reader bridge

    Keyboard navigation and focus rings are complete, but nothing is exposed to the platform's accessibility APIs. If a screen reader has to work, this is not the toolkit for that application yet.

  • Pre-1.0

    The API still moves between releases, and OpenGL is the only rendering path. Pin your version and read the release notes.

A window on screen in five minutes

The install guide ends with a running program. Everything after that is the guide, the component gallery and the API reference.