Skip to content
hikari
RenderingEditorAIToolchainDocumentation
Download

© 2026 Flying Rat Studio.
All rights reserved.

Explore the engineDocumentationContributorsDownloadLicenseBack to top
Documentation / Systems
Browse docs
Overview
Tutorials17
OverviewPlay, Edit, and scenesFirst UI documentFirst game projectFirst entityComponent properties and the inspectorFirst mesh and materialFirst physics body and triggerFirst character controllerFirst session servicesFirst UIFirst input actionFirst messagesFirst runtime spawnFirst motionFirst skeletal animationAssets in Play (soft refs and hot reload)Dynamic editor recompile
Guides25
OverviewWorking in the editorDevelopment guideGameplay APIActor and component lifecycleThreads, jobs, events, and routinesGameplay AI tasksGame AI: perception and movement playgroundBrain assetsBrain execution for swarm populationsLocalizationGame cursorsChoosing component storageBuild and packagingProject filePluginsHikari Plugin APIData-driven content, JSON, and pathsScripting with KawaShader authoringChoosing and scaling shadowsTime of dayUser interfaceUI layoutUI widgetsMigration from Unity / Unreal
Systems32
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 governorPipeline compilationSkeletal animationGPU particlesVisual ZonesVolumetric mediaInputAudioPhysicsMotion KitTemporal KitUI documentsAssets and ShinraPrefabsAsset residencyAsset formats (Shinra pipeline)Document schemasUI and editorEditor asset hot reloadEditor Project Selector
Language reference3
OverviewAkari GPU ABIAkari language referenceKawa language reference
Engine overview
Start exploring
  • No matching sections. Try fewer words or another topic.
NavigateEnter Openesc Close
Overview
Tutorials17
OverviewPlay, Edit, and scenesFirst UI documentFirst game projectFirst entityComponent properties and the inspectorFirst mesh and materialFirst physics body and triggerFirst character controllerFirst session servicesFirst UIFirst input actionFirst messagesFirst runtime spawnFirst motionFirst skeletal animationAssets in Play (soft refs and hot reload)Dynamic editor recompile
Guides25
OverviewWorking in the editorDevelopment guideGameplay APIActor and component lifecycleThreads, jobs, events, and routinesGameplay AI tasksGame AI: perception and movement playgroundBrain assetsBrain execution for swarm populationsLocalizationGame cursorsChoosing component storageBuild and packagingProject filePluginsHikari Plugin APIData-driven content, JSON, and pathsScripting with KawaShader authoringChoosing and scaling shadowsTime of dayUser interfaceUI layoutUI widgetsMigration from Unity / Unreal
Systems32
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 governorPipeline compilationSkeletal animationGPU particlesVisual ZonesVolumetric mediaInputAudioPhysicsMotion KitTemporal KitUI documentsAssets and ShinraPrefabsAsset residencyAsset formats (Shinra pipeline)Document schemasUI and editorEditor asset hot reloadEditor Project Selector
Language reference3
OverviewAkari GPU ABIAkari language referenceKawa language reference
Engine overview
Systems5 min read

Temporal Kit

On this page
On this pageGoalNon-goalsWhere it sitsCore typesUsageSingle delay / cooldownMulti-step routine (Flow)Timing and lifetime contractsValidationFiles Back to top

Shared time utilities for gameplay, session services, and host clocks. Implementation lives under src/hikari/sdk/src/temporal/. Exports: hi.temporal plus aliases hi.Timer / hi.Interval / hi.Cooldown / hi.Stopwatch / hi.Deadline / hi.Flow / hi.Timers / hi.nowNs / hi.wallTimeMs / hi.wallTimeSec / hi.wallTimeNs (and the *Io variants).

Goal

Delays, rate limits, intervals, multi-step routines, and clock helpers under hot-path constraints: plain values, no per-frame heap, poll edges — no callback graphs on the hot path.

Non-goals

  • Managing suspended call stacks. Use hi.Routine for engine-scheduled stackful gameplay routines; Flow / Timer remain small caller-driven helpers.
  • Host-mediated scene-load waits (use world.sceneLoadSnapshot / readiness stages).
  • Calendar / timezone / wall-clock scheduling.
  • Replacing Motion Kit duration/delay fields on tweens.

Timer / Cooldown / Interval cover single delays and rate limits. Flow is the multi-step story: explicit phases on caller-owned state, short update steps, no hidden suspension or heap iterators. Coming from another engine's coroutine model? See Migration.

Where it sits

text
entity / GameSubsystem state
        │
        ▼
   temporal.*   (Timer, Interval, Cooldown, Flow, Stopwatch, Deadline, Timers bus)
        │
        └─► tick with TickContext.dt / total_time each frame

Wall/mono “now” lives in sdk/src/time.zig (nowNs, wallTimeMs / Sec / Ns, Io variants; backed by std.Io from the hikari_std module). Temporal re-exports those as hi.nowNs / hi.wallTimeMs / … and as hi.temporal.nowNs. Session sim dt stays session-owned Time — do not call Zig std.Io.Timestamp from product code.

Core types

TypeRole
TimerOne-shot countdown; after, tick, pulse (finish edge)
IntervalRepeating period; tick → fire count (catch-up)
CooldownRate limit; ready / trigger / tick
FlowMulti-step routine; tick then wait / waitUntil / waitFrame / onEnter / advance
StopwatchAccumulator; start/pause/unpause
DeadlineAbsolute threshold vs total_time
TimersDense keyed one-shot bus

