Skip to content

Concepts

Architecture #

Knots keeps UI construction separate from windows and graphics APIs. The bundled app shell is one host of the UI. It is not the only way to use the library.

Knots architectureApplication UI builds a frame through ui.Context. Ending the frame yields a render packet and platform effects. The packet takes either the bundled Renderer path or the host-owned Painter path; both reach a GPU render pass. Effects go to the host window.APPLICATIONKNOTS UIRENDERINGApplication UIcomponents + stateui.Contextbegin → build → endHost windowcursor · clipboard · closeFrame.Outputpacket + effectsRendererowns surfaceGPU render passWebGPU or VulkanPainterprepare → encodebundled pathhost-owned pathplatform effects
One UI frame produces a portable packet and host effects. Choose Renderer when Knots owns presentation, or Painter when your host owns GPU passes.

The layers #

Layer Responsibility
ui Components, the layout engine, the style engine, input handling, widget state, the accessibility snapshot, and effects.
text Font parsing, shaping, wrapping, and the glyph curve data.
render.Packet The output of a frame. It is independent of the graphics API: vertices, glyph instances, clips, images, backdrops, and custom paint callbacks.
renderer.Painter Prepares a packet for the bundled GPU backend and records it into render passes that the host owns.
renderer.Renderer Owns a surface and draws packets with a Painter.
gpu A small GPU interface, with a WebGPU and a Vulkan implementation.
window Native windows: Cocoa, Win32, Wayland, and a browser canvas.
knots.App The app shell. It owns the windows, the event loop, the renderers, async completions, and accessibility adapters.

Frame lifecycle #

Each frame has the same steps, with or without App:

  1. The host collects input into an input.FrameInput.
  2. ui.Context.beginFrame(input) returns a ui.Frame.
  3. Application code emits components into the frame.
  4. ui.Context.endFrame(&frame) calculates layout, resolves styles, and builds the packet. It returns a Frame.Output.
  5. The host applies the effects and draws the packet.
var frame = try context.beginFrame(host_input);
defer frame.deinit();
try buildUi(&frame);
const output = try context.endFrame(&frame);

Frame.Output holds the packet, the effects (cursor_shape, clipboard_write, close, redraw, text_input, capture_pointer, capture_keyboard), and the accessibility snapshot. The data is borrowed. It is valid until the next beginFrame.

If your code returns an error during a frame, call abortFrame. Then the next beginFrame works. Frame.deinit also aborts a frame that did not end.

The app shell #

App runs the same lifecycle for each window. It is not a second UI API. For each window it:

  • Waits for events, then collects input.
  • Runs async completions for the window.
  • Calls your frame callback with a View and a ui.Frame.
  • Applies the effects to the native window.
  • Draws the packet with its Renderer.
  • Sends the accessibility snapshot to the platform.

Read Windows and async work for the App API.

The render packet #

A render.Packet uses its own conventions for geometry, glyphs, clips, and shaders. It does not depend on a graphics API. The bundled Painter reads it, and a custom renderer can read it too. A packet can contain extension commands, for example the paint callbacks of GPUCanvas. A renderer must check the extensions and reject commands that it does not support.

Embedding #

A program that already has a window or a GPU renderer can skip App. It uses ui.Context directly and draws with Painter or its own renderer. Read Embedding.

The HMR boundary #

With hot reloading, the host stays loaded. Modules are separate WASM binaries. A module gets input and bound state, and returns a serialized packet for its region. The host composes the packet into its own frame. The host owns all platform and GPU resources, so a reload does not restart the window or the GPU state. Read Hot reloading.