API reference Download Markdown

Runtime

The host drives cartridge callbacks and manages mounted levels.

Game lifecycle

engine.Run(engine.Hooks{...}) registers the cartridge's callbacks. The host owns the loop and advances the simulation at 60 Hz.

Scroll to see all columns.

HookCalled for
OnStartGame initialization
OnUpdate(Frame)Each simulation tick
OnKeyDown, OnKeyUp, OnPointerInput delivery
OnInputResetClearing held input after focus or routing changes
OnLevelReady, OnLevelFailed, OnLevelCancelled, OnLevelReleasedLevel operation results
OnStopCartridge shutdown

Frame.Number identifies the tick. Counting ticks measures simulation time; it excludes time while the game is suspended. Hooks run on the game loop, so blocking a callback also blocks updates.

Dispose screen-owned entities, subscriptions and playback when leaving that screen. OnStop only runs at cartridge shutdown.

Complete game · UI demo lifecycle

Entities and components

Typed entities expose the components they support. For example, Shape2D has transform, fill, alpha and drawing-layer methods.

func marker(game *engine.Game) *engine.Shape2D {
    dot := game.NewShape2D(engine.Circle(16), engine.NewVec2(240, 180))
    dot.SetFill(engine.RGBA(110, 231, 204, 255))
    dot.SetLayer(10)
    return dot
}

Scroll to see all columns.

OperationEffect
A setter such as SetFill or TranslateSends a change to the host
Direct assignment to a component fieldChanges only the local Go value
SetAlpha(0)Hides drawing and retains the entity
Destroy()Removes the entity

Keep references to entities you control. Destroy them when their owning screen closes. Level release removes its cameras, actors and resources; discard those references too. Entity IDs are runtime identities and do not belong in saves.

Transform 2D · Transform 3D · Media lab source

Levels and resources

Level operations are asynchronous. A RequestID identifies an operation; a LevelHandle identifies mounted content.

Scroll to see all columns.

Call or hookContract
LoadLevel(name, onReady)Loads a typed level reference and calls its ready callback
RequestLevel(name)Low-level string request; ready results use the global hook
OnLevelReady(LevelReady)Receives ready results from requests without a local callback
ReleaseLevel(handle)Returns the release operation's request ID
OnLevelReleased(LevelReleased)Confirms release
OnLevelFailed(LevelFailed)Supplies the failed request ID and diagnostic
OnLevelCancelledConfirms cancellation

Use a generated level reference and a request-local success callback:

func loadRoom(game *engine.Game, onReady func(engine.LevelReady)) engine.RequestID {
    return game.LoadLevel(assets.LevelRoom, onReady)
}

The CLI generates .karty/assets/levels.go from [level].name in each levels/*/level.toml; levels.room becomes assets.LevelRoom, while art.opening becomes assets.LevelArtOpening. Identifier collisions fail the build with both names. Source folder names do not change the logical reference.

Karty delivers success once to the registered callback; OnLevelFailed and OnLevelCancelled remain global hooks. Keep a returned request ID when you need cancellation or failure routing. Dynamic names can use an explicit engine.LevelName(name) conversion, or the existing RequestLevel(name) API.

For global failure and release hooks, match the event's RequestID to the pending operation before changing state. Create cameras and resolve level textures after readiness.

Release removes the level's cameras, actors, effects and resources. Discard their references when release completes. Project-level menu UI can outlive a level.

Admission bounds and cancellation semantics are in .karty/docs/level-lifecycle.md.

When the installed runtime supports prepared chunk streams, the CLI packages final render batches, material surface slots and liquid rest geometry at build time. The builder checks geometry and lightmap coverage before publishing the chunk checksum; the shipped file contains prepared streams without a duplicate semantic mesh. The host checks file identity and bounded stream structure, then uploads it without proving geometry again, rebuilding faces or tessellating liquid after download. Waves still animate in the vertex shader. The builder keeps output compatible with the installed runtime. Shader compilation and GPU uploads still need a live graphics device; the complete encoded chunk budget includes the prepared streams.

A render chunk downloads as one immutable file. With baked lighting enabled, its geometry, lighting layout and completed default lightmap travel together. Its checksum covers all of these resources; the host publishes the chunk after the complete unit is validated and uploaded. Shared material atlases belong to the root level, which is ready before its chunks. For time-of-day or weather lightmaps, the initial chunk embeds its default image. Other baked conditions are optional image modules loaded when selected. A replacement becomes active after its complete image is validated and uploaded.

Room setup · UI demo loading states

World bodies and queries

Use one World and Body API for ordinary 2D sprites and flat 3D actors. Rendering does not choose the physics mode. WorldPlanar keeps bodies on XY; WorldSpatial provides gravity and vertical motion for rigid boxes and upright CapsuleCollider characters. The shared character motor handles slopes and ordinary steps; the controllers-3d package supplies first-person input and mobile controls. Automatic navigation remains planar. All coordinates here use metres.

func character(game *engine.Game, level engine.LevelHandle, entity engine.EntityRef) (*engine.Body, error) {
    world := game.World(level)
    if err := world.Configure(engine.WorldSettings{
        Mode: engine.WorldPlanar,
        Min: engine.Vec2{}, Max: engine.Vec2{X: 20, Y: 10}, CellSize: .5,
    }); err != nil { return nil, err }
    return world.AttachBody(entity, engine.CharacterBodySettings())
}

func inspect(world engine.World, player *engine.Body) (engine.RequestID, error) {
    return world.Raycast(engine.RaycastQuery{
        Origin: player.Position(), Direction: engine.Vec3{X: 1},
        Distance: 10, Exclude: player.Ref(),
    })
}

func route(world engine.World, player *engine.Body) (engine.RequestID, error) {
    return world.FindPath(player, engine.Vec3{X: 8, Y: 5})
}

Use BoxCollider or EllipseCollider in BodySettings; presets provide useful defaults. body.Move(direction, speed) controls a planar character. body.SetHorizontalVelocity controls a spatial box while preserving falling velocity. body.Teleport changes a physical origin deliberately; body.Detach returns pose control to the entity. World.Navigate starts planar following; World.FindPath returns route data and also accepts an independent NavigationAgent without requiring a body.

Raycasts are read-only. OnRaycastResult distinguishes HitWorld, HitEntity and HitMiss; entity hits carry a lifetime-checked EntityRef. OverlapBox returns HitsWorld and a borrowed entity-reference slice through OnOverlapResult. Copy borrowed results to retain them after the update. ApplyRayImpulse and Explode are separate force-producing operations. See your project's .karty/docs/api.md for limits and exact signatures.