Usage

Single delay / cooldown

zig
// Inside component Data. Runtime state is not authored or serialized.
state: struct {
    door_timer: hi.Timer = .after(0.5),
    fire_cd: hi.Cooldown = .init(0.35),
} = .{},

pub fn update(self: *@This(), _: hi.ActorContext, ctx: *const hi.TickContext) void {
    if (self.state.door_timer.pulse(ctx.dt)) { /* open */ }
    self.state.fire_cd.tick(ctx.dt);
    if (hi.world().actionPressed("fire") and self.state.fire_cd.trigger()) { /* shoot */ }
}

Multi-step routine (Flow)

zig
const Phase = enum(u32) { windup, sound, wait_player, open, _ };

state: struct { flow: hi.Flow = .begin() } = .{},

pub fn update(self: *@This(), actor: hi.ActorContext, ctx: *const hi.TickContext) void {
    const f = &self.state.flow;
    if (!f.running()) return;
    f.tick(ctx.dt);

    _ = f.wait(@intFromEnum(Phase.windup), 0.5);
    if (f.onEnter(@intFromEnum(Phase.sound))) {
        // play sound
        f.advance();
    }
    // Guard expensive predicates: ordinary Zig arguments evaluate eagerly.
    if (f.isAt(@intFromEnum(Phase.wait_player))) {
        _ = f.waitUntil(@intFromEnum(Phase.wait_player), playerNear(actor));
    }
    if (f.onEnter(@intFromEnum(Phase.open))) {
        // open door
        f.stop();
    }
}

Keyed session delays: hi.Timers (after / tick / pulse / cancel).

Timing and lifetime contracts

Call flow.tick(dt) exactly once per owning update, before polling. wait(step, seconds) no longer accepts dt. A flow belongs to one clock and one update lane; do not drive the same value from both update and fixedUpdate.

  • The first active positive-duration wait consumes the tick's elapsed-time budget, even if it does not finish. Repeated polls and subsequent waits cannot spend it again. Excess elapsed time is discarded, not carried to the next phase. A newly armed first wait may consume the current tick; this is a tick-budget helper, not an absolute-deadline scheduler.
  • Zero/negative/non-finite durations complete immediately. They do not consume elapsed time. Positive waits capture their duration when armed; changing the argument while waiting does not retime them.
  • onEnter can perform setup before a wait on the same phase. Each phase uses one wait kind; changing between timed/frame/condition waits requires goto or advance.
  • waitFrame completes only after a later tick, regardless of repeated polling. It means the next owner update, which may be throttled, and is not a rendered-frame fence. tick(0) still establishes a new owner update; it freezes positive timed waits but does not suppress frame waits or conditions. Skip the entire flow update to pause all progress. Ordinary gameplay callbacks already respect the World simulation gate.
  • advance from the last usable phase stops. The reserved Flow.stopped value cannot execute actions or waits. start, reset, and goto reset phase entry and pending waits, but do not replenish elapsed time already spent this tick.
  • Flow does not manage owner lifetime or run cleanup. Components must author disable/re-enable policy and resource cleanup in lifecycle hooks. Never retain ActorContext or borrowed sibling pointers in flow state. Loops over immediate phases are caller-owned and must be bounded.

Timer.pulse(dt) reports an active-to-finished edge only: an immediate timer is already done and never pulses. Use done() for level readiness. The keyed Timers bus deliberately retains one completion pulse, including immediate timers, until consumed, replaced, or cancelled. Timers.active() counts running countdowns, not pending pulses. Completed slots retain their keys until release/cancel; both operations recycle without allocating. after may allocate at capacity boundaries; reserve the maximum concurrent keys before hot-path use. Lookup and ticking remain linear in admitted slots; this is a small keyed table, use hi.Routine for engine-scheduled suspended routines.

Negative and non-finite deltas are ignored by countdowns, intervals, cooldowns, and stopwatches. Non-finite/negative durations normalize to zero. Interval clamps invalid or sub-epsilon periods to floatEps(f32), computes catch-up arithmetically, and retains a double-precision fractional remainder. Its count saturates at maxInt(u32) and discards excess whole periods; game code must still bound work performed for returned counts. Stopwatches saturate at the largest finite f32. Deadline expects a finite advancing absolute clock; its delay follows the duration normalization rule.

Validation

From src/hikari, run zig build test-temporal -Dgame-src=<absolute-game-src>. This suite is also included in test-sdk and test. It covers tick-budget reuse, immediate waits, repeated frame polling, phase/stop transitions, invalid time, interval catch-up, and timer allocation failures.

Files

text
src/hikari/sdk/src/temporal/
  temporal.zig   barrel
  clock.zig      unit helpers
  timer.zig      Timer, Interval, Cooldown, Stopwatch, Deadline
  flow.zig       Flow
  bus.zig        Timers
PreviousMotion KitNext UI documents

Documentation follows the current engine checkout.

Snapshot 86f12d68Source docs/systems/temporal-kit.md
On this pageGoalNon-goalsWhere it sitsCore typesUsageSingle delay / cooldownMulti-step routine (Flow)Timing and lifetime contractsValidationFiles Back to top