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 (your index.html with the shot runner in place of its module scripts), and accepts recordings from F8. It also dedupes three and koota, so a game that uses kiln from a checkout (file: or linked) still has one copy of each; two copies of three make WebGPU drop the game's lights ("Light node not found"). 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"]