Knots - A library for building GUIs with Zig

,

Hi! In this post I briefly mentioned an immediate-mode GUI library I was working on, that is being used to build a watch-party MPV client. I wanted to make a more detailed post sharing more about the GUI library knots which i find myself spending more and more time on. knots is an immediate mode GUI rendering library/framework (I don’t like the idea of a framework but I think it makes it more clear that it handles more than just putting stuff on the screen).

You might already be familiar with projects like dvui (awesome project, you should check it out), and while there is definitely a large overlap in terms of features I would say the main differentiator between knots and dvui are the goals. dvui is a lot more “general-purpose”, from their README: “GUI toolkit for whole applications or debugging windows in existing apps/games.”. The current scope of knots is a lot slimmer/focused, I am mainly interested in providing a way for people to build really performant cross-platform standalone desktop applications, where “UI code” is essentially out of the way entirely, so you can spend more time on solving “real” problems. I.e, if you are looking for a library that lets you easily create a debug UI for your game engine, or really render anything on top of an existing application/surface, knots is not for you, and will likely never be. It goes without saying that this library is in very early stages, (I just recently decided to stop force pushing on to the initial commit, as I was making massive overhauls to parts of the library every other day). Particularly, you will find many basic widgets/components to not be implemented yet, as I am still putting most of my focus on trimming down the main core of the library.

Below are just some questions and answers I think most of you might have:

  • What platforms are supported?
    • Windows, macOS, Linux, and web via emscripten + webgpu
  • Any examples?
    • Like I mentioned before, this mpv client shaven is being built with knots. You can also clone the knots repo and run some of the examples, or test the playground app on the web Playground!
  • What are you currently working on?
    • I am trying to get rid of all my c/c++ dependencies, to trim down compile times and binary sizes. I used to depend on GLFW for windowing but have now dropped it in favor of platform specific implementations which have been implemented for windows, macOS (partially), and emscripten/web. Linux builds unfortunately still depend on GLFW. As of this PR, I deleted harfbuzz as a dependency as I was not using any of its features anyways, and it was pulling all kinds of garbage like libc++, this open PR will drop freetype as a dependency in favor of https://codeberg.org/andrewrk/TrueType (built by Andrew Kelley!) to remove the final C dependency. After that, I will likely spend some time improving how text is handled in the layout module, to support text wrapping and other niceties.
  • Cross platform GUI rendering? How?
    • This is particularly important to get right and I took some risks here which might have repercussions in the future. I bet essentially on two APIs, WebGPU and Vulkan. Why these two? Well I always wanted to have the door open for supporting web, and to do that I would need to either support WebGL or WebGPU, afaik WebGL is planned to be superseded by WebGPU, and while WebGPU will potentially not work on some browsers, I am fine with that knowing they will eventually support it. I also get the added benefit of WebGPU acting as a “catch-all” GPU API which can be used when targeting native builds as well. This means you can build knots with only the WebGPU backend and be able to target Windows, macOS, Linux and Web! So why also support Vulkan? I wanted to leave the door open for opting in to even better performance for native builds, and Vulkan seemed to be the best choice here for performant cross-platform graphics. I honestly can’t see myself maintaining more than 2 GPU backends, so it is likely this will not change any time soon. Also, the WebGPU backend for native builds depends on wgpu-native being statically linked to the binary, which bloats binary sizes quite a bit (however, ensure it will run on a lot more machines).

Okay, I rambled quite a bit now and need to get back to work, please let me know if you have any more questions/concerns. This is not really an ad, as I still think the library is far from being useable for production apps. I am just more interested in what people think, am I wasting my time, or is it something you would be interested in?

32 Likes

Just wanted to post some updates here for people interested:

  • Playground web build is now hosted on codeberg pages, here: Playground
    • Web wasm build is down to 1.93 MB (900KB compressed), with about 400KB of the uncompressed data being just the font ttf. I want to get this even lower, potentially by dropping emcc as a build dependency, but will require some work to support wasm-freestanding as a target currently.
  • Some new components added
    • Color picker
    • Checkbox
    • Multi-line text input
    • Virtual list
    • General improvements to all existing components
  • The theme system got some upgrades, the theme can now be defined as a .zon file, an also changed at runtime. Themes are checked at compile theme for errors (invalid hex, rgba e.t.c). You can view the existing ones that come with knots here https://codeberg.org/shahwali/knots/src/branch/main/src/ui/themes
  • The vulkan backend vertex shaders are now written in Zig, and compiled using Zig, I wrote about it here: Writing Compute Shaders In/With Zig - #13 by shahwali
  • Text word wrapping added.
  • Grid layout support added.
  • The renderer settings/dev tools got an upgrade, now with some more stats, and even a sparklines graph!

