- Zig 94.2%
- JavaScript 4.1%
- WGSL 1.7%
| examples | Add worker backed WASM dispatch runtime | |
| lib/js-bridge | Add worker backed WASM dispatch runtime | |
| src | Bump wgpu bindings dep | |
| tests/snapshots | Support wasmxx-freestanding as a compilation target ( #15 ) | |
| .gitignore | init | |
| .oxfmtrc.json | Support wasmxx-freestanding as a compilation target ( #15 ) | |
| .oxlintrc.json | Support wasmxx-freestanding as a compilation target ( #15 ) | |
| build.zig | Add worker backed WASM dispatch runtime | |
| build.zig.zon | Bump wgpu bindings dep | |
| LICENSE.md | init | |
| README.md | Add worker backed WASM dispatch runtime | |
Knots
Knots is a high performance cross-platform immediate-mode GUI library written in Zig.
Goals
Below goals are listed in order of importance.
- Provide a way to build highly performant cross-platform desktop applications.
- UI code should get out of the way, letting the developer spend more time on actual problems.
- Minimal lean builds, fast compile times.
- Highly configurable, with sane defaults.
Known limitations
- Linux windowing is Wayland-only.
- Text rendering is UTF-8/codepoint based. HarfBuzz shaping, bidi layout, ligatures, font fallback, and IME composition are not implemented yet.
Requirements
- Zig compiler, minimum version can be found in build.zig.zon. I try to keep up with the master branch.
- On Linux, Wayland development packages are required: wayland-client, wayland-cursor, wayland-protocols, wayland-scanner, pkg-config, and xkbcommon.
Supported platforms
| Platform | Supported GPU APIs |
|---|---|
| macOS | WebGPU and Vulkan (MoltenVK) |
| Linux | WebGPU and Vulkan |
| Windows | WebGPU and Vulkan |
| WASM (freestanding) | WebGPU |
Install
zig fetch --save git+https://codeberg.org/shahwali/knots.git
Minimal app
// build.zigconstknots=b.dependency("knots",.{.target=target,.optimize=optimize});exe.root_module.addImport(knots.module("knots"));// ormod.addImport(knots.module("knots"));conststd=@import("std");constknots=@import("knots");pubfnmain(init:std.process.Init)!void{varapp=tryknots.App.init(init.io,init.gpa,.{.window=.{.width=1280,.height=720,.title="Playground",},});deferapp.deinit();tryapp.start(frameCb);}fnframeCb(app:*knots.App)!void{constsize=app.viewport.window.getSize();returnapp.e(.{knots.component.Rect{.width=.fixed(@floatFromInt(size.width)),.height=.fixed(@floatFromInt(size.height)),.padding=.init(16,16,16,16),.dir=.column,.key=.src(@src()),},});}Browser WASM
The following is a complete web-only application.
// build.zigconststd=@import("std");constKnots=@import("knots");pubfnbuild(b:*std.Build)void{consttarget=b.standardTargetOptions(.{});constoptimize=b.standardOptimizeOption(.{});constknots=b.dependency("knots",.{.target=target,.optimize=optimize});constexe_mod=b.createModule(.{.root_source_file=b.path("src/main.zig"),.target=target,.optimize=optimize,.imports=&.{.{.name="knots",.module=knots.module("knots")}},});constexe=b.addExecutable(.{.name="app",.root_module=exe_mod});exe.entry=.disabled;Knots.installWeb(b,knots,exe_mod,exe,.{.index_html=b.path("src/index.html"),});}// src/main.zigconststd=@import("std");constknots=@import("knots");pubconststd_options:std.Options=.{.logFn=knots.web.logFn};fnframe(app:*knots.App)!void{constsize=app.viewport.window.getSize();tryapp.e(.{knots.component.Rect{.width=.fixed(@floatFromInt(size.width)),.height=.fixed(@floatFromInt(size.height)),.padding=.init(16,16,16,16),.key=.src(@src()),},.{knots.component.Text{.content="Hello from Knots",.key=.src(@src()),},},});}exportfnmain()callconv(.{.wasm_mvp=.{}})i32{constallocator=knots.web.allocator;// synchronizes Zig's single-threaded WASM allocator across workers.constio=knots.web.io;// provides worker dispatch, atomic waits, cancellation, clocks, randomness, and non-UI-blocking sleep.constapp=allocator.create(knots.App)catch|err|returnknots.web.fail(err);app.*=knots.App.init(io,allocator,.{.window=.{.width=1280,.height=720,.title="Knots Web App",.canvas_selector="#canvas",},})catch|err|{allocator.destroy(app);returnknots.web.fail(err);};app.start(frame)catch|err|{app.deinit();allocator.destroy(app);returnknots.web.fail(err);};return0;}<!-- src/index.html -->
<!doctype html>
<html>
<body>
<canvas id="canvas"></canvas>
<script type="module">
import { startKnots } from "./knots.js";
startKnots({ wasmUrl: "./app.wasm", canvas: "#canvas" }).catch(console.error);
</script>
</body>
</html>
zig build -Dtarget=wasm32-freestanding
wasm64-freestanding also works in browsers with memory64 support. installWeb configures shared memory and installs the WASM and worker files.
Serve the output over HTTP with:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
These headers isolate the page so browsers can safely expose SharedArrayBuffer, which shared WebAssembly memory requires. Cross-origin resources must allow CORS or embedding via Cross-Origin-Resource-Policy.
Distributing Vulkan applications on macOS
Knots checks for a bundled Vulkan loader before falling back to the system loader. A standalone application bundle using the Vulkan backend must include the loader, MoltenVK, and its ICD manifest:
MyApp.app/Contents/Frameworks/libvulkan.1.dylib
MyApp.app/Contents/Frameworks/libMoltenVK.dylib
MyApp.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json
The manifest's library_path must resolve to the bundled libMoltenVK.dylib.
Examples
See examples, you can also try the web version of the playground here.