Embedding Wax.
A practical overview of how to communicate between your app and Wax.
Your app communicates with Wax over a typed interface. The Wax compiler generates the host binding from the declarations in your Wax source, and the generated code makes sure the Wax library provides the same functions and types when your app starts. If the types on either side do not match, the app will not start.
| Path | What it does | Recording impact | Edit and continue support |
|---|---|---|---|
api fnHost → Wax |
A synchronous call into Wax. Use it when the host wants Wax to do something now or return a value. | Calls that can change state are recorded. Pure reads are omitted automatically. Marking an api fn as readonly ensures it is never recorded. |
An API used by the recording must still exist when execution continues against edited code. Removing an unused API is safe. |
host fnWax → Host |
A synchronous service supplied by the host. Required functions must be connected before the app loads or it will throw an error on startup. host? fn may be absent. |
The result is recorded. A void host fn that does not throw is not recorded. During replay Wax receives recorded results without calling the host again. |
Existing functions keep a stable identity across rebuilds. Adding another host function does not disturb them. |
channelHost → Wax |
Data the host pushes into Wax between frames. A value stays in place until the host changes it, so Wax can use it many times without the host sending it again. Events form a fresh batch for one frame. | Values enter the recording stream only when they change, not on every frame. Events are recorded only when the host pushes them. Replay feeds both back to Wax at the same frame boundaries. | The recorded channel set and schemas must still match. Removing a channel, changing its kind, or changing its payload blocks continuation. |
What the build gives your host.
An embedded desktop build publishes one application library, the Wax runtime library, an application manifest, and the requested generated bindings. Each binding has an application facade for your Wax API and a shared runtime sibling for common boundary values and loading. Keep those generated files together and regenerate them with the application. They are one checked contract, not source to edit independently.
Give the generated desktop loader the artifact directory when the application starts. It treats the application library, runtime, and manifest as one strict bundle and refuses a mismatch. It does not search a user installation or PATH. JavaScript and TypeScript receive a WebAssembly module and ES module facade instead. iOS receives a local Swift package whose Wax application and runtime are linked into the host.
Build targets keep these outputs in a repeatable location. Read Build targets for artifact directories, binding selection, and the difference between Native, generated C, and WebAssembly output.
A full example.
The interface lives in ordinary Wax source. This example shows every communication path from the table above, including static classes for grouping related host and API functions.
In this example, the host stages telemetry and starts a frame. Wax receives the new channel data at FrameBegin, then the host calls ApplyTelemetry to use it.
What can cross the interface?
Functions can pass booleans, integers, floats, characters, strings, json, enums, opaque host handles, built in vector, matrix, color, quaternion and time values, arrays, and structs made from those types. An API can also take a WriteOnlySpan. Only its size is recorded, not the data written into it, which makes it a cheap way to export data in bulk. secret is available where data must cross without entering a recording. Function parameters and results cannot be nullable, but channel values can.
Lifecycle and ownership.
Creating an application through the generated binding loads the bundle, creates a runtime instance, connects host functions, and validates that every required callback exists. Destroying the application releases that instance. A process may own several desktop Wax applications. Each gets separate state while the operating system shares the loaded runtime library. The iOS static package supports one generated Wax application and one live instance.
Drive an application from one owner thread. A frame begins, applies staged channel values, runs API calls, and ends with runtime maintenance and recording work. The scoped frame helper in each convenience binding closes the frame even when host code returns or throws. Host callbacks run synchronously and must not call back into the same application while it is already crossing the boundary.
Input strings and arrays are borrowed only for the call. Generated convenience bindings copy returned strings, arrays, structs, and errors into values owned by the host before temporary runtime storage is released. An opaque handle is different: it keeps a host object in the binding registry until the host explicitly releases it.
Failures, recordings, and saved state.
A Wax error becomes the normal failure form of the host language: an exception in C++, C#, Java, Kotlin, Python, or Swift, a Result in Rust, and an output error value in C. A host callback can return failure through the same boundary when its Wax declaration allows errors. Runtime panics and validation failures also surface through the generated application call.
Recordings capture calls, channel changes, visible host function results, frame timing, and inspectable runtime history. Exact replay and snapshot restoration require the build that created the file. Changed source replay instead takes compatible recorded inputs into a newly compiled build. Snapshots capture only Wax runtime state at a frame boundary. They do not capture host memory or make native handles valid after restore. Read Record and replay before choosing either as application storage.
In the C#, Python, and Rust bindings, snapshot and keyframe accessors return independently owned snapshots. Requesting a keyframe snapshot copies its state, so it remains valid after the recording advances or the application closes. Release it with the host language’s disposal mechanism. Use serialized keyframe bytes when you only need to save or transfer the state.
Host guides.
Choose a host language to wire this Wax interface into an application.