Still so much to do…, below is a screenshot of as much I could possibly pack in one frame on the playground app. Would like to write some docs when I have time, mostly to free up some space in my head but also if anyone else is willing to try knots out, at your own risk of course.

21 Likes

Very cool! I managed to get a basic app working (adjusted from the playground example) really quickly. There was a few updates I needed to make to get Knots working on latest Zig main (patch submitted btw) but it wasn’t much. Apologies for the gif compression:

edit: the gif breaks if I try to embed bc it’s too big
https://i.imgur.com/zlvw18f.gif

4 Likes

Another update as it has been nearly 2 months since the last one now. Quite a lot has changed so below are some of the highlights, in no particular order.

glslc is no longer a required system dependency to use the Vulkan backend :tada:.

You might be wondering how, and the TDLR is that the Zig compiler is now powerful enough that I can write my fragment and vertex shaders in Zig, and compiling them at build time using the Zig build system, and embedding the SPIRV as an anonymous import. Huge props to the Zig compiler team and any contributors that are working on the SPIRV backend. Easily one of the coolest usages of Zig compiler and build system in this project right now! Vulkan required version has also been bumped to 1.3 from 1.2 to support dynamic rendering, which allowed me to simplify the backend.

emscripten/emcc is no longer a required system dependency to compile a knots app for the web :tada:

In other words wasm<32|64>-freestanding is now a supported target, and wasmxx-emscripten support has been dropped. Unfortunately this was not solved quite as elegantly, and essentially required an unwanted amount of JS glue, and build time logic I had to delegate downstream to the user. The upsides are much smaller wasm binaries, simplified compilation machinery (no need to call emcc and research its flags e.t.c), and reduced compile times. Downsides are that I have taken on yet another compilation target I will have to test and support going forward.

Snapshot tests

.qoi files generated from a simple test runner are checked in to the repository such that I can easily detect regressions in the renderer. This is all currently being done manually however as I am too lazy to set up a CI pipeline for this.

GPU backend is no longer configurable at runtime

This was a cool feature, I however do not think it really has any real practical use-case besides easily debugging both backends in one instance. To support this I had to have wrap my backends behind a vtable, which is just an unnecessary performance hit, as well as just making the code harder to debug. GPU backends are now behind a comptime interface, so the GPU backend must be known at compile time.

Features

This is quite a long list and I won’t bother going in to every one so I’ll just list them as bullet points below. You can try all of these in the web playground.

  • Support for opening multiple native windows.
  • Floating in-app window panels as a component.
  • Tooltip component.
  • Context menu component.
  • Dialog component.
  • Menu button component.
  • Radio input component.
  • Basic input key
  • General bug fixes, performance improvements and housekeeping.

Going forward

I think I am going to spend some time building out the playground app, I want to find a way to showcase the true potential of knots, any suggestions would be appreciated. I also want to spend some time on improving documentation across the board, to make it easy for anyone to pick up the library and start building their next GUI application.

Side note

I am maintaining what I believe is the only wgpu-native bindings for Zig which are actually up to date, the reason I mention that is because the wgpu website and docs seem to point to a repository that has not had a commit in almost a year now wgpu_native_zig. With that being said, if you want to get in to GPU programming with Zig, and you do not want to dive head first in to the monstrosity that is Vulkan, I would recommend trying these bindings wgpu-zig and following Learn WebGPU for C++ documentation (it is in C++, but it is trivially translatable to Zig). You can probably also follow the rust guide Introduction | Learn Wgpu although I’m not sure how easy it is to translate to Zig. The bindings support Zig 0.16 and newer. Please open an issue or a PR if you find some part of the API I have not covered yet.

Now to enjoy the sun while it is still here :sun:

9 Likes

I want to try out (compute) shaders in zig so bad now.

