Kagari
Type to search documentation.

Syntax

Source Files and Modules

A game is a tree of modules, as in Rust. Each module is a source file, an inline mod { … } block, or a block with items in a function body. This chapter covers files, the module tree, paths, visibility, use, the standard library and its prelude, and assets.

.kis and .kix

ExtensionContains
.kisThe core language
.kixThe core language plus forms: s!(…), choice!{…}, t![…] and the UI forms such as button!{…}

Forms are fixed syntax that stands for ordinary expressions, so everything written in a .kix file can also be written, more verbosely, in a .kis file. They are not macros: Kagari has no macros, and the set of forms is fixed by the language. A form in a .kis file is a syntax error (E0001).

The extension decides the syntax only. It never decides what code may do: that is the job of function kinds. Any kind of function may appear in either file. By convention the story and UI are written in .kix files, and code without forms (types, constants, helpers, renders) in .kis files.

The root file

The compiler starts from one root file: project.entry in kagari.toml, by default src/main.kis (see Projects). It must be a .kis or .kix file (E1001). The root file is the module crate, and it must declare fn main(), which describes the game system (E7001). Every other file is reached through mod declarations; a file that no mod declares is not part of the game.

All files of a game are compiled together into one .kagari file.

Modules

kis
// src/main.kix: the root module
mod characters; // a file module
mod story;
mod ui;
mod helpers { // an inline module
pub const fn twice(x: u32) -> u32 { x * 2 }
}
use characters::{HANAKO, AZUSA};
  • mod name; declares a file module. Its file is looked up as described below.
  • mod name { … } declares an inline module with the items between the braces.
  • A function body or block that contains items is a nameless block scope; see Items in blocks. A file module cannot be declared in a block (E1005).
  • Items may be used before their declaration.

Where the file of a module is

For mod name; the compiler looks in the child directory of the declaring module:

  • in the root file, or in a file named mod.kis or mod.kix, the child directory is the directory of that file;
  • in any other file foo.kis or foo.kix, it is the directory foo/ next to the file;
  • inside an inline module mod a { … }, the name of each enclosing inline module is appended: a/.

In the child directory, these four candidates are tried: name.kis, name.kix, name/mod.kis and name/mod.kix. Exactly one must exist. None is E1002, more than one is E1003, and a file that is already loaded as another module is E1004.

crate src/main.kix (root) crate::characters src/characters.kis crate::story src/story.kix crate::ui src/ui/mod.kis crate::story::day1 src/story/day1.kix crate::ui::menu src/ui/menu.kis mod characters; mod story; mod ui; mod day1; mod menu;

Here story.kix declares mod day1;, so its child directory is src/story/. ui/mod.kis is the file of ui itself, so its child directory is src/ui/.

Paths

A path names an item: segments separated by ::, such as std::screen::size or Route::Hanako. A path may start with one of these keywords:

StartRefers to
cratethe root module of the game
selfthe current module
superthe parent module; super::super goes up two levels. Going above the root is E1016
Selfthe type of the current impl (see Methods)

A path cannot start with ::. Generic arguments are written in types as Vec<u8> and in expressions with the turbofish Vec::<u8>::new().

The first segment of a plain path is looked up in this order:

  1. the enclosing block scopes, innermost first;
  2. the items and imports of the current module;
  3. std, the standard library, unless the module defines or imports its own std;
  4. the prelude;
  5. the primitive types bool, Int and the Rust integer types.

The following segments are looked up in the module, enum or type that the path names so far: items of a module, variants of an enum, and associated functions and constants of a type. A name that is not found is E1016.

Namespaces

Each module has two namespaces:

NamespaceHolds
typesmodules, structs, enums, type aliases, traits
valuesfunctions, constants, statics, and the constructors of tuple and unit structs

A name may be used once in each namespace. A second definition in the same namespace is E1014. Because the namespaces are separate, a constant and a module may share a name: AZUSA is an entity id and AZUSA::CLOSE_EYE_SMILE one of her images.

kis
pub const AZUSA: EntityId = EntityId(217);
pub mod AZUSA {
pub const CLOSE_EYE_SMILE: ImageAsset = asset("azusa/close_eye_smile.png");
}

Visibility

An item is private unless it is marked pub. A private item is visible in the module that defines it and in all modules nested in that module. Naming a private item from anywhere else is E1017. There are no restricted forms such as pub(crate).

  • Associated functions and constants of an impl follow the same rule: they are private unless marked pub.
  • Enum variants are as visible as their enum.
  • Struct fields may be marked pub, as in the standard library. The compiler does not check field visibility yet: every field can be read and written wherever the struct is visible.

use declarations

use brings names into the current module or block:

FormEffect
use std::screen::size;imports size
use characters::{HANAKO, AZUSA};imports several names; groups may nest
use crate::widen as w;imports under another name
use crate::widen as _;imports without binding a name
use Route::*;glob import: every visible name of a module, or every variant of an enum
pub use self::text::Text;re-exports: the name becomes part of this module for others
  • An import takes the item from every namespace that has the name: use characters::AZUSA; imports both the constant and the module.
  • Use paths have no generic arguments.
  • A name defined in the module, or imported by name, takes precedence over a glob import.
  • When two glob imports supply different items for one name, using that name is E1018.
  • An import that leads nowhere is E1015. Importing a private item is E1017.

The standard library

The standard library is the module std. Its items are described in the STD Reference:

