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
Guides6 min read

Hikari Plugin API

On this page
On this pageOne product name; three domainsComposition in one pageChoose your domainEditorChromeScene transactionsGameplayPackage settings (hi.settings)Open components and the inspectorTyped plugin-to-plugin dependenciesRenderingThreading and lifetimeRelated docs Back to top

Product front door for author-facing plugin contracts. Composition (manifests, roles, packaging) lives in Plugins and plugin-system design. Normative contracts and residual deferred work: Plugin API design.

One product name; three domains

There is no runtime hi.plugin mega-vtable. Plugins compose into the game module, then call typed SDK surfaces:

DomainSurfaceThreadingLifetime
Editorhi.editorEditor main / UI thread onlyGame-module generation; chrome frozen after freezeChrome
Gameplayhikari_game + lifecycle Plugin + hi.settingsGame thread (and existing component rules)Process / scene as today
Renderinghi.render()Register on game thread (onLaunch); frame callbacks on render threadRegistration for module generation; frame resources frame-scoped
text
                    Hikari Plugin API  (product name + docs)
                    ┌─────────────────────────────────────┐
                    │  Source composition (unchanged)     │
                    │  hikari.plugins.json / plugin.json  │
                    └──────────────┬──────────────────────┘
           ┌───────────────────────┼───────────────────────┐
           ▼                       ▼                       ▼
     hi.editor              hikari_game               hi.render
      Editor                 Gameplay                 Rendering

Composition in one page

  1. Enable a package in project hikari.plugins.json.
  2. Package hikari.plugin.json declares modules by role (runtime, editor, developer, third_party).
  3. Build composes Zig sources into the game module; native link and staging inputs come from generated hikari-plugin-artifacts.json only.
  4. No runtime Zig package loader.

Full steps: Plugins guide. Invariants: plugin-system design.

Choose your domain

NeedStart here
Actions, menus, panels, scene editsEditor · UI and editor SDK
Entities, tick, libraries, settings, open componentsGameplay
Temporal reconstruction; typed post-process slotsRendering · Rendering system

Editor

Chrome

In an editor role module, export registerEditor and always call api.check() first:

zig
const hi = @import("hikari_game");
const editor = hi.editor;

pub fn registerEditor(api: *const editor.Api) void {
    api.check() catch return;
    // registerAction / menus / toolbar / panels / surfaces …
}

ActionDesc.invoke always receives host-filled ActionContext (playing, has_project, can_author, …). There is no register_command / CommandFn path.

Working sample: src/games/example/plugins/example_capability/editor/root.zig.

Scene transactions

Main-thread only. Use hi.editor.scene() → buffer-until-commit EditTransaction → shared host applyBatch (one undo label). Do not hold raw SceneDocument* / EditorApp*.

Builtin-only sketch (matches the example plugin demo):

zig
fn onCreateDemoLight(user: ?*anyopaque, ctx: *const editor.ActionContext) callconv(.c) void {
    _ = user;
    if (ctx.playing or !ctx.has_project or !ctx.can_author) return;
    const scene = editor.scene() orelse return;

    var edit = scene.beginEdit(.{
        .label = "Create Demo Light",
        .expected_revision = scene.revision(),
    }) catch return;
    defer edit.cancel(); // no-op after successful commit

    const actor = edit.createActor(.{
        .name = "Demo Light",
        .archetype = "light", // engine ids are lowercase ("light", "empty", …)
    }) catch return;

    edit.setField(actor, "light.intensity", 0, .{ .number = 5.0 }) catch return;
    edit.setTransform(actor, .{ .position = .{ 0, 3, 0 } }) catch return;
    edit.commit() catch return;

    // Selection is not an undoable document op — post-commit side effect only.
    scene.setSelection(&.{actor}) catch {};
}
RuleBehavior
Live documentUnchanged until successful commit
OpsBuffered until commit; max 64 (shared with MCP)
Mid-tx readsSee committed state only
setSelectionPost-commit side effect; not in the undo stack
Archetype idsCatalog ids ("light"), never UI labels ("Light")

Shared mutation plane with MCP: Plugin API design §3.1, Editor MCP.

Gameplay

Plugins use the same hikari_game surface as project game code:

  • Lifecycle methods on Plugin (create / destroy / onLaunch / onTick / scene hooks) via compose glue — see Lifecycle hooks.
  • Entities and components under runtime modules (entities / components paths on the runtime module).
  • Explicit native shared libraries via hi.shared_lib when declared as stage-only middleware.
  • Optional compile-time deps via hikari_plugin_options.
  • Package settings via hi.settings (below).

Working open-component sample: example_capability registers example_marker (attachable) with document-backed inspector fields (mutation plane / undo). Canonical skeleton: plugins/_template/.

Package settings (hi.settings)

Project-owned key/value storage for enabled packages. Does not dirty hikari.project.json.