I recetly switch from wgpu-native to dawn for my zig project. The reason is that I was not able to get the wgpu-native to work with sokol “which is currently only tested with Google’s Dawn library”. Unfortunatly, the only good dawn bindings I could find, zgpu, are also using outdated prebuilt libraries. Sigh, so I rolled my own bindings using the prebuilt libraries from google (which annoyingly don’t include dynimic libraries). Its still WIP so I shouldn’t share it yet.

2 Likes

Ah, I’ll humbly retract my statement then! Not sure if you are aware of this but you don’t need to have the wgpu-native artifacts checked in the repository. You can actually lean on the Zig build system to properly version the dependencies, see https://codeberg.org/shahwali/wgpu-zig/src/branch/main/build.zig.zon#L20-L64. But maybe there is a good reason to have them checked in? Makes me a bit wary though from a security perspective.

Sorry, it not my project, I only stumble upon it!

I’m not in that space, so I can’t comment on the code.
That said, I think the design is gorgeous.

I might take a look at it for building some UI tooling in the future.

1 Like

Another update here, probably the biggest one since I started this project.

Docs and a website

I finally got around to vibe-slopping an official website for knots with docs, and a logo (that I designed myself :slight_smile: ), and hopefully some interesting blog posts, starting with my first one: Why knots? · Knots

The knots repository is now on GitHub, and has been archived on Codeberg.

The TLDR here is that too many issues with Codeberg (downtime, codeberg pages static site hosting limitations, lack of visibility, and other small annoyances) made me come crawling back to GitHub. This is not an endorsement of GitHub to be clear.

Accessibility via AccessKit

I had been putting this off for quite some time, but finally got around to adding support for emitting accessibility trees from rendered UI, and ended up bringing in AccessKit as a dependency for this. It works nice enough, and has cross platform support, probably won’t visit this feature again unless performance becomes an issue.

Hot module reloading via wasm + wasmtime

Too much to cover here, you can read about it in the docs: Hot reloading · Knots
TLDR: you can run your knots app in a dev/HMR mode which lets you test your UI changes without restarting your app. Super useful if you are anything like me and can’t stand having to constantly restart yout app to test tiny UI changes.

Plainview

One thing I have always wanted was a flagship piece of software built with knots to showcase that it can be used to build real software. And I believe plainview will be that project. Plainview is a media player built with knots, using libplacebo as the underlying video processing implementation. I want to be absolutely clear, this project is completely AI coded, with me essentially laying out the architecture, what dependencies it was allowed to use, and what the UI should look like. I’m sure some of you will be disappointed, and I would like to make a case for why I went down this path.

  • I am limited in what I can do by myself, I don’t want development of knots being slowed down by Plainview, and one is clearly more important to me than the other.
  • We need more software written in Zig! If an LLM can help boost the overall opinion that good software is written in Zig, then I think it is worth it.
  • IMO, the sign of a good API/library is that even an LLM can’t mess up using it.

I also want to say that I was absolutely floored by how quickly and efficiently Claude (Opus 5.5 on medium effort) managed to build the MVP of Plainview. 1 prompt that took 1 hour to complete gave me a media player that I felt was sufficient for me to ditch MPV and VLC forever. To get it to the state that it got it to (built the way I specified) would have genuinely took me several months, it was a real “Aha!” moment for me when it came to LLM based coding. It is obviously built on technology that many people have spent years building, it is by no means creating a media player from scratch, but I was still quite impressed. Plainview is not yet ready for public release (lacking cross-platform support, and some polish), it will be open source. For now, here is a sneak peek :eyes:



12 Likes

5 posts were split to a new topic: Discussion on real world use of AI with zig

This is awesome! I have been somewhat following knots development for a while. In my personal project I just started adding ImGui on top of my image editor, but after reading your blog, I might try to switch over to knots! I like the logo btw.

1 Like

I tried latest main and got an error, I am on Alpine Linux (musl libc).

panic: AccessKit Linux archive requires the GNU ABI
/home/rw/prabe/knots/lib/accesskit/build.zig:9:71: 0x13e9205 in build (build.zig)
    if (target.result.os.tag == .linux and target.result.abi != .gnu) @panic("AccessKit Linux archive
requires the GNU ABI");

Any plans on supporting/allowing musl?

I am currently working on adding wasmxx-wasi as a supported target, after that I can look in to feasibility of musl as a supported target ABI, I’ll create an issue on GH for it so you can keep tabs on the progress.

2 Likes