ModuleContentsSTD Reference
stdString, Vec, Range, Rat, Decimal, Option, Result; the traits Eq, Integer, RandChoose and Asset; the asset types, asset and system_fontstd
std::preludethe names imported into every modulestd::prelude
std::timeDuration (whole milliseconds), ms, secsstd::time
std::narrativesteps (Step, push_step, Say, Choice), rng_choose, meta fields, the Narrative handlestd::narrative
std::narrative::textText, text effects, ToText, text stylesstd::narrative::text
std::screenthe screen: entities, properties, layers, Hud, camera, filtersstd::screen
std::screen::animtweens, transitions, motions, easingsstd::screen::anim
std::screen::audiomusic and ambience channels, sounds, audio fadesstd::screen::audio
std::stagecharacters, slots and side faces on top of std::screenstd::stage
std::systemSystem, settings, seen sets, bindingsstd::system
std::system::navnavigation between modes, starting and loading the storystd::system::nav
std::system::savessave slots and save informationstd::system::saves
std::uiUI elements, signals, the functions behind the UI formsstd::ui
std::ui::stylethe looks of UI controlsstd::ui::style

The prelude

The prelude is imported into every module. A module’s own items and imports take precedence over it. It contains:

FromNames
stdString, Vec, Rat, Decimal, Range, Option, Result, Eq, Integer, RandChoose, ImageAsset, AudioAsset, VoiceAsset, VideoAsset, FontAsset, asset, system_font; the variants None, Some, Ok, Err
std::timeDuration, ms, secs
std::screenScreen, Render, EntityId, Part, Point, Offset, Size, Anchor, Color, Plane, Visibility, point, offset, rgb, hex, BLACK, WHITE, TRANSPARENT
std::screen::animEasing, Tween, Transition, Motion, tween, instant, cut, fade, rule, wipe, through, motion, jump, nod, shake, quake
std::screen::audiolooped, sound, crossfade
std::stageCharacter, character
std::narrativeText, TextEffect, Say, Choice, ChoiceArm, push_step, rng_choose, Meta, meta, Narrative
std::narrative::textemphasis, dots, color_text, bold, pause, click_wait, mark
std::systemnav, Setting, setting, System, Settings, Volume, volume, RootPage, Overlay, GameOverlay, Dialog
std::uisignal, Element

Everything else is imported with use, for example use std::screen::size;.

Assets

An asset is a file of the game, bound to a constant:

kis
const CG_001: ImageAsset = asset("cg/cg_001.png");
const AZUSA_0072: VoiceAsset = asset("voice/azusa_0072.ogg");
kis
#[builtin] pub const fn asset<A: Asset>(path: narrative::Text) -> A;

These types implement the trait Asset:

TypeFile
ImageAssetan image: PNG, JPEG or WebP
AudioAssetmusic or a sound
VoiceAsseta voice file, bound to a line
VideoAsseta movie
FontAsseta font
ColorShader<A>, MixShader<A>, TextureShader<A>, MaskShader<A>, LocalMotionShader<A>, TransitionalMotionShader<A>a WGSL shader (.wgsl) of one role, with arguments of type A; see std::screen::shader

The type of the constant chooses A; asset::<ImageAsset>("…") names it explicitly. The path follows these rules:

  • It is a string literal (E2028) and not empty (E1028).
  • It is relative to the asset directory: [assets] dir in kagari.toml, by default assets in the project directory. Both / and \ separate directories.
  • It stays inside the asset directory: no absolute path, no drive (C:), and no .. that leaves it (E1029).
  • It does not enter the top-level directories system-fonts and std, which are reserved for system fonts and the standard library’s own assets (E1032).
  • The file exists when the game is compiled (E1027), so a missing file is a compile error, not a failure during play.

A file asset type is a finite type: its values are exactly the asset constants of the program. The story may therefore choose between assets at runtime, for example a voice line by route. Two asset values are equal when they name the same file. The shader types are not finite; renders use them.

System fonts

A font installed on the building machine can be bound without copying it into the asset directory:

kis
const GOTHIC: FontAsset = system_font("msgothic.ttc");
kis
#[builtin] pub const fn system_font(path: narrative::Text) -> FontAsset;

The path is a non-empty string literal relative to a system font directory and must stay inside it. The directories are searched in this order, and the first one that has the file wins:

PlatformDirectories
Windows%LOCALAPPDATA%\Microsoft\Windows\Fonts, %WINDIR%\Fonts
macOS~/Library/Fonts, /Library/Fonts, /System/Library/Fonts
Linux and others$XDG_DATA_HOME/fonts (default ~/.local/share/fonts), ~/.fonts, then fonts in each directory of $XDG_DATA_DIRS (default /usr/local/share:/usr/share)

A font that is not found is E1030. [assets] system-fonts = false in kagari.toml forbids system fonts (E1031). The standard library ships no fonts, and System::font, the default font, is required.

Assets in the build

kagari build packs every asset file the program uses into archives (.kiarchive) next to the .kagari file. Each file is stored under its path relative to the asset directory, with / separators, and goes into the archive named after the first directory of that path:

Asset pathArchive
bg/classroom.pngbg.kiarchive
cg/cg_001.pngcg.kiarchive
logo.png (directly in the asset directory)data.kiarchive
a system font msgothic.ttcsystem-fonts.kiarchive, as system-fonts/msgothic.ttc
a std shader such as std::shaders::color::BLURstd.kiarchive, as std/shaders/color/blur.wgsl

Grouping files by top-level directory therefore decides how the game’s data is split into archives. See The kagari CLI for the build output.

Last updated Oct 29, 2026