No description
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
vonhyou 2fe9cdbb03
Release 0.1.1
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 23:49:39 -04:00
src Build StereoKit 0.3.11 from source with the Zig build system 2026-10-05 23:38:39 -04:00
tools Build StereoKit 0.3.11 from source with the Zig build system 2026-10-05 23:38:39 -04:00
.gitignore Build StereoKit 0.3.11 from source with the Zig build system 2026-10-05 23:38:39 -04:00
build.zig Build StereoKit 0.3.11 from source with the Zig build system 2026-10-05 23:38:39 -04:00
build.zig.zon Release 0.1.1 2026-10-05 23:49:39 -04:00
README.md Release 0.1.1 2026-10-05 23:49:39 -04:00

stereokit-zig

Warning

This project was generated by Claude, an AI model, and has not been tested. Use it at your own risk.

StereoKit for Zig. The package builds StereoKit from its pinned upstream sources with the Zig toolchain. It needs no CMake and no prebuilt binaries, and it links StereoKit statically into your executable. On top of that, a thin layer exposes the API in idiomatic Zig.

  • Zig: 0.17.0
  • StereoKit: 0.3.11
  • Platforms: Linux x86_64 builds and runs. Windows x86_64 (x86_64-windows-gnu) compiles the library, but linking and running an executable is untested.

Using it

zig fetch --save=stereokit https://git.ray.works/sfeng/stereokit-zig/archive/v0.1.1.tar.gz
// build.zig
const std = @import("std");
const stereokit = @import("stereokit");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const sk = b.dependency("stereokit", .{ .target = target, .optimize = .fast });
    const exe = b.addExecutable(.{
        .name = "app",
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
            .imports = &.{.{ .name = "sk", .module = sk.module("stereokit") }},
        }),
    });
    b.installArtifact(exe);

    // Compile your .hlsl shaders and install them next to your assets.
    const shaders = stereokit.addShaders(b, sk, .{
        .root = b.path("assets/shaders"),
        .files = &.{"my_shader.hlsl"},
        .optimize = optimize,
    });
    b.installDirectory(.{ .source_dir = shaders, .install_dir = .bin, .install_subdir = "assets/shaders" });
}
// src/main.zig
const sk = @import("sk");

const App = struct {
    window_pose: sk.Pose,

    pub fn step(app: *App) void {
        sk.ui.window("Hello", &app.window_pose, .{});
        if (sk.ui.button("Quit")) sk.quit();
        sk.ui.windowEnd();
    }
};

pub fn main() !void {
    try sk.init(.{ .app_name = "Hello" });
    // Pose math calls into StereoKit, so it runs at runtime, not as a default.
    var app: App = .{ .window_pose = .lookAt(.init(0, 0, -0.5), .zero) };
    sk.run(&app);
}

On Linux the build needs the system development headers for EGL, gbm, X11, Xfixes and fontconfig.

Options

These are passed through b.dependency("stereokit", .{ ... }):

Option Default
target, optimize native, debug StereoKit is large C++, so pass .fast even for debug app builds.
openxr .bundled .bundled compiles the Khronos loader in. .system links the OS's libopenxr_loader.

What it provides

  • Module stereokit: the Zig layer. Importing it links the static library.
  • Module stereokit_c: the raw translate-c bindings, also available as stereokit.c.
  • Artifact StereoKitC: the static library.
  • addShaders(): compiles .hlsl files to .sks, as StereoKit's NuGet package does for C#.

The Zig layer

The layer keeps StereoKit's names and concepts, so the C# documentation still applies. It changes only what C makes awkward:

  • bool32_t becomes bool, and C enums become Zig enums.
  • Math is value types with methods. Rotations and matrices call StereoKit's own C code, so conventions match exactly: -Z is forward, and mul follows C#'s operand order.
  • Default arguments become option structs.
  • Missing assets become errors.
  • Asset handles have an explicit release(): every constructor, including find, returns a reference you own.
  • sk.run(&app) drives any struct with a step method, which may return an error, and an optional shutdown method.
  • sk.frameFmt formats strings into scratch memory that is reset each frame, for immediate-mode labels.
File Contents
stereokit.zig init, run, quit, time, frameFmt, and re-exports.
math.zig Vec2/3/4, Quat, Matrix, Pose, Ray, Bounds.
color.zig Color and Color32.
asset.zig Tex, Shader, Material, Mesh, Vertex, Model, Sound, Sprite.
ui.zig Windows, buttons, radio, toggle, text, input, slider, layout, tint and theme.
input.zig Head, eyes, controllers, hand visibility.
draw.zig Lines, world text, the transform hierarchy, screenshots.
log.zig Formatted logging, plus stdLogFn to route std.log through StereoKit.
interop.zig Conversions to and from C types, with comptime layout checks.

Only part of the API is wrapped so far. Everything else is reachable through sk.c.

How the build works

The build.zig.zon pins StereoKit and exactly the dependency versions that StereoKit's CMake build pins: sk_gpu, the OpenXR SDK, meshoptimizer and basis_universal. build.zig then:

  1. Compiles StereoKit's built-in shaders into C headers with the prebuilt skshaderc from the sk_gpu release.
  2. Compiles StereoKitC, the basis_universal transcoder and meshoptimizer, and, by default, the OpenXR loader. Physics (ReactPhysics3D) is not built.
  3. Runs translate-c over a C-compatible copy of the public headers.

Upstream quirks it works around, each commented where it lives:

  • No executable bit. Zig's fetcher drops Unix permissions from zip archives, so skshaderc is copied with the executable bit set (tools/copy_executable.zig).
  • Header bug. stereokit.h 0.3.11 is not valid C, because sk_quit has a C++ default argument. tools/patch_header.zig fixes it before translate-c, and fails loudly once upstream fixes the header.
  • Silent skshaderc failures. It exits 0 even when it rejects its arguments. -i takes a separate argument, and -o needs a trailing slash to mean a folder.
  • Missing headers on Zig. Zig is never MSVC, so StereoKit's bundled DirectXMath headers (lib/include_no_win) are used on every target. Windows also needs _USE_MATH_DEFINES.

Upgrading StereoKit

  1. Run zig fetch --save=stereokit_src <new tarball url>. Update the other dependencies to the commits pinned in the new release's CMakeLists.txt.
  2. Diff that CMakeLists.txt against the source lists and defines in build.zig.
  3. Run zig build test. It checks that every wrapper type's layout matches its C twin and exercises the math against StereoKit. patch_header reports any fixes that are no longer needed.

Development

zig build                # builds the static library into zig-out/lib
zig build test
zig fmt --check build.zig src tools
zlint src tools