Skip to content
hikari
RenderingEditorAIToolchainDocumentation
GitHub
hikari

© 2026 Flying Rat Studio.
All rights reserved.

Explore the engineDocumentationContributorsLicenseBack to top
Documentation / Guides
Browse docs
Overview
Tutorials16
OverviewFirst game projectFirst entityAuthored user_data and the inspectorFirst mesh and materialFirst physics body and triggerFirst character controllerFirst session servicesFirst UIFirst input actionFirst messagesPlay, Edit, and scenesFirst runtime spawnFirst motionFirst skeletal animationAssets in Play (soft refs and hot reload)Dynamic editor recompile
Guides16
OverviewDevelopment guideGameplay APIChoosing component storageBuild and packagingProject filePluginsHikari Plugin APIData-driven content, JSON, and pathsScripting with KawaShader authoringTime of dayUser interfaceUI layoutUI widgetsMigration from Unity / UnrealActor and component lifecycle
Systems28
OverviewArchitectureApplication lifecycleFrontends and driversPlatforms and supportSession services and cross-scene stateScenes and gameplayActor communication (hi.actors)Game-facing refsSave / replication wire versionCoordinate space and camera conventionsRenderingRenderer architecture mapFrame governorGPU particlesVisual ZonesVolumetric mediaInputAudioPhysicsMotion KitTemporal KitAssets and ShinraPrefabsAsset residencyAsset formats (Shinra pipeline)UI and editorEditor asset hot reloadEditor Project Selector
Language reference2
OverviewAkari language referenceKawa language reference
Engine overview
Start exploring
  • No matching sections. Try fewer words or another topic.
NavigateEnter Openesc Close
Overview
Tutorials16
OverviewFirst game projectFirst entityAuthored user_data and the inspectorFirst mesh and materialFirst physics body and triggerFirst character controllerFirst session servicesFirst UIFirst input actionFirst messagesPlay, Edit, and scenesFirst runtime spawnFirst motionFirst skeletal animationAssets in Play (soft refs and hot reload)Dynamic editor recompile
Guides16
OverviewDevelopment guideGameplay APIChoosing component storageBuild and packagingProject filePluginsHikari Plugin APIData-driven content, JSON, and pathsScripting with KawaShader authoringTime of dayUser interfaceUI layoutUI widgetsMigration from Unity / UnrealActor and component lifecycle
Systems28
OverviewArchitectureApplication lifecycleFrontends and driversPlatforms and supportSession services and cross-scene stateScenes and gameplayActor communication (hi.actors)Game-facing refsSave / replication wire versionCoordinate space and camera conventionsRenderingRenderer architecture mapFrame governorGPU particlesVisual ZonesVolumetric mediaInputAudioPhysicsMotion KitTemporal KitAssets and ShinraPrefabsAsset residencyAsset formats (Shinra pipeline)UI and editorEditor asset hot reloadEditor Project Selector
Language reference2
OverviewAkari language referenceKawa language reference
Engine overview
Guides10 min read

Build and packaging

On this page
On this pageOne conductor: KajiProduct optionsProfiler and diagnostics switchesPlan and explainGraphics diagnostics (D3D12 / Metal)Windows: D3D12 Agility SDKLogging under --runStage modelDistribution packagesPackaging identityHost supportLocal engine build tasks Back to top

One conductor: Kaji

Kaji is the only product build tool. Run it from the repository root:

bash
# Game, monolithic debug build (staged under bin/game/)
bin/kaji/kaji game --workspace="$PWD" --project="$PWD/src/games/example" --type=monolithic --config=debug

# Project-bound editor: develop a game with Play/Edit, scene authoring, recompile
bin/kaji/kaji editor --workspace="$PWD" --project="$PWD/src/games/example" --type=dynamic --config=debug --run

# Standalone editor (no project; boots the project selector)
bin/kaji/kaji editor --workspace="$PWD" --type=dynamic --config=debug --run

# Shippable artifacts
bin/kaji/kaji game --workspace="$PWD" --project="$PWD/src/games/example" --type=monolithic --config=release --package=bundle
bin/kaji/kaji editor --workspace="$PWD" --type=monolithic --config=release --vendor:zig=fetch+bundle --package=bundle

Kaji builds vendor units, the native bridge, Opus, Kawa, Shinra, Akari, the engine core, shader packages, and the native frontends as one dependency graph. Units that do not read each other's outputs build concurrently. Zig emits application and plugin objects; Kaji owns the final executable link on every host. Build units are *.kaji.cs files stored beside their source; vendors enter the graph only through KajiUnitCatalog.RegisterVendor (Tenkai3D yes, Tenkai2D no).

The platform scripts under build/ (build-game-macos.sh, build-editor-windows.ps1, …) only bootstrap Kaji and forward options. Product policy lives in Kaji, never in those scripts.

