Getting started
You can write an app and see it without a phone. This takes a few minutes on a Linux computer.
Install on a phone from here with one command. zrn install installs for the user on the computer it runs on. Every build made on a laptop so far is x86_64 (or any for Python). See Status and gaps.
What you need
- Python 3.11 or newer, a C compiler,
make,pkg-config. - Development files for Wayland, Mesa (EGL, GLES2, GBM), FreeType, HarfBuzz, fontconfig, libpng and xkbcommon.
libzrnui/Makefileasks pkg-config forwayland-client wayland-egl egl glesv2 freetype2 harfbuzz fontconfig libpng xkbcommon gbm. - A C++ compiler for C++ apps,
cargofor Rust apps. - For
zrn runwith no Wayland session:kwin_waylandorweston. Onlykwin_waylandhas been tried for this site.
Install
git clone https://github.com/Zerone-Laboratories/zrn-sdk
cd zrn-sdk
export PATH="$PWD/tools:$PATH" # zrn and zrn-exec
zrn doctor # what is missing, and the command that fixes it
A healthy zrn doctor, from the machine that built this site:
ok python 3.14
ok cc /usr/sbin/cc
ok pkg-config /usr/sbin/pkg-config
ok make /usr/sbin/make
ok c++ /usr/sbin/c++
ok cargo /usr/sbin/cargo
ok libraries all present
ok libzrnui .../libzrnui/build
ok fonts .../libzrnui/fonts
ok crypto python-cryptography
opt dev key ~/.config/zrn/keys/dev.key
fix: zrn keygen (zrn pack also makes one)
ok headless compositor kwin_wayland
ok offscreen GPU /dev/dri/renderD*
zrn build builds libzrnui from the tree the first time if it is not installed. To install it for the whole system: make -C libzrnui && sudo make -C libzrnui install PREFIX=/usr.
Your first app
zrn new "Notes" --lang c --id io.github.you.notes # or cpp, python, rust
cd notes
zrn build
zrn render out.png # no phone, no display
It writes zrnapp.toml, icon.svg, main.c, AGENTS.md and .gitignore. A cut-down version of what each template holds:
#include <zrnui.h>
static zui_node *status;
static void tapped(zui_node *button, void *data) {
zui_set_text(status, "Tapped");
zui_toast(zui_node_app(button), "Hello from Notes");
}
int main(void) {
zui_app *app = zui_app_new("io.github.you.notes", "Notes");
zui_node *page = zui_page(app, "Notes");
zui_node *card = zui_section(page, "Status");
status = zui_heading(card, "Not tapped yet");
zui_button(page, "Tap me", tapped, NULL);
return zui_run(app, page);
}
#include <zrnui.hpp>
int main() {
zrn::ui::App app("io.github.you.notes", "Notes");
auto page = app.page("Notes");
auto status = page.section("Status").heading("Not tapped yet");
page.button("Tap me", [&]() {
status.set_text("Tapped");
app.toast("Hello from Notes");
});
return app.run(page);
}
fn main() {
let app = zrnui::App::new("io.github.you.notes", "Notes");
let page = app.page("Notes");
let status = page.section("Status").heading("Not tapped yet");
let app_ref = &app;
page.button("Tap me").on_click(move || {
status.set_text("Tapped");
app_ref.toast("Hello from Notes");
});
std::process::exit(app.run(page));
}
import zrnui
app = zrnui.App("io.github.you.notes", "Notes")
page = app.page("Notes")
status = page.section("Status").heading("Not tapped yet")
def tapped():
status.set_text("Tapped")
app.toast("Hello from Notes")
page.button("Tap me", tapped)
app.run(page)
Look at it
zrn render out.png # first page, 360x740 points at scale 2
zrn render --page 1 out.png # by creation order
zrn render --page "Wi-Fi" --scroll 300 out.png
zrn render --style dotmatrix out.png
zrn render --scale 4 out.png # the S9's 1440x2960
zrn run # a phone-sized window
zrn run --headless --script "wait 500; tap 180 170; wait 600; snap after.png; quit"
The style flag is only for looking. An app cannot pick its style: the system does. zrn render draws through the same GLES2 path as a running app, using Mesa's surfaceless EGL. --page counts pages in the order your code creates them, which is not the order they open.
Next
UI elements shows what you can put on a page. Package and publish shows how it reaches people.
Checked against zrn-sdk commit ea16771. Pictures are real renders of code that compiled. Source and proof.