ItemValue
Path<project>/.engine/plugin-settings/<package_id>.json
APIhi.settings — not hi.plugin_* (no service bus)
ValuesJSON object of bool / number / string keys
BoundaryTyped host API; plugin code never receives filesystem access
zig
const package_id = "com.hikari.example_capability";
const radius = hi.settings.getFloat(package_id, "radius") orelse 5.0;
const enabled = hi.settings.getBool(package_id, "enabled_feature") orelse true;
try hi.settings.setFloat(package_id, "radius", 8.0);
try hi.settings.setBool(package_id, "enabled_feature", true);
// Strings: getStringAlloc(allocator, …) returns owned copy; setString writes.

Optional schema in the package manifest (drives Project Settings → Plugins package-settings rows when present):

json
"settings": {
  "schema_version": 1,
  "keys": [
    { "key": "enabled_feature", "type": "bool", "default": true, "label": "Enable feature" },
    { "key": "radius", "type": "float", "default": 5.0, "min": 0.1, "max": 100.0 }
  ]
}

Runtime read/write does not require the schema. Schema keys and file-only keys under .engine/plugin-settings/ both surface in Project Settings (immediate write; does not dirty Save / hikari.project.json).

Open components and the inspector

Attachable components from game/plugin *_component.zig files register by name. Authors Add Component by registry name; defineComponent reflects supported Data fields into the same normalized authoring schema used by engine components. Data.metadata.properties supplies optional labels, tooltips, ranges, ordering, visibility, and widget overrides. Numeric scalars/vectors of any supported arity, booleans, colors, text, ordinary/optional enums, and typed resources all use the generic inspector path. Resource fields share picker, typed-path, remove, and drag/drop behavior. Runtime field offsets remain a separate generated table for fast live access.

  • One write path (undoable): inspector rows, hi.editor.scene(), and MCP set_field all use component.field paths through the shared mutation plane (scene_mutation → document payload). No live-column / localScalar poke for open-component fields.
  • Live world: document apply re-spawns the actor so decode reloads the new payload; save also runs syncOpenComponentPayloads as a safety net for any other column drift.
  • Undo: begin/write/commit on inspector widgets joins one history transaction (same as builtins).
  • Policy: hidden, read_only, field kind/arity, enum tags, and numeric bounds are enforced at the shared mutation boundary, not only by disabled inspector controls.
  • Runtime cost: schema rows are static per component declaration. There is no per-instance property storage, allocation, lookup map, or tick work; standalone gameplay still uses typed rows and generated offset accessors.

Automated place → edit → save for open components: document tests in editor/scene_document/tests.zig (place edit save open component document round-trip); binding undo in editor/property_binding.zig.

Typed plugin-to-plugin dependencies

Already legal via module deps + @import. Rules:

RuleDetail
Import nameDependents @import the module import name from hikari.plugin.json (e.g. hikari_plugin_example), never package id strings at runtime
Optional"optional": true on a dep + compile-time hikari_plugin_options.enabled("…")
Visibility"public": true|false follows the resolver — private deps are not re-exported
RolesRuntime may depend on runtime / third_party only; editor may depend on runtime / editor / third_party

Package id is for enablement, settings storage, and packaging — not a runtime service lookup key.

Rendering

ExtensionStatusRegister
Temporal reconstructionShipped (native escape hatch)hi.render().registerReconstructionProvider in onLaunch
Typed post-process passesShipped (P1 v1) — five fixed slots + host tinthi.render().registerPostProcessPass in onLaunch

Samples: metalfx_temporal (macOS), fsr4 (Windows), host_tint (after_tonemap host fullscreen tint). Details: dual resolution, plugin post-process.

Threading and lifetime

Call siteThreadNotes
registerEditor, action invoke, panel update, SceneApi / EditTransactionEditor UI / mainNever from game or render thread
Lifecycle onTick / scene hooksGameSame rules as game subsystems
registerReconstructionProvider / registerPostProcessPassGame (onLaunch)
Reconstruction / PP frame callbacksRenderFrame-scoped opaque resources only
show_message / setControl*Editor UICopy api.* during registerEditor; do not keep the registration stack pointer

Game-module reload rebinds callbacks; structural chrome does not change after freezeChrome.

Related docs

DocRole
PluginsComposition, manifests, lifecycle, surfaces table
Plugin API designNormative contracts and key decisions
Plugin system designSource composition invariants
UI and editorhi.editor ABI table
RenderingReconstruction and PP
PreviousPluginsNext Data-driven content, JSON, and paths

Documentation follows the current engine checkout.

Snapshot cc148c75Source docs/guides/plugin-api.md
On this pageOne product name; three domainsComposition in one pageChoose your domainEditorChromeScene transactionsGameplayPackage settings (hi.settings)Open components and the inspectorTyped plugin-to-plugin dependenciesRenderingThreading and lifetimeRelated docs Back to top