Product options

OptionMeaning
game / editorProduct frontend
--workspace=<repo>Workspace root (auto-discovered inside an engine workspace)
--project=<dir|hikari.project.json>Game project. Required for game; optional for editor (omit = standalone host + project selector). Flag form only; bare positional paths are rejected
--type=monolithic|dynamicDriver/application/platform linkage (default monolithic). The editor always loads game code dynamically regardless of this flag
--config=debug|releaseProduct configuration (default debug)
--runLaunch the staged product from bin/game/ after success, with live stdio
--log=<path>With --run, forward as the product's engine log file path
--cleanWipe bin/* and build/cache/* (except Kaji) plus disposable <project>/.engine/* (keeps .engine/user/). Asks for confirmation unless --yes / --agent
--yes / --silent / --agentSkip the --clean confirmation; --silent and --agent also quiet conductor output
--ui=auto|log|liveConsole frontend. live redraws a status block in place; log prints one line per action (CI, pipes). auto picks live on an interactive terminal
--trace=<path>Write a Chrome trace-event file of every action and unit (open in Perfetto or chrome://tracing). The finish line also prints the critical path: the dependency chain that bounded the wall clock
--until=<unit,…>Build the named units and everything they depend on, nothing after them. Ids are what kaji plan prints: hikari, kawa, shaders, package-host, assets, …
--only=<unit,…>Build just the named units; what they depend on must already be built
--events=<path>Stream build events as NDJSON (one object per line) for the editor console and CI
--parallel=<jobs>Concurrent tool processes per unit (default: host CPU count)
--dep-config=<unit>:debug|releaseOverride the configuration of a vendor unit, shinra, or akari
--shinra:config=debug|releaseShinra tool configuration. Default release even under product debug; flip only when debugging Shinra
--shaders:config=debug|releaseAkari shader compile profile (default debug). Independent of product --config
--resources=bundles|loose (alias --data-layout=)game and kaji assets only. Product data layout under data/ (default bundles). Rejected on kaji editor, which never seals packs. Pack membership and delivery come from configs/layout.json (Project file)
--package[=bundle|loose]After staging, emit a distribution artifact under bin/packages/ (Distribution packages)
--vendor:zig=fetch|bundle|fetch+bundleEditor only. fetch downloads the pinned Zig archive into 3rd-party/zig/ when missing; bundle stages it into bin/game/tools/zig/ and fails on a missing archive or version mismatch. Without it the editor discovers a toolchain at runtime (Zig toolchain discovery)
--vendor:engine=bundleEditor only. Stage src/hikari/sdk/ into bin/game/sdk/ with the shader contract overlaid into sdk/shaders/. A leftover bin/game/sdk wins over the source tree, so restage or --clean after editing the SDK
--memory:asanAddressSanitizer for supported native builds. Rejected on macOS with Zig 0.16 (shadow-mapping conflict with Zig's allocator)
--memory-leak-checkWith --run, set HIKARI_FAIL_ON_LEAK=1 so a subsystem-allocator leak at shutdown fails the process (ReleaseSafe; Debug already panics unless HIKARI_ALLOW_LEAK=1; ReleaseFast does not track)

Caution

--clean deletes build outputs under bin/* and build/cache/* (except Kaji), plus disposable project .engine/* data. It preserves .engine/user/. Adding --yes, --silent, or --agent skips the confirmation; check the workspace and project paths first.

Profiler and diagnostics switches

OptionMeaning
--profiler-timing[=true|false]Compile the CPU profiler (default: on in debug, off in release). Runtime Recording is off until enabled; the editor Profiler tab works in Edit mode
--profiler-timing:modules=markers,pix,xcodeProfiler modules: markers (GPU pass labels and debug names for encoders, buffers, textures, pipelines; default in debug), pix (Windows PIX), xcode (Instruments)
--profiler-physics[=true|false]Physics memory/state diagnostics for the editor Physics tab (default off)
--profiler-audio[=true|false]Audio bus/voice/worker diagnostics for the editor Audio tab (default off)
--profiler-residency[=cpu|gpu|cpu+gpu|off]Residency diagnostics (default off). cpu adds recent-free history and editor snapshots; gpu adds renderer snapshots. Refcounts, budgets, and eviction are always compiled (asset residency)

Older spellings (--profiler, --profiler:modules, --residency) are rejected.

Plan and explain

Host packaging, editor shader staging, the asset toolchain, the game cook and seal, and the distribution package are nodes of the same graph with declared inputs and outputs, so a rebuild that changed nothing runs none of them and explain reports why one would.

Tip

Run kaji doctor [--workspace=…] before your first build or when setting up another machine. Without building, it checks the declared toolchain minimums, host tools (Metal toolchain and Xcode utilities, Visual Studio, patchelf), open-file limit, vendored Zig pin, workspace lock, and build cache. Exit status 1 means a build would fail.

kaji plan game|editor [options] prints the lowered action graph (every command, input, and output) without running anything. kaji explain … adds each action's cache verdict, so "why did that rebuild" is answered before the build. Both take the product options above plus --json.

Graphics diagnostics (D3D12 / Metal)

--d3d12-* and --metal-* are aliases for the same knobs; only the host backend emits defines. All are build-time, accept --flag, --flag=true, or --flag=false, and work in either configuration.

FlagDefaultD3D12Metal
--d3d12-debug-layer / --metal-debug-layeron in debug, off in releaseDebug layer + DRED; validation info queue drained into the log once per presentMTL_DEBUG_LAYER=1 set before Metal initializes
--d3d12-trace / --metal-traceoffVerbose native tracing (HIKARI_D3D12_LOG_TRACE)Verbose native tracing
--d3d12-gbv / --metal-gbvoffGPU-Based Validation (very slow)MTL_SHADER_VALIDATION=1
--metal-warning-mode=nslog|assert|ignoreignore—MTL_DEBUG_LAYER_WARNING_MODE; implies --metal-debug-layer

Notes:

  • Metal validation is process-wide and also wraps AppKit/QuartzCore encoders. On macOS 26 those emit false unused binding warnings at startup, so the warning channel defaults to ignore. Validation errors always log. Use --metal-warning-mode=nslog for an audit, or assert to halt inside the first offending call under a debugger. The mode is compiled in with setenv(…, 0), so MTL_DEBUG_LAYER_WARNING_MODE=ignore ./hikari-editor … overrides it per run.
  • Two known false positives in Metal's own bookkeeping (acceleration-structure encoders and MetalFX-opened encoders) are corrected by shims in src/hikari/src/native/macOS/src/MetalDebug.m. Each logs a one-shot "obsolete on this OS, delete it" line if an OS update fixes the layer itself.
  • Attribution help in a markers build: the once-a-second encoder census (encoders opened by kind, how many were empty, how many vendor encodes ran) and the per-pass empty-encoder report. If Metal prints an empty-encoder warning and the report does not name a pass, the encoder is not ours.
  • On Windows the native logger also appends to %TEMP%\hikari-d3d12-debug.log when the debug layer is compiled in, since a windowed process has no console. D3D12 INFO chatter is filtered at the info-queue storage filter.
  • Changing any of these flags rewrites the product-settings stamp and rebuilds the native bridge, so a toggle always takes effect. .cpp/.m edits are tracked build inputs and do not need --clean.

Windows: D3D12 Agility SDK

Windows stages copy the pinned Agility redistributable into bin/game/D3D12/ (D3D12Core.dll, d3d12SDKLayers.dll) and WinPixEventRuntime.dll beside the executables. The frontends export D3D12SDKVersion / D3D12SDKPath so the system loader picks them up. Pins: 3rd-party/d3d12-agility/, 3rd-party/winpix-event-runtime/.

Logging under --run

  1. Terminal: product stdio is inherited. Build steps capture their own output; only the final process streams live.
  2. Editor UI: the bottom console sink stays registered.
  3. File: NDJSON. The editor defaults to <project>/.engine/logs/editor-latest.json once a project is open; the game needs --log=<path>.

Engine-side contract: Frontends and drivers.

Stage model

Typed plans in src/kaji/src/HikariBuildPlans.cs choose the host and build units. Engine build products install under bin/hikari/; the runnable stage is bin/game/; distribution artifacts land under bin/packages/.

  • Dynamic stages separate application, platform, windowing, input, renderer, physics, scripting, and game-UI libraries. Monolithic links them into the executable. The two modes exercise different ownership and loading paths; a monolithic success is not evidence for a dynamic module lifecycle change.
  • The editor always dlopens the project's game module after open/recompile. --type= never bakes game code into the editor.
  • ProjectConfig emits the driver package manifest from the same typed recipes runtime selection uses, so the stage list cannot drift from configuration.
  • Game modules built under the editor pass -Deditor-contrib=true, exposing hi.editor. kaji game omits it, so editor-only game code stays out of shipping binaries (Game editor SDK).
  • Plugins are source-composed into the game module from hikari.plugins.json; native link and runtime staging inputs come only from the generated hikari-plugin-artifacts.json (Plugins).

Distribution packages

Without --package, Kaji stops at the staged bin/game/ tree. With it, Kaji filters the stage into a shippable payload and asks the host packager for an artifact:

FlagmacOSWindowsLinux
--package / --package=bundle.app (ad-hoc signed).msix (Windows SDK MakeAppx)not implemented
--package=loose.zip.zip.zip
text
bin/packages/<slug>-<version>-<platform>-<arch>-<config>/
  Sample.app | Editor.app | *.msix | *.zip

Game slug is the packaging title; the editor slug is editor. A standalone editor package ships the host plus project selector. A project-bound editor package (--project=<game> --package) uses that game's packaging.editor block.

ProductKeepsStrips
gamehikari, game application/module, sealed data/ (packs plus loose configs and identity; every content dir ships, packable ones inside the content pack, others loose for modding). configs/packaging.json never shipseditor frontend and application, tools/
editorhikari-editor, editor application, backends, tools/ (Shinra, Zig SDK), loose data/_enginegame frontend, game application/module, game packs

Product resource layout. After cook and shaders, Kaji seals packs under data/bundles/ with the catalog product.shinbundle.map.json (bundles mode) or stages the same paths loose. Without configs/layout.json there is one game pack plus product-sealed engine shaders. With one, each pack declares membership globs, delivery (packed or loose), deps, and labels; a loose pack stages as a directory and stays moddable. Cooked art no pack claims joins default_pack and is reported, never a build failure. Content dirs marked bundled: true may enter the content pack; bundled: false always stages loose under data/<path>/. Details: Data, JSON, and modding, Project file, design in resources-layout-and-logical-packs.

Zig bundling for self-contained editor packages that compile game modules offline:

--vendor:zig=Effect
(omitted)No download, no tools/zig/ staging; runtime discovery uses env/PATH
fetchDownload the pinned host archive into 3rd-party/zig/ if missing
bundleStage into tools/zig/; fail if missing or version mismatch
fetch+bundleBoth

Pin: 3rd-party/zig/VERSION, which must match src/hikari/build/Hikari.kaji.cs.

Host details. Loose and MSIX staging hardlink where the volume allows; the macOS .app copies because codesign rewrites Mach-O in place. Inside an .app, binaries live in Contents/MacOS/ and content in Contents/Resources/data/ with a MacOS/data symlink so exe_dir/data/… lookups keep working. A PNG or .icns icon becomes AppIcon.icns; an Icon Composer .icon (packaging.macos.icon) is compiled with actool into Assets.car plus a fallback .icns and CFBundleIconName, so macOS 26 renders Liquid Glass. Windows .msix requires the Windows 10/11 SDK; certificate selection is left to your release pipeline. Packagers live under src/kaji/src/Packaging/ (IPlatformPackager per host).

Packaging identity

Shipping identity is product-owned JSON that Kaji reads without compiling the game:

ProductDescriptor
kaji game … --packageGame configs/packaging.json (required)
kaji editor … --package (no project)src/hikari/editor.project.json → packaging
kaji editor --project=<game> … --packageGame configs/packaging.json → editor (falls back to the shared fields)

Fields: title, product_version, identifier, icon, authors/publisher, macos / windows blocks, optional startup_background_color (#RRGGBB, default black; the solid cover a standalone game shows while the first scene loads; embedded at build time, so rebuild after changing it). See Project file.

Host support

  • macOS: Apple Silicon (aarch64) only, Metal 4 MSL, product floor macOS 26. Tenkai3D compiles with Homebrew Odin plus llvm@22 / lld@22. An unversioned brew install llvm produces a stub libtenkai3d.dylib (every export is brk); Kaji fails that at the Odin step. Repair with brew install llvm@22 && brew reinstall odin.
  • Windows: x86_64, D3D12 with the Agility SDK staged beside the exe.
  • Linux: x86_64 build plumbing exists, but the runtime is incomplete and skips Akari shader compile. Not a product peer (Platforms).

Smoke matrices: build/test-build-macos.sh runs clean debug/release × dynamic/monolithic (dynamic via kaji game, monolithic via kaji editor, every cell with --project=). build/test-build-windows.ps1 runs the same grid but omits --project= on its game cells, so it is not yet a green peer. Both first run the Tenkai3D physics gate (python3 src/tenkai3d/test.py --soak; needs Python 3 and Odin on PATH) and stop on a physics failure (physics verification).

Local engine build tasks

From src/hikari, direct Zig tasks remain useful for focused validation: zig build test -Dgame-src=<abs game src>, zig build entity-bench …, zig build check-windows …. Use Kaji for real game and editor products; do not assemble ad hoc links. See Development → Validation.

PreviousChoosing component storageNext Project file

Documentation follows the current engine checkout.

Snapshot cc148c75Source docs/guides/build-and-packaging.md
On this pageOne conductor: KajiProduct optionsProfiler and diagnostics switchesPlan and explainGraphics diagnostics (D3D12 / Metal)Windows: D3D12 Agility SDKLogging under --runStage modelDistribution packagesPackaging identityHost supportLocal engine build tasks Back to top