Skip to content

Configuration ​

kiln.config.ts at the game root default-exports the config. defineConfig only adds types:

ts
import { defineConfig } from 'kiln'

export default defineConfig({
  fixedHz: 60,
  seed: 1,
  mode: '3d',
  hotReload: 'keep',
})

src/main.ts passes it to createApp(config). Every field is optional.

FieldDefault
fixedHz60Fixed simulation rate; time.step is 1 / fixedHz
seed0Seed for the Rng resource
mode'3d''2d': orthographic camera, z as draw order, Z-only rotation, Rapier 2D
hotReload'keep''keep', 'snapshot' or false. See Hot reload
devon unless NODE_ENV=productionDev guards and read-only proxies
maxFrameDt0.25Frame delta clamp in seconds, so a background tab doesn't spiral
maxFixedSteps5Most fixed steps per frame
vsynctruePace browser frames with requestAnimationFrame
maxFps0Cap on browser frames per second; 0 means no cap
recordtrueRecord dev browser sessions so F8 or a crash saves a recording
entry'src/main.ts'The module whose default export builds the app

vsync and maxFps can change at runtime through app.frameRate, and the debug panel has controls for both. With vsync: false frames run back to back, keeping at most one frame of GPU work in flight, which is useful for measuring throughput.

Overrides ​

The CLI and the scenario runner override config without editing the game: kiln run --seed 3, scenario(name, fn, { seed: 3, config: { fixedHz: 30 } }), and replays use the recording's loop config. Overrides apply inside createApp, so they reach the app however main.ts builds it.

Vite ​

vite.config.ts adds the kiln() plugin:

ts
import { defineConfig } from 'vite'
import { kiln } from 'kiln/vite'

export default defineConfig({ plugins: [kiln()] })

It applies the names transform, wires hot reload, serves the page kiln shot renders, and accepts recordings from F8. Pass kiln({ entry }) if the entry isn't src/main.ts.

Bun ​

bunfig.toml preloads the names transform for tests:

toml
[test]
preload = ["kiln/preload"]