# NacatamalOn

> NacatamalOn is a 2D and 3D game engine for the web, written in TypeScript, built for the look of the consoles from SNES to GameCube. It draws with WebGPU where there is one and WebGL2 where there is not. Scenes and components are plain functions, and state comes from hooks: there are no classes to extend.

## How to read this file

- What almost every game uses is imported from `nacatamalon`, and a subject of its own from its own door (`nacatamalon/math`, `/pixels`, `/shader`, `/effects`, `/transitions`, `/files`). The optional physics live in `nacatamalon/physics2d` and `nacatamalon/physics3d`.
- `nacatamalon/headless` runs a game from Node or Bun with no browser (`createHeadlessGame`): press keys, run frames, look at where things are and what is on screen. For tests and scripts that check a game; a game never imports it.
- `nacatamalon/react` (from 0.1.9) puts a game in a React page, with the HUD written as React components over the canvas: `<Game>`, `useGame`, `useGameStore`, `useSignal`. Browser only; the engine's own `llms.txt` explains it.
- Every signature and example below comes from the engine's own documentation, and every example is compiled by the engine's tests.
- Engine version: 0.7.0.

## The lifecycle of a scene

Everything that happens to a scene from the moment it starts until it leaves, in the exact order the engine does it.

In NacatamalOn a scene is a function. This page tells what the engine does with it: when it runs
it, what happens every frame, what pausing it means, how you switch from one to another and what
gets cleaned up when it leaves. Every step is read from the engine's code, in the order it happens.

### The states of a scene

States of a scene:
- **Registered**: a name, not started
- **Being born**: its body runs once
- **Held**: loading, not seen, not running
- **Running**: useUpdate every frame
- **Paused**: drawn, not updated
- **Stopped**: useCleanup runs
Transitions: Registered → Being born (start · launch · change); Being born → Running (createScene()); Being born → Held (change with transition); Held → Running (at the swap); Running ⇄ Paused (pause() / resume()); Running or Paused → Stopped (stop · change · destroy); Stopped → Registered (it can start again, from scratch).
A scene goes through these states. Only "Running" runs its useUpdate; "Running" and "Paused" are the ones on screen.

- **Registered.** Each key of the object you hand the game is the name of a scene. Registered
  means it exists and can be started by its name, nothing more.
- **Being born.** The engine runs the function's body **once**. That is where everything in the
  scene is declared.
- **Running.** Its `useUpdate` callbacks run every frame, it reacts to clicks and it is drawn.
- **Paused.** It is still drawn, but its `useUpdate` callbacks do not run and it does not react
  to clicks. It is what a pause menu over a frozen level needs.
- **Held.** Only happens to the scene coming in with a transition: it has been born and is
  loading what it asked for, but it is not seen, not updated and not clicked until the swap.
- **Stopped.** It leaves the list of running scenes, stops being drawn and its cleanups run,
  its `useCleanup` among them (further down, in "Leaving"). If it
  is started again, it starts from scratch: the body runs again.

### Being born: the body runs once

```ts
const Level = () => {
    // All of this runs ONCE, when the scene starts.
    const hero = useLoadTexture({ src: '/hero.png' });
    const player = createSprite({ texture: hero, transform: { x: 160, y: 120 } });
    const keys = useKeyboard();

    // This does not run now: it is registered, and the engine calls it every frame.
    useUpdate((dt) => {
        if (keys.isDown('ArrowRight')) player.transform.x += 120 * dt;
    });

    return createScene();
};
```

Three things about this moment matter:

- **It is synchronous.** The body cannot be `async` or wait for anything. A scene that returns a
  promise is refused with an error that says so.
- **Hooks are called here and only here.** `useUpdate`, `useKeyboard` or `useLoadTexture` attach
  to the scene being born. Outside the body (in a click, in a timer) no scene is being born. To
  create objects later, the body prepares a factory with `useSpawn`, and that factory can be
  called from a `useUpdate` or from a click.
- **Loading does not block.** `useLoadTexture` starts loading and hands the texture back at
  once, still empty. The scene starts anyway, and the sprite appears as soon as the image lands.
  For a progress bar there is `useLoadProgress`.

At the end, `createScene()` closes the scene with everything declared and the engine adds it to
the list of running scenes.

### First the object, then what is inside it

Every object is born before the ones it holds: its body runs, and only then are its children built.
In a scene written in code this hardly shows, because the body creates its children itself. In a
scene made as a document it always does: a behaviour on the root runs before the boxes under it
exist, and `findObject` does not find them.

That is what `useReady` is for. What you give it runs **once**, when everything being built with it
exists (the whole scene, children included) and before the first frame:

```ts
registerScript('match', () => {
    const root = useSelf();

    // Here, in the body, the scene's boxes do not exist yet.
    useReady(() => {
        const pitcher = findObject(root, 'Pitcher');   // here they do
        if (pitcher === null) return;
        useUpdate((dt) => {
            pitcher.transform!.x += 10 * dt;
        });
    });
});
```

- **It runs with its object as the one being built**, so a hook inside it (like that `useUpdate`)
  belongs to that object.
- **Parents' before children's**, each in the order it was registered.
- **For what is created later** (a `useSpawn` called from a `useUpdate` or a click), it runs as soon
  as that thing and everything inside it are built, before the call returns.

The game's first scene is the first key of the object, or the one you name:

```ts
const game = createGame('#app', { width: 320, height: 240 });
game({ Menu, Level });            // starts Menu
game({ Menu, Level }, 'Level');   // starts Level
```

### One frame, in order

Sixty times a second, in this order:

1. **Input** (also while paused): The clicks and touches that arrived since the last frame are handed to their listeners, and the gamepad is read. It happens here, at a moment the game chooses, and not whenever the browser fires them.
2. **Transition** (also while paused): If a scene change with a transition is under way, it moves on. It never freezes: the screen always ends up uncovered.
3. **Updates**: Each running scene, in the order it was launched, runs its useUpdate callbacks with dt. Paused and held scenes are skipped.
4. **Keyboard closes** (also while paused): justPressed stops being true. A press counts for a single frame, and in that frame it counts for everyone who asks.
5. **Removals** (also while paused): What was destroyed with destroy leaves now, while nobody is walking the scenes. It is never drawn one last time.
6. **Sound** (also while paused): The listener and the sounds placed in the world follow what is on screen.
7. **Drawing** (also while paused): Where everything is gets worked out and the frame is painted, scene by scene in the order they were launched.

With the game paused only step 3 is skipped: everything else carries on. That is why a pause menu can be seen and pressed.

`dt` is the seconds since the previous frame. It has two adjustments worth knowing:

- **It is capped at 0.25 seconds.** If the browser stalls (you switch tabs and come back), the
  game does not jump several seconds at once.
- **It is game time.** It is multiplied by `timeScale` (slow motion) and is `0` while the game is
  paused.

If a `useUpdate` throws, the rest of **its** scene's updates for that frame are lost, nothing
more: the other scenes carry on and the frame is drawn. The game reports it with its `error`
event.

### Inside the scene: who goes first

First the parent's useUpdate callbacks, in the order they were registered, then each child with its whole branch. Whatever is born during a frame does not move until the next one.

A scene is a tree of objects, walked from the top down: first the object, then its children,
branch by branch. So when the sword updates, the player it hangs from has already moved this
frame.

Two details that head off strange bugs:

- **What is born in a frame waits for the next.** A bullet does not travel on the frame it is
  fired, and something that creates objects cannot chain them all inside a single frame.
- **What is destroyed stops on the spot.** `destroy` stops updating it at once, even though it
  only leaves at the removals step. A bullet cannot report a second hit after the one that killed
  it.

### Pausing is not stopping

|   | Paused | Stopped |
| --- | --- | --- |
| Its `useUpdate` | does not run | does not run |
| Drawn | yes | no |
| Its clicks | does not react | does not react |
| `useCleanup` | does not run | runs, once |
| How it comes back | `resume()`, where it was | by starting it again, from scratch |

A pause menu is exactly both at once: the level paused, still on screen, and a menu scene
launched on top.

```ts
const Level = () => {
    const scene = useScene();
    const keys = useKeyboard();

    useUpdate(() => {
        if (keys.justPressed('Escape')) {
            scene.pause();              // the level freezes, but stays on screen
            scene.launch('PauseMenu');  // and the menu is drawn on top
        }
    });

    return createScene();
};

const PauseMenu = () => {
    const scene = useScene();
    const keys = useKeyboard();

    useUpdate(() => {
        if (keys.justPressed('Escape')) {
            scene.resume('Level');      // the level carries on where it was
            scene.stop();               // and the menu goes away
        }
    });

    return createScene();
};
```

The level cannot unpause itself: while paused, its `useUpdate` callbacks do not run. That is why
the menu does it.

### Changing scene

`scene.change('Level2')` replaces the scene that calls it with another one. There are two ways.

`scene.change('Level2')`:

|  | This frame | The next one |
| --- | --- | --- |
| Menu | Stops on the spot: no longer drawn, and its useCleanup runs |   |
| Level2 | Is born (its body runs) and is already drawn | Its first useUpdate |

The hard cut: the new scene is born first and the old one stops after it, in the same frame. So if the name does not exist, the error comes before anything stops.

`scene.change('Level2', { transition: fade(0.3) })`:

|  | Cover | Swap | Uncover |
| --- | --- | --- | --- |
| Menu | On screen and still updating | Stops: its useCleanup runs |   |
| Level2 | Held: already born and loading, but neither seen nor updated | Released: updates and is drawn, still under the cover | On screen; once uncovered, its useSceneTransitionEnd runs |

With a transition, the swap waits for the later of two things: the screen being covered, and everything the new scene asked to load having landed. The transition hides the cut and the loading as well.

Rules worth knowing:

- **Only the first call counts.** The condition that triggers a change usually stays true for
  several frames, and each of them would start another copy. The engine ignores the rest.
- **One change at a time.** While a transition is under way, another `change` is ignored with a
  warning, and so is a `launch` or a `stop` that asks for a transition of its own.
- **Changing to its own name restarts it**, always with a hard cut: covering it would need two
  copies of the same scene at once.
- **The transition runs on its own clock.** Neither pausing nor `timeScale` slows it down: slow
  motion cannot stretch a 300 ms fade into three seconds.
- **Behind the cover, the new scene's clock is stopped**, so a timer or a tween started in its
  `useReady` waits with it. A sound, music or song set to `autoplay` waits too, for the swap. What
  is started by hand keeps real time and does not wait: a `play()`, a vibration. Start those in
  `useSceneTransitionEnd(() => song.play())`, which runs once the screen is uncovered, before that
  frame's update. Without a transition it runs on the scene's first frame, so the
  scene does the same either way.
- **Music crosses with `fadeOut` and `fadeIn`.** A scene's music is cut when the scene leaves,
  unless it has a `fadeOut`: then it goes down over those seconds, heard after the scene has gone.
  With `fadeIn` on the new scene's `autoplay`, the two cross at the swap, under the cover.

### Several scenes at once

Running scenes are drawn in the order they were launched: the last one on top.

`scene.launch('Map')` starts another scene **alongside** the ones already running, without
stopping any. Each has its own updates, its own pause and its own cleanups, and the one launched
later is drawn on top.

```ts
const Level = () => {
    const scene = useScene();
    const keys = useKeyboard();
    let mapOpen = false;

    // M opens the map over the level, and closes it.
    useUpdate(() => {
        if (keys.justPressed('m')) {
            if (mapOpen) scene.stop('Map');
            else scene.launch('Map');
            mapOpen = !mapOpen;
        }
    });

    return createScene();
};
```

A scene can only be running once: launching a name that is already running is an error. And
`scene.stop()` with no name stops the scene that calls it, like a menu closing on its own button.

Both take a transition too, as `change` does: `scene.launch('PauseMenu', { transition: iris(0.4) })`
opens the menu on top behind an iris, and `scene.stop({ transition: iris(0.4) })` closes it behind
another. The scene launched waits behind the cover until it has loaded; the one stopped leaves at
the swap. What is underneath keeps running: pause it first to freeze the game behind the menu.

### Leaving: cleaning up what is not the engine's

When a scene stops (with `stop`, with `change`, or because the whole game is destroyed), the
engine walks its tree and runs its cleanups, **children before parents**: that way a sword's
cleanup can still reach the player it hung from.

What the engine gave you goes away with the scene by itself: its sprites, its `useUpdate`
callbacks, its listeners, its signals. Loaded images are kept, so the next scene that asks for
them does not download them again.

What **you** started outside the engine stays alive unless you stop it: a `setInterval`, a
listener on `window`, a request you want to abort. That is what `useCleanup` is for. It runs
once when the scene leaves (and not when it is paused). Called inside a component made with
`useSpawn`, it runs when that component is destroyed instead, which may be long before the scene
leaves:

```ts
const Level = () => {
    const onKey = (event: KeyboardEvent) => console.log(event.key);
    window.addEventListener('keydown', onKey);

    // Without this, the listener stays alive after the level ends and fires in the next scene.
    useCleanup(() => window.removeEventListener('keydown', onKey));

    return createScene();
};
```

### The whole game

Above the scenes sits the game, with its own beginning and its own end:

1. `createGame('#app', { … })` keeps the settings and returns the function that starts it.
2. Called with the scenes, the engine picks what to draw with (WebGPU or, failing that, WebGL2).
3. It registers each scene under its name.
4. It starts the initial scene: its body runs and, with the scene whole, its `useReady` callbacks.
5. It draws the first frame, with `dt` equal to `0`.
6. It announces itself with its `ready` event.

**Pausing the game is not pausing a scene.** `useGame().pause()` freezes the whole game: no scene
updates and `dt` is `0`, but everything is still drawn and input is still read. The two pauses are
independent: resuming the game does not resume a scene paused on its own, nor the other way round.

At the end, `destroy()` stops every scene (with all their cleanups) and the game announces its
`destroy` event.

### In one table

| What | When it runs |
| --- | --- |
| The scene's body | Once, when it starts |
| `useReady` | Once, when everything built with it exists; before the first frame |
| `useSceneTransitionEnd` | Once, when the transition it came in with is over; without one, on its first frame |
| `useUpdate` | Every frame, while the scene is running |
| `useCleanup` | Once, when the scene leaves (not when it is paused) |
| `scene.change` | Cut: at once. With a transition: the old one leaves at the swap |
| `scene.launch` | At once, alongside the others and drawn on top. With a transition: at the swap |
| `scene.pause` / `resume` | At once; the scene is still drawn |
| `destroy(object)` | Stops updating at once, and leaves at the end of the frame |
| The game's `ready` event | After the first frame |

## What the engine hands a shader

Every name a material or a screen effect can read, for sprites, models and the whole screen, on both graphics backends.

A material is a small function you write: the engine runs it for every pixel (and, on a model,
for every corner) and hands it what it knows about that place. This page lists all of it. The names
are a promise: a shader written against them keeps working in later versions, so none is ever
renamed. The engine's tests check that every name on this page exists on both graphics backends.

To see each input at work, every one drawing something you can see, open the
[Shader inputs](/examples/shader-inputs) example.

The hooks are written in WGSL for WebGPU (`fragment`, `vertex`) and in GLSL for WebGL2
(`fragmentGlsl`, `vertexGlsl`). Every name is the same in both; only the types are written the GLSL
way (`vec4` for `vec4<f32>`). If you build the material with the shader composer, it writes both
for you.

### In every shader: `mu`

A material's parameters arrive in `mu`. The engine's come first and always in the same order, then
yours, by the name you gave them: `uniforms: { charge: 0 }` is read as `mu.charge`.

| Name | Type | What it is |
| --- | --- | --- |
| `mu.time` | `f32` | Game seconds. `timeScale` speeds it up or slows it down, and a pause stops it. |
| `mu.resolution` | `vec2<f32>` | The size of what is being drawn into, in pixels. |
| `mu.progress` | `f32` | Screen effects only: how far through its half a scene change is, from 0 to 1. |
| `mu.phase` | `f32` | Screen effects only: 0 while a scene change covers the screen, 1 while it uncovers it. |

Outside a scene change, `progress` and `phase` are both 0. Those four names are taken: a parameter
of your own cannot use them.

A parameter is one of four types: `f32`, `vec2<f32>`, `vec3<f32>` or `vec4<f32>`. The whole block
holds 64 numbers, the engine's included. A `vec3` or a `vec4` starts on a multiple of four, so a
single number just before one leaves a small gap. A material that declares more than fits is drawn
with the built-in look, and the console says why.

### Sprites, text and map layers

A material with `shader: 'sprite2d'` (the default) has one hook:

```wgsl
fn effect(color: vec4<f32>, uv: vec2<f32>) -> vec4<f32> {
    return color;
}
```

- `color` is the picture already read and already tinted. `return color;` draws exactly what the
  engine would have drawn, so an effect is a change to that.
- On the letters of a vector font, `color` is the text's tint, with the letter's edge in its alpha.
- `uv` is the place on the sheet. It is not kept inside the sprite's frame: reading elsewhere on
  the sheet is allowed.
- `sampleTexture(uv)` reads the sheet at any place, also inside an `if` or a loop.

The same material works on a map layer without changing a word.

### Models

A material with `shader: 'mesh3d'` has two hooks. Both are optional: the one you leave out does
what the engine does.

```wgsl
fn vertex(pos: vec3<f32>, ctx: VertContext) -> vec3<f32> {
    return pos;
}

fn effect(surface: vec4<f32>, ctx: FragContext) -> vec4<f32> {
    return vec4<f32>(surface.rgb * ctx.light + ctx.shine + ctx.emissive, surface.a);
}
```

That `effect` is the engine's own, word for word: a good place to start from.

- `vertex` moves a corner, in the model's own space. Writing it costs a little: the surface is
  measured again after the move, which calls it two more times per corner.
- `vertex` can also hand four numbers to `effect`: `carry = vec4<f32>(...)`. They arrive as
  `ctx.carry`, blended across the triangle. A wave's height for foam on its crests, a light worked
  out at the corners, or `pos` itself, so stripes stay on the model as it moves.
- `surface` is the picture times the tint times the colour painted on the corners.
- Fog goes over what `effect` returns, and `alphaCutoff` cuts by its alpha.

What the corner hook is told:

| VertContext | Type | What it is |
| --- | --- | --- |
| `normal` | `vec3<f32>` | Which way the corner faces, in the model's space. |
| `uv` | `vec2<f32>` | The picture's place. |
| `color` | `vec4<f32>` | The colour painted on the corner, white if none. |
| `tangent`, `bitangent` | `vec3<f32>` | Which way across and down the picture runs, in the model's space. |
| `uv2` | `vec2<f32>` | A second picture coordinate: the file's own, or `uv` again. |

What the colour hook is told:

| FragContext | Type | What it is |
| --- | --- | --- |
| `uv` | `vec2<f32>` | The picture's place, already stretched if the material is `affine`. |
| `normal` | `vec3<f32>` | Which way the surface faces, in the world. |
| `worldPos` | `vec3<f32>` | Where the pixel is, in the world. |
| `light` | `vec3<f32>` | The light that falls here, shadows and ambient included. |
| `shine` | `vec3<f32>` | The highlight. |
| `emissive` | `vec3<f32>` | The material's own glow, through its glow picture. |
| `viewDir` | `vec3<f32>` | From the surface towards the camera. |
| `color` | `vec4<f32>` | The painted colour on its own. It is already in `surface`. |
| `screen` | `vec2<f32>` | Where on the screen, in game pixels, `(0, 0)` at the top left. |
| `tangent`, `bitangent` | `vec3<f32>` | Which way across and down the picture runs, in the world. |
| `uv2` | `vec2<f32>` | The second picture coordinate. |
| `frontFacing` | `bool` | False only on the back of a material with `cull: 'front'` or `'none'`. |
| `carry` | `vec4<f32>` | What `vertex` wrote in `carry`, blended across the triangle. Zero if nothing. |

`light` and `shine` are worked out at the corners and blended across the triangle, not worked out
again for every pixel. That is how the consoles of the era lit a model, and it is the look the engine
is after.

Helpers a model's shader can call:

- `sampleTexture(uv)`: the model's picture.
- `sampleMap_<name>(uv)`: one of the material's `maps`, by its name (`maps: { noise }` is read with
  `sampleMap_noise(ctx.uv)`). A map the material does not have reads white.
- `envUv(ctx)`: where a chrome reflection lands on a round picture of the surroundings. Read it with
  `sampleMap_env(envUv(ctx))`.

### Screen effects

An effect over the whole screen (`usePostProcess({ fragment })`) has the same hook as a sprite. `color` is the frame already read at `uv`.

| Helper | What it reads |
| --- | --- |
| `sampleTexture(uv)` | The frame, pixel by pixel. |
| `sampleTextureSmooth(uv)` | The frame, blended between pixels: for blurs, bends and smaller passes. |
| `sampleInput(uv)` | The frame the effect was handed. It is only different from `sampleTexture` in an effect with `passes`. |
| `sampleHistory(uv)` | What the effect showed last frame: black at first, white if the effect did not ask for it. |
| `paletteSize()`, `paletteColor(i)` | The palette given to the effect: how many colours, and colour `i`. |
| `lutSize()`, `lutTexel(x, y, slice, size)`, `lutColor(rgb)` | The colour table given to it. `lutColor` grades a colour through it. |

An effect reads one data picture: a palette or a colour table, never both.

## Hooks

Everything a scene or an object asks for while it is being built: loading, input, cameras, lights, sound, state and the frame loop.

### Game

#### useGame

Import from `nacatamalon`. The game itself: its size, its background, whether images are kept crisp, how fast time runs, and whether everything is frozen.

```ts
useGame() => TGameHandle
```

Example:

```ts
export const Level: TSceneFn = () => {
    const game = useGame();
    const keys = useKeyboard();

    useUpdate(() => {
        // A pause menu: everything stops, everything is still drawn.
        if (keys.justPressed('Escape')) {
            game.isPaused() ? game.resume() : game.pause();
        }
        // Slow motion while the key is held.
        game.setTimeScale(keys.isDown('Shift') ? 0.25 : 1);
    });

    return createScene();
};
```

### Scenes

#### useScene

Import from `nacatamalon`. Returns the controls for the game's scenes: launch, stop, change, pause, resume and isPaused.

```ts
useScene() => TSceneHandle
```

Example:

```ts
export const Intro: TSceneFn = () => {
    const scene = useScene();
    let elapsed = 0;
    useUpdate((dt) => {
        elapsed += dt;
        if (elapsed > 3) scene.change('Level');
    });
    return createScene();
};
```

### Lifecycle

#### useCleanup

Import from `nacatamalon`. Runs when its owner goes away: the component it is called in when it is destroyed, or the scene when it stops.

```ts
useCleanup(cleanup: () => void) => void
```

Example:

```ts
const PauseMenu = () => {
    const onKey = (event: KeyboardEvent) => console.log(event.key);
    window.addEventListener('keydown', onKey);

    // Without this, the listener stays alive after the menu is gone and fires behind it.
    useCleanup(() => window.removeEventListener('keydown', onKey));
};
```

#### useLateUpdate

Import from `nacatamalon`. Runs your code once per frame, after every useUpdate of the scene has run.

```ts
useLateUpdate(callback: (delta: number, time: number) => void) => void
```

Example:

```ts
const Player = () => {
    const place = useTransform({ x: 40, y: 112 });
    useUpdate((delta) => {
        place.x += 100 * delta;
    });
};

export const Level: TSceneFn = () => {
    const camera = useCamera2d();
    const player = useSpawn(Player)();

    useLateUpdate(() => {
        // where the player is now, after its own update moved it this frame
        camera.transform.x = player.transform!.x - 128;
    });

    return createScene();
};
```

#### useReady

Import from `nacatamalon`. Runs your code once, when everything being built with it exists: the whole scene, children and all, before its first frame.

```ts
useReady(callback: () => void) => void
```

Example:

```ts
// A behaviour on the root of a scene laid out as a document.
registerScript('match', () => {
    const root = useSelf();

    // Here, in the body, the boxes under the root are not built yet.
    useReady(() => {
        const pitcher = findObject(root, 'Pitcher');
        if (pitcher === null) return;
        useUpdate((delta) => {
            pitcher.transform!.x += 10 * delta;
        });
    });
});
```

#### useSceneTransitionEnd

Import from `nacatamalon`. Runs your code once, when the transition this scene came in with is over: the screen uncovered and nothing left over it.

```ts
useSceneTransitionEnd(callback: () => void) => void
```

Example:

```ts
const Level = () => {
    const song = useSong(useLoadSong({ src: '/music/level.song' }));

    // Not in the body: the screen is still covered while the level loads behind it.
    useSceneTransitionEnd(() => song.play());
    return createScene();
};

useScene().change('Level', { transition: fade(0.6) });
```

#### useTimer

Import from `nacatamalon`. Runs code later, in game time: once after a while, or again and again.

```ts
useTimer() => TTimer
```

Example:

```ts
declare const font: TBitmapFont;
declare const spawnEnemy: () => void;

export const Level: TSceneFn = () => {
    const timer = useTimer();
    const banner = createText({ text: 'READY', font, transform: { x: 160, y: 100 } });

    timer.after(1.5, () => { banner.text = 'GO!'; });
    timer.after(2.5, () => { banner.visible = false; });

    // A new enemy every three seconds, for as long as the level runs
    timer.every(3, () => { spawnEnemy(); });

    return createScene();
};
```

#### useUpdate

Import from `nacatamalon`. Runs your code once per frame, for as long as the scene is on screen.

```ts
useUpdate(callback: (delta: number, time: number) => void) => void
```

Example:

```ts
export const Level: TSceneFn = () => {
    const hero = createSprite({ key: 'hero', transform: { x: 40, y: 112 } });

    useUpdate((delta) => {
        // 100 pixels per second, whatever the screen does
        hero.transform.x += 100 * delta;
    });

    const coin = createSprite({ key: 'coin', transform: { x: 200, y: 100 } });
    useUpdate((delta, time) => {
        // bobs up and down once a second
        coin.transform.y = 100 + Math.sin(time * Math.PI * 2) * 4;
    });

    return createScene();
};
```

### Game objects

#### useSelf

Import from `nacatamalon`. The thing being built right now, so it can refer to itself later: almost always to end its own life once it has done its job.

```ts
useSelf() => TGameObject
```

Example:

```ts
const Spark = () => {
    const self = useSelf();
    let left = 0.4;

    useUpdate((delta) => {
        left -= delta;
        if (left &#x3C;= 0) {
            destroy(self);
        }
    });
};
```

#### useSpawn

Import from `nacatamalon`. Prepares a maker for component, so the scene can create as many of them as it likes, whenever it likes: while it is being built, from a frame, from a timer, from a click.

```ts
useSpawn(component: (args: TArgs) => TApi, given: string | TSpawnOptions) => (args: TArgs) => TGameObject<TApi>
```

Example:

```ts
// An enemy that its level can hurt.
const Bat = ({ x }: { x: number }) => {
    const self = useSelf();
    let life = 3;
    createSprite({ key: 'bat', transform: { x, y: 120 } });
    return {
        hurt: (damage: number) => {
            life -= damage;
            if (life &#x3C;= 0) destroy(self);
        },
    };
};

export const Cave: TSceneFn = () => {
    const bat = useSpawn(Bat, 'Bat')({ x: 200 });
    const keys = useKeyboard();

    useUpdate(() => {
        if (keys.justPressed('x')) bat.api.hurt(1);
    });

    return createScene();
};
```

#### useTransform

Import from `nacatamalon`. Gives this object a place of its own, and moves everything inside it.

```ts
useTransform(options: TTransformOptions) => TTransform3d
```

Example:

```ts
const Turret = (x: number) => {
    const place = useTransform({ x, y: 200 });

    // Both are placed relative to the turret, so turning it turns them together.
    createSprite({ key: 'base', transform: { x: 0, y: 0 } });
    createSprite({ key: 'barrel', transform: { x: 0, y: -12 } });

    useUpdate((delta) => {
        place.rotation += delta;
    });
};
```

### Scripts

#### useScript

Import from `nacatamalon`. Attaches a behaviour by name to the object being built, and runs it.

```ts
useScript(ref: string, props?: TScriptProps, options: TUseScriptOptions) => void
```

Example:

```ts
const Guard = () => {
    const texture = useLoadTexture({ src: '/assets/guard.png' });
    createSprite({ texture, transform: { x: 40, y: 112 } });
    useScript('patrol', { speed: 80 });
};
```

#### useScriptApi

Import from `nacatamalon`. Reaches the api of another behaviour on the same object: what its function returned, by the name it was registered under, or what one offered with provide under a word of its own.

```ts
useScriptApi(name: string, self?: TGameObject) => () => T | null
```

Example:

```ts
registerScript('spin', (self) => {
    let running = true;
    useUpdate((delta) => { if (running) self.drawables[0].transform.rotation += delta; });
    return {
        stop: () => { running = false; },
        start: () => { running = true; },
        isRunning: () => running,
    };
});

registerScript('toggle', () => {
    const spin = useScriptApi&#x3C;{ stop(): void; start(): void; isRunning(): boolean }>('spin');
    const keys = useKeyboard();

    useUpdate(() => {
        if (!keys.justPressed('Space')) return;
        const api = spin();
        if (api === null) return;
        if (api.isRunning()) api.stop(); else api.start();
    });
}, { requires: ['spin'] });
```

### Input

#### useAction

Import from `nacatamalon`. One action, which is what most game code reads.

```ts
useAction(name: string, options: TUseActionsOptions) => TAction
```

Example:

```ts
declare const velocity: { x: number; y: number };

const jump = useAction('jump');
useUpdate(() => { if (jump.justPressed()) velocity.y = -320; });
```

#### useActions

Import from `nacatamalon`. The game's named actions.

```ts
useActions(options: TUseActionsOptions) => TActionsHandle
```

Example:

```ts
declare const hero: TSprite;
declare const jump: () => void;
declare let charge: number;

export const Player: TSceneFn = () => {
    const input = useActions();

    useUpdate((delta) => {
        // Four actions, one direction: the keys, the d-pad and the stick all arrive here, and the
        // dead zone is round, so a gentle diagonal stays a diagonal.
        const { x, y } = input.direction('move_left', 'move_right', 'move_up', 'move_down');
        hero.transform.x += x * 120 * delta;
        hero.transform.y += y * 120 * delta;

        if (input.justPressed('jump')) jump();
        // A trigger bound to 'fire' reads how far it is pulled; a key bound to it reads 1.
        charge = input.value('fire');
    });

    return createScene();
};
```

#### useDirection

Import from `nacatamalon`. Four actions read as one direction: the way to move a character from the input map.

```ts
useDirection(negativeX: string, positiveX: string, negativeY: string, positiveY: string, options: TUseActionsOptions & { deadzone }) => () => { x, y }
```

Example:

```ts
declare const hero: TSprite;
const SPEED = 120;

const move = useDirection('move_left', 'move_right', 'move_up', 'move_down');
useUpdate((delta) => {
    const { x, y } = move();
    hero.transform.x += x * SPEED * delta;
    hero.transform.y += y * SPEED * delta;
});
```

#### useDraggable

Import from `nacatamalon`. Makes a sprite, a text, a nine-slice or a whole object follow the pointer that drags it: pressed with the main button or a finger, it moves with it, held where it was taken, until it is let go.

```ts
useDraggable(target: TSprite | TBox | TText | TNineSlice, options: TUseDraggableOptions) => TDraggableHandle
```

Example:

```ts
export const Table: TSceneFn = () => {
    const slot = createSprite({ key: 'slot', transform: { x: 240, y: 120 } });
    useTag('slot').add(slot);

    const card = createSprite({ key: 'card', transform: { x: 80, y: 120 } });
    useDraggable(card, {
        dropOn: 'slot',
        snapBack: true,
        onDragStart: () => { card.zIndex = 10; },
        onDragEnd: ({ dropped }) => {
            card.zIndex = 0;
            // Into the slot, right in the middle.
            if (dropped !== null) {
                card.transform.x = slot.transform.x;
                card.transform.y = slot.transform.y;
            }
        },
    });

    return createScene();
};
```

#### useDropZone

Import from `nacatamalon`. Makes a sprite, a text, a nine-slice or a whole object a place things can be dragged into: a slot of an inventory, the square of a board, a bin.

```ts
useDropZone(target: TSprite | TBox | TText | TNineSlice, options: TUseDropZoneOptions) => TDropZoneHandle
```

Example:

```ts
export const Table: TSceneFn = () => {
    const card = createSprite({ key: 'card', transform: { x: 80, y: 120 } });
    useTag('card').add(card);
    useDraggable(card, { snapBack: true });

    const slot = createSprite({ key: 'slot', transform: { x: 240, y: 120 } });
    const zone = useDropZone(slot, {
        accepts: 'card',
        onDrop: (dragged) => {
            if (dragged.transform === null) return;
            dragged.transform.x = slot.transform.x;
            dragged.transform.y = slot.transform.y;
        },
    });

    // Dim while a card could go in, bright while one is over it.
    const idle = getColor('#1e2533');
    const can = getColor('#3a4560');
    const over = getColor('#ffcc33');
    useUpdate(() => {
        slot.tint = zone.isOver() ? over : zone.canDrop() ? can : idle;
    });

    return createScene();
};
```

#### useGamepad

Import from `nacatamalon`. A gamepad, to ask in every frame what the player is holding, has just pressed, or how far a stick is pushed.

```ts
useGamepad(target: TGamepadTarget, options: TUseGamepadOptions) => TGamepad
```

Example:

```ts
declare const fire: () => void;

export const Level: TSceneFn = () => {
    const pad = useGamepad();
    const keys = useKeyboard();
    const ship = createSprite({ key: 'ship' });

    useUpdate((delta) => {
        const { x, y } = pad.leftStick();
        ship.transform.x += x * 150 * delta;
        ship.transform.y += y * 150 * delta;
        if (pad.justPressed('a') || keys.justPressed('Space')) fire();
    });

    return createScene();
};
```

#### useInputMap

Import from `nacatamalon`. Remapping: what a game's controls screen is built on.

```ts
useInputMap() => TInputMapHandle
```

Example:

```ts
declare const jumpRow: TText;

export const Controls: TSceneFn = () => {
    const map = useInputMap();

    // "Press a key or a button for JUMP..."
    listen(jumpRow, { onClick: () => {
        map.capture((binding) => {
            if (binding === null) return;              // they pressed Escape
            if (map.conflicts(binding, 'jump').length > 0) return;
            map.bind('jump', binding, 0);
        });
    } });

    return createScene();
};
```

#### useKeyboard

Import from `nacatamalon`. The keyboard, to ask in every frame what the player is holding or has just pressed.

```ts
useKeyboard() => TKeyboard
```

Example:

```ts
declare const Bullet: (x: number) => void;

export const Level: TSceneFn = () => {
    const keys = useKeyboard();
    const ship = createSprite({ key: 'ship', transform: { x: 160, y: 200 } });
    const fire = useSpawn(Bullet);

    useUpdate((delta) => {
        if (keys.isDown('ArrowLeft')) ship.transform.x -= 150 * delta;
        if (keys.isDown('ArrowRight')) ship.transform.x += 150 * delta;
        if (keys.justPressed('Space')) fire(ship.transform.x);
    });

    return createScene();
};
```

#### usePointer

Import from `nacatamalon`. Listens to the mouse and to touch: presses, releases, movement, and what is under the pointer.

```ts
usePointer() => TPointerHandle
```

Example:

```ts
export const Board: TSceneFn = () => {
    const pointer = usePointer();
    const red = getColor('#ff5566');

    pointer.onDown((info) => {
        if (info.target !== null) {
            info.target.tint = red;
        }
    });

    // The wheel zooms the camera: towards you is out, away is in.
    const camera = useCamera2d();
    pointer.onWheel(({ deltaY }) => {
        camera.zoom = Math.min(4, Math.max(0.5, camera.zoom * Math.exp(-deltaY * 0.001)));
    });

    return createScene();
};
```

#### useTouchAction

Import from `nacatamalon`. Makes what is being built an on-screen button for action: while a finger is on it, or on anything inside it, the action is held, and the game reads it like any other press, actions.isDown('jump'), whatever pressed it.

```ts
useTouchAction(action: string) => void
```

Example:

```ts
declare const jump: () => void;

const Button = ({ x, action }: { x: number; action: string }) => {
    createSprite({ key: 'button', transform: { x, y: 280 } });
    useTouchAction(action);
};

export const Level: TSceneFn = () => {
    const actions = useActions();
    useSpawn(Button)({ x: 400, action: 'jump' });
    useSpawn(Button)({ x: 440, action: 'fire' });

    useUpdate(() => {
        if (actions.justPressed('jump')) jump();
    });

    return createScene();
};
```

### Store

#### useStore

Import from `nacatamalon`. Reacts from a scene when one value of a store changes, and stops by itself when that part of the scene goes away.

```ts
useStore(store: TGameStore<S, A>, selector: TStoreSelector<S, T>, handler: TStoreListener<T>, equals?: (a: T, b: T) => boolean) => void
```

Example:

```ts
declare const petStore: TGameStore&#x3C;{ hunger: number }>;
const GREEN = getColor('#40c040');
const RED = getColor('#e04040');

export const Pet: TSceneFn = () => {
    const pet = createSprite({ tint: GREEN, width: 64, height: 64 });
    useStore(petStore, (s) => s.hunger > 30, (content) => {
        pet.tint = content ? GREEN : RED;
    });
    return createScene();
};
```

#### useStoreLink

Import from `nacatamalon`. Says that this object uses one of the game's stores.

```ts
useStoreLink(key: string) => void
```

Example:

```ts
const Pet = () => {
    useStoreLink('pet');
    // ...and now a behaviour on this object can reach it with storeOf(self).
};
```

### State

#### useData

Import from `nacatamalon`. State that lives with a piece of the scene, as [data, setData].

```ts
useData(initial: T) => [TDataRecord<T>, TDataSetter<T>]
```

Example:

```ts
declare const counter: TText;

export const Level: TSceneFn = () => {
    const [lives, setLives] = useData(3);

    useWatch(() => {
        counter.text = `LIVES ${lives.value}`;
    }, [lives]);

    usePointer().onDown(() => setLives((n) => n - 1));

    return createScene();
};
```

#### useWatch

Import from `nacatamalon`. Runs your code on the first frame, and again whenever one of the things you listed changes.

```ts
useWatch(fn: () => void, deps: typeOperator) => void
```

Example:

```ts
declare const counter: TText;
declare const hero: TSprite;

declare const coins: TGameStore&#x3C;{ coins: number }>;

export const Level: TSceneFn = () => {
    const texture = useLoadTexture({ src: '/assets/hero.png' });
    const [lives, setLives] = useData(3);

    useWatch(() => {
        counter.text = `LIVES ${lives.value}  COINS ${coins.state.coins}`;
        hero.visible = texture.status === 'ready';
    }, [lives, coins, texture]);

    return createScene();
};
```

### Signals

#### useReport

Import from `nacatamalon`. Lets a piece report to whoever placed it, and gives back the function that sends the report: a pack's button saying it was pressed, a behaviour saying its box is out of lives.

```ts
useReport(name: string) => (payload: T) => boolean
```

Example:

```ts
registerScript('button', (self, props) => {
    const pressed = useReport&#x3C;{ label: string }>('pressed');
    listen(self, { onClick: () => pressed({ label: String(props.label) }) });
});
```

#### useSignal

Import from `nacatamalon`. Listens to a signal from a scene, and stops listening by itself when that part of the scene goes away.

```ts
useSignal(signal: TGameSignal<T>, handler: TSignalHandler<T>) => void
```

Example:

```ts
declare const coinCollected: TGameSignal&#x3C;number>;

export const Hud: TSceneFn = () => {
    let score = 0;
    useSignal(coinCollected, (points) => { score += points; });
    return createScene();
};
```

### Particles

#### useParticleCollider2d

Import from `nacatamalon`. Makes this object solid to particles in a flat scene: sparks bounce off it and rain ends on it.

```ts
useParticleCollider2d(options: { shape, enabled }) => TParticleCollider2d
```

Example:

```ts
const Ledge = () => {
    useTransform({ x: 200, y: 300 });
    useParticleCollider2d({ shape: { kind: 'rect', width: 120, height: 16 } });
};
```

#### useParticleCollider3d

Import from `nacatamalon`. Makes this object solid to particles in three dimensions.

```ts
useParticleCollider3d(options: { shape, enabled }) => TParticleCollider3d
```

Example:

```ts
const Step = (y: number, z: number) => {
    useTransform({ y, z });
    createMesh({ geometry: useCubeGeometry() });
    useParticleCollider3d({ shape: { kind: 'box', size: [1, 1, 1] } });
};
```

### Camera

#### useCamera2d

Import from `nacatamalon`. Gives the scene a camera, so its world can be bigger than the screen and scroll, turn and zoom.

```ts
useCamera2d(options?: TCamera2dOptions) => TCamera2d
```

Example:

```ts
export const Level: TSceneFn = () => {
    const camera = useCamera2d();
    const hero = createSprite({ key: 'hero', transform: { x: 900, y: 600 } });

    useUpdate(() => {
        // The screen is 480x320: half of it keeps the hero in the middle
        camera.transform.x = hero.transform.x - 240;
        camera.transform.y = hero.transform.y - 160;
    });

    return createScene();
};
```

#### useCamera3d

Import from `nacatamalon`. Gives the scene a camera to look at its models through, and returns it.

```ts
useCamera3d(options: TCamera3dOptions) => TCamera3d
```

Example:

```ts
const Level = () => {
    const camera = useCamera3d({ projection: 'perspective', transform: { z: 6 } });

    useUpdate((delta) => {
        camera.transform.rotationY += delta * 0.5;
    });

    return createScene();
};
```

#### useCameraFollow

Import from `nacatamalon`. Makes the scene's camera follow something, the way a platformer's does: a box in the middle the character moves inside freely (deadzone), never past the edges of the level (bounds), a little ahead of where it goes (lead), and a shake for when something hits hard.

```ts
useCameraFollow(target: TCameraTarget | null, options: TCameraFollowOptions) => TCameraFollow
```

Example:

```ts
declare const Hero: () => void;

export const Level: TSceneFn = () => {
    const map = createTilemap({ src: '/maps/level.tilemap' });
    const hero = useSpawn(Hero)();
    const follow = useCameraFollow(hero, { deadzone: { width: 32, height: 48 }, bounds: map });
    const keys = useKeyboard();

    useUpdate(() => {
        if (keys.justPressed('x')) follow.shake(4, 0.3);
    });
    return createScene();
};
```

#### useFog

Import from `nacatamalon`. Puts the scene in fog: its models fade into color between near and far from the camera.

```ts
useFog(options: TFogOptions) => TFog
```

Example:

```ts
export const Level: TSceneFn = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 8 } });
    const fog = useFog({ color: '#1a1426', near: 6, far: 40 });

    useUpdate((delta, time) => {
        // Breathing: the fog comes in and goes out
        fog.far = 40 + Math.sin(time) * 10;
    });

    return createScene();
};
```

#### useParallax

Import from `nacatamalon`. Makes this object, and everything it draws and holds, move with the camera only so much: 1 as everything else, 0 not at all, as if pinned to the screen, and in between further away.

```ts
useParallax(factor: { x, y }) => void
```

Example:

```ts
const Mountains = () => {
    useParallax({ x: 0.3, y: 0 });
    createSprite({ key: 'mountains', transform: { x: 240, y: 120 } });
};
```

#### useScreenSpace

Import from `nacatamalon`. Keeps what is created here fixed on the screen, even while the scene's camera moves.

```ts
useScreenSpace(on: boolean) => void
```

Example:

```ts
const Hud = () => {
    useScreenSpace();
    createSprite({ tint: '#4ade80', width: 120, height: 8, transform: { x: 70, y: 16 } });
};

export const Level: TSceneFn = () => {
    useCamera2d();
    useSpawn(Hud)();
    return createScene();
};
```

#### useWorldOffScreen

Import from `nacatamalon`. Has the screen draw only what this scene pins to it (useScreenSpace): its world is seen through views of it alone (createSpriteTexture({ sees: 'world' })).

```ts
useWorldOffScreen(on: boolean) => void
```

Example:

```ts
declare const Half: (player: TSprite) => void;
declare const Screens: (left: TTexture, right: TTexture) => void;

export const Level: TSceneFn = () => {
    useWorldOffScreen();
    const p1 = createSprite({ key: 'red' });
    const p2 = createSprite({ key: 'blue' });
    const left = createSpriteTexture({ width: 240, height: 270, sees: 'world' }, Half, p1);
    const right = createSpriteTexture({ width: 240, height: 270, sees: 'world' }, Half, p2);
    // The two halves, pinned to the screen in a part of their own.
    useSpawn(Screens)(left, right);
    return createScene();
};
```

### Masks

#### useMask

Import from `nacatamalon`. Makes a mask: what keeps objects, and everything under them, to a rectangle or to the shape of another object, so nothing of them is drawn, or touched by the pointer, outside it.

```ts
useMask(options: TMaskOptions) => TMask
```

Example:

```ts
const Flashlight = () => {
    useTransform({ x: 100, y: 80 });
    createSprite({ texture: 'cone' });
};

const Ground = () => {
    createSprite({ texture: 'cave' });
};

const Bats = () => {
    createSprite({ texture: 'bat', transform: { x: 200, y: 60 } });
};

export const Cave: TSceneFn = () => {
    const light = useSpawn(Flashlight)();
    const mask = useMask({ by: light, soft: true });
    mask.add(useSpawn(Ground)());
    mask.add(useSpawn(Bats)());
    useTween()({ from: 40, to: 300, duration: 2, yoyo: true, loop: true, onUpdate: (x) => { light.transform!.x = x; } }).play();
    return createScene();
};

const Inventory = () => {
    useMask({ rect: { x: 0, y: 0, width: 200, height: 120 } }).add(useSelf());
    createSprite({ texture: 'items' });
};
```

### Filters

#### useFilter

Import from `nacatamalon/effects`. Makes a filter: effects run over what an object, and everything under it, draws, before it is laid on the screen.

```ts
useFilter(options: TFilterOptions) => TFilter
```

Example:

```ts
const Hero = () => {
    createSprite({ texture: 'body' });
    createSprite({ texture: 'sword', transform: { x: 10, y: -4 } });
};

export const Level: TSceneFn = () => {
    const hero = useSpawn(Hero)();
    useFilter({ effects: [outline({ color: '#ffffff' }), dropShadow({ offset: { x: 2, y: 3 } })] }).add(hero);

    const coin = useSpawn(() => { createSprite({ texture: 'coin' }); })();
    const shine = useFilter({ effects: [glow({ color: '#ffd23f' })] });
    shine.add(coin);
    let time = 0;
    useUpdate((delta) => {
        time += delta;
        shine.effects[0]!.uniforms.intensity = 1.5 + Math.sin(time * 4) * 0.5;
    });

    return createScene();
};
```

### Lighting

#### useAmbientLight

Import from `nacatamalon`. The level everything is lit to before any lamp reaches it.

```ts
useAmbientLight(options: { color, intensity }) => TAmbientLight
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 5 } });
    useAmbientLight({ intensity: 1.2 });
    createMesh({ geometry: useSphereGeometry() });

    return createScene();
};
```

#### useAmbientLight2d

Import from `nacatamalon`. How bright a 2D world is where no lamp reaches, and in what colour, apart from any 3D light.

```ts
useAmbientLight2d(options: { color, intensity }) => TAmbientLight2d
```

#### useBlobShadow

Import from `nacatamalon`. Puts a soft dark disc on the ground under something and keeps it there.

```ts
useBlobShadow(options: TBlobShadowOptions) => TMesh
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { y: 3, z: 6, rotationX: -0.5 } });
    createMesh({ geometry: usePlaneGeometry({ width: 12, depth: 12 }) });

    const hero = createMesh({ geometry: useCubeGeometry(), transform: { y: 0.5 } });
    useBlobShadow({ target: hero, radius: 0.75, followHeight: true });

    return createScene();
};
```

#### useDirectionalLight

Import from `nacatamalon`. A light from far away, like the sun.

```ts
useDirectionalLight(options: TLightOptionsBase & { shadowArea, shadowDistance }) => TDirectionalLight
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 5 } });
    useDirectionalLight({ intensity: 1.2 });
    createMesh({ geometry: useSphereGeometry() });

    return createScene();
};
```

#### useLight2d

Import from `nacatamalon`. A lamp for a 2D world: it lights the sprites, texts, nine-slices, maps and particles around it, with no normal map.

```ts
useLight2d(options: TLight2dOptions) => TLight2d
```

Example:

```ts
useAmbientLight2d({ intensity: 0.15 });
useLight2d({ transform: { x: 160, y: 90 }, radius: 100, color: '#ffbb66' });
```

#### usePointLight

Import from `nacatamalon`. A lamp: it has a place, shines every way, and fades out with distance.

```ts
usePointLight(options: TLightOptionsBase & { range }) => TPointLight
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 5 } });
    usePointLight({ intensity: 1.2 });
    createMesh({ geometry: useSphereGeometry() });

    return createScene();
};
```

#### useRectLight

Import from `nacatamalon`. A panel: a lamp shaped like a rectangle, which shines from its front only.

```ts
useRectLight(options: TLightOptionsBase & { range, width, height }) => TRectLight
```

Example:

```ts
const Room = () => {
    useCamera3d({ projection: 'perspective', transform: { y: 2, z: 6 } });
    useRectLight({ width: 3, height: 0.4, range: 6, transform: { y: 3 } });
    createMesh({ geometry: usePlaneGeometry({ width: 10, depth: 10, widthSegments: 20, depthSegments: 20 }) });

    return createScene();
};
```

#### useSpotLight

Import from `nacatamalon`. A torch: a lamp that only shines inside a cone.

```ts
useSpotLight(options: TLightOptionsBase & { range, angle, penumbra }) => TSpotLight
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 5 } });
    useSpotLight({ intensity: 1.2 });
    createMesh({ geometry: useSphereGeometry() });

    return createScene();
};
```

#### useUnlit2d

Import from `nacatamalon`. Leaves what is created here out of the scene's 2D lights: it keeps its own colours however dark the world is, and still moves with the camera, unlike the HUD.

```ts
useUnlit2d(on: boolean) => void
```

Example:

```ts
const Torch = () => {
    useLight2d({ radius: 90, color: '#ff9c3a' });
    useSpawn(() => {
        useUnlit2d();
        createSprite({ width: 6, height: 6, tint: '#ffcf6b' });
    })();
};
```

### Geometry

#### useCircleGeometry

Import from `nacatamalon`. A flat disc facing up.

```ts
useCircleGeometry(options: TCircleGeometryOptions) => TGeometry
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 4 } });
    const shape = useCircleGeometry();
    createMesh({ geometry: shape });

    return createScene();
};
```

#### useConeGeometry

Import from `nacatamalon`. A cone with a base.

```ts
useConeGeometry(options: TConeGeometryOptions) => TGeometry
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 4 } });
    const shape = useConeGeometry();
    createMesh({ geometry: shape });

    return createScene();
};
```

#### useCubeGeometry

Import from `nacatamalon`. A cube, or any box shape: as wide, tall and deep as you ask.

```ts
useCubeGeometry(options: TCubeGeometryOptions) => TGeometry
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 4 } });
    const shape = useCubeGeometry();
    createMesh({ geometry: shape });

    return createScene();
};
```

#### useCylinderGeometry

Import from `nacatamalon`. A tube with a lid and a base.

```ts
useCylinderGeometry(options: TCylinderGeometryOptions) => TGeometry
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 4 } });
    const shape = useCylinderGeometry();
    createMesh({ geometry: shape });

    return createScene();
};
```

#### useIcoSphereGeometry

Import from `nacatamalon`. A ball whose triangles are all about the same size.

```ts
useIcoSphereGeometry(options: TIcoSphereGeometryOptions) => TGeometry
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 4 } });
    const shape = useIcoSphereGeometry();
    createMesh({ geometry: shape });

    return createScene();
};
```

#### useLatheGeometry

Import from `nacatamalon`. A shape turned on a lathe: half an outline, its profile, spun about the upright axis into a solid, as a turner shapes a chair leg or a potter a vase.

```ts
useLatheGeometry(options: TLatheGeometryOptions) => TGeometry
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { y: 1, z: 4 } });
    useDirectionalLight({ intensity: 1 });

    const bottle = useLatheGeometry({
        profile: [
            { x: 0, y: 0 },       // the middle of the base
            { x: 0.4, y: 0 },     // its rim, given twice: a hard edge
            { x: 0.4, y: 0 },
            { x: 0.4, y: 1.2 },   // the body
            { x: 0.15, y: 1.6 },  // the shoulder
            { x: 0.12, y: 2 },    // the neck, open at the top
        ],
        segments: 24,
    });
    createMesh({ geometry: bottle, tint: '#5fd068' });

    return createScene();
};
```

#### usePlaneGeometry

Import from `nacatamalon`. A flat sheet lying like a floor, facing up.

```ts
usePlaneGeometry(options: TPlaneGeometryOptions) => TGeometry
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 4 } });
    const shape = usePlaneGeometry();
    createMesh({ geometry: shape });

    return createScene();
};
```

#### useSphereGeometry

Import from `nacatamalon`. A ball built as lines of latitude, the way a globe is drawn.

```ts
useSphereGeometry(options: TSphereGeometryOptions) => TGeometry
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 4 } });
    const shape = useSphereGeometry();
    createMesh({ geometry: shape });

    return createScene();
};
```

#### useTorusGeometry

Import from `nacatamalon`. A ring, like a doughnut.

```ts
useTorusGeometry(options: TTorusGeometryOptions) => TGeometry
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 4 } });
    const shape = useTorusGeometry();
    createMesh({ geometry: shape });

    return createScene();
};
```

### Animation

#### useMotion

Import from `nacatamalon`. Plays a motion on the scene this is called in: a .motion file of values moved over time by keys (a camera sweeping the field, a title coming in), made in an editor's motion panel.

```ts
useMotion(motion: TMotionDoc | TMotionFile, options: TMotionOptions) => TMotionHandle
```

Example:

```ts
export const Intro: TSceneFn = () => {
    const intro = useMotion(useLoadMotion({ src: '/motions/intro.motion' }), { autoplay: true });
    intro.on('titleShown', () => console.log('the title is up'));
    intro.onEnd(() => console.log('over'));
    return createScene();
};
```

#### useSkeletalAnimation

Import from `nacatamalon`. Makes a placed model move: plays the movements its file came with, and eases between them.

```ts
useSkeletalAnimation(model: TAnimatable, options: TSkeletalAnimationOptions) => TSkeletalAnimation
```

Example:

```ts
export const Level: TSceneFn = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 200 } });
    useDirectionalLight({ intensity: 1 });

    const file = useLoadModel({ src: '/models/fox.glb' });
    const fox = createModel({ model: file });
    const moves = useSkeletalAnimation(fox, { play: 'Walk', fade: 0.25 });

    const keys = useKeyboard();
    useUpdate(() => {
        if (keys.justPressed('Space')) moves.play('Run');
    });

    return createScene();
};
```

#### useSpriteAnimation

Import from `nacatamalon`. Plays a sprite's pictures in order, so it walks, attacks or spins.

```ts
useSpriteAnimation(sprite: TSprite, options: TSpriteAnimationOptions) => TSpriteAnimation
```

Example:

```ts
declare const walk: TSpriteAtlas;
declare let hurt: boolean;

const enemy = createSprite({ atlas: walk, frame: 0, transform: { x: 80, y: 120 } });

const anim = useSpriteAnimation(enemy, {
    clips: {
        walk: { frames: [0, 1, 2, 3, 4, 5], fps: 12 },
        hit: { frames: [6, 7], fps: 20, loop: false },
    },
    play: 'walk',
});

useUpdate(() => {
    if (hurt) anim.play('hit');
});
```

#### useTween

Import from `nacatamalon`. Declares tweens: a number, or the fields of an object, going from one value to another over time, so something can slide in, grow, fade or bounce without you writing the arithmetic for it every frame.

```ts
useTween() => TTweenStarter
```

Example:

```ts
export const Level: TSceneFn = () => {
    const hero = createSprite({ key: 'hero', transform: { x: 40, y: 112 } });
    const door = createSprite({ key: 'door', transform: { x: 280, y: 112 } });
    const coin = createSprite({ key: 'coin', transform: { x: 160, y: 90 } });
    const tween = useTween();

    // Up and down for ever, from the moment the scene starts
    tween({ target: coin, to: { transform: { y: 84 } }, duration: 0.6, ease: easeInOutSine, yoyo: true, loop: true }).play();

    // Declared once, played on every hit: two blinks and a step back at the same time
    const hurt = tween.all(
        tween({ target: hero, to: { tint: { a: 0 } }, duration: 0.08, yoyo: true, repeat: 3 }),
        tween({ target: hero, by: { transform: { x: -12 } }, duration: 0.2, ease: easeOutQuad }),
    );
    const keys = useKeyboard();
    useUpdate(() => {
        if (keys.justPressed('h')) hurt.play();
    });

    // One after another: the door opens, then the hero walks through
    tween.chain(
        tween({ target: door, to: { transform: { y: -40 } }, duration: 0.5 }),
        tween({ target: hero, to: { transform: { x: 300 } }, duration: 1 }),
    ).play();

    return createScene();
};
```

### Post-processing

#### usePostProcess

Import from `nacatamalon/effects`. Adds a full-screen effect: a shader that reads the finished frame and gives back what to show.

```ts
usePostProcess(options: TPostProcessOptions<U>) => TPostEffect<U>
```

Example:

```ts
const Level = () => {
    const nes = useLoadPalette({ src: '/palettes/nes.palette' });

    usePostProcess({ ...paletteMatch(), palette: nes });

    return createScene();
};
```

### Color & palettes

#### useColor

Import from `nacatamalon`. Converts any color format to a normalized TColor object.

```ts
useColor(input: TColorInput) => TColor
```

### Pixels

#### useLoadPixels

Import from `nacatamalon/pixels`. Loads a drawn picture as pixels, to process it in code: a palette swap, a white silhouette for a hit, an outline, a collision mask taken from the drawing, a level read from an image.

```ts
useLoadPixels(options: TUseLoadPixelsOptions) => TLoadedPixels
```

Example:

```ts
const Level = () => {
    const hero = useLoadPixels({ src: '/assets/hero.png' });
    const gold = createPixelTexture(hero, (p) => swapColors(p, [['#3a7bff', '#ffd75a']]));
    createSprite({ texture: gold, width: 32, height: 32 });
    return createScene();
};
```

### Audio

#### useAudio

Import from `nacatamalon`. The game's volumes: the general one and one per channel, plus a way to silence everything.

```ts
useAudio() => TAudioHandle
```

Example:

```ts
declare const settings: TGameStore&#x3C;{ music: number }>;
declare const quieter: TText;

export const Options: TSceneFn = () => {
    const audio = useAudio();
    audio.setVolume(settings.state.music, 'music');

    listen(quieter, { onClick: () => audio.setVolume(audio.getVolume('music') - 0.1, 'music') });
    return createScene();
};
```

#### useAudioListener

Import from `nacatamalon`. Puts the game's ears on this object: every sound placed in the world is heard from where it is, and in a 3D scene, facing the way it faces.

```ts
useAudioListener(options: { enabled }) => TAudioListener
```

Example:

```ts
const Player = () => {
    useTransform({ x: 0, y: 0.5, z: 0 });
    createMesh({ geometry: useCubeGeometry() });
    useAudioListener();
};
```

#### useMusic

Import from `nacatamalon`. Plays one piece of music recorded in several files (its layers, or stems) that sound together, and hands back the controls for it.

```ts
useMusic(options: TMusicOptions) => TMusicHandle
```

Example:

```ts
declare const player: TSprite;

const Level = () => {
    const music = useMusic({
        layers: {
            land: { src: '/audio/theme_land.ogg' },
            water: { src: '/audio/theme_water.ogg', volume: 0 },
        },
        autoplay: true,
    });

    useUpdate(() => {
        const under = player.transform.y &#x3C; 0;
        music.setLayer('land', under ? 0 : 1, 0.5);
        music.setLayer('water', under ? 1 : 0, 0.5);
    });
    return createScene();
};
```

#### useSong

Import from `nacatamalon`. Plays a song written as notes (a .song file, or one written in code) with the engine's 8-bit synthesizer, and hands back the controls.

```ts
useSong(song: TSongDoc | TSongFile | TSongAccompaniment, options: TSongOptions) => TSongHandle
```

Example:

```ts
declare const crowd: TSprite;
declare const lastInning: TGameSignal;

const Ballpark = () => {
    const song = useSong(useLoadSong({ src: '/music/minuet.song' }), { autoplay: true });

    song.onBeat((beat) => {
        crowd.transform.y = beat === 1 ? 118 : 120;
    });
    useSignal(lastInning, () => song.setTempo(song.tempo * 1.2));
    return createScene();
};
```

#### useSound

Import from `nacatamalon`. Plays one audio file (.wav, .mp3, .ogg), or an effect made from a preset, and hands back the controls for it.

```ts
useSound(source: TSoundSource, options: TSoundOptions) => TSoundHandle
```

Example:

```ts
export const Level: TSceneFn = () => {
    const music = useSound({ src: '/audio/theme.mp3' }, { channel: 'music', loop: true, autoplay: true });
    const jump = useSound({ src: '/audio/jump.mp3' }, { volume: 0.6 });
    const keys = useKeyboard();

    useUpdate(() => {
        if (keys.justPressed('Space')) jump.play();
    });

    useScene().onPaused(() => music.setVolume(0.2));
    return createScene();
};
```

### Assets & loading

#### useLoadAtlas

Import from `nacatamalon`. Loads a sheet written as a file: the image, how it is cut into frames, and the named runs of frames over it.

```ts
useLoadAtlas(options: TUseLoadAtlasOptions) => TLoadedAtlas
```

Example:

```ts
export const Level: TSceneFn = () => {
    const coin = useLoadAtlas({ src: '/assets/atlases/coin.atlas' });

    const sprite = createSprite({ atlas: coin, transform: { x: 160, y: 120 } });
    // The runs come from the file, so the clip is not written twice.
    useSpriteAnimation(sprite, { clips: coin.sequences, play: 'spin' });

    return createScene();
};
```

#### useLoadAudio

Import from `nacatamalon`. Loads a sound, and hands it back at once, still loading, for useSound to play.

```ts
useLoadAudio(options: TUseLoadAudioOptions) => TAudioClip
```

Example:

```ts
declare const chest: TSprite;

export const Level: TSceneFn = () => {
    const coin = useLoadAudio({ src: '/audio/coin.mp3', key: 'coin' });
    const pickup = useSound(coin);
    listen(chest, { onClick: () => pickup.play() });
    return createScene();
};
```

#### useLoadBitmapFont

Import from `nacatamalon`. Loads a bitmap font: an image with the characters drawn in it, and a file saying where each one is.

```ts
useLoadBitmapFont(options: TUseLoadBitmapFontOptions) => TBitmapFont
```

Example:

```ts
export const Title: TSceneFn = () => {
    const font = useLoadBitmapFont({
        json: '/fonts/arcade/arcade.json',
        atlas: '/fonts/arcade/arcade.png',
    });

    createText({ text: 'PRESS START', font, transform: { x: 16, y: 16 } });

    return createScene();
};
```

#### useLoadFont

Import from `nacatamalon`. Loads a vector font: a .ttf or .woff file, the kind a computer has installed.

```ts
useLoadFont(options: TUseLoadFontOptions) => TFont
```

Example:

```ts
export const Title: TSceneFn = () => {
    const font = useLoadFont({ src: '/fonts/Inter.ttf' });

    createText({ text: '¡Hola, ñandú!', font, style: { fontSize: 32 }, transform: { x: 16, y: 16 } });

    return createScene();
};
```

#### useLoadLut

Import from `nacatamalon/effects`. Loads a colour grading table.

```ts
useLoadLut(options: TUseLoadLutOptions) => TLut
```

Example:

```ts
const evening = useLoadLut({ src: '/luts/evening.cube' });
usePostProcess({ ...lutGrade({ amount: 0.8 }), lut: evening });
```

#### useLoadModel

Import from `nacatamalon`. Loads a model made somewhere else: something sculpted in Blender, bought in a pack, exported from anywhere that writes glTF.

```ts
useLoadModel(options: TUseLoadModelOptions) => TGltfModel
```

Example:

```ts
export const Level: TSceneFn = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 6 } });
    useDirectionalLight({ intensity: 1 });

    const tower = useLoadModel({ src: '/models/tower.glb' });
    const placed = createModel({ model: tower });

    useUpdate((delta) => {
        placed.transform.rotationY += delta * 0.5;
    });

    return createScene();
};
```

#### useLoadMotion

Import from `nacatamalon`. Loads a motion: a .motion file of values moved over time by keys, made in an editor's motion panel.

```ts
useLoadMotion(options: TUseLoadMotionOptions) => TMotionFile
```

Example:

```ts
const intro = useLoadMotion({ src: '/motions/intro.motion' });
useMotion(intro, { autoplay: true });
```

#### useLoadPack

Import from `nacatamalon`. Loads a pack: boxes and scenes somebody made in another project, with everything they need.

```ts
useLoadPack(options: TUseLoadPackOptions) => TLoadedPack
```

Example:

```ts
const Plaza: TSceneFn = () => {
    const signs = useLoadPack({ src: '/packs/signpost' });
    createPack({ pack: signs, box: 'Signpost', transform: { x: 100, y: 120 } });
    return createScene();
};
```

#### useLoadPalette

Import from `nacatamalon/effects`. Loads a set of colours for an effect to match a frame against.

```ts
useLoadPalette(options: TUseLoadPaletteOptions) => TPalette
```

Example:

```ts
const nes = useLoadPalette({ src: '/palettes/nes.palette' });
usePostProcess({ ...paletteMatch(), palette: nes });
```

#### useLoadParticles

Import from `nacatamalon`. Loads an effect: what a puff of smoke or a shower of sparks is made of.

```ts
useLoadParticles(options: TUseLoadParticlesOptions) => TParticlesFile
```

Example:

```ts
const fire = useLoadParticles({ src: '/effects/fire.particles' });
createParticles({ effect: fire, transform: { x: 150, y: 250 } });
```

#### useLoadProgress

Import from `nacatamalon`. Keeps count of what a scene is loading, for a loading bar or to wait before changing scene.

```ts
useLoadProgress(assets?: typeOperator) => TLoadProgress
```

Example:

```ts
export const Loading: TSceneFn = () => {
    const scene = useScene();
    const loader = useLoadProgress([
        useLoadTexture({ src: '/assets/hero.png', key: 'hero' }),
        useLoadTexture({ src: '/assets/tiles.png', key: 'tiles' }),
    ]);
    const bar = createSprite({ width: 0, height: 8, transform: { x: 60, y: 112 } });
    useUpdate(() => {
        bar.width = loader.progress * 200;
        if (loader.done) scene.change('Level');
    });
    return createScene();
};
```

#### useLoadScene

Import from `nacatamalon`. Loads a scene laid out in the editor: a .scene file.

```ts
useLoadScene(options: TUseLoadSceneOptions) => TLoadedScene
```

Example:

```ts
const Loading: TSceneFn = () => {
    const scene = useScene();
    const cave = useLoadScene({ src: 'scenes/cave.scene' });
    const loader = useLoadProgress();
    useUpdate(() => {
        if (loader.done) scene.change(cave.name);
    });
    return createScene();
};
```

#### useLoadShader

Import from `nacatamalon/shader`. Loads a shader written in a file, so an effect can live beside the game instead of inside it.

```ts
useLoadShader(options: TUseLoadShaderOptions) => TShader
```

Example:

```ts
export const Level: TSceneFn = () => {
    const crt = useLoadShader({ src: '/shaders/crt.wgsl' });
    const texture = useLoadTexture({ src: '/assets/hero.png' });

    createSprite({ texture, material: createMaterial({ effect: crt, uniforms: { lines: 80 } }) });

    return createScene();
};
```

#### useLoadSong

Import from `nacatamalon`. Loads a song written as notes: a .song file, a few kilobytes where the same tune recorded would be megabytes.

```ts
useLoadSong(options: TUseLoadSongOptions) => TSongFile
```

Example:

```ts
const theme = useLoadSong({ src: '/music/minuet.song' });
useSong(theme, { autoplay: true });
```

#### useLoadTexture

Import from `nacatamalon`. Loads an image to draw with, and hands it back at once, still loading: a sprite made with it appears by itself when the file arrives, so nothing has to wait for it.

```ts
useLoadTexture(options: TUseLoadTextureOptions) => TTexture
```

Example:

```ts
export const Level: TSceneFn = () => {
    const hero = useLoadTexture({ src: '/assets/hero.png', key: 'hero' });
    createSprite({ texture: hero });
    return createScene();
};
```

#### useLoadTrackerSong

Import from `nacatamalon`. Loads a song made in a NES tracker, FamiTracker or FamiStudio, from its text export, and plays it on the engine's NES: the same song useLoadSong gives, for useSong, with its own tempo, key, pan and beats.

```ts
useLoadTrackerSong(options: TUseLoadTrackerSongOptions) => TSongFile
```

Example:

```ts
const theme = useLoadTrackerSong({ src: '/music/theme.txt' });
useSong(theme, { autoplay: true });
```

### Tags

#### useTag

Import from `nacatamalon`. Gives the tag name, to put on things, take off, ask about and find: what something is, 'enemy', 'coin', 'damageable', as many as it likes.

```ts
useTag(name: string) => TTag
```

Example:

```ts
const Enemy = () => {
    useTag('enemy').add(useSelf());
    createSprite({ key: 'bat' });
};

export const Level: TSceneFn = () => {
    const coins = useTag('coin');
    coins.add(createSprite({ key: 'coin', transform: { x: 80, y: 60 } }));
    useUpdate(() => {
        if (coins.all().length === 0) console.log('all taken');
    });
    return createScene();
};
```

### Hitboxes

#### useCollisionBetween

Import from `nacatamalon`. Calls callback for every thing of kind a touching one of kind b, every frame they touch: a rule of the scene, shots against enemies, the player against coins.

```ts
useCollisionBetween(a: A, b: B, callback: (a: TCollisionMatched<A>, b: TCollisionMatched<B>) => void) => () => void
```

Example:

```ts
declare const Bat: (props: { x: number }) => { hurt(damage: number): void };

export const Cave: TSceneFn = () => {
    useSpawn(Bat)({ x: 200 });
    useCollisionBetween('shot', Bat, (shot, bat) => {
        destroy(shot);
        bat.api.hurt(1);
    });
    return createScene();
};
```

#### useCollisionWith

Import from `nacatamalon`. Calls callback with each thing the object being built touches, of one kind: a bullet and the enemies, the player and the coins.

```ts
useCollisionWith(match: M, callback: (other: TCollisionMatched<M>) => void, options: TCollisionWithOptions) => () => void
```

Example:

```ts
declare const Bat: () => { hurt(damage: number): void };

const Bullet = ({ x }: { x: number }) => {
    const self = useSelf();
    createSprite({ width: 4, height: 4, transform: { x, y: 200 } });
    useHitbox();
    useCollisionWith(Bat, (bat) => {
        destroy(self);
        bat.api.hurt(1);
    });
};
```

#### useHitbox

Import from `nacatamalon`. Gives this object a part that can be touched: what useCollisionWith, useCollisionBetween and collisionBetween measure it by instead of its picture.

```ts
useHitbox(options: THitboxOptions) => THitbox
```

Example:

```ts
const Hero = () => {
    const keys = useKeyboard();
    createSprite({ key: 'hero', transform: { x: 40, y: 160 } });
    useTag('player').add(useSelf());
    const body = useHitbox({ width: 10 });

    useUpdate(() => {
        // A hero 24 pixels tall, crouching: 12 tall, and 6 lower so the feet stay where they were.
        const crouching = keys.isDown('ArrowDown');
        body.height = crouching ? 12 : undefined;
        body.y = crouching ? 6 : 0;
    });
};
```

### Physics

#### useCharacterBody

Import from `nacatamalon/physics3d`. Makes this object a walking character: a capsule moved by asking the world what is in the way, and the thing that makes a room walkable.

```ts
useCharacterBody(options: TCharacterOptions) => TCharacterBody | null
```

#### useCharacterBody2d

Import from `nacatamalon/physics2d`. Makes this object a platformer's character: the game says how fast it goes, and the world stops it at the map's walls, holds it on ledges and slopes, and lets it push what the simulation moves.

```ts
useCharacterBody2d(options: TCharacterOptions2d) => TCharacterBody2d
```

Example:

```ts
import { createSprite, useKeyboard, useUpdate } from 'nacatamalon';
import { useCharacterBody2d } from 'nacatamalon/physics2d';

const Hero = () => {
    createSprite({ key: 'hero', transform: { x: 40, y: 100 } });
    const body = useCharacterBody2d({ width: 12, height: 24 });
    const keys = useKeyboard();

    useUpdate(() => {
        const walk = (keys.isDown('ArrowRight') ? 1 : 0) - (keys.isDown('ArrowLeft') ? 1 : 0);
        body.velocity.x = walk * 90;
        if (keys.justPressed('x') &#x26;&#x26; body.isOnGround) body.velocity.y = -300;
        if (keys.justPressed('ArrowDown') &#x26;&#x26; body.isOnOneWay &#x26;&#x26; !body.isOnLadder) body.dropThrough();

        // Onto a ladder with up in front of it, or with down standing on its top.
        const up = keys.isDown('ArrowUp') &#x26;&#x26; !body.isOnOneWay;
        const down = keys.isDown('ArrowDown') &#x26;&#x26; body.isOnOneWay;
        if (body.isOnLadder &#x26;&#x26; (up || down)) body.climbing = true;
        if (body.climbing) body.velocity.y = keys.isDown('ArrowUp') ? -60 : keys.isDown('ArrowDown') ? 60 : 0;
    });
};
```

#### useJoint2d

Import from `nacatamalon/physics2d`. Joins two objects' bodies, or one to a point of the world when b is null: the planks of a bridge, a pendulum on its nail, a platform on a spring, a door on its hinge.

```ts
useJoint2d(a: TGameObject, b: TGameObject | null, joint: TJoint2d) => TJointHandle2d
```

Example:

```ts
import { createScene, createSprite, usePhysicsBody2d, usePhysicsWorld2d, useSpawn } from 'nacatamalon';
import { useJoint2d } from 'nacatamalon/physics2d';
import type { TGameObject, TSceneFn } from 'nacatamalon';

// A bridge of eight planks between two ledges, pinned to the world at both ends.
const Plank = (x: number) => {
    createSprite({ key: 'plank', transform: { x, y: 120 } });
    usePhysicsBody2d({ body: 'dynamic', collider: { shape: 'rect', width: 16, height: 4 } });
};

export const River: TSceneFn = () => {
    usePhysicsWorld2d();
    const spawnPlank = useSpawn(Plank);
    const planks: TGameObject[] = [];
    for (let i = 0; i &#x3C; 8; i++) planks.push(spawnPlank(72 + i * 16));

    useJoint2d(planks[0], null, { type: 'hinge', at: { x: 64, y: 120 } });
    for (let i = 1; i &#x3C; planks.length; i++) {
        useJoint2d(planks[i - 1], planks[i], { type: 'hinge', at: { x: 64 + i * 16, y: 120 } });
    }
    useJoint2d(planks[7], null, { type: 'hinge', at: { x: 64 + 8 * 16, y: 120 } });
    return createScene();
};
```

#### usePhysicsBody2d

Import from `nacatamalon`. Gives this object a flat shape to collide with.

```ts
usePhysicsBody2d(options: TPhysicsBody2dOptions) => TPhysicsBody2d
```

Example:

```ts
const BROWN = getColor('#8b5a2b');

const Crate = (x: number, y: number) => {
    createSprite({ tint: BROWN, width: 24, height: 24, transform: { x, y } });
    usePhysicsBody2d({ body: 'dynamic', collider: { shape: 'rect', width: 24, height: 24 }, bounce: 0.2 });
};
```

#### usePhysicsBody3d

Import from `nacatamalon`. Gives this object a shape in three dimensions to collide with.

```ts
usePhysicsBody3d(options: TPhysicsBody3dOptions) => TPhysicsBody3d
```

#### usePhysicsWorld2d

Import from `nacatamalon`. Opens the flat simulation for this scene, and says how hard things fall.

```ts
usePhysicsWorld2d(options: { id, gravity }) => TPhysicsWorld2d
```

Example:

```ts
export const Level: TSceneFn = () => {
    usePhysicsWorld2d({ gravity: { x: 0, y: 900 } });
    // ...then objects with usePhysicsBody2d, which fall in it.
    return createScene();
};
```

#### usePhysicsWorld3d

Import from `nacatamalon`. Opens the simulation in three dimensions for this scene.

```ts
usePhysicsWorld3d(options: { id, gravity }) => TPhysicsWorld3d
```

#### useSoftBody2d

Import from `nacatamalon/physics2d`. Makes the object being built a soft body in the scene's 2D world: something that squashes, wobbles and rolls rather than keeping its shape.

```ts
useSoftBody2d(options: TSoftBodyOptions2d) => TSoftBody2d
```

Example:

```ts
import { createGame, createPolygon, createScene, useKeyboard, usePhysicsWorld2d, useSpawn, useTransform, useUpdate } from 'nacatamalon';
import { installPhysics2d, useSoftBody2d } from 'nacatamalon/physics2d';

installPhysics2d();

// A green blob that hops with space.
const Blob = () => {
    useTransform({ x: 160, y: 40 });
    const ring = Array.from({ length: 16 }, (_, i) => ({ x: Math.cos(i / 16 * Math.PI * 2) * 20, y: Math.sin(i / 16 * Math.PI * 2) * 20 }));
    const polygon = createPolygon({ points: ring, tint: '#5bd75b' });
    const blob = useSoftBody2d({ shape: 'disk', radius: 20, polygon });
    const keys = useKeyboard();
    useUpdate(() => {
        if (keys.justPressed(' ')) blob.kick(0, -300);
    });
};

const Level = () => {
    usePhysicsWorld2d();
    useSpawn(Blob)();
    return createScene();
};

createGame('#app', { width: 320, height: 180 })({ Level });
```

#### useTileBody2d

Import from `nacatamalon`. Makes this object a platformer's character that walks on the scene's maps, with no physics engine: the game says how fast it goes, and the map stops it at walls, holds it on ledges and slopes, lets it climb ladders and carries it on belts.

```ts
useTileBody2d(options: TCharacterShape2d) => TCharacterBody2d
```

Example:

```ts
const Hero = () => {
    createSprite({ key: 'hero', transform: { x: 40, y: 100 } });
    const body = useTileBody2d({ width: 12, height: 24 });
    const keys = useKeyboard();

    useUpdate(() => {
        const walk = (keys.isDown('ArrowRight') ? 1 : 0) - (keys.isDown('ArrowLeft') ? 1 : 0);
        body.velocity.x = walk * 90;
        if (keys.justPressed('x') &#x26;&#x26; body.isOnGround) body.velocity.y = -300;
        if (keys.justPressed('ArrowDown') &#x26;&#x26; body.isOnOneWay &#x26;&#x26; !body.isOnLadder) body.dropThrough();

        // Onto a ladder with up in front of it, or with down standing on its top.
        const up = keys.isDown('ArrowUp') &#x26;&#x26; !body.isOnOneWay;
        const down = keys.isDown('ArrowDown') &#x26;&#x26; body.isOnOneWay;
        if (body.isOnLadder &#x26;&#x26; (up || down)) body.climbing = true;
        if (body.climbing) body.velocity.y = keys.isDown('ArrowUp') ? -60 : keys.isDown('ArrowDown') ? 60 : 0;
    });
};
```

### Capture

#### useCapture

Import from `nacatamalon`. A camera for the game itself: takes photos of what the player sees, the whole screen or a part of it, for a photography game, an album, a "spot the difference", a sticker of the boss beaten.

```ts
useCapture() => TCapture
```

Example:

```ts
const capture = useCapture();
const shutter = useAction('shoot');
// The print the last photo shows on, made once in the body.
const film = createPixels(96, 72);
const print = createPixelTexture(film);
createSprite({ texture: print, transform: { x: 270, y: 50 } });
let score = 0;

useUpdate(() => {
    if (shutter.justPressed()) {
        void capture({ region: 'Viewfinder', hide: ['Hud'], width: 96, height: 72 }).then((photo) => {
            const bird = photo.boxes.find((b) => b.name === 'Bird');
            score += bird ? Math.round(bird.seen * 100) : 0;
            film.data.set(photo.data);
            updatePixelTexture(print);
        });
    }
});
```

### Math

#### useRandom

Import from `nacatamalon/math`. The random numbers of this game.

```ts
useRandom() => TRandomHandle
```

Example:

```ts
export const Level: TSceneFn = () => {
    const random = useRandom();

    for (let i = 0; i &#x3C; 20; i++) {
        createSprite({ key: 'tree', transform: { x: random.rand(0, 480), y: random.rand(0, 320) } });
    }

    return createScene();
};
```

### Debug

#### useHelperAxis

Import from `nacatamalon`. Draws the three axes as coloured lines from where it is placed: X red, Y green, Z blue, the colours every 3D tool uses.

```ts
useHelperAxis(options: TUseHelperAxisOptions) => TLines
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', fov: 60, transform: { y: 1, z: 4, rotationX: -0.2 } });
    useHelperAxis({ size: 2, negative: true });
    return createScene();
};
```

#### useHelperGrid

Import from `nacatamalon`. Draws a floor grid on the ground plane, the one every 3D editor shows, with its middle lines in the colours of the X and Z axes.

```ts
useHelperGrid(options: TUseHelperGridOptions) => TLines
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', fov: 60, transform: { y: 4, z: 8, rotationX: -0.45 } });
    useHelperGrid({ half: 5 });
    return createScene();
};
```

#### useLines

Import from `nacatamalon`. Draws any lines you like, and hands back the function that replaces them at any time.

```ts
useLines(options: TUseLinesOptions) => TLinesHandle
```

Example:

```ts
declare const target: { x: number; y: number; z: number };

const Level = () => {
    useCamera3d({ projection: 'perspective', fov: 60, transform: { z: 6 } });
    const [, setPath] = useLines();

    useUpdate(() => {
        // One yellow line from the origin to wherever the enemy is heading.
        setPath(new Float32Array([0, 0, 0, 1, 1, 0, 1, target.x, target.y, target.z, 1, 1, 0, 1]));
    });

    return createScene();
};
```

## Creators

What puts something in the game: the game itself, scenes, sprites, text, models, particles, packs and the rest.

### Game

#### createGame

Import from `nacatamalon`. Puts a game in the page: a canvas inside target, drawn at width × height.

```ts
createGame(target: TGameTarget, game_options: TGameOptions) => (scenes: Readonly<Record<string, TSceneFn>>, initialScene?: string) => TGameInstance
```

Example:

```ts
const Title: TSceneFn = () => createScene();
const Level: TSceneFn = () => createScene();

const game = createGame('#game', {
    width: 320,
    height: 224,
    background: '#000000',
    scaling: 'integer',
})({ Title, Level }, 'Title');

game.on('error', (error) => console.error(error));
```

### Scenes

#### createScene

Import from `nacatamalon`. Ends a scene's body, and is what it returns: return createScene().

```ts
createScene(children: typeOperator) => TScene
```

Example:

```ts
const Menu: TSceneFn = () => createScene();

const Level: TSceneFn = () => {
    const hero = createSprite({ width: 16, height: 16 });
    useUpdate((delta) => {
        hero.transform.x += 30 * delta;
    });
    return createScene();
};

createGame('#app', { width: 320, height: 240 })({ Menu, Level });
```

### Game objects

#### createMaterial

Import from `nacatamalon`. Makes a material: a shader of your own, and for models the surface it is drawn over.

```ts
createMaterial: TCreateMaterial
```

Example:

```ts
const Level = () => {
    const texture = useLoadTexture({ src: '/assets/hero.png' });

    const crt = createMaterial({
        fragment: `fn effect(color: vec4&#x3C;f32>, uv: vec2&#x3C;f32>) -> vec4&#x3C;f32> {
            let band = 0.5 + 0.5 * sin(uv.y * mu.lines + mu.time * 6.0);
            return vec4&#x3C;f32>(color.rgb * band, color.a);
        }`,
        uniforms: { lines: 200 },
    });

    createSprite({ texture, material: crt });

    return createScene();
};
```

#### createMesh

Import from `nacatamalon`. Puts a model in the scene: a shape, somewhere, with a surface.

```ts
createMesh(options: TMeshOptions) => TMesh
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 4 } });
    const crate = createMesh({ geometry: useCubeGeometry(), tint: '#c08040' });

    useUpdate((delta) => {
        crate.transform.rotationY += delta;
    });

    return createScene();
};
```

#### createModel

Import from `nacatamalon`. Puts a loaded model in the scene.

```ts
createModel(options: TModelOptions) => TModel
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 6 } });
    useDirectionalLight({ intensity: 1 });

    const tower = useLoadModel({ src: '/models/tower.glb' });
    const placed = createModel({ model: tower, transform: { y: -1 } });

    useUpdate((delta) => {
        placed.transform.rotationY += delta * 0.5;
    });

    return createScene();
};
```

#### createNineSlice

Import from `nacatamalon`. Puts a picture in the scene that can be any size without deforming: a panel, a button, a dialogue box, a health bar.

```ts
createNineSlice(options: TNineSliceOptions) => TNineSlice
```

Example:

```ts
export const Menu: TSceneFn = () => {
    useLoadTexture({ src: '/assets/ui/panel.png', key: 'panel' });
    const panel = createNineSlice({
        key: 'panel',
        slice: 8,
        width: 200,
        height: 64,
        transform: { x: 160, y: 120 },
        onClick: () => { panel.width += 16; },
    });

    return createScene();
};
```

#### createPack

Import from `nacatamalon`. Places one thing a pack offers: a box, or a scene as one part of the scene that creates it.

```ts
createPack(options: TPackOptions) => TGameObject
```

Example:

```ts
const Menu: TSceneFn = () => {
    const ui = useLoadPack({ src: '/packs/ui-kit' });
    createPack({ pack: ui, box: 'Button', transform: { x: 160, y: 120 } });
    createPack({ pack: ui, box: 'Button', transform: { x: 160, y: 160 }, props: { label: 'QUIT' } });
    return createScene();
};
```

#### createParticles

Import from `nacatamalon`. Puts an emitter in the scene: a place that makes particles, following an effect from a file.

```ts
createParticles(options: TParticlesOptions) => TParticles
```

Example:

```ts
const Level = () => {
    const fire = useLoadParticles({ src: '/effects/fire.particles' });

    createParticles({ effect: fire, transform: { x: 150, y: 250 } });

    return createScene();
};
```

#### createParticles3d

Import from `nacatamalon`. Puts an emitter in a scene in three dimensions, following an effect written for them.

```ts
createParticles3d(options: TParticles3dOptions) => TParticles3d
```

Example:

```ts
const Campfire = () => {
    const fire = useLoadParticles({ src: '/effects/campfire3d.particles' });

    createParticles3d({ effect: fire, transform: { y: 0.1 } });

    return createScene();
};
```

#### createPolygon

Import from `nacatamalon`. Puts a shape in the scene whose corners the game moves: a picture stretched over an outline that can change every frame.

```ts
createPolygon(options: TPolygonOptions) => TPolygon
```

Example:

```ts
export const Pond: TSceneFn = () => {
    // A blob of twelve corners round a circle, wobbling.
    const corners = Array.from({ length: 12 }, (_, i) => ({ angle: (i / 12) * Math.PI * 2 }));
    const blob = createPolygon({
        points: corners.map(({ angle }) => ({ x: Math.cos(angle) * 24, y: Math.sin(angle) * 24 })),
        tint: '#5bd75b',
        transform: { x: 160, y: 90 },
    });

    useUpdate((_delta, time) => {
        corners.forEach(({ angle }, i) => {
            const r = 24 + Math.sin(time * 4 + angle * 3) * 3;
            blob.points[i].x = Math.cos(angle) * r;
            blob.points[i].y = Math.sin(angle) * r;
        });
    });
    return createScene();
};
```

#### createShapes

Import from `nacatamalon`. Puts shapes in the scene, drawn by the graphics card as they are each frame: rectangles, with rounded corners or not, circles, ellipses and slices of them, outlines of any number of corners (hollowed in too, a star or a letter), and lines as wide as asked for.

```ts
createShapes(options: TShapesOptions) => TShapes
```

Example:

```ts
export const Level: TSceneFn = () => {
    let life = 1;
    let cooldown = 0;
    const hud = createShapes({
        transform: { x: 8, y: 8 },
        shapes: [
            { type: 'rect', x: 0, y: 0, width: 60, height: 6, radius: 2, fill: '#e15554' },
            { type: 'rect', x: 0, y: 0, width: 60, height: 6, radius: 2, stroke: { width: 1, color: '#ffffff', align: 'outside' } },
            { type: 'circle', x: 80, y: 3, radius: 6, start: -Math.PI / 2, end: -Math.PI / 2, fill: '#ffd23f' },
        ],
    });
    const bar = hud.shapes[0] as TShapeRect;
    const pie = hud.shapes[2] as TShapeCircle;

    useUpdate((delta) => {
        cooldown = Math.min(1, cooldown + delta / 3);
        bar.width = 60 * life;
        pie.end = -Math.PI / 2 + cooldown * Math.PI * 2;
    });
    return createScene();
};
```

#### createSpriteTexture

Import from `nacatamalon`. Makes a picture and fills it with a component: whatever component draws, and whatever it creates with useSpawn, ends up in the picture instead of on the screen, every frame.

```ts
createSpriteTexture(options: TSpriteTextureOptions, component?: (args: TArgs) => unknown, args: TArgs) => TTexture
```

Example:

```ts
const Screen = () => {
    const pet = createSprite({ key: 'pet', transform: { x: 40, y: 40 } });
    useUpdate((delta) => {
        pet.transform.x += delta * 10;
    });
};

const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { z: 3 } });
    const screen = createSpriteTexture({ width: 128, height: 128 }, Screen);
    createMesh({ geometry: useCubeGeometry(), texture: screen });
    return createScene();
};
```

#### createText

Import from `nacatamalon`. Writes something on screen with a bitmap font: a score, a title, a menu, "PRESS START".

```ts
createText(options: TTextOptions) => TText
```

Example:

```ts
export const Level: TSceneFn = () => {
    // No font given: the engine's own.
    const score = createText({ text: 'SCORE 0', style: { fontSize: 16 }, transform: { x: 8, y: 8 } });
    // A font of your own, loaded from its two files.
    const font = useLoadBitmapFont({ json: '/fonts/arcade/arcade.json', atlas: '/fonts/arcade/arcade.png' });
    createText({ text: 'HI SCORE', font, transform: { x: 8, y: 28 } });
    // A font an earlier scene loaded with useLoadFont({ src, key: 'title' }), by its key alone.
    createText({ text: 'LEVEL 1', font: 'title', transform: { x: 8, y: 48 } });
    let points = 0;

    useUpdate(() => {
        points += 1;
        score.text = 'SCORE ' + points;
    });

    return createScene();
};
```

#### createView

Import from `nacatamalon`. A view of the world in a rectangle of the screen: half of a split screen, a minimap in a corner, a rear-view mirror.

```ts
createView(options: TViewOptions, component?: (args: TArgs) => unknown, args: TArgs) => TSprite
```

Example:

```ts
const Half = (player: TSprite) => {
    useCamera2d();
    useCameraFollow(player);
};

const Level = () => {
    useWorldOffScreen();
    const p1 = createSprite({ key: 'hero', transform: { x: 40, y: 200 } });
    const p2 = createSprite({ key: 'hero', transform: { x: 120, y: 200 } });
    createView({ x: 0, y: 0, width: 240, height: 270 }, Half, p1);
    createView({ x: 240, y: 0, width: 240, height: 270 }, Half, p2);
    return createScene();
};
```

### Store

#### createGameStore

Import from `nacatamalon`. Makes a store: data of the game that outlives a scene (lives, coins, options, a pet), with a way to be told when it changes and, optionally, to be saved and loaded.

```ts
createGameStore(config: TGameStoreConfig<S, A>) => TGameStore<S, A>
```

Example:

```ts
declare const pet: TSprite;
const GREEN = getColor('#40c040');
const RED = getColor('#e04040');

// stores/pet.ts
export const petStore = createGameStore({
    key: 'pet',
    state: { hunger: 70, timesFed: 0 },
    actions: (set) => ({
        feed: () => set((s) => { s.hunger = Math.min(100, s.hunger + 15); s.timesFed++; }),
    }),
    persist: { adapter: localStorageAdapter() },
});

// when the game starts
await petStore.load();

// in a scene
useStore(petStore, (s) => s.hunger > 30, (content) => { pet.tint = content ? GREEN : RED; });
```

### Signals

#### createGameSignal

Import from `nacatamalon`. Makes a signal: a way for one part of the game to say "this happened" and for any other part to react, without the two knowing about each other.

```ts
createGameSignal() => TGameSignal<T>
```

Example:

```ts
let score = 0;

// events.ts
export const coinCollected = createGameSignal&#x3C;number>();

// coin.ts
coinCollected.emit(10);

// hud.ts, inside a scene
useSignal(coinCollected, (points) => { score += points; });
```

### Sprites

#### createBackdrop

Import from `nacatamalon`. A strip of picture that repeats endlessly across the world and moves with the camera only so much: the far layers of a platformer, clouds, hills, a city skyline, as the 16-bit consoles drew them.

```ts
createBackdrop(options: TBackdropOptions) => TSprite
```

Example:

```ts
export const Level: TSceneFn = () => {
    useLoadTexture({ src: '/backdrops/clouds.png', key: 'clouds' });
    useLoadTexture({ src: '/backdrops/hills.png', key: 'hills' });
    createBackdrop({ key: 'clouds', parallax: { x: 0.1, y: 0 }, y: 20 });
    createBackdrop({ key: 'hills', parallax: { x: 0.4, y: 0.2 }, y: 120 });
    return createScene();
};
```

#### createSprite

Import from `nacatamalon`. Puts a picture in the scene: an image from useLoadTexture, a frame of a sheet, or a plain coloured rectangle when it has neither.

```ts
createSprite: TCreateSprite
```

Example:

```ts
export const Level: TSceneFn = () => {
    const texture = useLoadTexture({ src: '/assets/hero.png' });
    const hero = createSprite({ texture, transform: { x: 160, y: 120 } });
    // No picture: a coloured rectangle, placed by its top-left corner.
    createSprite({ width: 320, height: 16, tint: '#3a2a1a', anchor: { x: 0, y: 0 }, transform: { y: 224 } });

    useUpdate((delta) => {
        hero.transform.rotation += delta;
    });

    return createScene();
};
```

### Tilemaps

#### createTilemap

Import from `nacatamalon`. Loads a map and draws it: one call per layer, however many cells it has.

```ts
createTilemap(options: TTilemapOptions) => TTilemap
```

Example:

```ts
export const Level: TSceneFn = () => {
    const map = createTilemap({ src: '/maps/level1.tilemap' });
    const player = createSprite({ key: 'hero', zIndex: 0 });

    useUpdate((delta) => {
        // The ground layer is drawn under the player and the treetops over them, because the
        // file says 'under' and 'over' and the player never mentioned a number at all.
        if (!solidAtPoint(map, player.transform.x + 8, player.transform.y)) {
            player.transform.x += 60 * delta;
        }
    });

    return createScene();
};
```

### Geometry

#### createGeometry

Import from `nacatamalon`. Makes a shape of your own, corner by corner: a terrain from a height map, a trail, a ramp, a star pushed out into a solid, anything the shape hooks do not make.

```ts
createGeometry(data: TGeometryData) => TGeometry
```

Example:

```ts
const Level = () => {
    useCamera3d({ projection: 'perspective', transform: { y: 1, z: 4 } });
    useDirectionalLight({ intensity: 1 });

    // A ramp: four corners and two triangles, counter-clockwise from the side that is seen.
    const ramp = createGeometry({
        positions: [-1, 0, 1, 1, 0, 1, 1, 1, -1, -1, 1, -1],
        indices: [0, 1, 2, 0, 2, 3],
        uvs: [0, 1, 1, 1, 1, 0, 0, 0],
    });
    createMesh({ geometry: ramp, tint: '#ff9e3d' });

    return createScene();
};
```

### Pixels

#### createPixels

Import from `nacatamalon/pixels`. A new picture to paint on in code, transparent or filled with one colour: what to use instead of a <canvas> whenever a picture has to be generated rather than drawn.

```ts
createPixels(width: number, height: number, fill?: TColorOption) => TPixels
```

Example:

```ts
const tile = createPixels(16, 16, '#2a6b3a');
fillRect(tile, 0, 15, 16, 1, '#1a4a28');
```

#### createPixelTexture

Import from `nacatamalon/pixels`. Turns a picture into a texture a sprite, a model's material or a shader shows like any image loaded from a file.

```ts
createPixelTexture(source: TPixels | TLoadedPixels, paint: TPixelPaint, options?: TPixelTextureOptions) => TTexture
createPixelTexture(source: TPixels | TLoadedPixels, options?: TPixelTextureOptions) => TTexture
```

Example:

```ts
const Level = () => {
    // Painted in code.
    const art = createPixels(16, 16, '#1d2b53');
    fillCircle(art, 8, 8, 6, '#ffec27');
    createSprite({ texture: createPixelTexture(art), width: 64, height: 64 });

    // A drawn sprite in another colour, made from the one file.
    const hero = useLoadPixels({ src: '/assets/hero.png' });
    const red = createPixelTexture(hero, (p) => swapColors(p, [['#3a7bff', '#e23d3d']]));
    createSprite({ texture: red, width: 32, height: 32 });
    return createScene();
};
```

### Assets & loading

#### createSpriteAtlas

Import from `nacatamalon`. Describes a sheet as a grid of frames, so many pictures can live in one image.

```ts
createSpriteAtlas(options: TSpriteAtlasOptions) => TSpriteAtlas
```

Example:

```ts
const walk = useLoadTexture({ src: '/assets/walk.png', key: 'walk' });
const sheet = createSpriteAtlas({ texture: walk, columns: 6 });

createSprite({ atlas: sheet, frame: 0, transform: { x: 80, y: 120 } });
```

### Physics

#### createPhysicsBody2d

Import from `nacatamalon`. A complete flat collider from a partial description.

```ts
createPhysicsBody2d(options: { id, name, body, collider } & Partial<TPhysicsSurface>) => TPhysicsBody2d
```

#### createPhysicsBody3d

Import from `nacatamalon`. A complete collider in three dimensions from a partial description: the sibling of createPhysicsBody2d, filling in the same defaults.

```ts
createPhysicsBody3d(options: { id, name, body, collider, offset } & Partial<TPhysicsSurface>) => TPhysicsBody3d
```

### Headless

#### createHeadlessGame

Import from `nacatamalon/headless`. Makes a game that runs with no browser and no screen, in Node or Bun: the same scenes, hooks and updates as in a page, moved a frame at a time by whoever holds it.

```ts
createHeadlessGame(options: THeadlessGameOptions) => THeadlessGame
```

Example:

```ts
import { expect, test } from 'bun:test';
import { writeFileSync } from 'node:fs';
import { createHeadlessGame } from 'nacatamalon/headless';
import { encodePng } from 'nacatamalon/authoring';
import { Level } from '../src/scenes/level';

test('the player walks right', async () => {
    const game = createHeadlessGame({ width: 320, height: 180, scenes: { Level } });
    await game.loaded();
    const start = game.find('Player')!.transform!.x;

    game.press('ArrowRight');
    game.run(60);
    game.release('ArrowRight');

    expect(game.find('Player')!.transform!.x).toBeGreaterThan(start);
    expect(game.drawn().some((p) => p.text === 'SCORE 0')).toBe(true);
    writeFileSync('level.png', encodePng(game.capture({ scale: 4 })));   // to look at it
    game.destroy();
});
```

## A complete game

The Breakout the tutorial builds, as its final chapter leaves it: a working game in three files.

`index.ts`:

```ts
import {
    createGame,
    createScene,
    createSprite,
    getColor,
    useKeyboard,
    useSpawn,
    useUpdate,
} from 'nacatamalon';
import { dither, usePostProcess, POST_EFFECTS } from 'nacatamalon/effects';
import { BRICK_COLS, BRICK_ROWS, BRICK_H, Brick, brickAt } from './bricks';
import type { BrickHandle } from './bricks';
import { createHud, showMessage } from './hud';

const WIDTH = 480;
const HEIGHT = 320;

const PADDLE_W = 64;
const PADDLE_H = 10;
const PADDLE_Y = HEIGHT - 24;
const PADDLE_SPEED = 340;

const BALL_SIZE = 8;
const BALL_SPEED = 230;

const Game = () => {
    const keys = useKeyboard();
    const hud = createHud(WIDTH);

    const paddle = createSprite({
        anchor: { x: 0, y: 0 },
        transform: { x: (WIDTH - PADDLE_W) / 2, y: PADDLE_Y },
        width: PADDLE_W,
        height: PADDLE_H,
        tint: getColor('#ff6b1a'),
    });

    const ball = createSprite({
        anchor: { x: 0, y: 0 },
        transform: { x: WIDTH / 2, y: 200 },
        width: BALL_SIZE,
        height: BALL_SIZE,
        tint: getColor('#ede6f5'),
    });

    const bricks: BrickHandle[] = [];
    const spawnBrick = useSpawn(Brick);
    for (let row = 0; row < BRICK_ROWS; row++) {
        for (let col = 0; col < BRICK_COLS; col++) {
            spawnBrick({ row, col, bricks });
        }
    }

    usePostProcess(dither({ levels: POST_EFFECTS.COLOR_LEVELS.genesis }));

    let velocityX = BALL_SPEED * 0.55;
    let velocityY = -BALL_SPEED;
    let score = 0;
    let lives = 3;
    let over = false;

    const serve = () => {
        ball.transform.x = WIDTH / 2;
        ball.transform.y = 200;
        velocityX = BALL_SPEED * 0.55;
        velocityY = -BALL_SPEED;
    };

    useUpdate((dt) => {
        if (over) return;

        if (keys.isDown('ArrowLeft')) paddle.transform.x -= PADDLE_SPEED * dt;
        if (keys.isDown('ArrowRight')) paddle.transform.x += PADDLE_SPEED * dt;
        paddle.transform.x = Math.min(Math.max(paddle.transform.x, 0), WIDTH - PADDLE_W);

        ball.transform.x += velocityX * dt;
        ball.transform.y += velocityY * dt;

        if (ball.transform.x <= 0) {
            ball.transform.x = 0;
            velocityX = Math.abs(velocityX);
        }
        if (ball.transform.x + BALL_SIZE >= WIDTH) {
            ball.transform.x = WIDTH - BALL_SIZE;
            velocityX = -Math.abs(velocityX);
        }
        if (ball.transform.y <= 0) {
            ball.transform.y = 0;
            velocityY = Math.abs(velocityY);
        }

        const onPaddle =
            velocityY > 0 &&
            ball.transform.y + BALL_SIZE >= PADDLE_Y &&
            ball.transform.y <= PADDLE_Y + PADDLE_H &&
            ball.transform.x + BALL_SIZE >= paddle.transform.x &&
            ball.transform.x <= paddle.transform.x + PADDLE_W;

        if (onPaddle) {
            ball.transform.y = PADDLE_Y - BALL_SIZE;
            velocityY = -Math.abs(velocityY);
            const offset = (ball.transform.x + BALL_SIZE / 2 - paddle.transform.x) / PADDLE_W - 0.5;
            velocityX = offset * BALL_SPEED * 1.6;
        }

        const brick = brickAt(bricks, ball.transform.x, ball.transform.y, BALL_SIZE);
        if (brick) {
            brick.hit();
            const above = ball.transform.y + BALL_SIZE / 2 < brick.y + BRICK_H / 2;
            velocityY = above ? -Math.abs(velocityY) : Math.abs(velocityY);
            score += 100;
            hud.score.text = `SCORE ${score}`;
        }

        if (ball.transform.y > HEIGHT) {
            lives -= 1;
            hud.lives.text = `LIVES ${Math.max(lives, 0)}`;
            if (lives <= 0) {
                over = true;
                showMessage(hud, 'GAME OVER');
            } else {
                serve();
            }
        }

        if (bricks.length === 0) {
            over = true;
            showMessage(hud, 'YOU WIN');
        }
    });

    return createScene();
};

const game = createGame('#app', {
    width: WIDTH,
    height: HEIGHT,
    background: getColor('#12102b'),
});

game({ Game });
```

`bricks.ts`:

```ts
import { createSprite, destroy, getColor, useSelf } from 'nacatamalon';

export const BRICK_COLS = 8;
export const BRICK_ROWS = 5;
export const BRICK_W = 52;
export const BRICK_H = 16;

const MARGIN_X = 18;
const GAP_X = 4;
const TOP = 56;
const GAP_Y = 6;

const ROW_COLORS = ['#b14bff', '#ff6b1a', '#ffb347', '#5ad1e6', '#ede6f5'];

export type BrickHandle = {
    x: number;
    y: number;
    hit: () => void;
};

export const Brick = ({ row, col, bricks }: { row: number; col: number; bricks: BrickHandle[] }) => {
    const self = useSelf();
    const x = MARGIN_X + col * (BRICK_W + GAP_X);
    const y = TOP + row * (BRICK_H + GAP_Y);

    createSprite({
        anchor: { x: 0, y: 0 },
        transform: { x, y },
        width: BRICK_W,
        height: BRICK_H,
        tint: getColor(ROW_COLORS[row]),
    });

    const brick: BrickHandle = {
        x,
        y,
        hit: () => {
            bricks.splice(bricks.indexOf(brick), 1);
            destroy(self);
        },
    };

    bricks.push(brick);
};

export const brickAt = (bricks: BrickHandle[], x: number, y: number, size: number): BrickHandle | undefined =>
    bricks.find(
        (brick) =>
            x + size > brick.x &&
            x < brick.x + BRICK_W &&
            y + size > brick.y &&
            y < brick.y + BRICK_H,
    );
```

`hud.ts`:

```ts
import { createText, getColor, useLoadBitmapFont } from 'nacatamalon';
import type { TText } from 'nacatamalon';

export type Hud = {
    score: TText;
    lives: TText;
    message: TText;
};

export const createHud = (width: number): Hud => {
    const font = useLoadBitmapFont({
        json: '/hud_font.json',
        atlas: '/hud_font.png',
    });

    const score = createText({
        text: 'SCORE 0',
        font,
        style: { fontSize: 16 },
        transform: { x: 16, y: 14 },
        tint: getColor('#ede6f5'),
    });

    const lives = createText({
        text: 'LIVES 3',
        font,
        style: { fontSize: 16 },
        anchor: { x: 1, y: 0 },
        transform: { x: width - 16, y: 14 },
        tint: getColor('#ffb347'),
    });

    const message = createText({
        text: '',
        font,
        style: { fontSize: 24 },
        anchor: { x: 0.5, y: 0 },
        transform: { x: width / 2, y: 200 },
        tint: getColor('#ff6b1a'),
        zIndex: 10,
    });

    return { score, lives, message };
};

export const showMessage = (hud: Hud, text: string) => {
    hud.message.text = text;
};
```

## API reference

Every symbol a game can import, by topic. Functions first, then types.

### Game

Starting an instance, the handle a running game exposes, its events, and what the renderer resolved to.

#### createGame (function, from `nacatamalon`)

```ts
createGame(target: TGameTarget, game_options: TGameOptions) => (scenes: Readonly<Record<string, TSceneFn>>, initialScene?: string) => TGameInstance
```

Puts a game in the page: a canvas inside `target`, drawn at `width × height`.

It is called twice. The first call makes the canvas and returns a function; calling that with the
game's scenes starts it, on the first one or on the one named second. A scene is named by its key,
which is how `useScene().change('Level')` finds it later.

Nothing waits. The renderer starts on its own time, so what comes back is the running game at
once: `on('ready', …)` hears when it is up, and `on('error', …)` why it could not start.

- `target: TGameTarget`: Where the canvas goes: a CSS selector, an element, or `null` for the page's body.
- `game_options: TGameOptions`: Its resolution, background, and how it scales to the page. See `TGameOptions`.

#### useGame (function, from `nacatamalon`)

```ts
useGame() => TGameHandle
```

The game itself: its size, its background, whether images are kept crisp, how fast time runs,
and whether everything is frozen.

These are the settings that belong to the whole game rather than to one scene, and until now
they could only be chosen once, when the game was created. This is how a pause menu, an options
screen or a boss entrance changes them while it runs.

Call it while the scene is being built, like every hook, and keep what it returns: the handle
stays live, so reading it in a frame long afterwards still gives today's answer.

#### VERSION (variable, from `nacatamalon`)

```ts
VERSION: string
```

The engine's version, as a string: what the console prints when a game starts, and what a bug
report should quote.

It is read from the package's own `package.json`, so it is always the version that is running.

#### TCanvasAlign (type, from `nacatamalon`)

```ts
type TCanvasAlign: 'center' | 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
```

Where the game sits in its container when the container is bigger than the game: which side the
space left over goes to. It never changes the game's size, which `scaling` decides first; with
nothing left over (`'fill'`, or a `keep` that shows more world instead) there is nothing to place.

- `'center'`: in the middle both ways.
- `'top'`, `'bottom'`: against that edge, in the middle across.
- `'left'`, `'right'`: against that edge, in the middle up and down.
- `'top-left'`, `'top-right'`, `'bottom-left'`, `'bottom-right'`: in that corner.

The names are in `SCREEN.ALIGN` too: `SCREEN.ALIGN.TOP_LEFT` is `'top-left'`.

#### TCanvasKeep (type, from `nacatamalon`)

```ts
type TCanvasKeep: 'both' | 'height' | 'width' | 'none'
```

Which sides of the resolution stay pinned. Writes the drawing buffer, not CSS: anything
but `'both'` makes the game see more or less world instead of leaving bars.

- `'both'`: the buffer is always `width × height`. Every player sees exactly the same
frame, and space the scale cannot fill stays as letterbox or pillarbox bars. The default,
and the only value where a level designer can be sure what is off-screen.
- `'height'`: the height is pinned and the width follows the container, so a wide screen sees
more world to the sides. What a side-scroller usually wants.
- `'width'`: the width is pinned and the height follows, so a tall screen sees more above and
below. The vertical-shooter version of the same idea.
- `'none'`: nothing pinned. With no fixed side there is no reference to compute a scale
from, so it stays at 1× and the buffer becomes the container's size in CSS pixels.
Desktop-app behaviour, not game behaviour. On a screen at 125% or 150% a CSS pixel is 1.25 or
1.5 of the screen's, so each game pixel is stretched to that and comes out 1 of them in some
columns and 2 in others; for crisp pixel art add `pixelRatio: 'device'`, which draws at the
screen's own pixels so nothing is stretched, at the cost of drawing more of them.

Pairs with `TCanvasScaling`, which is the other axis and decides how big it is drawn.

The names are in `SCREEN.KEEP` too: `SCREEN.KEEP.HEIGHT` is `'height'`.

#### TCanvasScaling (type, from `nacatamalon`)

```ts
type TCanvasScaling: 'none' | 'integer' | 'contain' | 'fill'
```

How much the game's image is magnified. Writes CSS, never the drawing buffer: the game
keeps drawing the same number of pixels, only the size they are painted at changes.

- `'none'`: 1×. The canvas is painted at exactly `width × height` CSS pixels and ignores
the space around it. The default, and what a fixed-size embed wants.
- `'integer'`: the largest whole multiple that fits (2×, 3×, 4×…), counted in the
screen's own pixels. The pixel-art one: every game pixel lands on an exact square block, so
nothing is resampled and no row ends up one pixel taller than its neighbour. On a screen at
125% (a density of 1.25) a whole CSS multiple is not a whole one of the screen, so the CSS size
comes out with fractions (5.6×, which is 7 of the screen's pixels); on a density of 2 it can be
3.5×. Leaves bars when the fit is not exact. Below 1× there is no whole number to snap to, so
it falls back to the exact fit.
- `'contain'`: the largest fit, fractions allowed (2.37×). Fills more of the container than
`'integer'` and keeps the aspect ratio, at the cost of resampling: pixel art shimmers,
smooth art does not care.
- `'fill'`: stretched to the container on both axes, aspect ratio broken. The only value
that ignores `keep`: the buffer stays at the base resolution and CSS does the
distorting. For a background or an effect where the shape does not matter.

Pairs with `TCanvasKeep`, which is the other axis and decides how much world is drawn.

The names are in `SCREEN.SCALING` too: `SCREEN.SCALING.INTEGER` is `'integer'`.

#### TCaptureOptions (type, from `nacatamalon`)

```ts
type TCaptureOptions
```

What a capture is a picture of. Every field left out means what the screen is showing, so
`capture()` with nothing is a picture of the screen.

In the cameras and in `layers`, leaving a field out and writing `null` differ, and the difference
is the reason a capture has options at all. Left out, it is the screen's: the camera a tool is
flying, the kinds it is hiding. `null` is the game's: each scene's own camera, or none, and
every kind. A tool working on the flat half of a level hides the models and looks through a camera
of its own, and a picture of the game taken from it has to undo both.

- `camera3d?: TCamera3d | null`: The 3D camera to look through. Left out, the screen's: the tool's, or else each scene's own. `null`, each scene's own.
- `camera2d?: TCamera2d | null`: The 2D camera to look through. Left out, the screen's. `null`, each scene's own, and a scene
- `width?: number`: Width of the picture in pixels. Left out, the canvas's.
- `height?: number`: Height of the picture in pixels. Left out, the canvas's.
- `layers?: ReadonlyArray<indexedAccess> | null`: The kinds of drawing to keep. Left out, what the screen keeps; `null`, all of them.
- `background?: TColorOption`: The colour behind everything. Left out, the game's background.

#### TConsoleScreen (type, from `nacatamalon`)

```ts
type TConsoleScreen: Required<Pick<TGameOptions, 'width' | 'height' | 'pixelAspect' | 'step'>>
```

A console's screen as `createGame` takes it: how many pixels it drew, their shape and how many
pictures a second. Spread into the options, and whatever comes after wins:
`createGame('#app', { ...SCREEN.CONSOLES.snes, scaling: 'integer' })`.

#### TConsoleScreenName (type, from `nacatamalon`)

```ts
type TConsoleScreenName: 'nes' | 'snes' | 'genesis' | 'gameBoy' | 'gba' | 'ps1' | 'n64'
```

Which consoles `SCREEN.CONSOLES` has the screen of.

#### TGameEventName (type, from `nacatamalon`)

```ts
type TGameEventName: typeOperator
```

The name of one of the game's events.

#### TGameEvents (type, from `nacatamalon`)

```ts
type TGameEvents
```

What a running game tells the page around it, and what each event carries.

- `ready`: the renderer is up and the first scene has run. Carries the game's handle, which is
how anything outside a scene (a React HUD, a debug panel) gets to talk to the game.
- `error`: the game could not start, most often because the browser has neither WebGPU nor
WebGL2, or a scene's update threw once it was running. Carries why, so the page can say so
instead of showing an empty box. A game that throws goes on running, so this is heard once,
for the first error.
- `destroy`: the game has been taken down and its canvas removed.

- `ready: TGameHandle`
- `error: Error`
- `destroy: void`

#### TGameHandle (type, from `nacatamalon`)

```ts
type TGameHandle
```

What a game can do with itself while it runs: the settings that belong to the whole game rather
than to any one scene.

Everything here is live. Read it in a frame, change it from a menu, and the next frame is drawn
with the answer.

- `getWidth: `
- `getHeight: `
- `onResized: `
- `getBackground: `
- `setBackground: `
- `isSmooth: `
- `setSmooth: `
- `getPixelSnap: `
- `setPixelSnap: `
- `getPixelAspect: `
- `setPixelAspect: `
- `getScaling: `
- `setScaling: `
- `hasContextMenu: `
- `setContextMenu: `
- `isPostProcessEnabled: `
- `setPostProcessEnabled: `
- `getTimeScale: `
- `setTimeScale: `
- `pause: `
- `resume: `
- `isPaused: `
- `onPaused: `
- `onResumed: `
- `getBackend: `
- `getFps: `
- `getSceneCount: `
- `getDrawableCount: `
- `enterFullscreen: `
- `exitFullscreen: `
- `isFullscreen: `
- `releaseAsset: `
- `onHidden: `
- `isHidden: `
- `onShown: `
- `onBlurred: `
- `onFocused: `
- `hasFocus: `
- `onLeaving: `

#### TGameInstance (type, from `nacatamalon`)

```ts
type TGameInstance
```

What the second call of `createGame` returns: the handle to a running game.

`destroy` - Stops the loop, releases the renderer and the store, and removes the canvas. Safe to call at any time; subsequent calls have no effect.

`on` - Listens to what the game tells the page around it: `'ready'`, `'error'` and `'destroy'`.

- `destroy: `
- `on: `

#### TGameOptions (type, from `nacatamalon`)

```ts
type TGameOptions
```

How a game's window is set up, for `createGame`: its resolution, the colour behind everything,
how it scales to the page and which renderer draws it. Only `width` and `height` are required.

- `width: number`: The game's resolution: how many pixels it draws, not how big it looks. `320 × 224`
- `height: number`: See `TGameOptions.width`: the two are one decision.
- `background?: TColorOption`: Colour the frame is cleared to before anything draws. Defaults to `darkviolet`,
- `renderer?: TRendererType`: Which backend draws.
- `msaa?: 1 | 4`: MSAA (multisample anti-aliasing) sample count. `1` (default) disables it: the
- `smooth?: boolean`: Default texture filtering for sprites. `false` (default) renders them pixelated
- `pixelSnap?: TPixelSnap`: Which pixels everything flat is drawn on: where each sprite, text, panel and map is drawn is
- `pixelAspect?: number`: How wide a game pixel is painted against how tall: `1` (default), square; `8 / 7` for the NES
- `pixelRatio?: number | 'device'`: How many real pixels each game pixel is drawn with. `1` (default) draws exactly
- `scaling?: TCanvasScaling`: How big the game's image is painted. Writes CSS only: the game keeps drawing
- `keep?: TCanvasKeep`: Which sides of the resolution stay fixed. This one writes the buffer: anything
- `fullscreenScaling?: TCanvasScaling`: How the game's image is painted while it is full screen (`enterFullscreen` on the game
- `align?: TCanvasAlign`: Where the game sits in its container when the container is bigger than the game: `'center'`,
- `step?: TGameStep`: How the game's time moves forward. Default `'display'`.
- `pauseOnBlur?: boolean`: Freeze the loop while nobody can see the page, and unfreeze on the way back. `true` by
- `seed?: number`: Initial seed for this game's random generator (`useRandom`). Absent means a different
- `actions?: TActionMap`: The game's input map: named actions (`'jump'`, `'move_left'`) and the keys, gamepad
- `actionsPersist?: TActionPersist`: Where the player's own rebindings are kept, so they are still there next time. Left out, the
- `contextMenu?: boolean`: Whether a right click over the game opens the browser's menu. `false` (default): the game
- `physicsLayers?: string[]`: Names for the physics layers, in order: the first is layer 0. With them a body says
- `post?: TPostChain`: The full-screen effects this game is shown through, in order.
- `banner?: boolean`: Print the startup line in the console: the engine, its version and which backend started.
- `splash?: boolean | 'always'`: Open with "Made with NacatamalOn": the N grows in, the name slides in beside it, and the game

#### TGamePauseEvent (type, from `nacatamalon`)

```ts
type TGamePauseEvent
```

What `onPaused` and `onResumed` are told: who held the game or let it go. `pause()` with no
reason says `'game'`.

- `reason: string`

#### TGameResizeEvent (type, from `nacatamalon`)

```ts
type TGameResizeEvent
```

What `onResized` is told: the game's new size, in its own pixels, the same numbers `getWidth` and
`getHeight` give from then on.

- `width: number`
- `height: number`

#### TGameStep (type, from `nacatamalon`)

```ts
type TGameStep: 'display' | number
```

How a game's time moves forward: `'display'`, one update per picture the screen shows, or a
number of fixed steps a second (`60`, `50`). See `TGameOptions.step`.

#### TGameTarget (type, from `nacatamalon`)

```ts
type TGameTarget: string | HTMLElement | null
```

Where a game puts its canvas: a CSS selector or an element to put it in (a `<canvas>` is drawn on
as it is), or `null` for the page's body.

#### TPixelSnap (type, from `nacatamalon`)

```ts
type TPixelSnap: 'screen' | 'game' | 'off'
```

Which pixels a game draws on: `'screen'`, whole pixels of the screen; `'game'`, whole pixels of the
game, as the consoles drew; `'off'`, between them. See `TGameOptions.pixelSnap`, and
`RENDER.PIXEL_SNAP` for the three as constants.

### Scenes

Declaring scenes, switching between them, pausing one without stopping the loop, the transitions, and the scene document.

#### boxFromDoc (function, from `nacatamalon/files`)

```ts
boxFromDoc(doc: TSceneDoc, place: { x, y, z }, src: string) => void
```

Builds the box a document describes, while the game runs: a reusable box (a `.box` file, whose
document holds the one box) made again and again, a coin a level drops, an enemy a door lets out.
Give it to `useSpawn`, which says where it goes and builds it at the moment it is called.

Built exactly as a scene builds its boxes: its pictures and sounds loaded, its components and its
children made, and its behaviours running. Each copy gets ids of its own, and `place` puts it
somewhere other than where the document says.

- `doc: TSceneDoc`: The document, as `parseSceneDoc` gave it back: of a reusable box (its root holding
- `place: { x, y, z }`: Where it goes instead (`x`, `y`, `z`), each one left out staying as the document says.
- `src: string`: Where the document came from, named in any warning it causes.

#### copyBox (function, from `nacatamalon`)

```ts
copyBox(box: TBox, place: { x, y, z }) => void
```

Builds another of a box of the scene while the game runs, as the document described it: the same
components, children and behaviours, with ids of its own, where `place` says (or where the
document put the first). Give it to `useSpawn`, as `boxFromDoc`: `useSpawn(copyBox)(enemy)`. What
the first has done since (moved, been hit) is not copied: the copy starts as it was written.

Only a box a scene document built can be copied this way; a box made in code is made again by
calling what made it.

- `box: TBox`: The box to make another of.
- `place: { x, y, z }`: Where it goes instead (`x`, `y`, `z`), each one left out staying as the document says.

#### createScene (function, from `nacatamalon`)

```ts
createScene(children: typeOperator) => TScene
```

Ends a scene's body, and is what it returns: `return createScene()`.

Everything the body made (sprites, models, objects from `useSpawn`) is already in the scene, so
it is usually called with nothing. `children` is for objects built somewhere else and handed in.
It touches nothing itself: it only describes.

It takes no name. A scene is named by its key in what `createGame` is given, so the game knows
every scene by name as soon as it starts, before any of them has run.

- `children: typeOperator`: Objects made outside this body that should hang from the scene too.

#### crossDissolve (function, from `nacatamalon/transitions`)

```ts
crossDissolve(duration: number, mask: TTexture, options: TCrossDissolveOptions) => TTransition
```

The scene coming in shows through in the order a picture says, darkest first, until the one going
out is gone: a `dissolve` with the new scene where the colour was, in one run instead of two.

The picture is the shape, as in `dissolve`: a clock face sweeps like a hand, a star opens from the
middle as a star, noise eats the old scene grain by grain. The one going out is shown as its last
picture, still.

- `duration: number`: How long it takes in seconds, from all of the old scene to all of the new.
- `mask: TTexture`: The shape: a file (`useLoadTexture`) or one painted in code (`createPixelTexture`).
- `options: TCrossDissolveOptions`: How soft its edge is.

#### crossfade (function, from `nacatamalon/transitions`)

```ts
crossfade(duration: number) => TTransition
```

The scene going out fades into the one coming in, both on screen at once, with no colour between.

Halfway through you see half of each. The one going out is shown as its last picture, still: it
has stopped by then, and the one coming in is already playing underneath it.

- `duration: number`: How long it takes in seconds, from all of the old scene to all of the new.

#### crossIris (function, from `nacatamalon/transitions`)

```ts
crossIris(duration: number, center: { x, y }) => TTransition
```

A circle opens from a point with the scene coming in inside it, until the one going out is gone
past the corners.

The one going out is shown as its last picture, still, around the circle. As with `iris`, the
circle stays a circle on a wide screen, and it opens past the furthest corner wherever its centre is.

- `duration: number`: How long it takes to open, in seconds.
- `center: { x, y }`: Where it opens from, in screen fractions: `{ x: 0, y: 0 }` is the top left corner

#### crossWipe (function, from `nacatamalon/transitions`)

```ts
crossWipe(duration: number, direction: TWipeDirection) => TTransition
```

A straight edge crosses the screen once, with the scene coming in behind it and the one going out
ahead of it: the wipe of the films, with no colour in the middle.

The one going out is shown as its last picture, still, while the edge eats it.

- `duration: number`: How long the edge takes to cross, in seconds.
- `direction: TWipeDirection`: Which way the edge travels. To the right unless you say otherwise.

#### dissolve (function, from `nacatamalon/transitions`)

```ts
dissolve(duration: number, mask: TTexture, options: TDissolveOptions) => TTransition
```

The screen is covered in the order a picture says, darkest first, and uncovered the same way back.

The picture is the shape. Grey levels are the order things are covered in: a star painted
black outside and white in the middle closes on the middle as a star, a spiral covers as a spiral,
noise eats the screen grain by grain, and a picture with stripes is a set of blinds. It is stretched
over the whole screen and read blended between its pixels, so a small one still has smooth edges.

Uncovering runs back the way it came, as an iris opens: what was covered last shows first.

- `duration: number`: How long the whole thing takes in seconds, covering and uncovering together.
- `mask: TTexture`: The shape: a file (`useLoadTexture`) or one painted in code (`createPixelTexture`).
- `options: TDissolveOptions`: The colour it covers with, and how soft its edge is.

#### fade (function, from `nacatamalon/transitions`)

```ts
fade(duration: number, color: TColorOption) => TTransition
```

The screen sinks into one colour, the scenes swap behind it, and it comes back.

The one every game reaches for, and the one to reach for when nothing else fits: it says "we went
somewhere else" without saying anything about where, so it never looks wrong.

Note that a fade runs before the rest of the chain, so on a game that reduces its colours the
middle of it is quantized like everything else: stepped, not smooth. That is how a fade looked on
the hardware this engine is aimed at, and it is the reason the transition goes first.

- `duration: number`: How long the whole thing takes in seconds, covering and uncovering together.
- `color: TColorOption`: What the screen sinks into. Black unless you say otherwise.

#### iris (function, from `nacatamalon/transitions`)

```ts
iris(duration: number, color: TColorOption, center: { x, y }) => TTransition
```

A circle closes over the screen and opens again on the scene behind it.

The centre is a parameter, and that is the whole point of the effect. An iris that can only
close on the middle of the screen is a decoration; one that closes on where the character is
standing is the shot that ends a level, and it is what this has been used for since it was done
with a piece of card in front of a lens.

The circle is corrected for the shape of the window, so it stays a circle on a wide screen instead
of becoming an ellipse, and it opens far enough past the furthest corner that "open" really means
nothing covered, wherever the centre is.

- `duration: number`: How long the whole thing takes in seconds, closing and opening together.
- `color: TColorOption`: What the outside of the circle is painted. Black unless you say otherwise.
- `center: { x, y }`: Where it closes on, in screen fractions: `{ x: 0, y: 0 }` is the top left corner

#### parseSceneDoc (function, from `nacatamalon/files`)

```ts
parseSceneDoc(value: unknown, src: string) => TSceneDoc
```

Reads a scene document, whatever state it is in.

Total and never throws. A field that is missing, of the wrong shape or plain nonsense falls
back to its default and the scene still opens. A scene is written by tools and edited by hand, so
treating it as a promise would mean one typo costs you the level.

What it does say out loud, once per document rather than once per field:

- a file that is not a scene of this engine, which comes back empty, because carrying on would
mean quietly presenting somebody else's file as an empty level;
- one written by a newer version, which is read anyway: most of it is still readable, and what
is not is kept rather than lost;
- components and assets this version does not know, which are counted and named.

Nothing is deleted on the way in. An unknown component is kept beside its box and an unknown field
is kept on it, so a document opened here and written back out again is the document that came in.

- `value: unknown`: Whatever `JSON.parse` gave back.
- `src: string`: Where it came from, named in every warning. A game opens a dozen scenes and they all

#### pixelate (function, from `nacatamalon/transitions`)

```ts
pixelate(duration: number, color: TColorOption, minBlocks: number) => TTransition
```

The picture falls apart into bigger and bigger blocks, then into flat colour, then back.

The only one of the five that reads the frame again rather than painting over what it was
handed: it snaps the point it reads to a grid, so one block's worth of screen all comes back the
same colour.

Two numbers here are not arbitrary and both were arrived at by looking at it:

- The block size is what moves in a straight line, not the number of blocks. Size is one over
count, so a straight line in size is a doubling of the count each step, which is what reads as
the picture breaking down. Moving the count instead crawls for most of the transition and then
jumps at the end.
- The colour comes in on `amount * amount`, so the blocks are seen for a good while before the
colour swallows them. Bringing it in straight hides the effect under the paint.

At rest it snaps to the centre of each pixel, which reads back exactly what was there: with
nothing to cover it changes nothing.

- `duration: number`: How long the whole thing takes in seconds, breaking down and coming back.
- `color: TColorOption`: What it flattens into once the blocks are as big as they get.
- `minBlocks: number`: How many blocks across the short side of the screen at the worst of it. Six.

#### push (function, from `nacatamalon/transitions`)

```ts
push(duration: number, direction: TWipeDirection) => TTransition
```

The scene coming in slides onto the screen and pushes the one going out off the other side.

The two travel together, edge to edge, so nothing is ever covered: what leaves on the right came
in on the left. The one going out is shown as its last picture, still.

- `duration: number`: How long it takes in seconds, from all of the old scene to all of the new.
- `direction: TWipeDirection`: Which way both scenes travel. `'right'` brings the new one in from the left. To

#### sceneFromDoc (function, from `nacatamalon/files`)

```ts
sceneFromDoc(doc: TSceneDoc, src: string, options: TSceneFromDocOptions) => () => TScene
```

Turns a scene document into an ordinary scene function.

That is the whole design, and it is what keeps this to one code path. A scene from a file is
handed to `createGame` like any other, and from then on `change`, a transition, a pause and a
destroy all work on it without knowing where it came from. The body it
returns calls the same `createSprite`, `createText` and `createMesh` a person would have written,
so the format cannot express anything the engine's own API cannot build. A second path that
assembled records by hand would be a second path free to drift from the first.

Nothing is thrown. A missing asset costs you that asset and not the scene, because a level that
refused to open over one broken path is a level nobody can fix.

- `doc: TSceneDoc`: The document, as `parseSceneDoc` gave it back.
- `src: string`: Where it came from, named in any warning it causes later.
- `options: TSceneFromDocOptions`: How its behaviours are treated: see `TSceneFromDocOptions`.

#### useScene (function, from `nacatamalon`)

```ts
useScene() => TSceneHandle
```

Returns the controls for the game's scenes: `launch`, `stop`, `change`, `pause`, `resume` and
`isPaused`.

Call it in the scene body, keep the handle, and use it later from `useUpdate`: it holds the game
and the scene it was called in, so it keeps working after the body is over.

#### wipe (function, from `nacatamalon/transitions`)

```ts
wipe(duration: number, direction: TWipeDirection, color: TColorOption) => TTransition
```

A straight edge crosses the screen, and crosses it again the same way to uncover.

The direction travels to the card as a vector, not as the word it was written with. A shader
that branched on a name would need one branch per direction and a fifth for anything mistyped;
projected onto an axis, all four are one line of arithmetic and a diagonal would cost nothing.

This is the one built-in that does not ask `coverAmount()`. It reads `progress` and `phase`
itself and flips which side of the edge is covered, so the edge goes on travelling the same way in
both halves. Taking the leftover of `progress` instead would bring the edge back the way it came.

- `duration: number`: How long the whole thing takes in seconds, covering and uncovering together.
- `direction: TWipeDirection`: Which way the edge travels. To the right unless you say otherwise.
- `color: TColorOption`: What the covered side is painted. Black unless you say otherwise.

#### TAssetEntry (type, from `nacatamalon/files`)

```ts
type TAssetEntry: { type, key, source } | { type, key, src } | { type, key, json, atlas } | { type, key, src, size } | { type, key, src } | { type, key, src } | { type, key, src } | { type, key, src } | { type, key, src } | { type, key, src } | { type, key, src }
```

One thing the scene needs loaded before it can be built, named by the key everything in the
document refers to it by.

The manifest exists so the loading can start before the tree does. Every loader in this engine
registers its slot the moment it is asked and fills it in later, so walking this list first means
that by the time a sprite says "my texture is `hero`", `hero` is already there, possibly still
arriving. Without it, the first frame of a scene would be the frame that discovers what it needs.

A file is named, never inlined. The pixels of an image, the text of a shader, the cells of a
map and the numbers of an effect all stay in their own file. A document that carried them would be
a document that goes stale the moment somebody edits the file, and a diff nobody can read.

`key` is the name and `src` is where it is. They are usually the same string, and they are two
fields because they are two different jobs: one is what the scene calls it, the other is where it
is fetched from. Two scenes naming one file the same thing share one fetch.

#### TAudioListenerComponent (type, from `nacatamalon/files`)

```ts
type TAudioListenerComponent
```

The game's ears, on this object rather than on the camera.

- `type: 'audio-listener'`
- `id: string`
- `enabled?: boolean`: Absent for on.

#### TBoxNode (type, from `nacatamalon/files`)

```ts
type TBoxNode
```

One box, written down: an identity, a place, what it is, and what is under it.

- `id: string`: Stable for the life of the box, and the only thing anything outside the engine can address it
- `name: string`: What it is called where it was placed. Not unique: two enemies can share it.
- `transform: TTransform3d | null`: Where it is, which moves everything under it.
- `components: TComponentDoc[]`: What this box is: its picture, its shape, its camera, its lamp.
- `children: TBoxNode[]`: The boxes placed relative to it. A child is another object, never a part of this one.
- `data?: TDataEntry[]`: The state the box asked for with `useData`, in the order it asked for it.
- `tags?: string[]`: What it is, as `useTag` says it: `["enemy", "damageable"]`. Absent is none.
- `visible?: boolean`: Absent means drawn. Only ever written when it is `false`.
- `screenSpace?: boolean`: Absent means it lives in the world. Only ever written when it is `true`.
- `unlit2d?: boolean`: Left out of the scene's 2D lights (`useUnlit2d`). Only ever written when it is `true`.
- `worldOffScreen?: boolean`: On the scene's top box: the screen draws only what is pinned to it, and the world is seen
- `extra?: Record<string, unknown>`: Whatever else the file said about this box, kept exactly as written and not acted on.

#### TCamera2dComponent (type, from `nacatamalon/files`)

```ts
type TCamera2dComponent
```

The camera the flat half of a scene is seen through.

This one carries its own placement, and it is the exception to the rule above. A camera is
what everything else is measured against, so nothing above it in the tree moves it; putting its
position on its box would claim otherwise, and the claim would come true the moment somebody gave
that box a parent with a transform of its own.

- `type: 'camera-2d'`
- `id: string`
- `transform: { x, y, rotation }`
- `zoom: number`

#### TCamera3dComponent (type, from `nacatamalon/files`)

```ts
type TCamera3dComponent
```

The camera the three-dimensional half is seen through. Carries its own placement, for the reason
given on its flat twin.

- `type: 'camera-3d'`
- `id: string`
- `projection: 'perspective' | 'orthographic'`
- `transform: TTransform3d`
- `fov: number`
- `near: number`
- `far: number`
- `zoom: number`
- `follow2d?: boolean`: Orthographic only: the models placed in the 2D world, seen through the 2D camera. See

#### TComponentDoc (type, from `nacatamalon/files`)

```ts
type TComponentDoc: TSpriteComponent | TNineSliceComponent | TTextComponent | TMeshComponent | TTilemapComponent | TParticlesComponent | TParticles3dComponent | TCamera2dComponent | TCamera3dComponent | TFogComponent | TLightComponent | TScriptComponent | TSoundComponent | TMusicComponent | TSongComponent | TMotionComponent | TAudioListenerComponent | TMaskComponent | TFilterComponent | TShapesComponent | TStoreComponent | TPhysicsBody2dComponent | TPhysicsBody3dComponent | TPhysicsWorld2dComponent | TPhysicsWorld3dComponent | TParticleCollider2dComponent | TParticleCollider3dComponent | TSpriteTextureComponent
```

One thing a box is: its picture, its shape, its camera, its lamp.

Three rules hold across every member here, and each one is a format migration avoided:

- A drawable carries no placement. Where a picture is belongs to its box, so a sprite and the
shadow under it move together by saying it once. A camera and a lamp are the exception and for
one reason: composition deliberately does not reach them. Nothing moves a camera, because it is
what everything else is measured against; and a lamp is moved but never turned, so that a
lantern held by someone who spins does not sweep the room. What the tree cannot decide, the
component has to carry.
- A file is named, never inlined. Shader source, atlas tables, map cells and effect numbers
all stay in their own file and appear here as an asset key.
- A field left out is a field at its default. There is no second convention: `visible` absent
means visible and `flipX` absent means not flipped, because those are what they start at.

The list is uniform even though the runtime keeps typed slots for a camera and a light. A second
light on one box is not an error anywhere in the engine (the last one wins, quietly), and a format
that could not write that would be a format that cannot describe what the engine allows.

#### TCrossDissolveOptions (type, from `nacatamalon/transitions`)

```ts
type TCrossDissolveOptions
```

What `crossDissolve` accepts beyond its duration and its mask.

- `softness?: number`: How soft the edge of the shape is, from `0`, a hard edge, to `1`, which is nearly a crossfade.

#### TDataEntry (type, from `nacatamalon/files`)

```ts
type TDataEntry
```

One piece of state a box asked for with `useData`, written down.

Matched by position, which is the whole design and its whole risk. There is no name to match
on because `useData` does not take one: the first `useData` in a body is the first entry here,
the second is the second. Reading a scene back therefore assumes the body still asks for them in
the same order, and adding one in the middle of a body that has already been saved hands the old
values to the wrong places.

- `value: unknown`

#### TDissolveOptions (type, from `nacatamalon/transitions`)

```ts
type TDissolveOptions
```

What `dissolve` accepts beyond its duration and its mask.

- `color?: TColorOption`: What the covered part is painted. Black unless you say otherwise.
- `softness?: number`: How soft the edge of the shape is, from `0`, a hard edge, to `1`, which is nearly a fade. A

#### TFilterComponent (type, from `nacatamalon/files`)

```ts
type TFilterComponent
```

A filter on this object: effects run over what it, and everything under it, draws.

Written on the object it filters. A filter the game adds to several objects is written on each of
them, with the same `id`. Its `effects` are written as a project's `post` list is: an engine
effect by its key (`builtin`) or a file by its path (`effect`), with its knobs. One written in code
has nothing to be written as and is left out. What is left out is the default: `rect` and `padding`
only when given, `enabled` only when off.

- `type: 'filter'`
- `id: string`
- `effects: TPostChainEntry[]`
- `rect?: { x, y, width, height }`: The part of the screen its effects work on, in this object's space.
- `padding?: number`: Pixels of room around what the object draws, when not what its effects ask.
- `enabled?: boolean`: Left out when on.

#### TFogComponent (type, from `nacatamalon/files`)

```ts
type TFogComponent
```

The fog a scene is seen through. It belongs to the view the way the camera does, so it is written
on the box the camera is: the scene's root, or a box drawn into a picture.

- `type: 'fog'`
- `id: string`
- `color: TColor`
- `near: number`
- `far: number`
- `distance?: boolean`: Left out when on, which is the default: only `false` is written.
- `height?: { top, bottom, enabled }`: The fog by height: where it starts and where it is complete. Left out when it is off and its
- `enabled?: boolean`: Left out when on, which is the default.

#### TLightComponent (type, from `nacatamalon/files`)

```ts
type TLightComponent
```

A lamp.

Its own placement, and five fields of it rather than nine. A lamp is moved by the tree and
never turned by it, so where it ends up is its own place composed with its box's, while which way
it faces stays exactly what is written here. A lamp has no scale and no roll, so writing those
would be writing down three numbers that are structurally always one and one that is always zero.

An ambient light is the whole room at once: no place, no direction, no reach.

- `type: 'light'`
- `id: string`
- `kind: 'directional' | 'point' | 'spot' | 'rect' | 'ambient' | 'point-2d' | 'ambient-2d'`
- `radius?: number`: How far a 2D lamp (`point-2d`) reaches, in pixels of the world.
- `falloff?: number`: How a 2D lamp fades towards its edge: `1` evenly, `2` dimmer sooner, `0` a hard edge.
- `color: TColor`
- `intensity: number`
- `transform?: { x, y, z, rotationX, rotationY }`: Where it is and which way it shines. Not on an ambient one, which is everywhere at once.
- `ambient?: number`: How much of this light reaches a surface facing away from it. Not on an ambient one.
- `range?: number`: How far it reaches. Point, spot and rect only.
- `width?: number`: How big the panel is. Rect only.
- `height?: number`
- `angle?: number`: How wide the cone is, and how soft its edge. Spot only.
- `penumbra?: number`
- `castShadow?: boolean`: Whether the scene's shadows are drawn from this one. Left out is no.
- `shadowBias?: number`: How far the shadow test is pushed off the surface, against a surface striping itself.
- `shadowStrength?: number`: How dark what the light cannot see goes, `0` to `1`.
- `shadowArea?: number`: How far the shadows reach in front of the camera. Directional only.
- `shadowDistance?: number`: How far back the light stands to look at that square. Directional only.

#### TMaskComponent (type, from `nacatamalon/files`)

```ts
type TMaskComponent
```

A mask on this object: it, and everything under it, kept to a rectangle in its own space or to
the shape of another object of the scene, named by its `id`.

Written on the object it masks. A mask the game adds to several objects is written on each of
them, with the same `id`. What is left out is the default: `channel` is written only for
`'luminance'`, and the four switches only when they are not what a mask is made with.

- `type: 'mask'`
- `id: string`
- `rect?: { x, y, width, height }`: The rectangle it keeps to, in this object's space.
- `by?: string`: The `id` of the object whose shape it keeps to. Wins over `rect`.
- `channel?: 'alpha' | 'luminance'`
- `soft?: boolean`
- `invert?: boolean`
- `showShape?: boolean`
- `enabled?: boolean`: Left out when on.

#### TMaterialDoc (type, from `nacatamalon/files`)

```ts
type TMaterialDoc
```

What a material is, written down.

Two ways in, and the first is the one to use: `effect` names a `.wgsl` file, and the hooks
below are left empty. The source itself is only written when there is no file to name, which
happens for a material built in code with its shader typed at the call site.

That preference is the same one a script gets: the file is the truth, and a copy of its contents
living in a scene is a copy that goes stale the moment somebody edits the file. A document that
carried compiled shader source would be as wrong as one carrying a compiled script.

There is no signature here. Which kind each knob is, is worked out from the values themselves on
the way back in, so writing it down would be saying the same thing twice, and two statements of
one fact are two statements that can disagree.

- `name: string | null`
- `effect: string | null`: The `.wgsl` it came from, by asset key. When this is set, the four hooks below are `null`.
- `fragment: string | null`
- `fragmentGlsl: string | null`
- `vertex: string | null`
- `vertexGlsl: string | null`
- `uniforms: TUniformValues | null`: What its knobs are set to. `null` when it has none.
- `maps?: Record<string, TMaterialMapDoc>`: Extra pictures its shader reads by name, each by asset key. Left out when there are none.

#### TMaterialMapDoc (type, from `nacatamalon/files`)

```ts
type TMaterialMapDoc
```

One extra map of a material, as a document writes it: the picture by asset key, and how
it is read when that differs from the defaults.

- `texture: string`
- `wrap?: TTextureWrap | { u, v }`
- `smooth?: boolean`

#### TMeshComponent (type, from `nacatamalon/files`)

```ts
type TMeshComponent
```

A shape in three dimensions.

There is no skeleton here. A rig belongs to the model file it was read out of, and it is adopted
from there when the file lands: writing it into a scene would be writing down a copy of something
the artist owns, in a place they will never look for it.

- `type: 'mesh'`
- `id: string`
- `transform?: TTransform3d`: Where it sits on its box, in three dimensions, left out when that is the box's own origin.
- `geometry: string`: The shape, by asset key.
- `material: TMeshMaterialDoc`
- `uniforms?: TUniformValues`
- `visible?: boolean`
- `zIndex?: number`
- `castShadow?: boolean`: Whether it is drawn into the shadow map. Left out casts, which is what nearly everything
- `alwaysDrawn?: boolean`: Drawn even when nothing of it is on screen. Left out, it is skipped while it is out of view.
- `boundsMargin?: number`: How far past its shape it can reach, in its own units, before it is judged off screen: what a

#### TMeshMaterialDoc (type, from `nacatamalon/files`)

```ts
type TMeshMaterialDoc: TMaterialDoc & { texture, tint, emissive, emissiveTexture, occlusion, specular, shininess, alpha, smooth, wrap, vertexSnap, affine, cull, alphaCutoff, transparent, lighting, steps }
```

A model's material, which is a material plus a surface: what the light finds when it gets
there.

A sprite's has none of this, and that asymmetry is deliberate rather than an omission. In the
plane, the colour, the sheet and how it is read belong to the object: two sprites of one character
tinted differently are two sprites, not two materials. In three dimensions they belong to the
surface, because that is what a light is asking about.

- `texture: string | null`: The sheet, by asset key.
- `tint: TColor`
- `emissive: TColor`
- `emissiveTexture?: string`: The picture the glow comes through, by asset key. Left out when there is none, and for a
- `occlusion?: { texture, strength, uv, wrap }`: Where the light from all around is kept out of its creases: the picture by asset key, how much
- `specular: TColor`
- `shininess: number`
- `alpha: number`
- `smooth?: boolean`
- `wrap?: TTextureWrap | { u, v }`: What the picture does past its edge, one word for both ways or one each. Left out when it
- `vertexSnap?: boolean | number`: Corners on a coarse grid of the screen: `true` for the game's own rows, or how many rows. Left
- `affine?: boolean`: The picture stretched across the screen without correcting for depth. Left out when off, which
- `cull?: TCullMode`: Which side of its triangles is thrown away: `'back'`, `'front'` or `'none'`. Left out when it was
- `alphaCutoff?: number`: The alpha below which its picture is cut out, from 0 to 1: a fence, a leaf. Left out when
- `transparent?: boolean`: Whether it is seen through whatever its alpha: `true` for a picture with see-through parts or a
- `lighting?: TLightingLook`: How it is lit, as the look is written down (`{ look: 'toon', bands: 2, ... }`). When a material
- `steps?: TStepLook[]`: What is done to it once lit, in order, as written down. Left out when there are none.
- `name: string | null`
- `effect: string | null`: The `.wgsl` it came from, by asset key. When this is set, the four hooks below are `null`.
- `fragment: string | null`
- `fragmentGlsl: string | null`
- `vertex: string | null`
- `vertexGlsl: string | null`
- `uniforms: TUniformValues | null`: What its knobs are set to. `null` when it has none.
- `maps?: Record<string, TMaterialMapDoc>`: Extra pictures its shader reads by name, each by asset key. Left out when there are none.

#### TMotionComponent (type, from `nacatamalon/files`)

```ts
type TMotionComponent
```

A motion this object plays: the `.motion` file by asset key, and how it was asked to play. Like a
song, never where it had got to.

- `type: 'motion'`
- `id: string`
- `motion: string`: The `.motion` file, by asset key.
- `autoplay?: boolean`
- `loop?: boolean`: Absent for what the file says.
- `speed?: number`: Absent for `1`.

#### TMusicComponent (type, from `nacatamalon/files`)

```ts
type TMusicComponent
```

A piece of music in layers this object carries: the same tune in several files, played together,
each at its own volume.

Written like a sound: the files by asset key and how loud each layer is meant to be, never how far
into the tune it was.

- `type: 'music'`
- `id: string`
- `layers: { name, audio, volume }[]`: In the order they were given. A layer's name is what the game moves it by.
- `volume?: number`
- `channel?: string`: Absent for `'music'`.
- `autoplay?: boolean`
- `fadeIn?: number`: Seconds `autoplay` takes to come up from silence, and to go down to it when the object goes
- `fadeOut?: number`
- `loopStart?: number`: Where the loop goes back to and turns back, in seconds into the tune. Absent for the whole
- `loopEnd?: number`
- `bpm?: number`: The tune's tempo, its bar and where its first beat falls, for whatever follows its beat.
- `beatsPerBar?: number`
- `firstBeat?: number`

#### TNineSliceComponent (type, from `nacatamalon/files`)

```ts
type TNineSliceComponent
```

A panel: a picture cut in nine, its corners kept and its edges and middle stretched (or repeated)
to any size. A button, a dialogue box, a frame.

Its picture is named as a sprite's is: a texture or a sheet and its frame, by asset key. Unlike a
sprite, its `width` and `height` are always written, because stretching to a size is the point.

- `type: 'nine-slice'`
- `id: string`
- `transform?: TTransform2d`: Where it sits on its box, left out when that is the box's own corner.
- `texture: string | null`: The picture, by asset key.
- `atlas?: string`: A sheet, by asset key, and which cell of it to cut.
- `frame?: number`
- `uvOffset?: { x, y }`: The window into the picture, in 0 to 1, when it is not the whole of it nor a cell of `atlas`:
- `uvScale?: { x, y }`
- `width: number`
- `height: number`
- `slice: TNineSliceBorders`: How far in from each edge of the picture the corners end, in its pixels.
- `mode?: TNineSliceMode | { x, y }`: How the edges and the middle fill: left out for `'stretch'`.
- `tint: TColor`
- `anchor?: { x, y }`
- `pivot?: { x, y }`
- `smooth?: boolean`
- `blend?: 'additive'`: Left out for `'alpha'`, the ordinary way of covering what is behind.
- `parallax?: { x, y }`: How much it moves with the camera. Absent, `{ x: 1, y: 1 }`.
- `visible?: boolean`
- `zIndex?: number`
- `material?: TMaterialDoc`
- `uniforms?: TUniformValues`

#### TParticleCollider2dComponent (type, from `nacatamalon/files`)

```ts
type TParticleCollider2dComponent
```

Something solid to particles in a flat scene. Its shape is measured on its object, which places it.

`enabled` is written only when it is off, the way every field at its default is left out.

- `type: 'particle-collider-2d'`
- `id: string`
- `shape: TParticleColliderShape2d`
- `enabled?: boolean`

#### TParticleCollider3dComponent (type, from `nacatamalon/files`)

```ts
type TParticleCollider3dComponent
```

Something solid to particles in a scene in three dimensions. The same as the flat one.

- `type: 'particle-collider-3d'`
- `id: string`
- `shape: TParticleColliderShape3d`
- `enabled?: boolean`

#### TParticles3dComponent (type, from `nacatamalon/files`)

```ts
type TParticles3dComponent: Omit<TParticlesComponent, 'type' | 'transform'> & { type, transform }
```

An emitter in three dimensions: the flat one's fields, placed in space.

Its own type rather than a flag on the flat one, for the reason the two physics bodies are apart:
the file it names is a different kind of thing, and a component that could name either would make
pointing it at the wrong one impossible to report.

- `type: 'particles-3d'`
- `transform?: TTransform3d`: Where the emitter sits on its box, in three dimensions, left out when that is the box's own origin.

#### TParticlesComponent (type, from `nacatamalon/files`)

```ts
type TParticlesComponent
```

An emitter.

Nothing about how the effect behaves is here: the rate, the lifetimes, the curves and the
shape of the emission are all in the `.particles` file, which is what makes three torches one
document and one picture. What a scene decides is where it is, what colour it is laid in, whether
it starts lit, and how this one differs in size or speed from the file's own.

`emitting` and `paused` are deliberately absent. They are what the effect is doing right now, and
a document records what was authored: `autoplay` is the authored intent, and a scene saved while
an explosion happened to be mid-burst must not come back permanently mid-burst.

- `type: 'particles'`
- `id: string`
- `transform?: TTransform2d`: Where the emitter sits on its box, left out when that is the box's own corner.
- `name: string`
- `effect: string`: The effect, by asset key.
- `tint: TColor`
- `alpha: number`
- `autoplay: boolean`: Whether it starts lit. The authored intent, never what it is doing at the moment of saving.
- `seed: number | null`: Fixed, so the same cloud comes back every time. `null` takes it from the clock.
- `overrides?: TParticleOverrides`
- `smooth?: boolean`
- `visible?: boolean`
- `zIndex?: number`

#### TPhysicsBody2dComponent (type, from `nacatamalon/files`)

```ts
type TPhysicsBody2dComponent: { type } & Omit<TPhysicsBody2d, '_type'>
```

A flat collider, written down: what this object is made of, physically.

A component and not a child object, because it is something the object is, the same as its
picture and its sound. Where it is comes from the object, by the standing rule that a component
carries no placement of its own, which is also why moving the object moves its collider.

The engine can keep this, write it and read it back while being completely unable to move it: a
scene with physics opens and saves losslessly with nothing installed to simulate, and a tool can
draw a collider's outline without loading a physics engine.

- `type: 'physics-body-2d'`

#### TPhysicsBody3dComponent (type, from `nacatamalon/files`)

```ts
type TPhysicsBody3dComponent: { type } & Omit<TPhysicsBody3d, '_type'>
```

A collider in three dimensions, written down: the sibling of `TPhysicsBody2dComponent`,
differing only in the shapes it can name (a sphere is not a circle).

- `type: 'physics-body-3d'`

#### TPhysicsWorld2dComponent (type, from `nacatamalon/files`)

```ts
type TPhysicsWorld2dComponent: { type } & Omit<TPhysicsWorld2d, '_type'>
```

The flat simulation's settings, written down. Lives on the scene's root: gravity belongs to
the scene rather than to anything in it, and the root is an object like any other, so it needs no
new home in the file.

Only the root's is read, the same way only one camera is.

- `type: 'physics-world-2d'`

#### TPhysicsWorld3dComponent (type, from `nacatamalon/files`)

```ts
type TPhysicsWorld3dComponent: { type } & Omit<TPhysicsWorld3d, '_type'>
```

The simulation's settings in three dimensions, written down. See
`TPhysicsWorld2dComponent` for why it lives on the root, and `TPhysicsWorld2d` for
why its gravity is in metres with +y up while the flat one is in pixels with +y down.

- `type: 'physics-world-3d'`

#### TScene (type, from `nacatamalon`)

```ts
type TScene
```

What a scene body returns (createScene): the nodes that hang from its root.

- `children: typeOperator`

#### TSceneChangeOptions (type, from `nacatamalon`)

```ts
type TSceneChangeOptions: TSceneLaunchOptions<P>
```

What `useScene().change` accepts beyond the scene: its props, and a transition. Without a
transition it is the hard cut.

- `props?: P`: What the scene's function is called with. Left out, it gets `{}` and its defaults.
- `transition?: TTransition`: Covers the screen while the scene starts, and uncovers it with the scene on top: a menu that

#### TSceneDoc (type, from `nacatamalon/files`)

```ts
type TSceneDoc
```

One scene, written down.

The unit is the scene and never the game. What belongs to the whole game (the size of the
window, its background, the input map, the look of the screen) is the project's, because "this
game looks like a Mega Drive" is not a fact about whichever room happens to be open.

The three fields, in the order they are read:

- `format` and `version` say what this is, so a file that is not one of these is refused instead
of half read.
- `assets` is everything the scene needs fetched, named. It is walked first, before a single
box is built, so that by the time something says "my texture is `hero`" the slot for `hero`
already exists, possibly still arriving.
- `root` is the tree.

- `format: 'nacatamalon-scene'`
- `version: number`
- `name: string`: What the scene is called, and what it is started by.
- `assets: TAssetEntry[]`: Everything the scene needs fetched, by the key the tree refers to it by.
- `root: TBoxNode`

#### TSceneFn (type, from `nacatamalon`)

```ts
type TSceneFn: (props: P) => TScene
```

A scene: a function that runs once, when the scene starts, and returns `createScene()`. What it
makes and asks for in between (sprites, `useUpdate`, `useKeyboard`...) is what the scene has.

It may take props, like a component: how this scene is opened (which level, which door), given by
`useScene().change(Level, { props: { level: 3 } })`. Every prop needs a default, because a scene
also starts with none: the first one, a restart, the editor's Play.

#### TSceneFromDocOptions (type, from `nacatamalon/files`)

```ts
type TSceneFromDocOptions
```

What `sceneFromDoc` can be told besides the document.

- `scripts?: 'run' | 'attach'`: Whether the objects' behaviours run, or are only recorded as attached.

#### TSceneHandle (type, from `nacatamalon`)

```ts
type TSceneHandle
```

What a scene can do with the game's scenes, returned by `useScene`. Every name is one of the keys
passed to `createGame`. Where the name is optional, leaving it out means the scene that called
`useScene`.

`launch` and `change` take the scene by name or by its function. By the function, TypeScript
checks the props: `change(Level, { props: { level: 3 } })`.

- `launch: `
- `stop: `
- `change: `
- `pause: `
- `resume: `
- `isPaused: `
- `onPaused: `
- `onResumed: `

#### TSceneLaunchOptions (type, from `nacatamalon`)

```ts
type TSceneLaunchOptions
```

What `useScene().launch` accepts beyond the scene.

- `props?: P`: What the scene's function is called with. Left out, it gets `{}` and its defaults.
- `transition?: TTransition`: Covers the screen while the scene starts, and uncovers it with the scene on top: a menu that

#### TSceneStopOptions (type, from `nacatamalon`)

```ts
type TSceneStopOptions
```

What `useScene().stop` accepts beyond the scene.

- `transition?: TTransition`: Covers the screen, stops the scene behind the cover and uncovers what is left: a menu that

#### TScriptComponent (type, from `nacatamalon/files`)

```ts
type TScriptComponent
```

A behaviour this object carries: which one, and what it was set to.

It holds a name and not code, for the same reason a sprite holds the name of its picture: a
file cannot carry a function, and the behaviour lives in the project's own source where somebody
can read and change it. What is written down is the link.

- `type: 'script'`
- `id: string`
- `ref: string`: The behaviour's name, as `registerScript` was given it.
- `props?: TScriptProps`: What this attachment was set to, which is what turns a fixed behaviour into a configurable

#### TShapesComponent (type, from `nacatamalon/files`)

```ts
type TShapesComponent
```

Shapes on a box, what `createShapes` makes: rectangles, circles, ellipses and slices of them,
outlines of any number of corners and wide lines, each filled, outlined or both, drawn in the order
of the list. Each shape is written as the game has it, its colours as `{ r, g, b, a }`.

- `type: 'shapes'`
- `id: string`
- `transform?: TTransform2d`: Where they are measured from on their box, left out when that is the box's own corner.
- `shapes: TShape[]`
- `tint: TColor`
- `parallax?: { x, y }`
- `visible?: boolean`
- `zIndex?: number`

#### TSongComponent (type, from `nacatamalon/files`)

```ts
type TSongComponent
```

A song written as notes this object carries: the `.song` file by asset key, and how it was asked to
play. Like music, never where it had got to.

- `type: 'song'`
- `id: string`
- `song: string`: The `.song` file, by asset key.
- `volume?: number`
- `channel?: string`: Absent for `'music'`.
- `autoplay?: boolean`
- `fadeIn?: number`: Seconds `autoplay` takes to come up from silence, and to go down to it when the object goes
- `fadeOut?: number`
- `voices?: Partial<Record<TSongVoiceName, number>>`: How loud each voice is, only those not at `1`.
- `pan?: Partial<Record<TSongVoiceName, number>>`: Where each voice is heard, left (`-1`) to right (`1`), only those changed from the file's.
- `tempo?: number`: Beats per minute, absent for what the file says.
- `transpose?: number`: Semitones from how it is written, absent for none.

#### TSoundComponent (type, from `nacatamalon/files`)

```ts
type TSoundComponent
```

A sound this object carries.

Unlike everything else on the list there is nothing to look at, and that is exactly why it has
to be written down: a torch that crackles and a level with music are things the objects are,
and a format that could only describe what is drawn would leave half of a game unauthorable.

What is written is how it was asked to play, never what it is playing. A scene saved while
the music was halfway through opens ready to start it, because where a voice had got to is a
fact about one run and a level is not. The knobs go the other way: a volume an options screen
turned down is written down turned down, the same as a sprite is written with the tint it has
rather than the one it was made with.

- `type: 'sound'`
- `id: string`
- `audio: string`: The clip, by asset key.
- `volume?: number`
- `loop?: boolean`
- `loopStart?: number`: Where the loop goes back to and turns back, in seconds into the file. Absent for the whole
- `loopEnd?: number`
- `rate?: number`: How fast it plays, which also changes the pitch.
- `pan?: number`: Where between left (`-1`) and right (`1`) it is heard, absent for the middle.
- `channel?: string`: Which volume group it belongs to, so an options screen can move all the music at once
- `autoplay?: boolean`: Starts as soon as the file is there, with nothing having to ask.
- `spatial?: boolean`: Comes from a side and fades with distance, following the object it hangs off.
- `refDistance?: number`: How far it is heard at full volume. Placed sounds only. In the scene's measure (pixels, or
- `maxDistance?: number`: Past this distance it is not heard. Placed sounds only, and absent for the default.
- `cone?: TSoundCone`: Louder ahead of the object than behind it. Placed sounds only.
- `zone?: TSoundZone`: Fills an area around the object instead of coming from a point. Wins over `spatial`.
- `duck?: TSoundDuck`: Another channel made quieter while this plays.

#### TSpriteAnimationDoc (type, from `nacatamalon`)

```ts
type TSpriteAnimationDoc
```

A sprite's animation as a document keeps it: which run of its sheet it starts on and how fast.
The runs themselves are in the `.atlas` file and are never copied here.

- `autoplay: string | null`: The run to start on, or `null` to rest on the sprite's `frame`.
- `speed: number`: How fast, where `1` is the run's own `fps`.

#### TSpriteComponent (type, from `nacatamalon/files`)

```ts
type TSpriteComponent
```

A flat picture.

`width` and `height` may be left out, and leaving them out is not the same as writing the numbers
the texture happens to have: it means take the texture's size, decided when the texture lands.
Writing today's numbers down would freeze a sprite at the size of the art it had when it was saved.

- `type: 'sprite'`
- `id: string`
- `transform?: TTransform2d`: Where it sits on its box, left out when that is the box's own corner.
- `texture: string | null`: The sheet, by asset key.
- `atlas?: string`: A sheet cut into a grid, by asset key, and which cell of it to show.
- `frame?: number`
- `animation?: TSpriteAnimationDoc`: The run of `atlas` it plays from the start, if any. Needs `atlas`: the runs are the sheet's.
- `width?: number`
- `height?: number`
- `tint: TColor`
- `anchor?: { x, y }`
- `pivot?: { x, y }`: The point it turns and grows about, in 0-1, when it is not `anchor` (see `TSprite.pivot`).
- `uvOffset?: { x, y }`: Which part of the sheet to show, when it is not cut by an atlas.
- `uvScale?: { x, y }`
- `flipX?: boolean`
- `flipY?: boolean`
- `smooth?: boolean`
- `blend?: 'additive'`: Left out for `'alpha'`, the ordinary way of covering what is behind.
- `tile?: boolean`: Repeats the picture across the sprite's `width` and `height` instead of stretching it: a wall
- `tileOffset?: { x, y }`: With `tile`, where the pattern starts inside the sprite, in its picture's pixels. Absent,
- `parallax?: { x, y }`: How much it moves with the camera: `1` as everything else, `0` not at all, between them further
- `visible?: boolean`
- `zIndex?: number`
- `material?: TMaterialDoc`
- `uniforms?: TUniformValues`: This sprite's own values for that material's knobs, laid over the material's own.

#### TSpriteTextureComponent (type, from `nacatamalon/files`)

```ts
type TSpriteTextureComponent
```

A picture the object and everything under it is drawn into, instead of the screen.

The picture is named by `key`, and that is how a model shows it: its surface asks for `key` the
way it asks for a loaded image. It is made before any object in the scene, so a model that comes
earlier in the file than the screen it shows still finds it.

`sees` is written only when it is not `'inside'`, the way every field at its default is left out.
A document saved when `'3d'` was called `"scene"` is read as `'3d'`, and saving it writes the new
name.

- `type: 'sprite-texture'`
- `id: string`
- `key: string`
- `width: number`
- `height: number`
- `background: TColor`
- `sees?: '3d' | 'world'`
- `hide?: TViewFilterDoc`: For a view of the world: what it leaves out and what alone it takes in, by tag and by kind. A
- `only?: TViewFilterDoc`

#### TStoreComponent (type, from `nacatamalon/files`)

```ts
type TStoreComponent
```

An object saying that it uses one of the game's stores.

The important word is uses. A store is not in the object and cannot be: it outlives every
scene, and one living inside an object would die with its scene and be two the moment a second
object named it. So this carries a name and nothing else, exactly as a behaviour does, and the
store itself lives in its own file and in the engine's index.

It buys two things an ordinary import cannot. A behaviour attached in an editor can be handed the
state through the scene (`storeOf(self)`), so a scene put together by hand can wire behaviour to
state with no file to write. And the tree becomes able to answer "what touches this state?",
which nothing else could.

- `type: 'store'`
- `id: string`: Worked out from the object and the name rather than kept, which is the one identity in this
- `ref: string`: The store's name: what its file is called, and the key `createGameStore` was given.

#### TTextComponent (type, from `nacatamalon/files`)

```ts
type TTextComponent
```

Words drawn with a bitmap or a vector font.

- `type: 'text'`
- `id: string`
- `transform?: TTransform2d`: Where it sits on its box, left out when that is the box's own corner.
- `text: string`
- `font: string`: The font, by asset key.
- `style: TTextStyle`
- `tint: TColor`
- `anchor?: { x, y }`
- `pivot?: { x, y }`: The point it turns and grows about, in 0-1, when it is not `anchor` (see `TSprite.pivot`).
- `smooth?: boolean`
- `blend?: 'additive'`: Left out for `'alpha'`, the ordinary way of covering what is behind.
- `parallax?: { x, y }`: How much it moves with the camera, as a sprite's. Absent, `{ x: 1, y: 1 }`.
- `visible?: boolean`
- `zIndex?: number`
- `material?: TMaterialDoc`
- `uniforms?: TUniformValues`

#### TTilemapComponent (type, from `nacatamalon/files`)

```ts
type TTilemapComponent
```

A map.

One of these per map, not per layer, and that is what the engine can actually be asked for:
`createTilemap` reads the file and puts every layer of it on the box in one go. A document with
one entry per layer would describe something no call can make.

The cells are not here either: they live in the `.tilemap`, which is the file a person edits and a
map editor writes. What a scene decides is where the map sits, how it is tinted, how its sheet is
read, and then whatever it changed about individual layers.

`transform` is the map's own and is here rather than on the box, for a reason worth knowing: a map
is cached and shared, so its placement is shared too, and two scenes showing one level are
looking at the same object. Writing it as the box's placement would say each scene had its own.

- `type: 'tilemap'`
- `id: string`
- `map: string`: The map, by asset key.
- `transform?: TTransform2d`: Where the map's top-left corner sits. Shared by its layers: they sort apart, not move apart.
- `tint?: TColor`: Multiplies the colour of every layer.
- `smooth?: boolean`
- `layers?: TTilemapLayerOverride[]`: What this scene changed about individual layers, by the name the file gave them.

#### TTilemapLayerOverride (type, from `nacatamalon/files`)

```ts
type TTilemapLayerOverride
```

What one scene changed about one layer of a map. Everything optional, because everything left out
is the file's own answer.

- `name: string`: Which layer, by the name the file gave it.
- `tint?: TColor`
- `visible?: boolean`
- `zIndex?: number`
- `material?: TMaterialDoc`
- `uniforms?: TUniformValues`

#### TTransition (type, from `nacatamalon/transitions`)

```ts
type TTransition
```

A way of getting from one scene to another: a full-screen effect plus how long it lasts.

The same shape a post effect has, plus a duration, and that is deliberate. Writing your own
transition should cost exactly what writing your own effect costs, because it is the same hook
over the same finished picture. What the engine adds is not the shader, it is the lifecycle: the
scene coming in exists without being seen or updated, and the swap waits for the screen to be
covered and for its assets to land.

The hook is handed `progress`, which runs 0 to 1 twice, and `phase`, which says which of the two
runs it is in: 0 while the screen is being covered, 1 while it is being uncovered. Both halves
count up, never down, because an uncover that counted back down would play a wipe in mirror
image.

- `name?: string`: What a warning about it will call it, and what an editor lists. Never an identity.
- `duration: number`: How long the whole thing takes, in seconds, covering and uncovering together.
- `fragment: string`: `fn effect(color: vec4<f32>, uv: vec2<f32>) -> vec4<f32>`, and anything it calls.
- `fragmentGlsl?: string`: The same hook in GLSL.
- `uniforms?: TUniformValues`: The knobs, and what they start at.
- `uniformSig?: TUniformSignature`: What kind each one is. Worked out from the values when left out.
- `mask?: TTexture`: A picture the hook reads with `sampleMask(uv)`, stretched over the screen: the shape a
- `ease?: TEaseFn`: How each half moves along, a curve from `useTween`'s: `easeInOutSine` starts and ends gently.
- `cross?: boolean`: Whether it crosses from one scene to the other instead of covering the screen between them: a

#### TWipeDirection (type, from `nacatamalon/transitions`)

```ts
type TWipeDirection: 'left' | 'right' | 'up' | 'down'
```

Which way the edge travels. `uv.y` points down, so `'down'` is a curtain falling.

### Lifecycle

What runs every frame, and what runs when a scene is taken down.

#### useCleanup (function, from `nacatamalon`)

```ts
useCleanup(cleanup: () => void) => void
```

Runs when its owner goes away: the component it is called in when it is destroyed, or the scene
when it stops. Not on pause.

For what you started outside the engine: a window listener, a `setInterval`, a request. What the
engine made goes away by itself. `destroy` is what sets it off.

- `cleanup: () => void`: What to undo. It receives nothing and is called exactly once.

#### useLateUpdate (function, from `nacatamalon`)

```ts
useLateUpdate(callback: (delta: number, time: number) => void) => void
```

Runs your code once per frame, after every `useUpdate` of the scene has run.

It exists for code that reads where something ended up rather than moving it: a camera that
follows the player, a marker over an enemy's head, a shadow under a sprite, a sword held in a
hand. In `useUpdate` the scene runs before the objects inside it, so a camera there reads the
player where it was before it moved this frame, and the player is drawn one step away from where
the camera put it. While the player runs at a steady speed the gap stays the same and nobody
notices; when it starts, stops, lands or hits a wall the gap changes from one frame to the next,
and the character shakes against the screen. Here everything has already moved, tweens and
physics included, so what is read is what will be drawn.

The rule is short: move things in `useUpdate`, follow them in `useLateUpdate`.

It receives the same `delta` and `time` as `useUpdate` in the same frame, and it follows the same
rules: it stops while the scene is paused, an object spawned this frame starts on the next one,
and an object destroyed during the updates does not run it.

Call it in the scene body, next to the sprites. It only registers the function, it does not run
it.

- `callback: (delta: number, time: number) => void`: What to run each frame, once the updates are over. `delta` is the seconds since

#### useReady (function, from `nacatamalon`)

```ts
useReady(callback: () => void) => void
```

Runs your code once, when everything being built with it exists: the whole scene, children and
all, before its first frame.

A body runs as its object is made, and an object is made before the ones inside it. In a scene
written in code that rarely shows, because the body makes its children itself; in a scene laid out
as a document it always does: a behaviour on the scene's root runs before the boxes under it are
there, so `findObject(root, 'Pitcher')` in its body finds nothing. Inside `useReady` it finds it.

For something spawned later (a `useSpawn` called from a `useUpdate` or a click), it runs as soon
as that thing and everything inside it are built, before the spawner's call returns.

It runs with its object as the one being built, so hooks called inside it (a `useUpdate` that
needs what was found) belong to that object. Register as many as you like: they run in the order
they were registered, parents' before children's.

- `callback: () => void`: What to do once everything is there. It receives nothing and runs exactly once.

#### useSceneTransitionEnd (function, from `nacatamalon`)

```ts
useSceneTransitionEnd(callback: () => void) => void
```

Runs your code once, when the transition this scene came in with is over: the screen uncovered
and nothing left over it.

A scene that comes in with a transition (`change('Level', { transition: iris(0.6) })`) starts at
once, so its loading starts too, and waits behind the cover: its body and its `useReady` have
already run, but it is not seen. Its clock does not move while it waits, so a timer or a tween
started in `useReady` waits with it, and so does a sound set to `autoplay`, until the swap. What
does not wait is what is started by hand and keeps real time: a `play()`, a vibration. Started
here, they begin when the player can see the scene. So does handing over the controls once
nothing covers the level.

Without a transition it runs on the scene's first frame, before its first update, so a scene does
the same with its transition or without it. In something spawned once the scene is already on
screen, it runs at the start of the next frame.

It runs with its object as the active one, so hooks called inside it belong to that object. If
the scene stops before it is seen, it never runs.

- `callback: () => void`: What to do once the scene is in full view. It receives nothing and runs exactly once.

#### useTimer (function, from `nacatamalon`)

```ts
useTimer() => TTimer
```

Runs code later, in game time: once after a while, or again and again.

It is what `setTimeout` and `setInterval` would be if they knew about the game. The wait is
counted in the same seconds as `useUpdate`'s `delta`, so it stops while the scene is paused,
follows `timeScale`, and is gone when the object that asked for it is: a cutscene that waits two
seconds still waits two seconds after a pause, and a timer of a scene that ended never fires into
the next one. A `setTimeout` does none of the three.

Call `useTimer` in the body, like any hook. What it returns can be used from anywhere after that,
an update, a click or another timer, so a sequence is just calls that schedule the next step.

Several calls due in the same frame run in the order they were due, so a long frame cannot swap
the steps of a sequence. One scheduled from inside another runs on a later frame at the soonest.

#### useUpdate (function, from `nacatamalon`)

```ts
useUpdate(callback: (delta: number, time: number) => void) => void
```

Runs your code once per frame, for as long as the scene is on screen.

The function you pass receives `delta`: how many seconds the last frame took, about
`0.016` on a 60 Hz screen and about `0.008` on a 120 Hz one. Multiply by it whenever
something moves or counts down, and the game runs at the same speed on every machine.
Ignore it, and the same game is twice as fast on a better screen.

It also receives `time`: the seconds the scene has been running. It is what a sine wave, a
blink or a pulse wants, so nothing needs its own counter. It is game time, like `delta`: it
stops while the scene is paused, follows `timeScale`, and starts again from zero when the
scene does. Every object in the scene reads the same clock, so two things that pulse together
stay together, whenever each one was spawned.

Call it in the scene body, next to the sprites. It only registers the function, it does not
run it: from then on the game loop calls it every frame, and stops when the scene does.

- `callback: (delta: number, time: number) => void`: What to run each frame. `delta` is the seconds since the previous frame, and

#### TTimer (type, from `nacatamalon`)

```ts
type TTimer
```

What `useTimer` hands back: the two ways of running something later.

- `after: `
- `every: `
- `cancelAll: `

#### TTimerHandle (type, from `nacatamalon`)

```ts
type TTimerHandle
```

One waiting call, handed back by `after` and `every` so it can be called off.

- `cancel: `
- `done: boolean`: Whether it is over: an `after` that has run, or anything cancelled. An `every` is never done on its own.

### Game objects

What a scene is filled with: sprites, texts, models, emitters, lines, materials, and destroying them.

#### clearParticles (function, from `nacatamalon`)

```ts
clearParticles(emitter: TParticles | TParticles3d) => void
```

Takes every particle out of the air at once, and leaves the emitter as it was.

- `emitter: TParticles | TParticles3d`: The emitter, as `createParticles` or `createParticles3d` gave it back.

#### createMaterial (variable, from `nacatamalon`)

```ts
createMaterial: TCreateMaterial
```

Makes a material: a shader of your own, and for models the surface it is drawn over.

It is a plain value, not a resource. Nothing caches it and nothing looks it up by name, so two
things share one by being handed the same one. That is also what makes a shared effect cheap: one
material is one compiled shader, however many things are drawn with it, and each of those can
still run it with its own numbers by passing `uniforms` of its own.

What it carries depends on what it is for, and the two are deliberately different. A material for
models owns the surface, because a hundred crates want to share one. A material for sprites owns
only the effect, because a sprite's picture and colour are its own and travel with it: putting the
colour on something shared would mean one material per sprite to vary it.

#### createMesh (function, from `nacatamalon`)

```ts
createMesh(options: TMeshOptions) => TMesh
```

Puts a model in the scene: a shape, somewhere, with a surface.

The shape comes from one of the shape hooks and is shared, so making a hundred of these from one
shape costs a hundred placements and not a hundred shapes.

It is drawn through the scene's 3D camera, or flat on in the game's pixels when it has none. It
shares its draw order with everything else, so a model can sit between two sprites, and on a tie
the model is drawn first and the sprite paints over it.

What comes back is the model itself, plain data: move it, turn it, tint it, hide it.

- `options: TMeshOptions`: The shape, and anything else about how it looks.

#### createModel (function, from `nacatamalon`)

```ts
createModel(options: TModelOptions) => TModel
```

Puts a loaded model in the scene.

A model file is usually several pieces, each with its own colour or picture: a vehicle is a body,
glass and tyres. All of them are drawn, and one placement moves the lot, however deep the
file's own structure went. Where each piece sits inside the model is already worked into it.

The pieces appear when the file does. On the frame you call this the model may still be on the
way, so `parts` is empty and nothing is drawn; a frame or two later it fills in and the model is
there. Nothing has to be waited for or wired up.

`tint` multiplies, so the colours the model was made with survive: white leaves it exactly as it
was made, and a darker colour shades all of it at once.

What comes back is used like any other drawing: `tint`, `alpha`, `zIndex`, `castShadow` and
`visible` on it change every piece at once, and can be set straight away (a model hidden before its
file arrives arrives hidden); `destroy(model)` takes it all. To change one piece alone, reach into
`parts` once the file is here.

- `options: TModelOptions`: The model, and anything else about how it looks.

#### createNineSlice (function, from `nacatamalon`)

```ts
createNineSlice(options: TNineSliceOptions) => TNineSlice
```

Puts a picture in the scene that can be any size without deforming: a panel, a button, a dialogue
box, a health bar.

The picture is cut into nine parts by four borders (`slice`), measured in its own pixels. The
corners are drawn as they are, the top and bottom edges only grow sideways, the left and right
edges only grow downwards, and the middle fills the rest. So a 24x24 frame drawn at 300x80 keeps
its corners crisp, which stretching it as a sprite would not.

- `mode` decides how the edges and the middle fill their space: stretched (the default), repeated
and cut at the end (`'tile'`), or repeated a whole number of times (`'tile-fit'`). Repeating
draws one sprite per copy, so a small pattern over a large panel is many sprites.
- Drawn smaller than its borders, the corners shrink in proportion instead of overlapping.
- `transform.scaleX`/`scaleY` stretch the whole of it, corners included. To make it bigger and
keep the corners, change `width` and `height`.

It is placed by its anchor, its middle unless you say otherwise, like a sprite, and takes the same
pointer events (`onClick`, `onPointerOver`...), which fire anywhere inside it.

- `options: TNineSliceOptions`: Its picture, borders, size, place and look: see `TNineSliceOptions`.

#### createPack (function, from `nacatamalon`)

```ts
createPack(options: TPackOptions) => TGameObject
```

Places one thing a pack offers: a box, or a scene as one part of the scene that creates it.

It appears when the pack does, the way `createModel` fills in when its file arrives: what comes
back is the object at once, empty on the frame you call this, and the pack's box is built inside it
the moment the pack has landed. Nothing has to be waited for.

Each call is its own copy, with its own place (`transform`) and its own settings (`props`), and what
comes back is an ordinary object: move it, hide it, destroy it like any other. The pack's box sits
inside it at the place it was made at, so a script that moves its own box keeps doing so relative to
wherever you put the copy.

A script the pack's box attaches has to be registered, which is what importing the pack's
`<name>.pack.ts` does. One that is not is named in a warning, and the box is built without it.

- `options: TPackOptions`: Which thing of which pack, where, and with which settings.

#### createParticles (function, from `nacatamalon`)

```ts
createParticles(options: TParticlesOptions) => TParticles
```

Puts an emitter in the scene: a place that makes particles, following an effect from a file.

The file says what the effect is and is shared, so three torches reading one document are
three emitters with their own particles, their own clock and their own stream of chance. What this
decides is where it is, what colour it is laid in, and whether it starts lit.

It appears at once and draws nothing until its file lands, which is a working state and not a
half-built one: the emitter is already in the right place, so nothing that depends on where it is
has to wait for a download.

- `options: TParticlesOptions`: The effect to follow, and what this emitter decides for itself.

#### createParticles3d (function, from `nacatamalon`)

```ts
createParticles3d(options: TParticles3dOptions) => TParticles3d
```

Puts an emitter in a scene in three dimensions, following an effect written for them.

The same thing `createParticles` is, one dimension up: the file says what the effect is and is
shared, and this decides where it is, what colour it is laid in and whether it starts lit. Each
particle is drawn as a square that always faces the camera, however the camera turns, and
hides behind the models in front of it without hiding the other particles.

Its effect has to be a `particles-3d` file. Handing it a flat one is refused, because every number
in that file is a pixel.

- `options: TParticles3dOptions`: The effect to follow, and what this emitter decides for itself.

#### createPolygon (function, from `nacatamalon`)

```ts
createPolygon(options: TPolygonOptions) => TPolygon
```

Puts a shape in the scene whose corners the game moves: a picture stretched over an outline that
can change every frame. For what a rectangle cannot be: a blob that squashes as it lands, a jelly,
a flag, a drop.

Each corner holds on to its point of the picture, so moving it stretches the picture under it.
The corners are `points`, plain numbers on what comes back: change them every frame, from a
physics body, a wave or anything else, and the next frame draws them. It is drawn from its middle
out, so it suits outlines that can be seen whole from their middle.

Drawn in the scene's order like a sprite, through the scene's camera, in either backend.

- `options: TPolygonOptions`: Its outline, picture, place and look: see `TPolygonOptions`.

#### createShapes (function, from `nacatamalon`)

```ts
createShapes(options: TShapesOptions) => TShapes
```

Puts shapes in the scene, drawn by the graphics card as they are each frame: rectangles, with
rounded corners or not, circles, ellipses and slices of them, outlines of any number of corners
(hollowed in too, a star or a letter), and lines as wide as asked for. Each is filled, outlined, or
both, in any colour, and they are all drawn together, in the order of the list.

- `rect`: `x`, `y` (its top-left), `width`, `height`; `radius` rounds its corners (one, or one
each from the top-left round), `rotation` turns it about its middle.
- `circle`: `x`, `y` (its middle), `radius`; with `start` and `end`, in radians clockwise from the
right, a slice: filled, a pie (a cooldown); outlined, the arc alone (a ring that fills up).
- `ellipse`: as a circle, with `radiusX`, `radiusY` and `rotation`.
- `polygon`: `points`, three or more round it.
- `line`: `points`, two or more; `closed` joins the last to the first.
- `fill`: a colour. `stroke`: `{ width, color }`, with `join` (`'miter'`, `'round'`, `'bevel'`),
`cap` for the ends of what is open (`'butt'`, `'round'`, `'square'`), and `align` for what is
closed (`'center'`, `'inside'`, `'outside'`).

They are plain data on what comes back, `shapes`: change a number (a bar's `width`, a slice's
`end`), push one, take one out, and the next frame draws it. Their places are measured from the
object's `transform`, which moves, turns and scales them all.

Their edges are hard, as everything the engine draws, and widths are in the game's pixels, so they
grow with the camera's zoom. However many there are, the card draws them in one go: what they cost
is working their corners out again each frame.

- `options: TShapesOptions`: The shapes, their place and their look: see `TShapesOptions`.

#### createSpriteTexture (function, from `nacatamalon`)

```ts
createSpriteTexture(options: TSpriteTextureOptions, component?: (args: TArgs) => unknown, args: TArgs) => TTexture
```

Makes a picture and fills it with a component: whatever `component` draws, and whatever it
creates with `useSpawn`, ends up in the picture instead of on the screen, every frame. A model
then shows the picture the way it shows any image, so the 2D moves with the model and is seen in
its perspective: a handheld's screen, a monitor, a sign.

The component is a full part of the scene: it updates, it runs its scripts, it pauses with the
scene and `destroy` takes it away. What it draws is measured in the picture's pixels, from its
top-left corner, the way a scene without a camera is. A camera it asks for is the picture's own
and leaves the scene's alone, and a lamp it holds lights only the picture.

With `sees: '3d'` the picture also shows the models of the world around it, from the
component's own 3D camera: a security camera, and the monitor is whatever shows the picture.

With `sees: 'world'` it is a view of the whole world of its scene, through the component's own
cameras: a minimap, a mirror, half of a split screen. See `TSpriteTextureOptions.sees`, and
`viewToWorld` for clicks on it.

Whatever you pass after the component reaches it as its arguments, as with `useSpawn`.

- `options: TSpriteTextureOptions`: How big the picture is, what is behind it, what it sees and, for a view of the
- `component?: (args: TArgs) => unknown`: What is drawn into it. Left out, the picture shows only its background, or the
- `args: TArgs`: What `component` is called with, the way a maker from `useSpawn` passes them on.

#### createText (function, from `nacatamalon`)

```ts
createText(options: TTextOptions) => TText
```

Writes something on screen with a bitmap font: a score, a title, a menu, "PRESS START".

You get the text back and change it by changing its fields: `score.text = 'SCORE ' + points`
shows on the next frame. It draws nothing until its font has loaded, and appears on its own then.

The font is optional: left out, the text uses the one the engine carries (Nacatamal Arcade, 8 px,
with lowercase, accents and ñ), which is there from the start. Two more are always there too,
named by key: `font: 'nacatamalon:square'` (the same weight, square corners) and
`font: 'nacatamalon:thin'` (one-pixel strokes, narrower). Pass `font` from `useLoadFont` (a
`.ttf` or `.woff`) or `useLoadBitmapFont` for a font of your own, or the key one was loaded under,
by this scene or an earlier one: a loading scene can load every font once and the rest of the
game name them, `font: 'title'`.

- `style.fontSize` is how tall a line is, in pixels. A pixel font looks crisp at whole multiples of
its own height and uneven in between; the console says so if that happens.
- `\n` starts a new line, and `style.maxWidth` breaks one that runs wider at a space, so a
dialogue box needs no `\n` put in by hand. `style.align` lines them up.
- `x` and `y` are the top-left corner of the text unless `anchor` says otherwise: `{ x: 0.5, y: 0 }`
centres a title on `x`.
- A lowercase letter the font does not have is drawn as its capital, since most arcade fonts only
have capitals.

It moves with the camera and obeys `zIndex` and `useScreenSpace` like a sprite, and takes the same
pointer events (`onClick`, `onPointerOver`...), which fire anywhere inside the block.

- `options: TTextOptions`: What it says, how it looks, and optionally with which font.

#### createView (function, from `nacatamalon`)

```ts
createView(options: TViewOptions, component?: (args: TArgs) => unknown, args: TArgs) => TSprite
```

A view of the world in a rectangle of the screen: half of a split screen, a minimap in a corner, a
rear-view mirror.

The short way to write what `createSpriteTexture({ sees: 'world' })` does with a sprite pinned to
the screen showing it: `component` asks for the view's cameras and draws what only the view shows,
exactly as there. For a view that is round, carries a material or sits on something that moves,
make the picture and show it yourself.

It does not take the world off the screen: a split screen, where the halves cover it, says so with
`useWorldOffScreen`, and a minimap over the game does not.

Whatever you pass after the component reaches it as its arguments, as with `useSpawn`.

- `options: TViewOptions`: Where on the screen it goes and how big it is, what is behind it, and what of the
- `component?: (args: TArgs) => unknown`: What the view looks through and draws on top: its `useCamera2d`, `useCamera3d`
- `args: TArgs`: What `component` is called with, the way a maker from `useSpawn` passes them on.

#### destroy (function, from `nacatamalon`)

```ts
destroy(target: TDrawable | TGameObject | TModel, options?: TDestroyOptions) => void
```

Removes something from the game for good: a sprite, or a whole thing made by a spawner, with
its sprites, its per-frame code and its cleanups. It stops being drawn in this same frame,
whether it goes at once or at the end of it, and a spawned object stops running the moment you
say so.

Destroying twice, or destroying something that was never on screen, does nothing and says
nothing: you should not have to find out whether someone else got there first. What is
destroyed is gone, not recycled, so keep making new ones rather than reviving old ones.

A sprite of something spawned goes alone: the thing it belongs to stays, with its other sprites
and its `useUpdate`. `destroy(ownerOf(sprite))` takes the whole thing.

Images are not freed. Several things can be showing the same one, and the game keeps it for
whoever asks next, so destroying a sprite never costs the next one a reload.

- `target: TDrawable | TGameObject | TModel`: The sprite, model or spawned object to remove. A scene is not one of these: it leaves
- `options?: TDestroyOptions`: See `TDestroyOptions`. Leave it out for the usual behaviour.

#### emitParticles (function, from `nacatamalon`)

```ts
emitParticles(emitter: TParticles | TParticles3d, count: number) => void
```

Asks for `count` particles to appear, over and above whatever the effect does by itself.

What a game calls when something happens: a hit, a footstep, a jump. It works on an emitter that
is switched off, which is the usual way a one-shot is used.

They appear on the next step rather than inside this call, and that is deliberate: a particle
is born where its emitter ends up, and that is not known until the tree has been walked later in
the same frame. So this works even for the ordinary pattern of moving the emitter and firing it in
the same breath, which is exactly where doing it on the spot would use the place it was before.

- `emitter: TParticles | TParticles3d`: The emitter, as `createParticles` or `createParticles3d` gave it back.
- `count: number`: How many to make now.

#### findObject (function, from `nacatamalon`)

```ts
findObject(root: TBox, name: string) => TBox | null
```

The first thing called `name` inside `root`, looking at `root` itself first and then down through
everything in it, or `null` when there is none.

What a behaviour uses to reach a named part of what it is attached to: the turret of a tank, the
hand a sword goes in, the door that has to slide. `null` is an ordinary answer and has to be
handled, because a part can be renamed while the game runs, or come in with another name when a
model is exported again. Nothing here throws.

Something destroyed is not found, nor anything inside it, even before the end of the frame takes
it out: a coin collected this frame is not there to be collected again. To look across the whole
scene rather than inside one thing, start from `sceneOf(self)`.

By name and not by id, because a name is what an author can type.

- `root: TBox`: Where to look.
- `name: string`: The name to find.

#### findObjects (function, from `nacatamalon`)

```ts
findObjects(root: TBox, name: string) => TBox[]
```

Everything called `name` inside `root`, `root` included, in the order `findObject` would meet
them. For names shared on purpose: the four wheels of a car, every spawn point of a level.

An empty list rather than `null` when nothing matches, so a `for ... of` over it needs no guard.
Leaves out what is destroyed, as `findObject` does.

- `root: TBox`: Where to look.
- `name: string`: The name to find.

#### ownerOf (function, from `nacatamalon`)

```ts
ownerOf(drawable: TDrawable) => TGameObject | null
```

The object a sprite, a text or a mesh belongs to: the thing `useSpawn` made that drew it, or the
scene for one made straight in the scene's body. `null` once it or its piece has been destroyed,
or for one that was never placed.

The way back from what was touched to the whole piece: `destroy(sprite)` takes only that sprite
away and leaves its piece running, with its other sprites and its `useUpdate`; `destroy(ownerOf( sprite))` takes the piece. And `ownerOf(sprite).api` is what the piece offers.

- `drawable: TDrawable`: Something drawn.

#### particleCount (function, from `nacatamalon`)

```ts
particleCount(emitter: TParticles | TParticles3d) => number
```

How many are in the air right now.

- `emitter: TParticles | TParticles3d`: The emitter, as `createParticles` or `createParticles3d` gave it back.

#### pauseParticles (function, from `nacatamalon`)

```ts
pauseParticles(emitter: TParticles | TParticles3d, paused: boolean) => void
```

Freezes it, or lets it go again.

Nothing moves and nothing is born, and it keeps drawing exactly what it last drew. Freezing rather
than hiding, because the useful half of this is being able to look at it: a still flame is
something you can line something else up against, and a hidden one is a hole where you have to
remember an effect was.

- `emitter: TParticles | TParticles3d`: The emitter, as `createParticles` or `createParticles3d` gave it back.
- `paused: boolean`: `true` to freeze it, `false` to let it go again.

#### playParticles (function, from `nacatamalon`)

```ts
playParticles(emitter: TParticles | TParticles3d) => void
```

Starts the effect from the beginning: new particles appear, and a burst fires again.

The cycle is put back to exactly zero, and that is what re-arms a burst: a one-shot that has
already gone off is a one-shot that can be fired again.

- `emitter: TParticles | TParticles3d`: The emitter, as `createParticles` or `createParticles3d` gave it back.

#### resizeSpriteTexture (function, from `nacatamalon`)

```ts
resizeSpriteTexture(picture: TTexture, width: number, height: number) => void
```

Makes a picture from `createSpriteTexture` another size: half of a split screen when the window
grows, a monitor that opens wider. It stays the same picture, so whatever shows it goes on showing
it; only the texture on the graphics card behind it is made again, and the old one let go of. A
view sees more or less of the world, as the screen does when the game is resized.

- `picture: TTexture`: The picture, as `createSpriteTexture` gave it back.
- `width: number`: In pixels, rounded down and at least 1.
- `height: number`: In pixels, rounded down and at least 1.

#### sceneOf (function, from `nacatamalon`)

```ts
sceneOf(object: TBox) => TGameObject
```

The scene something is in: the top of its tree, the scene itself for a scene.

What a behaviour starts from to reach the rest of its scene by name: `findObject(sceneOf(self), 'Score')` finds the score wherever it sits, not only among the behaviour's own children. A box in
a screen inside the game (`createSpriteTexture`) is still in the scene that screen is in.

- `object: TBox`: Anything in a scene: a box, a component, a scene.

#### setViewFilter (function, from `nacatamalon`)

```ts
setViewFilter(picture: TTexture, filter: { hide, only }) => void
```

Changes what a view of the world (`createSpriteTexture({ sees: 'world' })`) takes in, while the game
runs: a minimap that starts showing the rooms already visited, a mirror that stops showing the
player's own car. What is given replaces what it had; `null` or left out, none.

- `picture: TTexture`: The view, as `createSpriteTexture` gave it back.
- `filter: { hide, only }`: What it leaves out (`hide`) and what alone it takes in (`only`). `hide` wins.

#### stopParticles (function, from `nacatamalon`)

```ts
stopParticles(emitter: TParticles | TParticles3d) => void
```

Stops new ones appearing.

Not the same as clearing. Whatever is already in the air goes on living, moving and drawing
until its own time runs out, which is what putting a torch out looks like. Use `clearParticles`
for the other thing.

- `emitter: TParticles | TParticles3d`: The emitter, as `createParticles` or `createParticles3d` gave it back.

#### transformForward (function, from `nacatamalon`)

```ts
transformForward(transform: TTransform3d) => { x, y, z }
```

Which way something is facing.

Forward is -Z, which is the convention every model format and every camera in this engine
already uses: a camera at the origin looks that way, and a model exported facing the viewer faces
that way. A light shines along it, so a light is aimed by turning it, exactly like a camera.

A `quaternion`, when the transform has one, decides the answer, as it decides the turn everywhere
else. The lights and cameras of a frame pass theirs as `null` on purpose and are aimed by their
angles. What comes back is always one unit long, and the roll never changes it.

- `transform: TTransform3d`: Where something is and how it is turned.

#### useSelf (function, from `nacatamalon`)

```ts
useSelf() => TGameObject
```

The thing being built right now, so it can refer to itself later: almost always to end its own
life once it has done its job.

Inside a component made by a spawner, this is that one thing. Inside a scene body it is the
scene, which cannot be destroyed this way: a scene leaves through `useScene().stop()`.

Call it while the thing is being built, like every hook, and keep what it returns.

#### useSpawn (function, from `nacatamalon`)

```ts
useSpawn(component: (args: TArgs) => TApi, given: string | TSpawnOptions) => (args: TArgs) => TGameObject<TApi>
```

Prepares a maker for `component`, so the scene can create as many of them as it likes, whenever
it likes: while it is being built, from a frame, from a timer, from a click.

This is what gives each one a life of its own. Calling `Bullet(10)` yourself is an ordinary
function call, and the `useUpdate` inside it belongs to whoever called it, mixed in with
everything else there. Made through the maker, each bullet gets its own place: its sprites, its
per-frame code and its cleanups are its, and `destroy` can take exactly one of them away.

Whatever you pass to the maker reaches the component as its arguments: one object of props,
`makeBullet({ x: 10 })`, reads like the tag it stands for and keeps its names. They arrive once,
when the thing is made; the component is not run again when they change. What comes back is the
thing itself, for later: keep it to destroy it, or let the component end its own life with
`useSelf`.

What is the same for every one goes to `useSpawn`, what changes from one to the next to the
maker: the name and the place are decided once, here, while the scene is built; the props are
given to each one, whenever it is made. Two buttons of one component with names of their own are
two makers, which cost nothing.

Whatever the component returns is the thing's `api`, so the one who made it can talk to it:
an enemy that returns `{ hurt }` is hurt with `enemy.api.hurt(1)`, typed, with no signal and no
lookup. It is there once the maker returns, never while the component is still being run.

Call `useSpawn` while the scene is being built, like every hook, and the maker whenever: from
`useUpdate`, a timer or a listener, which is how things are made while the game runs. It remembers
the scene it was called in, so the maker keeps working for as long as that scene runs. Called once the
scene has stopped (from a `setTimeout` or a listener that outlived it), it makes nothing, says so
once, and gives back something already destroyed; under something destroyed, the same in silence.

- `component: (args: TArgs) => TApi`: The function describing one of these things. Whatever it returns becomes the
- `given: string | TSpawnOptions`

#### useTransform (function, from `nacatamalon`)

```ts
useTransform(options: TTransformOptions) => TTransform3d
```

Gives this object a place of its own, and moves everything inside it.

Without it, each sprite, text or model carries its own position and they are moved one by one.
With it, they are placed once, relative to the object, and moving the object moves all of them
together: a turret and its barrel, a card and its number, a ship and everything bolted to it.
Anything made inside this one is placed relative to it too, however deep it goes.

What comes back is the placement itself, so changing it is all it takes and it takes effect on
the next frame drawn. `rotation` turns it in the plane, which is the only one a 2D game needs;
`rotationX` and `rotationY` are for three dimensions.

For something free to point anywhere there is `quaternion`, and setting one puts the three angles
aside: `quatFromEuler` starts one off facing the same way they did, and `quatMultiply` turns it on
from there (by a `quatFromAxisAngle`). Note that it speaks for three dimensions only: pictures and
writing inside this one are still turned by `rotation`.

Asking twice gives back the same placement, so a later call never quietly throws away where
something already was.

- `options: TTransformOptions`: Where to start. Anything left out starts at the origin, unturned and unscaled.

#### viewToRay (function, from `nacatamalon`)

```ts
viewToRay(sprite: TSprite, point: TViewPointInput) => TViewRay
```

The ray of a view's 3D camera through a point on the sprite showing the view: what a click on it
picks among the models it sees. Test it against what can be picked, or against the floor's plane,
as with `screenToRay`, which this is for a view.

A view sees through its own 3D camera, or the scene's when it asked for none; one that follows the
2D camera (`follow2d`) follows the view's.

- `sprite: TSprite`: The sprite showing the view.
- `point: TViewPointInput`: The point on it, or the pointer's `info`.

#### viewToWorld (function, from `nacatamalon`)

```ts
viewToWorld(sprite: TSprite, point: TViewPointInput) => TViewToWorld
```

Turns a point on the sprite that shows a view into the point of the world the view sees there:
clicking on a minimap to go where was clicked, aiming through a scope.

The view is a picture from `createSpriteTexture({ sees: 'world' })` and this is the sprite showing
it, wherever it is and however it is placed: grown, turned, mirrored, held by any point, under
anything that moves it, showing a frame of the picture or repeating it. The point is in the
sprite's place, the world of its scene or the screen's pixels for a sprite pinned to the screen;
the pointer's `info` can be handed over as it is, and the right one is taken from it.

What comes back is in the view's 2D world. A view's 3D camera sees through a point along a whole
line rather than at one place: `viewToRay` gives that line. On a picture that sees only a world of
its own (`'inside'`), the point is in that world.

- `sprite: TSprite`: The sprite showing the view.
- `point: TViewPointInput`: The point on it, or the pointer's `info`.

#### worldToView (function, from `nacatamalon`)

```ts
worldToView(sprite: TSprite, point: { x, y }) => TWorldToView
```

Turns a point of the world a view sees into where it lands on the sprite showing the view: a
marker over the minimap, or an arrow at its edge for what is off it.

Never empty-handed: a point the view does not take in still lands somewhere past the sprite, and
`edge` is where the sprite's edge is in that direction, seen from its middle.

- `sprite: TSprite`: The sprite showing the view.
- `point: { x, y }`: A point of the view's 2D world.

#### T2dOptions (type, from `nacatamalon`)

```ts
type T2dOptions: { transform, anchor, pivot, tint, visible, zIndex, smooth, parallax, blend, tags, material, uniforms } & TSpriteEvents
```

What the flat pictures share, a sprite, a text and a panel: a rectangle placed by its anchor, turned
about its pivot, touched by the pointer. Each adds what is its own (a picture, the words, the
borders); these mean the same on all three.

Not everything drawn flat: a polygon, a tilemap and particles have options of their own, and
something new that is not one of these pictures does not have to fit it. `T3dOptions` is the same
for the lit models.

- `transform?: Partial<TTransform2d>`: Where it is, how it is turned and how big. Only what is given: the rest is no turn and a scale
- `anchor?: { x, y }`: Which point of it sits on its transform, in 0-1 of its own size: `{ x: 0.5, y: 0.5 }` is its
- `pivot?: { x, y }`: The point it turns and grows about, in 0-1 of its own size, when it is not the anchor: the
- `tint?: TColorOption`: Its colour, multiplied over what it draws: `'#ff6b1a'`, `0xff6b1a`, `'tomato'` or a `TColor`.
- `visible?: boolean`: Whether it is drawn. Default `true`. `false` makes it hidden from the start, which is how a set
- `zIndex?: number`: Which others it is drawn in front of, within its own scene: higher numbers go on top, and
- `smooth?: boolean`: Crisp (`false`) or blended (`true`) when scaled: `false` keeps pixel art crisp. Default: the
- `parallax?: { x, y }`: How much it moves with the camera: `1` (the default) as everything else, `0` not at all, and in
- `blend?: 'alpha' | 'additive'`: How it lands on what is behind it. `'alpha'` (the default) covers it as far as its alpha says.
- `tags?: string[]`: What it is, as `useTag` says it: `['wall']`. The same as adding them after.
- `material?: TSpriteMaterial`: An effect of its own, from `createMaterial`. Omitted is the built-in shader.
- `uniforms?: TUniformValues`: Its own values for that material's knobs, laid over the material's own.
- `onPointerOver?: TPointerListener`: The pointer comes over the sprite.
- `onPointerOut?: TPointerListener`: The pointer stops being over it, or leaves the game.
- `onPointerMove?: TPointerListener`: The pointer moves while over it.
- `onPointerDown?: TPointerListener`: A button is pressed, or a finger touches, over it.
- `onPointerUp?: TPointerListener`: A button is released, or a finger lifts, over it.
- `onClick?: TPointerListener`: Pressed and released without leaving it. The cursor turns into a hand over it.
- `onDragStart?: TPointerDragListener`: Pressed with the main button (or a finger) and moved far enough not to be a click: a few
- `onDrag?: TPointerDragListener`: The pointer that started the drag moved, wherever it is: over the sprite or not, in the game
- `onDragEnd?: TPointerDragListener`: The pointer that dragged it let go, with `target` and `hits` saying what it was let go over.

#### T3dOptions (type, from `nacatamalon`)

```ts
type T3dOptions: TMeshSurfaceOptions & { transform, zIndex, visible, castShadow, alwaysDrawn, boundsMargin }
```

What the lit models share, a shape made with `createMesh` and a model placed from a file with
`createModel`: where it is, how it is drawn and how its surface is painted. Each adds what is its
own (the shape and its material; the file and its looks).

The 3D side of `T2dOptions`, and as narrow: only the lit models use it. What else is drawn in three
dimensions (particles, lines) has options of its own, and something new that is not a lit model does
not have to fit it.

- `transform?: Partial<TTransform3d>`: Where it is. Anything left out is at the origin, unturned and unscaled.
- `zIndex?: number`: Draw order within its scene. Default `0`, and on a tie a model goes under a sprite.
- `visible?: boolean`: Whether it is drawn. Default `true`.
- `castShadow?: boolean`: Whether it is drawn into the shadow map. Default `true`.
- `alwaysDrawn?: boolean`: Drawn even when nothing of it is on screen. Default `false`: a model whose box is wholly
- `boundsMargin?: number`: How far past its own shape it can reach, in its own units: the height a wave of a `vertex`
- `tint?: TColorOption`: Multiplies its colour: its picture's, or the colours a model's file gave each piece, so they are
- `alpha?: number`: How solid it is, `0` to `1`. Default `1`.
- `transparent?: boolean`: Drawn as see-through even at full `alpha`: for a shader that works out its own alpha, or a
- `specular?: TColorOption`: The colour of its shine. Default none, which is a matt surface: a shine is the thing that
- `shininess?: number`: How tight that shine is. Default `32`. Higher is smaller and harder.
- `vertexSnap?: boolean | number`: Its corners land on a coarse grid of the screen, the way the PlayStation drew them: a model
- `affine?: boolean`: Its picture is stretched straight across the screen without correcting for depth, the way the
- `cull?: TCullMode`: Which side of its triangles is thrown away. `'back'` (the default) is a shape seen from
- `alphaCutoff?: number`: Cuts its picture out instead of blending it: a pixel whose alpha is below this number is not
- `smooth?: boolean`: Overrides the game's `smooth` for this model alone.
- `wrap?: TTextureWrap | { u, v }`: What its picture does past its edge. Default `'repeat'`, or what a model's file asked for each

#### TCullMode (type, from `nacatamalon`)

```ts
type TCullMode: 'back' | 'front' | 'none'
```

Which side of each triangle is thrown away, by which way it faces the camera.

- `'back'`: the side facing away. A closed shape seen from outside, which is nearly everything,
and half the work saved, since half of a closed shape always faces away.
- `'front'`: the side facing the camera. A shape seen from inside: a dome of sky, a room the camera
stands in. It is lit from inside too.
- `'none'`: nothing. Both sides drawn, each lit as the side it is: a leaf, a sheet, a flag, the
top of a liquid.

#### TDestroyOptions (type, from `nacatamalon`)

```ts
type TDestroyOptions
```

How `destroy` should go about it.

- `immediate?: boolean`: Ask for it to be gone the instant you say so, instead of at the end of the frame. It is a

#### TDrawable (type, from `nacatamalon`)

```ts
type TDrawable: TSprite | TText | TNineSlice | TTilemapLayer | TMesh | TParticles | TParticles3d | TLines | TPolygon | TShapes | TCustomDrawable
```

Anything drawn in a scene: a sprite, a text, a nine-slice, a map layer, a shape, shapes, a model, particles, lines, or a drawing of a kind a plugin registered. It grows by adding members (`TSprite | TText | TTilemap`), and every
member carries a `type`, so a `switch` on it knows which one it holds.

A union and not a shared base type: a base would need a cast to reach any field of its own,
and could never tell a backend that it forgot a kind.

#### TGameObject (type, from `nacatamalon`)

```ts
type TGameObject: TBox & { api }
```

One thing made by a spawner: what `destroy` takes to remove it whole, with its sprites, its
per-frame code and its cleanups.

`api` is whatever its component gave back, typed: `TGameObject<{ hurt(): void }>` for an enemy
whose component returns `{ hurt }`. Left out, it is `unknown`, which every object is.

- `id: string`: Unique for the life of the game.
- `name: string`: What the box is called where it was placed. Not unique: two enemies can share it.
- `tags: string[]`: What it is: `'enemy'`, `'coin'`, `'damageable'`, as many as it likes. What `useTag` adds and
- `parent: TBox | null`: `null` for a scene root. Kept so a box can be detached without searching the tree.
- `children: TBox[]`
- `updateCallbacks: (dt: number, time: number) => void[]`: Registered by `useUpdate`, called every frame with the delta and the scene's time, in seconds.
- `lateCallbacks: (dt: number, time: number) => void[]`: Registered by `useLateUpdate`, called every frame once every `updateCallbacks` of the scene
- `moveCallbacks: (dt: number, time: number) => void[]`: Registered by what moves a body against the world (a character body), called every frame
- `cleanups: () => void[]`: Run once, children first, when the box leaves the tree.
- `drawables: TDrawable[]`: What this box draws: its own sprites, not other boxes. Read every frame to fill the frame context.
- `scripts: TScriptAttachment[]`: The behaviours attached to it, in the order they were attached, which is the order they ran
- `scriptApis: Map<string, unknown>`: What each behaviour on this object returned, by its name, and what they offered with
- `api: unknown`: What its component gave back when `useSpawn` made it: usually a few functions for whoever
- `data: TDataRecord<unknown>[]`: The state its own body asked for with `useData`, in the order it asked.
- `sounds: TSoundAttachment | TMusicAttachment | TSongAttachment[]`: The sounds attached to it: which clip, and how each was asked to play.
- `motions: TMotionAttachment[]`: The motions it plays: which file, and how each was asked to play. Like a sound, never where it
- `stores: string[]`: The stores this object uses, by name.
- `physics: TPhysicsBody | null`: What this object is made of, physically, or `null` for one nothing can bump into.
- `physicsWorld: TPhysicsWorld | null`: The simulation's settings, on a scene's root and nowhere else.
- `hitboxes: THitbox[]`: The parts of it that can be touched, each on one side: `useHitbox`. Empty on almost every
- `paused: boolean`: Only meaningful on a scene root: while `true` the tick skips this scene's updates and still
- `held: boolean`: Only meaningful on a scene root: while `true` this scene is waiting behind a transition.
- `time: number`: Only meaningful on a scene root: the seconds of game time this scene has run, the `time` that
- `loads: TLoadable[]`: Only meaningful on a scene root: every asset the scene's body asked for, ready or not, in the
- `destroyed: boolean`: Set by `destroy`, on it or on an object above it, and never cleared. The frame that marked it
- `spawned: boolean`: Born after its scene finished being built, rather than as part of building it.
- `camera2d: TCamera2d | null`: Only meaningful on a scene root: the camera the scene's 2D is drawn through, or `null` to draw
- `camera3d: TCamera3d | null`: Only meaningful on a scene root: the camera the scene's 3D is seen through, or `null` to draw
- `fog: TFog | null`: Only meaningful on a view (a scene root, or a box drawn into a picture): the fog its models are
- `transform: TTransform3d | null`: Where this box is, which moves everything under it: its own drawables and every box beneath.
- `lights: TLight[]`: The lights this box gives off, in the order they were asked for: as many as it asks for, as a
- `particleCollider: TParticleCollider2d | TParticleCollider3d | null`: What this box is to particles: something solid, or `null`. One per box, like its light, and
- `audioListener: TAudioListener | null`: The ears of the game when they are on this object, or `null`. One per box. Without any, the
- `spriteTexture: TSpriteTexture | null`: The picture this box and everything under it is drawn into instead of the screen, or `null`.
- `screenSpace: boolean`: Pins this box, and everything under it, to the screen: drawn as if the scene had no camera.
- `unlit2d: boolean`: Leaves this box, and everything under it, out of the scene's 2D lights (`useUnlit2d`): drawn in
- `worldOffScreen: boolean`: On a scene's top box: the screen draws only what is pinned to it (`useScreenSpace`), and the
- `mask: TMask | null`: The mask that keeps this box, and everything under it, to a rectangle or a shape, or `null`.
- `filter: TFilter | null`: The filter whose effects run over what this box, and everything under it, draws, or `null`.
- `parallax: { x, y } | null`: How much this box, and everything under it, moves with the camera: `useParallax`. `null` moves
- `visible: boolean`: Whether this box and everything under it is drawn. Inherited downwards, so turning off a

#### TLines (type, from `nacatamalon`)

```ts
type TLines
```

A set of lines in space: plain, unlit, one colour per corner. What the debug helpers draw (an
axis gizmo, a floor grid, an outline), and anything else that is best said with a line.

The corners are not here. A record is plain data and a run of numbers the card reads is not,
so they live beside it: `useLines` hands back the function that writes them. What is here is
what an ordinary drawable has, where it is and whether it is drawn, and changing those is all it
takes for the next frame to show it.

Lines are hidden by the models in front of them, and hide what is behind them, the same as a
model: a line that ignored the scene would read as a wireframe floating over it. They are one
pixel wide on both backends, which is all the cards promise.

- `id: string`
- `type: 'lines'`
- `name?: string`: What to call it when you look at it, in a debugger or a log of your own. The engine never
- `transform: TTransform3d`: Where the lines are drawn from: their corners are measured from here, turned and scaled by it.
- `worldMatrix?: Float32Array`: Where it ends up once everything above it has moved it, or where it is when nothing does. Set
- `zIndex?: number`: Draw order within its scene. On a tie, lines and models are drawn in the order they were made.
- `visible?: boolean`: Whether they are drawn at all. Omitted is drawn.
- `destroyed: boolean`: Set by `destroy`, on it or on the object it belongs to, and never cleared.

#### TMesh (type, from `nacatamalon`)

```ts
type TMesh
```

A model: a shape, put somewhere, with a surface.

The shape is shared and the placement is its own, which is the whole split: a hundred crates are
one shape and a hundred of these. Plain data, like every other drawable, so changing a field is
all it takes and the next frame shows it.

The surface is a material, and a shared one: a hundred crates are one shape, one surface and a
hundred of these. A sprite is the other way round and keeps its own picture and colour, because in
two dimensions those vary per sprite and travel with it in the instance buffer.

So the question this used to leave open, of what a material is and what sharing one would mean,
turned out to have two answers rather than one, which is exactly why it was worth leaving open.

- `id: string`
- `type: 'mesh'`
- `geometry: TGeometry | null`: What shape it is. `null` draws nothing, which is what a model waiting on a file does.
- `transform: TTransform3d`: Where it is, how it is turned and how big.
- `worldMatrix?: Float32Array`: Where it ends up once everything above it has moved it, or where it is when nothing does. Set
- `skeleton: TSkeleton | null`: The bones that move it, or `null` for something that does not bend.
- `material: TMeshMaterial`: What it looks like: its picture, its colours, its shine, and any shader of its own.
- `zIndex?: number`: Draw order within its scene, the same one sprites use. On a tie, models are drawn first.
- `visible?: boolean`: Whether it is drawn at all. Omitted is drawn.
- `castShadow?: boolean`: Whether it is drawn into the shadow map. Omitted casts, which is what nearly everything
- `alwaysDrawn?: boolean`: Drawn even when nothing of it is on screen. Omitted, it is skipped while it is wholly outside
- `boundsMargin?: number`: How far past its shape it can reach, in its own units, before it is judged off screen. Needed
- `destroyed: boolean`: Set by `destroy`, on it or on the object it belongs to, and never cleared.

#### TMeshOptions (type, from `nacatamalon`)

```ts
type TMeshOptions: T3dOptions & { geometry, material, texture, key, emissive, emissiveTexture, occlusion }
```

What `createMesh` is asked for. Only the shape is needed.

- `geometry: TGeometry`: The shape it is, from one of the shape hooks.
- `material?: TMeshMaterial`: A surface to draw it with, from `createMaterial`, shared with anything else handed the same
- `texture?: TTexture`: A picture for its surface, from `useLoadTexture`.
- `key?: string`: The key a texture was loaded under, for a model built where the picture is not at hand.
- `emissive?: TColorOption`: Light it gives off by itself. Default none.
- `emissiveTexture?: TTexture`: A picture the glow comes through, multiplied by `emissive`: white where it glows, black where
- `occlusion?: TTexture | TMaterialOcclusion`: Where the light from all around is kept out of its creases, the shading a modelling program
- `transform?: Partial<TTransform3d>`: Where it is. Anything left out is at the origin, unturned and unscaled.
- `zIndex?: number`: Draw order within its scene. Default `0`, and on a tie a model goes under a sprite.
- `visible?: boolean`: Whether it is drawn. Default `true`.
- `castShadow?: boolean`: Whether it is drawn into the shadow map. Default `true`.
- `alwaysDrawn?: boolean`: Drawn even when nothing of it is on screen. Default `false`: a model whose box is wholly
- `boundsMargin?: number`: How far past its own shape it can reach, in its own units: the height a wave of a `vertex`
- `tint?: TColorOption`: Multiplies its colour: its picture's, or the colours a model's file gave each piece, so they are
- `alpha?: number`: How solid it is, `0` to `1`. Default `1`.
- `transparent?: boolean`: Drawn as see-through even at full `alpha`: for a shader that works out its own alpha, or a
- `specular?: TColorOption`: The colour of its shine. Default none, which is a matt surface: a shine is the thing that
- `shininess?: number`: How tight that shine is. Default `32`. Higher is smaller and harder.
- `vertexSnap?: boolean | number`: Its corners land on a coarse grid of the screen, the way the PlayStation drew them: a model
- `affine?: boolean`: Its picture is stretched straight across the screen without correcting for depth, the way the
- `cull?: TCullMode`: Which side of its triangles is thrown away. `'back'` (the default) is a shape seen from
- `alphaCutoff?: number`: Cuts its picture out instead of blending it: a pixel whose alpha is below this number is not
- `smooth?: boolean`: Overrides the game's `smooth` for this model alone.
- `wrap?: TTextureWrap | { u, v }`: What its picture does past its edge. Default `'repeat'`, or what a model's file asked for each

#### TMeshSurfaceOptions (type, from `nacatamalon`)

```ts
type TMeshSurfaceOptions
```

How the surface of a lit model is painted: what ends up in its material. A model placed from a
file has the same, laid over each piece's own.

- `tint?: TColorOption`: Multiplies its colour: its picture's, or the colours a model's file gave each piece, so they are
- `alpha?: number`: How solid it is, `0` to `1`. Default `1`.
- `transparent?: boolean`: Drawn as see-through even at full `alpha`: for a shader that works out its own alpha, or a
- `specular?: TColorOption`: The colour of its shine. Default none, which is a matt surface: a shine is the thing that
- `shininess?: number`: How tight that shine is. Default `32`. Higher is smaller and harder.
- `vertexSnap?: boolean | number`: Its corners land on a coarse grid of the screen, the way the PlayStation drew them: a model
- `affine?: boolean`: Its picture is stretched straight across the screen without correcting for depth, the way the
- `cull?: TCullMode`: Which side of its triangles is thrown away. `'back'` (the default) is a shape seen from
- `alphaCutoff?: number`: Cuts its picture out instead of blending it: a pixel whose alpha is below this number is not
- `smooth?: boolean`: Overrides the game's `smooth` for this model alone.
- `wrap?: TTextureWrap | { u, v }`: What its picture does past its edge. Default `'repeat'`, or what a model's file asked for each

#### TModel (type, from `nacatamalon`)

```ts
type TModel
```

A loaded model placed in the scene: one placement, and the pieces it is made of.

Move the placement and all of it moves, however many pieces the file turned out to hold. What
is set on the model (`tint`, `alpha`, `zIndex`, `castShadow`, `visible`) goes to every piece at once,
the ones still on the way too, and `destroy(model)` takes them all. The pieces are ordinary models
of their own, so one alone can still be changed through `parts`: the glass of a car.

`parts` fills in when the file arrives, so it is empty on the frame you make this and has what the
file held on a later one. Look a piece up by the name it had in the file when you need one in
particular.

- `id: string`
- `type: 'model'`
- `transform: TTransform3d`: Where the whole thing is. Shared by every piece, which is what moves them together.
- `model: TGltfModel`: The file it was placed from, with the movements it knows (`clips`).
- `parts: TMesh[]`: The pieces, in the order the file lists them. Empty until it arrives.
- `skeletons: TSkeleton[]`: Its own bones, one set per skeleton in the file, which its pieces wear: what
- `visible: boolean`: Whether any of it is drawn. Setting it hides or shows every piece at once, and holds for the
- `tint: TColor`: Multiplies the colour of every piece, so the file's own colours are kept and shaded: white
- `alpha: number`: How solid every piece is, `0` to `1`. Setting it fades them all at once.
- `zIndex: number`: Draw order within its scene, for every piece. `0` by default.
- `castShadow: boolean`: Whether its pieces throw a shadow. `true` by default.
- `alwaysDrawn: boolean`: Whether every piece is drawn even off screen. `false` by default: a piece wholly outside what
- `boundsMargin: number | undefined`: How far past its shape each piece can reach, in its own units, before it is judged off screen.
- `destroyed: boolean`: `true` once `destroy(model)` has been asked: its pieces are gone, and one still on the way

#### TModelOptions (type, from `nacatamalon`)

```ts
type TModelOptions: T3dOptions & { model, lighting, steps }
```

What `createModel` is asked for. Only the model is needed.

- `model: TGltfModel`: The loaded model, from `useLoadModel`.
- `lighting?: TLightingLook`: How every piece is lit, in place of the engine's light: `toonLighting()` or `unlitLighting()`. Each piece
- `steps?: typeOperator`: What is done to every piece once it is lit, in order: `mangaDots()`, `rim()`.
- `transform?: Partial<TTransform3d>`: Where it is. Anything left out is at the origin, unturned and unscaled.
- `zIndex?: number`: Draw order within its scene. Default `0`, and on a tie a model goes under a sprite.
- `visible?: boolean`: Whether it is drawn. Default `true`.
- `castShadow?: boolean`: Whether it is drawn into the shadow map. Default `true`.
- `alwaysDrawn?: boolean`: Drawn even when nothing of it is on screen. Default `false`: a model whose box is wholly
- `boundsMargin?: number`: How far past its own shape it can reach, in its own units: the height a wave of a `vertex`
- `tint?: TColorOption`: Multiplies its colour: its picture's, or the colours a model's file gave each piece, so they are
- `alpha?: number`: How solid it is, `0` to `1`. Default `1`.
- `transparent?: boolean`: Drawn as see-through even at full `alpha`: for a shader that works out its own alpha, or a
- `specular?: TColorOption`: The colour of its shine. Default none, which is a matt surface: a shine is the thing that
- `shininess?: number`: How tight that shine is. Default `32`. Higher is smaller and harder.
- `vertexSnap?: boolean | number`: Its corners land on a coarse grid of the screen, the way the PlayStation drew them: a model
- `affine?: boolean`: Its picture is stretched straight across the screen without correcting for depth, the way the
- `cull?: TCullMode`: Which side of its triangles is thrown away. `'back'` (the default) is a shape seen from
- `alphaCutoff?: number`: Cuts its picture out instead of blending it: a pixel whose alpha is below this number is not
- `smooth?: boolean`: Overrides the game's `smooth` for this model alone.
- `wrap?: TTextureWrap | { u, v }`: What its picture does past its edge. Default `'repeat'`, or what a model's file asked for each

#### TNineSlice (type, from `nacatamalon`)

```ts
type TNineSlice
```

A picture cut into nine parts so it can be any size without deforming its corners, as
`createNineSlice` gives it back: the panels, buttons, dialogue boxes and health bars of a UI.
Plain data: change any field (`width` above all) and the next frame shows it.

- `id: string`
- `type: 'nine-slice'`
- `width: number`: Width in pixels, before scale. Change it and the corners stay as they are.
- `height: number`: Height in pixels, before scale.
- `transform: TTransform2d`: Where it is, how it is turned and how big. Scale stretches the whole of it, corners included.
- `worldTransform?: TTransform2d`: Where it ends up once everything above it has moved it, worked out once a frame while the tree
- `texture: TTexture`: The picture it is cut from.
- `tint: TColor`
- `slice: TNineSliceBorders`: How wide each border is, in pixels of the picture.
- `mode: TNineSliceMode | { x, y }`: How the edges and the middle fill their space, the same both ways or one per direction:
- `atlas?: TSpriteAtlas`: The sheet its picture came from, if it came from one.
- `frame?: number`: Which frame of `atlas` it is cut from. Change it and the next frame is cut from that one.
- `uvOffset?: { x, y }`: Where the window into the texture starts, in 0-1. Wins over `atlas` and `frame`.
- `uvScale?: { x, y }`: How big that window is, in 0-1. Wins over `atlas` and `frame`.
- `anchor?: { x, y }`: Which point of it sits on its transform, in 0-1. Omitted is its middle, as for a sprite.
- `pivot?: { x, y }`: The point it turns and grows about, in 0-1 like `anchor`, when that is not its anchor: a
- `zIndex?: number`: Draw order within its scene, as for a sprite. All nine parts move as one.
- `smooth?: boolean`: Crisp or blended when it does not land on whole pixels. Omitted, the game's `smooth` decides.
- `parallax?: { x, y }`: How much it moves with the camera, as for a sprite: `1` (the default) as everything else, `0`
- `blend?: 'alpha' | 'additive'`: `'additive'` adds its colour to what is behind it, so it can only brighten; `'alpha'` (the
- `tags?: string[]`: What it is, for something drawn on its own rather than a piece: `['wall']`. What `useTag` adds and
- `visible?: boolean`: Whether it is drawn at all. Omitted is drawn.
- `material?: TSpriteMaterial`: An effect of its own, from `createMaterial`, carried by every part. Omitted is the built-in shader.
- `uniforms?: TUniformValues`: Its own values for that material's knobs, laid over the material's own.
- `destroyed: boolean`: Set by `destroy`, on it or on the object it belongs to, and never cleared.

#### TNineSliceBorders (type, from `nacatamalon`)

```ts
type TNineSliceBorders
```

How wide each border of a nine-slice is, in pixels of its picture (of its frame, with a sheet).
Those borders are what is never stretched: the corners stay as drawn, and the edges only grow
along their length.

- `left: number`
- `top: number`
- `right: number`
- `bottom: number`

#### TNineSliceMode (type, from `nacatamalon`)

```ts
type TNineSliceMode: 'stretch' | 'tile' | 'tile-fit'
```

How the parts of a nine-slice between its corners fill the space they are given.

- `'stretch'`: the part is drawn once, stretched to fit. The default, and what a plain panel wants.
- `'tile'`: the part is repeated at its own size from the corner onwards, and the last copy is cut
where the space ends. For a frame with a pattern along it (rivets, a rope, bricks) that must not
be deformed.
- `'tile-fit'`: repeated as many whole times as fit best, each copy stretched a little so none is
cut. The pattern stays whole at the cost of being slightly wider or narrower than drawn.

#### TNineSliceOptions (type, from `nacatamalon`)

```ts
type TNineSliceOptions: T2dOptions & { texture, key, atlas, frame, uvOffset, uvScale, slice, mode, width, height }
```

What `createNineSlice` is asked for: a picture, how wide its borders are, and the size to draw it at.

- `texture?: TTexture | string`: Its picture: a texture from `useLoadTexture`, or the key it was loaded under (`'panel'`). Wins
- `key?: string`: The key a texture was loaded under.
- `atlas?: TSpriteAtlas`: A sheet from `createSpriteAtlas`, with `frame` saying which part of it is the picture.
- `frame?: number`: Which frame of `atlas`, numbered from 0. Default `0`.
- `uvOffset?: { x, y }`: Where the picture starts inside the texture, in 0-1, for a picture that is not a whole frame.
- `uvScale?: { x, y }`: How big the picture is inside the texture, in 0-1.
- `slice: number | TNineSliceBorders`: How wide each border is, in pixels of the picture: one number for all four, or each one. The
- `mode?: TNineSliceMode | { x, y }`: How the edges and the middle fill their space: `'stretch'` (the default), `'tile'` or
- `width: number`: How wide to draw it, in pixels. Required: drawn at the size of its picture it would cut nothing,
- `height: number`: How tall to draw it, in pixels.
- `transform?: Partial<TTransform2d>`: Where it is, how it is turned and how big. Only what is given: the rest is no turn and a scale
- `anchor?: { x, y }`: Which point of it sits on its transform, in 0-1 of its own size: `{ x: 0.5, y: 0.5 }` is its
- `pivot?: { x, y }`: The point it turns and grows about, in 0-1 of its own size, when it is not the anchor: the
- `tint?: TColorOption`: Its colour, multiplied over what it draws: `'#ff6b1a'`, `0xff6b1a`, `'tomato'` or a `TColor`.
- `visible?: boolean`: Whether it is drawn. Default `true`. `false` makes it hidden from the start, which is how a set
- `zIndex?: number`: Which others it is drawn in front of, within its own scene: higher numbers go on top, and
- `smooth?: boolean`: Crisp (`false`) or blended (`true`) when scaled: `false` keeps pixel art crisp. Default: the
- `parallax?: { x, y }`: How much it moves with the camera: `1` (the default) as everything else, `0` not at all, and in
- `blend?: 'alpha' | 'additive'`: How it lands on what is behind it. `'alpha'` (the default) covers it as far as its alpha says.
- `tags?: string[]`: What it is, as `useTag` says it: `['wall']`. The same as adding them after.
- `material?: TSpriteMaterial`: An effect of its own, from `createMaterial`. Omitted is the built-in shader.
- `uniforms?: TUniformValues`: Its own values for that material's knobs, laid over the material's own.
- `onPointerOver?: TPointerListener`: The pointer comes over the sprite.
- `onPointerOut?: TPointerListener`: The pointer stops being over it, or leaves the game.
- `onPointerMove?: TPointerListener`: The pointer moves while over it.
- `onPointerDown?: TPointerListener`: A button is pressed, or a finger touches, over it.
- `onPointerUp?: TPointerListener`: A button is released, or a finger lifts, over it.
- `onClick?: TPointerListener`: Pressed and released without leaving it. The cursor turns into a hand over it.
- `onDragStart?: TPointerDragListener`: Pressed with the main button (or a finger) and moved far enough not to be a click: a few
- `onDrag?: TPointerDragListener`: The pointer that started the drag moved, wherever it is: over the sprite or not, in the game
- `onDragEnd?: TPointerDragListener`: The pointer that dragged it let go, with `target` and `hits` saying what it was let go over.

#### TPackOptions (type, from `nacatamalon`)

```ts
type TPackOptions
```

What `createPack` is asked for: which thing of which pack, where, and with which settings.

- `pack: TLoadedPack`: The pack, from `useLoadPack`.
- `box?: string`: Which of the boxes it offers. Left out, and with `scene` left out too, the pack's only offer is
- `scene?: string`: Which of the scenes it offers, placed as one part of the scene that creates it.
- `transform?: TTransformOptions`: Where this copy goes, measured from what creates it. The pack has no place of its own.
- `props?: TScriptProps`: This copy's own settings for the scripts on the thing itself (not its children), laid over the
- `on?: Record<string, TReportHandler>`: What to do when the copy reports something: what its behaviours send with `useReport` (a

#### TParticles (type, from `nacatamalon`)

```ts
type TParticles
```

An emitter: a place in the world that makes particles, following an effect written in a file.

Plain data from end to end, like every other record here. Its living particles, its own stream
of chance and the numbers bound for the card are not any of those things, so they are kept beside
it rather than in it, and found by it. That is what lets an emitter be written to a scene file at
all: what would be written is what is here, and all of it is JSON.

- `type: 'particles'`
- `id: string`
- `name: string`: What a warning about it will call it. Defaults to the file it came from.
- `file: TParticlesFile`: The effect it follows. Filled in when the file lands; until then it draws nothing.
- `transform: TTransform2d`: Where the emitter is. Particles are born here, turned by whatever is above it.
- `worldTransform?: TTransform2d`: Where it ended up once everything above it has moved it, when something did.
- `tint: TColor`: Multiplied into every particle, over the colour the effect's own curve gives it.
- `alpha: number`: Multiplied into every particle's opacity, the same way.
- `smooth?: boolean`: Crisp or blended. Default: the game's own setting.
- `overrides?: TParticleOverrides`: What this one disagrees with its file about, or nothing.
- `emitting: boolean`: Whether new ones are appearing.
- `paused: boolean`: Frozen. Nothing moves and nothing is born, and it keeps drawing exactly what it last drew.
- `autoplay: boolean`: Whether it starts emitting the moment its file lands.
- `seed: number | null`: The seed for this emitter's own chance, or `null` for one taken from the clock.
- `zIndex?: number`
- `visible: boolean`
- `destroyed: boolean`: Set by `destroy`, on it or on the object it belongs to, and never cleared. True means it is on

#### TParticlesOptions (type, from `nacatamalon`)

```ts
type TParticlesOptions
```

What `createParticles` is asked for.

Almost everything about the effect itself lives in its file, and that is the point: three torches
are one document. What is here is the handful of things this emitter decides for itself.

- `effect: TParticlesFile | TParticlesEffect | string`: The effect: from `useLoadParticles`, the name one was loaded under, or written right here in
- `name?: string`: What a warning about it will call it. Defaults to the file.
- `transform?: Partial<TTransform2d>`: Where it is. Particles are born here, turned and scaled by whatever is above it.
- `tint?: TColorOption`: Multiplied into every particle, over the colour the effect's own curve gives it.
- `alpha?: number`: Multiplied into every particle's opacity, the same way. Default `1`.
- `smooth?: boolean`: Crisp or blended. Default: the game's own setting.
- `overrides?: TParticleOverrides`: What this emitter disagrees with its file about: a smaller explosion, a slower fountain.
- `autoplay?: boolean`: Start as soon as the file lands. `true` by default, because a torch put in a scene should be
- `seed?: number`: Its own seed, so it replays exactly. Omitted, every run looks a little different.
- `zIndex?: number`: Draw order, as everywhere else.
- `visible?: boolean`: Whether it is drawn at all. Default `true`.

#### TPolygon (type, from `nacatamalon`)

```ts
type TPolygon
```

A shape whose corners the game moves: a picture stretched over an outline that can change every
frame. A blob that squashes as it lands, a flag in the wind, a jelly, a rope drawn as a strip.

Where a sprite is always a rectangle, this is whatever the corners say, and the picture goes with
them: each corner holds on to its own point of the picture, so pushing a corner out stretches the
picture under it. The corners are plain numbers on the record, changed like any other field, and
the next frame draws them.

It is drawn from its middle out, a triangle for each side, so it suits any outline that can be
seen whole from its middle (a circle squashed any way, a star, a drop). An outline that folds back
on itself past its middle is drawn with the fold showing.

- `type: 'polygon'`
- `id: string`
- `name?: string`: What to call it when you look at it, in a debugger or a log of your own. The engine never
- `transform: TTransform2d`: Where it is, turned and scaled. The corners are measured from here.
- `worldTransform?: TTransform2d`: Where it ends up once everything above it has moved it. Set by the engine every frame.
- `points: { x, y }[]`: The outline, corner after corner around it, measured from `transform`. Change them, or the
- `uvs: { x, y }[]`: Which point of the picture each corner holds, from `0, 0` (its top-left) to `1, 1`, one per
- `texture: TTexture | null`: The picture, or `null` for a shape of a plain colour (its `tint`).
- `atlas?: TSpriteAtlas`: A sheet, when the picture is one frame of it.
- `frame?: number`: Which frame of `atlas`.
- `tint: TColor`: Multiplies the colour of the picture, `a` as opacity. Its whole colour when it has no picture.
- `smooth?: boolean`: How the picture is read. Omitted, the game's own setting decides.
- `zIndex: number`: Draw order within the scene, as for any other drawing.
- `visible?: boolean`: Whether it is drawn. Default `true`.
- `parallax?: { x, y }`: How much it moves with the camera, as on a sprite.
- `destroyed: boolean`

#### TPolygonOptions (type, from `nacatamalon`)

```ts
type TPolygonOptions
```

What `createPolygon` is told.

- `points: { x, y }[]`: The outline, at least three corners, one after the other around it, measured from `transform`.
- `uvs?: { x, y }[]`: Which point of the picture each corner holds, from `0, 0` to `1, 1`. Left out, the picture is
- `texture?: TTexture`: The picture. Left out (and no `atlas`), a shape of a plain colour.
- `atlas?: TSpriteAtlas`: A sheet, to show one of its frames.
- `frame?: number`: Which frame of `atlas`. `0` by default.
- `tint?: TColorOption`: Multiplies the colour of the picture; the colour itself without one. White by default.
- `transform?: Partial<TTransform2d>`
- `zIndex?: number`
- `visible?: boolean`
- `smooth?: boolean`
- `parallax?: { x, y }`
- `name?: string`: What to call it when you look at it. The engine never looks it up: to find it, name its box.

#### TShape (type, from `nacatamalon`)

```ts
type TShape: TShapeRect | TShapeCircle | TShapeEllipse | TShapePolygon | TShapeLine
```

One of the shapes a `createShapes` draws.

#### TShapeCircle (type, from `nacatamalon`)

```ts
type TShapeCircle: TShapeFilled & { type, x, y, radius, start, end, segments }
```

A circle by its middle, or a slice of one from `start` to `end`: filled, a slice of a pie (a
cooldown); stroked, the arc alone (a ring that fills up).

- `type: 'circle'`
- `x: number`
- `y: number`
- `radius: number`
- `start?: number`: Where the slice starts and ends, in radians clockwise from the right; an `end` before its
- `end?: number`
- `segments?: number`: How many straight sides make it up. Left out, as many as keep it round at its size.

#### TShapeEllipse (type, from `nacatamalon`)

```ts
type TShapeEllipse: TShapeFilled & { type, x, y, radiusX, radiusY, rotation, start, end, segments }
```

An ellipse by its middle, turned by `rotation`, or a slice of one, as a circle is.

- `type: 'ellipse'`
- `x: number`
- `y: number`
- `radiusX: number`
- `radiusY: number`
- `rotation?: number`
- `start?: number`
- `end?: number`
- `segments?: number`

#### TShapeLine (type, from `nacatamalon`)

```ts
type TShapeLine: TShapeCommon & { type, points, stroke, closed }
```

A line through two points or more, as wide as its `stroke` says; `closed` joins the last to the
first.

- `type: 'line'`
- `points: { x, y }[]`
- `stroke: TShapeStroke`
- `closed?: boolean`

#### TShapePolygon (type, from `nacatamalon`)

```ts
type TShapePolygon: TShapeFilled & { type, points }
```

Any outline of three corners or more, one after another round it, hollowed in or not. One whose
sides cross is drawn from its middle out, folds showing.

- `type: 'polygon'`
- `points: { x, y }[]`

#### TShapeRect (type, from `nacatamalon`)

```ts
type TShapeRect: TShapeFilled & { type, x, y, width, height, radius, rotation }
```

A rectangle, by its top-left corner, with its corners rounded if `radius` says so.

- `type: 'rect'`
- `x: number`
- `y: number`
- `width: number`
- `height: number`
- `radius?: number | [number, number, number, number]`: How round its corners are: one radius for all four, or one each, top-left, top-right,
- `rotation?: number`: Turned about its middle, in radians.

#### TShapes (type, from `nacatamalon`)

```ts
type TShapes
```

Shapes drawn by the graphics card as they are each frame: rectangles, rounded or not, circles,
ellipses and slices of them, outlines of any number of corners, and lines as wide as asked for,
each filled, outlined or both, in any colour. A health bar and its frame, a cooldown, a ship drawn
in lines, a chart.

They are plain data, `shapes`, drawn in the order of the list, each filled first and then
outlined: change a number, push one, take one out, and the next frame draws it. However many
there are, the card draws them in one go; what they cost is working out their corners again
each frame.

- `type: 'shapes'`
- `id: string`
- `name?: string`: What to call it when you look at it, in a debugger or a log of your own. The engine never
- `transform: TTransform2d`: Where it is, turned and scaled. The shapes are measured from here.
- `worldTransform?: TTransform2d`: Where it ends up once everything above it has moved it. Set by the engine every frame.
- `shapes: TShape[]`: What is drawn, in order. Change it at any time.
- `tint: TColor`: Multiplies the colour of all of them, `a` as opacity.
- `zIndex: number`: Draw order within the scene, as for any other drawing.
- `visible?: boolean`: Whether it is drawn. Default `true`.
- `parallax?: { x, y }`: How much it moves with the camera, as on a sprite.
- `destroyed: boolean`

#### TShapesOptions (type, from `nacatamalon`)

```ts
type TShapesOptions
```

What `createShapes` is told.

- `shapes?: TShape[]`: What is drawn, in order, each filled first and then outlined. Can start empty and be filled in
- `tint?: TColorOption`: Multiplies the colour of all of them. White by default.
- `transform?: Partial<TTransform2d>`
- `zIndex?: number`
- `visible?: boolean`
- `parallax?: { x, y }`
- `name?: string`: What to call it when you look at it. The engine never looks it up: to find it, name its box.

#### TShapeStroke (type, from `nacatamalon`)

```ts
type TShapeStroke
```

The line drawn along a shape's outline, or what a `line` is.

- `width: number`: How wide, in the game's pixels: it grows with the object's scale and the camera's zoom.
- `color: TColorOption`
- `join?: 'miter' | 'round' | 'bevel'`: How two sides meet: a sharp corner (`'miter'`, the default, cut flat when sharper than four
- `cap?: 'butt' | 'round' | 'square'`: How an open line ends: flat at its last point (`'butt'`, the default), round, or flat half a
- `align?: 'center' | 'inside' | 'outside'`: Where a closed outline's line goes: on it (`'center'`, the default), all inside it, or all

#### TSpawnOptions (type, from `nacatamalon`)

```ts
type TSpawnOptions
```

What every thing a maker makes is called, and where it is placed. A string alone is the name,
the usual case: `useSpawn(Button, 'Jump')`.

- `name?: string`: What each one is called, for whatever finds it by name: a touch button's action
- `tags?: string[]`: What each one is, as `useTag` says it: `['coin']`. The same as `useTag('coin').add(useSelf())`
- `parent?: 'scene' | TGameObject`: Left out, each one hangs under whoever is making it: a tank that makes its own turret carries

#### TSprite (type, from `nacatamalon`)

```ts
type TSprite
```

A picture in the scene, as `createSprite` gives it back. Plain data: change any field
(`transform.x`, `tint`, `visible`...) and the next frame shows it.

- `id: string`
- `type: 'sprite'`
- `width?: number`: Width in pixels, before scale. Omitted, it is drawn as wide as what it shows of its texture
- `height?: number`: Height in pixels, before scale. Omitted, it is drawn as tall as what it shows of its texture,
- `transform: TTransform2d`
- `worldTransform?: TTransform2d`: Where it ends up once everything above it has moved it, worked out once a frame while the tree
- `texture: TTexture | null`: The image it shows, or `null` for a plain rectangle of its `tint`.
- `tint: TColor`
- `atlas?: TSpriteAtlas`: The sheet its picture came from, if it came from one. Set another and it shows the same frame
- `frame?: number`: Which frame of `atlas` it shows. Set it and that frame shows, as with any other field:
- `animation?: TSpriteAnimationDoc`: The run a scene document asked it to play, and how fast: what it plays, not how far it has got.
- `anchor?: { x, y }`: Which point of the sprite sits on its transform, in 0-1. Omitted is its middle.
- `pivot?: { x, y }`: The point it turns and grows about, in 0-1 like `anchor`, when that is not its anchor: a
- `uvOffset?: { x, y }`: Where the window into the texture starts, in 0-1. Omitted is the top-left corner.
- `uvScale?: { x, y }`: How big that window is, in 0-1. Omitted is the whole image.
- `flipX?: boolean`: Shows its picture mirrored left to right: a character facing the other way, with one drawing
- `flipY?: boolean`: The same top to bottom.
- `tile?: boolean`: Tiles its picture across its size instead of stretching it: a wall of bricks, a strip of
- `tileOffset?: { x, y }`: With `tile`, where the pattern starts inside the sprite, in its picture's pixels: moving it
- `parallax?: { x, y }`: How much it moves with the camera: `1` (the default) as everything else, `0` not at all, as if
- `tags?: string[]`: What it is, for something drawn on its own rather than a piece: `['wall']`. What `useTag` adds and
- `visible?: boolean`: Whether it is drawn at all. Omitted is drawn.
- `smooth?: boolean`: How its image is read when it does not land on whole pixels: `false` keeps it crisp and
- `blend?: 'alpha' | 'additive'`: How it lands on what is behind it. `'alpha'` (the default) covers it as far as its alpha says.
- `material?: TSpriteMaterial`: An effect of its own, from `createMaterial`. Omitted is the built-in shader, which is what
- `uniforms?: TUniformValues`: This sprite's own values for its material's knobs, laid over the material's every frame.
- `zIndex?: number`: Draw order within its scene: higher on top, ties in creation order. Omitted counts as `0`,
- `destroyed: boolean`: Set by `destroy`, on it or on the object it belongs to, and never cleared. True means it is on

#### TText (type, from `nacatamalon`)

```ts
type TText
```

A text in the scene, as `createText` gives it back: a string drawn with a bitmap or a vector font. Plain
data; change any field (`text` above all) and the next frame shows it.

- `id: string`
- `type: 'text'`
- `text: string`: What it says. `\n` starts a new line.
- `font: TBitmapFont | TFont`: A bitmap font (`useLoadBitmapFont`) or a vector one (`useLoadFont`). Can be swapped for another
- `style: TTextStyle`: Size, alignment and spacing. Held by reference, so a shared style changes every text using it.
- `tint: TColor`
- `transform: { x, y, rotation, scaleX, scaleY }`: Where the block is. Scale and rotation apply to the whole block, around its anchor.
- `worldTransform?: { x, y, rotation, scaleX, scaleY }`: Where it ends up once everything above it has moved it, worked out once a frame while the tree
- `anchor?: { x, y }`: Which point of the block sits on its transform, in 0-1. Omitted is its top-left corner.
- `pivot?: { x, y }`: The point of the block it turns and grows about, in 0-1 like `anchor`, when that is not its
- `zIndex?: number`: Draw order within its scene, as for a sprite. The whole text moves as one.
- `smooth?: boolean`: Crisp or blended when scaled. Omitted, the game's `smooth` decides.
- `parallax?: { x, y }`: How much it moves with the camera, as for a sprite: `1` (the default) as everything else, `0`
- `blend?: 'alpha' | 'additive'`: `'additive'` adds its colour to what is behind it, so it can only brighten; `'alpha'` (the
- `tags?: string[]`: What it is, for something drawn on its own rather than a piece: `['wall']`. What `useTag` adds and
- `visible?: boolean`: Whether it is drawn at all. Omitted is drawn. Hidden, it keeps what it says and costs nothing
- `material?: TSpriteMaterial`: An effect of its own, from `createMaterial`. Omitted is the built-in shader.
- `uniforms?: TUniformValues`: This text's own values for that material's knobs, laid over the material's own.
- `destroyed: boolean`: Set by `destroy`, on it or on the object it belongs to, and never cleared.

#### TTextOptions (type, from `nacatamalon`)

```ts
type TTextOptions: T2dOptions & { text, font, style }
```

What `createText` is asked for.

- `text: string`: What it says. `\n` starts a new line.
- `font?: TBitmapFont | TFont | string`: A font from `useLoadFont` (a `.ttf` or `.woff`, sharp at any size) or `useLoadBitmapFont` (a
- `style?: TTextStyle`: Size, alignment and spacing. Kept as given, not copied: pass the same object to several texts
- `transform?: Partial<TTransform2d>`: Where it is, how it is turned and how big. Only what is given: the rest is no turn and a scale
- `anchor?: { x, y }`: Which point of it sits on its transform, in 0-1 of its own size: `{ x: 0.5, y: 0.5 }` is its
- `pivot?: { x, y }`: The point it turns and grows about, in 0-1 of its own size, when it is not the anchor: the
- `tint?: TColorOption`: Its colour, multiplied over what it draws: `'#ff6b1a'`, `0xff6b1a`, `'tomato'` or a `TColor`.
- `visible?: boolean`: Whether it is drawn. Default `true`. `false` makes it hidden from the start, which is how a set
- `zIndex?: number`: Which others it is drawn in front of, within its own scene: higher numbers go on top, and
- `smooth?: boolean`: Crisp (`false`) or blended (`true`) when scaled: `false` keeps pixel art crisp. Default: the
- `parallax?: { x, y }`: How much it moves with the camera: `1` (the default) as everything else, `0` not at all, and in
- `blend?: 'alpha' | 'additive'`: How it lands on what is behind it. `'alpha'` (the default) covers it as far as its alpha says.
- `tags?: string[]`: What it is, as `useTag` says it: `['wall']`. The same as adding them after.
- `material?: TSpriteMaterial`: An effect of its own, from `createMaterial`. Omitted is the built-in shader.
- `uniforms?: TUniformValues`: Its own values for that material's knobs, laid over the material's own.
- `onPointerOver?: TPointerListener`: The pointer comes over the sprite.
- `onPointerOut?: TPointerListener`: The pointer stops being over it, or leaves the game.
- `onPointerMove?: TPointerListener`: The pointer moves while over it.
- `onPointerDown?: TPointerListener`: A button is pressed, or a finger touches, over it.
- `onPointerUp?: TPointerListener`: A button is released, or a finger lifts, over it.
- `onClick?: TPointerListener`: Pressed and released without leaving it. The cursor turns into a hand over it.
- `onDragStart?: TPointerDragListener`: Pressed with the main button (or a finger) and moved far enough not to be a click: a few
- `onDrag?: TPointerDragListener`: The pointer that started the drag moved, wherever it is: over the sprite or not, in the game
- `onDragEnd?: TPointerDragListener`: The pointer that dragged it let go, with `target` and `hits` saying what it was let go over.

#### TTextStyle (type, from `nacatamalon`)

```ts
type TTextStyle
```

How a text is set: size, alignment and spacing. Kept apart from the rest of a text so it can be
shared: several texts given the same style change together when it changes.

- `fontSize?: number`: How big the letters are, in pixels.
- `align?: 'left' | 'center' | 'right'`: Where each line sits: inside `maxWidth` when there is one, inside the width of the longest
- `maxWidth?: number`: How wide a line can be, in pixels, before its words go on to the next one: a dialogue box, a
- `letterSpacing?: number`: Extra pixels between two characters. Default `0`.
- `lineSpacing?: number`: Extra pixels between two lines. Default `0`.

#### TTransform2d (type, from `nacatamalon`)

```ts
type TTransform2d
```

Where a game object is, how it is turned and how big it is drawn.

Every field is required and written out, `rotation: 0, scaleX: 1, scaleY: 1` included. Spelled
out rather than filled in from a default, so a transform read from a file, one built by hand and
one the editor wrote are the same five numbers, and moving one never depends on which of the
three it came from.

That is the object once made. Making one takes only the fields that differ from the defaults
(`createSprite({ transform: { x: 40, y: 112 } })`), and the rest are filled in there.

- `x: number`
- `y: number`
- `rotation: number`
- `scaleX: number`
- `scaleY: number`

#### TTransform3d (type, from `nacatamalon`)

```ts
type TTransform3d: TTransform2d & { z, rotationX, rotationY, scaleZ, quaternion, pivot }
```

Where something is in three dimensions, how it is turned and how big it is drawn.

The 2D transform is exactly its flat half, with the same names: `x`, `y`, `rotation` (the turn
in the plane, around Z) and `scaleX`/`scaleY` mean here what they mean there. That is what lets one
box hold a 2D game, a 3D one, or both, without a second kind of placement to learn.

Rotations are in radians and applied Y, then X, then Z, and the whole is move, then turn, then
scale. Written out rather than filled in from defaults, for the reason `TTransform2d` gives.

A `quaternion` is the other way to say which way something is turned, and when one is set it is
the one that counts. Three angles cannot express every turn on their way to it: at certain
facings two of the three end up turning around the same line and one of them stops doing
anything, which is why a ship that is free to point anywhere, or a turn being eased from one
facing to another, is stored this way instead.

- `z: number`: Towards the viewer.
- `rotationX: number`: Looking up and down, in radians.
- `rotationY: number`: Turning left and right, in radians.
- `scaleZ: number`
- `quaternion?: TQuat | null`: Which way it is turned, as a whole. Set, it decides the turn and the three angles above are
- `pivot?: { x, y, z } | null`: The point it turns and grows about, in its own units from its origin, when that is not the
- `x: number`
- `y: number`
- `rotation: number`
- `scaleX: number`
- `scaleY: number`

#### TTransformOptions (type, from `nacatamalon`)

```ts
type TTransformOptions: Partial<TTransform3d>
```

Where to put something, and everything inside it.

#### TViewFilter (type, from `nacatamalon`)

```ts
type TViewFilter
```

Which part of the world a view (`sees: 'world'`) takes in, or leaves out: by tags (an object's,
`TBox.tags`, or a sprite's, text's or panel's own), by kind of drawing, or by naming objects.

The three add up: `{ tags: ['terrain'], objects: [player] }` is the terrain and the player. A tag
or a named object takes everything under it as well. As a view's `hide` it leaves what matches out,
and as its `only` it takes in nothing else; `hide` wins wherever the two meet. Neither reaches what
the view draws itself, and neither brings back what is hidden (`visible: false`), which no view
shows. A mask's shape left out still cuts what the view does draw, as it does on the screen.

- `tags?: typeOperator`: Objects carrying any of these tags, and everything under them, and sprites, texts and panels
- `kinds?: typeOperator`: Drawings of these kinds: `'sprite'`, `'text'`, `'nine-slice'`, `'tilemap'`, `'particles'`,
- `objects?: ReadonlyArray<TBox | TDrawable>`: These objects and everything under them, or these drawings. In code only: an object cannot be

#### TViewOptions (type, from `nacatamalon`)

```ts
type TViewOptions: Omit<TSpriteTextureOptions, 'sees'> & { x, y }
```

What `createView` is asked for: where on the screen the view goes, and the rest as for a view of
the world made with `createSpriteTexture` (its size, what is behind it, what of the world it takes
in).

- `x: number`: Its left edge, in screen pixels: from the left of the game when it is made in the scene's body,
- `y: number`: Its top edge, in screen pixels, measured as `x` is.

#### TViewPointInput (type, from `nacatamalon`)

```ts
type TViewPointInput: { x, y } | TPointerInfo
```

A point given to the view's coordinates: one in the place of the sprite showing the view (the world
of its scene, or the game's screen pixels when the sprite is pinned to the screen), or what the
pointer said, from which the right one of those is taken.

#### TViewRay (type, from `nacatamalon`)

```ts
type TViewRay: TRay & { inside }
```

The ray of the view's 3D camera through a point on the sprite showing it.

- `inside: boolean`: Whether the point is on the part of the picture the sprite shows.
- `origin: { x, y, z }`
- `direction: { x, y, z }`

#### TViewToWorld (type, from `nacatamalon`)

```ts
type TViewToWorld
```

Where a point on the sprite showing a view lands in the world the view sees.

- `x: number`: The point of the world the view sees, in its 2D world.
- `y: number`
- `inside: boolean`: Whether the point is on the part of the picture the sprite shows. Outside, `x` and `y` still say
- `picture: { x, y }`: The same point in the picture's pixels, from its top-left corner.

#### TWorldToView (type, from `nacatamalon`)

```ts
type TWorldToView
```

Where a point of the world a view sees lands on the sprite showing it.

- `x: number`: Where it lands, in the sprite's place: the world of its scene, or the screen's pixels for a
- `y: number`
- `inside: boolean`: Whether it is on the part of the picture the sprite shows.
- `edge: { x, y }`: The point of the sprite's edge in the direction of the point, seen from the sprite's middle: the

### Scripts

Attaching authored behaviour to an object, its declared settings, and what one script can expose to another.

#### provide (function, from `nacatamalon`)

```ts
provide(key: string, value: T) => void
```

Offers something to the other behaviours on the same object under `key`, besides what this one
returns under its own name: one word that several behaviours answer to (every one that can be hurt
offers `'health'`), or a second thing from one behaviour.

- `key: string`: The word the others ask for it by, with `useScriptApi(key)`.
- `value: T`: Anything: usually a small set of functions the others may call.

#### registerScript (function, from `nacatamalon`)

```ts
registerScript(name: string, fn: (self: TGameObject, props: TProps) => unknown, fields: TScriptField[] | TScriptOptions) => void
```

Registers a behaviour under `name`, so an object can attach it by that name: from scene code
with `useScript('chase')`, or from a saved scene that names it.

Registering the same name again replaces it. Not a hook: call it at the top level of the
file, next to where the behaviour is written.

The third argument declares the settings, which is what makes the behaviour configurable rather
than fixed: an editor builds a form from that list and hands the values back as the second
argument. Leave it out for a behaviour with nothing to tune. Declaring a setting is what makes
it editable; a script may of course read keys it never declared, but nothing will ever offer to
fill them.

It takes either the list of settings directly or an options object, for a script that also has
something to say about when it runs. Both shapes exist because settings are what almost
every script has and the rest is rare: making the ordinary case reach one level deeper to say
the ordinary thing would be the worse door.

- `name: string`: What the behaviour is called. A saved scene stores this word.
- `fn: (self: TGameObject, props: TProps) => unknown`: The behaviour itself. Whatever it returns is its api, which the other behaviours on the
- `fields: TScriptField[] | TScriptOptions`: Its settings, or the full options object.

#### useScript (function, from `nacatamalon`)

```ts
useScript(ref: string, props?: TScriptProps, options: TUseScriptOptions) => void
```

Attaches a behaviour by name to the object being built, and runs it.

Two things happen. The object records that it carries this behaviour, so the attachment is
saved with the scene and comes back when the scene is opened. And the behaviour runs right
here, inside this object's turn, receiving the object as its first argument, so whatever it
registers (per-frame work, a watch, a listener) belongs to this object and goes away with it.

Call it after the object has what it is made of: a behaviour reaches for the object's sprite or
its placement, which is the natural order in a scene body anyway. A scene opened from a file
gets this for free, because behaviours are always built last there.

The attachment is kept whether or not the behaviour runs, and the two reasons it might not
are the same reason: a saved scene outlives the code it names. A name nothing registered (its
file was renamed, or this project simply has not loaded it) says so and is kept, so opening a
scene shows you the problem instead of failing on it, and saving again does not delete somebody
else's work. A host showing a scene rather than playing it skips the call on purpose, so the
object carries its behaviour without performing it.

- `ref: string`: The behaviour's name, as `registerScript` was given it.
- `props?: TScriptProps`: The authored settings. What the behaviour actually receives is its declared
- `options: TUseScriptOptions`: What a tool needs and a game seldom does: see `TUseScriptOptions`.

#### useScriptApi (function, from `nacatamalon`)

```ts
useScriptApi(name: string, self?: TGameObject) => () => T | null
```

Reaches the api of another behaviour on the same object: what its function returned, by the name
it was registered under, or what one offered with `provide` under a word of its own.

The same rule a component follows with `useSpawn`, whatever it returns is its api, for behaviours
put together through a saved scene: the two files do not know about each other and cannot import
anything, so one names the other. The same word goes in `requires`.

It gives back a way to ask, not the answer. An object's behaviours run in one pass, in the
order they are listed, so one listed above the one it needs would find nothing yet. Asked at the
moment of use, everything on the object has run.

It answers `null` while that behaviour is not on the object, or returned nothing: say `?.`.
Reaching across objects is what signals are for.

- `name: string`: The other behaviour's name, as `registerScript` was given it, or the word it offered
- `self?: TGameObject`: Another object to ask instead of the one being built. Rarely needed.

#### TScriptAttachment (type, from `nacatamalon`)

```ts
type TScriptAttachment
```

One script attached to one object: which behaviour, and what it was given.

`props` is already resolved, the script's own defaults with the saved values laid over them, so
this says what the behaviour actually ran with, which is the question anything reading it
has.

- `id: string`
- `ref: string`
- `props: TScriptProps`

#### TScriptField (type, from `nacatamalon`)

```ts
type TScriptField
```

One setting a script declares about itself.

A behaviour's settings cannot be guessed from outside: they are variables inside a function and
nothing but the code knows they exist. So the code says so here, and an editor builds a form
from it without knowing anything about the behaviour.

`default` is not optional. It is what a freshly attached script starts with, and it is what
fills the gap when a script gains a setting that a scene saved earlier knows nothing about.
An absent value has no other sensible answer.

- `key: string`
- `label?: string`: What the form calls it. The key itself when left out.
- `type: 'number' | 'boolean' | 'string' | 'select' | 'key' | 'box' | 'sound' | 'song'`: How the value is written.
- `default: string | number | boolean`
- `min?: number`
- `max?: number`
- `step?: number`
- `options?: { label, value }[]`: `'select'` only: what may be chosen.

#### TScriptFn (type, from `nacatamalon`)

```ts
type TScriptFn: (self: TGameObject, props: TScriptProps) => unknown
```

A behaviour, written as a function and registered under a name.

It is handed the object it was attached to, so it can reach what that object carries
(`self.drawables[0]`, its placement) and register per-frame work over it.

A script adds behaviour, not things. It should not create sprites or meshes and should not
call `useData` in its body: what a scene is made of is the scene's business, and a script is run
again every time the scene is opened, so anything it built would be built twice. That line is
the same one the whole saved format rests on.

Whatever it returns is its api, for the other behaviours on the object: `useScriptApi(name)`.

#### TScriptOptions (type, from `nacatamalon`)

```ts
type TScriptOptions
```

What a script says about itself beyond its settings.

- `fields?: TScriptField[]`
- `tool?: boolean`: Whether this behaviour also runs while a scene is being edited rather than played.
- `requires?: TScriptRequirement[]`: What this script needs its object to already carry, so that an object missing it is

#### TScriptProps (type, from `nacatamalon`)

```ts
type TScriptProps: Record<string, string | number | boolean>
```

The authored settings of one attached script, and what makes a behaviour configurable
instead of fixed: two boxes can carry the same `patrol` at different speeds.

Only numbers, text and true or false, and that ceiling is on purpose rather than an oversight.
These are typed into a form by hand, so anything with structure (a point, a colour, a list)
would need an editor of its own and a story for changing it later. A behaviour that wants a
place already has one, its box's; one that wants a target names it and looks it up. If a nested
shape ever becomes unavoidable it should arrive as a new kind of field, not by opening this up.

#### TScriptRequirement (type, from `nacatamalon`)

```ts
type TScriptRequirement: string
```

One thing a script needs its object to already have: the type of a component (`'mesh'`), the
stand-in word `'drawable'` for "anything this object draws", or the name of another script.

One list and not three, because from inside a script the question is always the same one:
is this on my object? Splitting it would make an author pick a category before saying what they
need, and a name that matches nothing is reported exactly like a name that is genuinely missing,
so a typo is loud instead of a check that quietly never fires.

#### TUseScriptOptions (type, from `nacatamalon`)

```ts
type TUseScriptOptions
```

What `useScript` takes besides the behaviour and its settings.

- `id?: string`: The attachment's identity, to keep it across saving and opening: what a scene read from a

### Input

Keyboard, pointer and gamepad, plus named actions and the remapping layer a player owns.

#### bindingKey (function, from `nacatamalon`)

```ts
bindingKey(binding: TActionBinding) => string
```

A binding as one short string, so two of them can be compared and repeats can be spotted. Used by
the controls screen to find conflicts as well.

- `binding: TActionBinding`: The binding.

#### describeBinding (function, from `nacatamalon`)

```ts
describeBinding(binding: TActionBinding) => string
```

A binding the way a person reads it: `'Space'`, `'A'`, `'L STICK LEFT'`.

It lives next to the labels of the pad rather than in each controls screen, because the names and
the labels are one fact: a second copy would drift, and a wrong label still draws, so nobody would
be told. A key reads as its character when it is one, and a named key is split into its words
(`'ArrowLeft'` becomes `'Arrow Left'`).

- `binding: TActionBinding`: The binding.

#### gamepadAxisDirectionLabel (function, from `nacatamalon`)

```ts
gamepadAxisDirectionLabel(axis: TGamepadAxisName, dir: -1 | 1) => string
```

How one direction of an axis reads on a controls screen: "L STICK LEFT", "L STICK DOWN".

A direction, and not an axis, is what a binding really names: an axis goes both ways and the
thing bound to it does not. `y` grows downwards, so `dir: 1` on a vertical axis is down, and
writing that out is the whole point of this function: it is the one place that convention meets
a person.

- `axis: TGamepadAxisName`: The stick axis.
- `dir: -1 | 1`: Which way along it: `-1` or `1`.

#### listen (function, from `nacatamalon`)

```ts
listen(sprite: TSprite | TBox | TText | TNineSlice, events: TSpriteEvents) => () => void
```

Makes a sprite, a text, a nine-slice or a whole object react to the mouse and touch, after it was
created.

The same events `createSprite`, `createText` and `createNineSlice` accept in their options (`onClick`, `onPointerOver`, `onPointerOut`,
`onPointerMove`, `onPointerDown`, `onPointerUp`), for when they are not known at creation: a button
that only becomes clickable when a level is cleared, or a sprite made in one place that another
part of the game gives a behaviour.

- It can be called at any moment: while the scene is built, every frame, or from another event.
- It adds: whatever the sprite already reacted to keeps working, and two `onClick` both run, in
the order they were connected.
- It stops by itself when the sprite's part of the scene goes away. The function it returns stops
it earlier, and removes only what this call connected.

On a sprite that has been destroyed it does nothing.

A whole object reacts as one thing. A button made of a border, a fill and a label is one
button: a pointer over any of its pieces, or its children's, is over the object, and moving from the
fill to the label is not leaving it. That is also how a behaviour reaches pieces it did not create,
the ones a scene document or a pack made: `listen(self, { onClick })`. A piece that listens by
itself inside it still answers for itself, being the more specific.

- `sprite: TSprite | TBox | TText | TNineSlice`: The sprite, text, nine-slice or object that should react. A text reacts anywhere
- `events: TSpriteEvents`: Which events, and what to do on each.

#### normalizeActionMap (function, from `nacatamalon`)

```ts
normalizeActionMap(value: unknown) => TActionMap
```

Takes whatever came out of a file and gives back a map the engine can trust: unknown kinds of
binding are dropped, keys are written the one way the keyboard writes them, repeats inside one
action go (the strongest source wins at runtime, so a repeat would only be clutter in the controls
screen), and an action with no name is not an action.

Total and quiet about the details: a project file is not a promise, and the game still has to run.

- `value: unknown`: Whatever a file gave back, as `JSON.parse` left it.

#### useAction (function, from `nacatamalon`)

```ts
useAction(name: string, options: TUseActionsOptions) => TAction
```

One action, which is what most game code reads.

The same as `useActions` with one name filled in. Reach for `useActions` when something reads
several, and for this when it reads one.

- `name: string`: The action, as the input map names it.
- `options: TUseActionsOptions`: Which player's devices to read, as for `useActions`.

#### useActions (function, from `nacatamalon`)

```ts
useActions(options: TUseActionsOptions) => TActionsHandle
```

The game's named actions.

An action is a name, `'jump'`, that the game binds to whatever it likes: a key, a gamepad button,
a stick direction, several at once. The game asks about the name, so the same code works on a
keyboard and on a pad with no branching, and the player can change what it is bound to without the
game knowing.

The list of actions is the game's: given to `createGame`, or written in the project's
`project.json` by the editor.

- `options: TUseActionsOptions`: Which player's devices to read, in a game with more than one.

#### useDirection (function, from `nacatamalon`)

```ts
useDirection(negativeX: string, positiveX: string, negativeY: string, positiveY: string, options: TUseActionsOptions & { deadzone }) => () => { x, y }
```

Four actions read as one direction: the way to move a character from the input map.

It gives back a function, called each frame, like the one `useTween` gives. The dead zone is
round and applied once to the finished direction, which is what makes this identical to reading
the stick when the four actions are bound to one, and what stops a gentle diagonal from collapsing
onto the nearest straight line. A keyboard diagonal comes out the right length for free, which is
the bug in every hand written `x = right - left`.

- `negativeX: string`: The action that points left.
- `positiveX: string`: The action that points right.
- `negativeY: string`: The action that points up.
- `positiveY: string`: The action that points down.
- `options: TUseActionsOptions & { deadzone }`: Which player's devices to read, and the `deadzone` below which the direction is

#### useDraggable (function, from `nacatamalon`)

```ts
useDraggable(target: TSprite | TBox | TText | TNineSlice, options: TUseDraggableOptions) => TDraggableHandle
```

Makes a sprite, a text, a nine-slice or a whole object follow the pointer that drags it: pressed
with the main button or a finger, it moves with it, held where it was taken, until it is let go.

Built on the drag events every sprite has (`onDragStart`, `onDrag`, `onDragEnd`), and adding the
moving: what a card, a piece of a puzzle or an item of an inventory wants. For a drag that does
not move what was pressed (a slingshot pulled back, a dial turned), use those events with `listen`.

- It moves its position: an object's `transform`, in its parent's space, or a sprite's, in its
object's. The parents may be moved, turned and scaled, and the camera may be anywhere: it stays
under the pointer.
- A click is still a click: it only starts once the pointer has moved a few pixels, and a
drag never ends in `onClick`.
- Dropping is said by the places: a slot made with `useDropZone` says what it accepts, and
`onDragEnd` says in `dropped` which one it was let go into. Or by the thing dragged, with
`dropOn`, the tags of what it may land on. With `snapBack` it goes back when it was none.
- Two fingers drag two things, each its own.

It belongs to what calls it, like every hook: it stops when that goes away, and does nothing while
its scene is paused. Call it while the scene or the object is built; to let something be dragged
later, start it with `enabled: false` and call `enable()` then.

Only for 2D: a 3D object has nothing the pointer can pick.

- `target: TSprite | TBox | TText | TNineSlice`: What is dragged: a sprite, a text, a nine-slice, or a whole object (`self` in a
- `options: TUseDraggableOptions`: Which way, in what steps, inside what, onto what, and what to do on the way.

#### useDropZone (function, from `nacatamalon`)

```ts
useDropZone(target: TSprite | TBox | TText | TNineSlice, options: TUseDropZoneOptions) => TDropZoneHandle
```

Makes a sprite, a text, a nine-slice or a whole object a place things can be dragged into: a slot
of an inventory, the square of a board, a bin.

The zone says what it accepts, by tag, and its state is asked like the keyboard's, every frame:
`canDrop()` while something it accepts is dragged anywhere, `isOver()` while it is over this very
zone. That is all a highlight needs, and it turns itself off when the drag ends however it ends,
so nothing stays lit. `onEnter`, `onLeave` and `onDrop` say the same as events, at the start of
the frame.

What is dragged comes from `useDraggable`, or from anything with drag events (`onDrag` in its
options or with `listen`): `useDraggable` hands the zone it was dropped into in `dropped`, and the
events in `zone`. When zones overlap, the one on top takes it.

It belongs to what calls it, like every hook: it goes when that goes, and does nothing while its
scene is paused. To open a zone later, start it with `enabled: false` and call `enable()` then.

- `target: TSprite | TBox | TText | TNineSlice`: The place: a sprite, a text, a nine-slice, or a whole object, which counts the
- `options: TUseDropZoneOptions`: What it accepts, and what to do when something comes over it, leaves or is

#### useGamepad (function, from `nacatamalon`)

```ts
useGamepad(target: TGamepadTarget, options: TUseGamepadOptions) => TGamepad
```

A gamepad, to ask in every frame what the player is holding, has just pressed, or how far a stick
is pushed.

With no number it follows the first pad connected, which is what a one player game means. With
a number it is that port and only that one, which is what local multiplayer means: player two has
to keep meaning one particular controller even when player one unplugs theirs. Do not pin a single
player game to port 0: the ports belong to the browser, and a pad unplugged and plugged back in
usually lands in another one.

What comes back is alive. It says there is no pad while the scene is being built even with a
controller plugged in, because browsers hide a pad until a button is pressed on it, and it starts
saying there is one by itself when the player wakes theirs up. So read it inside `useUpdate`,
never once at the start.

- `target: TGamepadTarget`: The port, or nothing to follow the first pad connected.
- `options: TUseGamepadOptions`: The stick's dead zone, `0.2` by default.

#### useInputMap (function, from `nacatamalon`)

```ts
useInputMap() => TInputMapHandle
```

Remapping: what a game's controls screen is built on.

It lists the game's actions, says what each one listens to, changes them, puts them back and warns
that two actions are on the same button. If the game said where to keep them, every change is
written and comes back next time; starting a new game does not wipe them, because the controls
are a preference and not part of a saved game.

#### useKeyboard (function, from `nacatamalon`)

```ts
useKeyboard() => TKeyboard
```

The keyboard, to ask in every frame what the player is holding or has just pressed.

Ask it, do not wait for it. Games read the keyboard once per frame, from inside `useUpdate`,
because that is where movement happens: `isDown` for what continues while a key is held, and
`justPressed` for what happens once per press however long the key stays down.

Call it while the scene is being built, like every hook, and keep what it returns.

#### usePointer (function, from `nacatamalon`)

```ts
usePointer() => TPointerHandle
```

Listens to the mouse and to touch: presses, releases, movement, and what is under the pointer.

Each listener receives where the pointer is and what it is over:

- `screenX`/`screenY`: the position on the screen, in the game's pixels, ignoring any camera.
- `worldX`/`worldY`: the same point as a place in this scene's world, through its camera. With
no camera they are the same numbers.
- `target`: the sprite or text on top under the pointer, or `null` over empty space. `hits` has
all of them, top first. A text is touched anywhere inside its block, gaps and spaces included.
- `button`: `'main'` (the left button, or a finger), `'right'`, `'middle'`... (`INPUT.POINTER_BUTTONS`).
- `pointerId` and `pointerType`: which pointer, and whether it is a `'mouse'`, a `'touch'` or a
`'pen'`. Every finger has its own id from touching to lifting, so two thumbs are two pointers.

Listeners run at the start of the next frame, not the instant the browser reports the click, so
anything they do behaves as it would inside `useUpdate`. Several movements of one pointer in one
frame arrive as one, with the latest position. A touch the system takes back (a gesture of the
browser's) ends as a release, never as a click, so nothing stays held.

It can also be asked, like the keyboard, from an update: `isDown()` while a finger is on the
screen or a button is held, `justPressed()` and `justReleased()` for the one frame it went down
or came up, `position()` for where it is, `touches()` for every finger, and
`isPressing(button)` for whether a finger is on a sprite right now, which is what an on-screen
button needs. An action can be bound to one, too: `{ type: 'touch', box: 'Jump' }`.

They stop by themselves when the part of the scene that registered them goes away, and they are
not called while their scene is paused. Each one returns a function that stops it earlier.

`pick(x, y)` asks the same question at any screen point, whenever you like.

`onWheel` hears the mouse wheel (or two fingers on a trackpad), with how far it turned in
`deltaY`, and `deltaX` for sideways. While anything listens, the wheel over the game stops
scrolling the page.

`lock()` captures the mouse, for a first-person camera: the cursor disappears and moving the
mouse only changes `movementX`/`movementY`, as far as the hand goes, never stopping at the edge of
the game. Call it from a click or a key press, which is the only time the browser allows it; Esc
gives the mouse back to the player, and `isLocked()` says whether the game has it.

#### useTouchAction (function, from `nacatamalon`)

```ts
useTouchAction(action: string) => void
```

Makes what is being built an on-screen button for `action`: while a finger is on it, or on
anything inside it, the action is held, and the game reads it like any other press,
`actions.isDown('jump')`, whatever pressed it.

The button says which action it presses, so nothing has to match: no name for the action map
to find it by, and a button made twice for two actions is one component given two props. A finger
sliding from one button to the next moves from one action to the other, and two thumbs hold two.
It counts for a handle on every device (`'any'`).

It belongs to the button and goes with it: destroyed, it presses nothing. It is not a binding, so
a controls screen never lists, changes or saves it; it is part of the game, like the button's
picture. The action itself still comes from the game's map, with its other bindings: this adds a
way to press it. A map in a project file binds a button of a scene by its name instead,
`{ type: 'touch', box: 'Jump' }`.

Draw the button with the engine, never as HTML over the canvas.

- `action: string`: The action a finger on it holds.

#### TAction (type, from `nacatamalon`)

```ts
type TAction
```

One action, for the reading that most game code does. Returned by `useAction`.

- `name: TActionName`
- `isDown: `
- `justPressed: `
- `justReleased: `
- `pressedAt: `
- `value: `
- `heldFor: `

#### TActionBinding (type, from `nacatamalon`)

```ts
type TActionBinding: { type, key } | { type, button } | { type, axis, dir } | { type, box } | { type, button }
```

One physical thing an action listens to.

Three kinds of key and pad, and the reason there is no fourth: a gamepad button covers the
digital and the analogue ones alike, because a trigger already reports how far it is pressed. A
separate kind for triggers would be a second name for one fact.

And touch, for an on-screen button: `{ type: 'touch', box: 'Jump' }` holds the action while a
finger is on the object named `Jump` (or on anything inside it). The button is an ordinary object
of the scene, drawn by the engine and placed like any other, so a pad on a phone is a few sprites
and the game still reads `isDown('jump')`, whatever pressed it. A finger sliding from one button to
the next moves from one action to the other. It counts for a handle on every device (`'any'`).
In code, a button can say itself which action it presses with `useTouchAction`, and needs no name;
this binding is for a map written in a project file, where a scene's button has only its name.

And the mouse: `{ type: 'mouse', button: 'main' }` is held while that button is, `'main'` the
left one, then `'right'`, `'middle'`, `'back'` and `'forward'` (`INPUT.POINTER_BUTTONS`). A pen
counts; a finger does not, because it touches the screen for every button drawn on it and would
fire whatever the click is bound to each time: fingers are `touch`. It belongs to the keyboard's
player, so it counts for a handle on `'any'` and on `'keyboard'`, and not on a pad's port.

`dir` on an axis is not a threshold and is not optional: it is part of which input this is. An
axis goes both ways and an action does not, so "the horizontal axis of the left stick" does not
name an input at all until it says which way.

There is deliberately no kind for a whole stick. A stick is four axis bindings across four
actions: if one binding could be two-dimensional, every action would be either a
number or a direction, and that split spreads into the runtime, the saved file, the editor's
table and the controls screen. One direction per binding is what lets "WASD or stick or
d-pad" work through the same four names with no branching in the game.

#### TActionCaptureOptions (type, from `nacatamalon`)

```ts
type TActionCaptureOptions
```

What "press anything" listens to while it waits.

- `sources?: 'key' | 'button' | 'axis' | 'mouse'[]`: Which kinds of input can be bound. Keys, buttons and axes by default: the mouse only when
- `device?: TActionDevice`: Which pad to listen to. `'any'` by default.
- `cancelKey?: string | null`: The key that reports nothing instead of binding itself. `'Escape'` by default; `null` turns
- `axisThreshold?: number`: How far a stick has to be pushed to count, 0 to 1. `0.7` by default: well past any dead zone,

#### TActionDef (type, from `nacatamalon`)

```ts
type TActionDef
```

One named action and everything it listens to.

`deadzone` and `press` are how the action feels, and they belong to the action rather than to
one of its bindings: they have to be shared by every source, or the same action would mean one
thing on a stick and another on a key.

- `name: TActionName`
- `bindings: TActionBinding[]`
- `deadzone?: number`: Below this, the action reads as exactly 0. Default `0.2`, the same as the stick's, so four
- `press?: number`: How far an analogue source has to travel before it counts as pressed. Default `0.5`.

#### TActionDevice (type, from `nacatamalon`)

```ts
type TActionDevice: number | 'any' | 'keyboard'
```

Which devices a reader listens to. Not a property of a binding: a property of whoever is
reading, which is what makes local multiplayer the same action names read twice.

`'any'` (the default) is the keyboard and every pad. A number is that pad and nothing else: a
keyboard is not a port, so naming a port means pad only, which is exactly what player two wants.
`'keyboard'` is the other half of that split.

#### TActionMap (type, from `nacatamalon`)

```ts
type TActionMap: TActionDef[]
```

A game's whole input map.

A list and not an object keyed by name, for two reasons a controls table makes obvious: renaming
inside an object is a remove and an insert, which jumps the row out from under the cursor, and a
repeated name can be seen and reported instead of quietly swallowed.

(The player's own changes are an object, because that one is sparse and nobody renames an action
from a controls screen.)

#### TActionName (type, from `nacatamalon`)

```ts
type TActionName: string
```

What a game calls one of its actions: `'jump'`, `'move_left'`, `'pause'`. Just a string, so a
project can name them however it likes.

#### TActionOverrides (type, from `nacatamalon`)

```ts
type TActionOverrides: Record<TActionName, TActionBinding[]>
```

The player's own bindings, on top of the game's. Sparse: an action they never touched is absent.

#### TActionPersist (type, from `nacatamalon`)

```ts
type TActionPersist
```

Where the player's own bindings are kept.

The adapter is the store's (`localStorageAdapter`, `indexedDbAdapter` or one of your own). What is
deliberately not reused is the store itself: a remap is a preference, not part of a saved
game, and starting a new game must not reset the controls.

- `adapter: TStorePersistence<TActionOverrides>`
- `key?: string`: What to save it under. Has a prefix by default, because browser storage belongs to the whole

#### TActionPresetName (type, from `nacatamalon`)

```ts
type TActionPresetName: 'nes' | 'genesis' | 'snes' | 'arcade' | 'platformer'
```

Which input map `INPUT.ACTION_PRESETS` has ready.

#### TActionsHandle (type, from `nacatamalon`)

```ts
type TActionsHandle
```

The actions of a game, as it reads them. Returned by `useActions`.

- `device: TActionDevice`: Which devices this handle is listening to.
- `isDown: `
- `justPressed: `
- `pressedAt: `
- `heldFor: `
- `justReleased: `
- `value: `
- `rawValue: `
- `strength: `
- `direction: `
- `has: `
- `names: typeOperator`: Every action name, in the order the game declared them.

#### TDraggableDropInfo (type, from `nacatamalon`)

```ts
type TDraggableDropInfo: TPointerDragInfo & { dropped }
```

What `useDraggable`'s `onDragEnd` is told: the drag's info, and what it was dropped on.

- `dropped: TSprite | TText | TNineSlice | TBox | null`: Where it was dropped. With `dropOn`, what it was let go over that carries one of those tags:
- `startX: number`: Where the press began, as a place in the world through the camera as it is now: the point
- `startY: number`
- `deltaX: number`: How far the pointer is from where the press began, in the world: `worldX - startX`. What a
- `deltaY: number`
- `zone: TSprite | TText | TNineSlice | TBox | null`: The drop zone (`useDropZone`) under the pointer that accepts what is being dragged, or `null`:
- `screenX: number`: Where on the screen, in the game's pixels from the top-left corner. Ignores any camera.
- `screenY: number`
- `worldX: number`: The same point as a place in the world, through the camera of the scene that asked for the
- `worldY: number`
- `movementX: number`: How far the mouse moved since the last event, in pixels of the page rather than of the game,
- `movementY: number`
- `button: TPointerButtonName`: Which button: `'main'` (the left, or a finger), `'middle'` (the wheel pressed), `'right'`,
- `pointerId: number`: Which pointer this is: each finger on a touch screen has its own, the same from the moment it
- `pointerType: TPointerType`: What it is: `'mouse'`, `'touch'` (a finger) or `'pen'`. A game can show its on-screen buttons
- `time: number`: When it happened, in seconds on the page's clock: the moment the finger touched or the button
- `target: TSprite | TText | TNineSlice | null`: The sprite, text or nine-slice on top under the pointer, or `null` over empty space.
- `hits: ReadonlyArray<TSprite | TText | TNineSlice>`: Every sprite, text and nine-slice under the pointer, the one on top first.

#### TDraggableHandle (type, from `nacatamalon`)

```ts
type TDraggableHandle
```

What `useDraggable` returns, to turn the dragging on and off and to ask about it.

- `enable: `
- `disable: `
- `isEnabled: `
- `isDragging: `

#### TDropZoneHandle (type, from `nacatamalon`)

```ts
type TDropZoneHandle
```

What `useDropZone` returns: its state, read every frame, and a way to turn it on and off.

- `isOver: `
- `canDrop: `
- `enable: `
- `disable: `
- `isEnabled: `

#### TDropZoneListener (type, from `nacatamalon`)

```ts
type TDropZoneListener: (dragged: TSprite | TText | TNineSlice | TBox, info: TPointerDragInfo) => void
```

A function a drop zone calls with what is being dragged into it, and the drag.

#### TGamepad (type, from `nacatamalon`)

```ts
type TGamepad
```

One pad, as a game reads it. Returned by `useGamepad`.

Everything here is live: a handle taken while the scene is being built says there is no pad,
and starts saying there is one by itself when the player's shows up. That matters more than it
sounds, because browsers hide a pad until a button is pressed on it (so that a page cannot tell
who you are by your hardware): "no pad" is the normal state at the start of a scene even with a
controller plugged in.

Held and just pressed split exactly like the keyboard's, and for the same reason: hold `left` to
walk, `justPressed('a')` to jump once however long the button is held.

One thing it cannot do, and it is the browser's limit rather than a choice: a press and a
release that both happen between two frames leave no trace. A pad sends no events, it can only be
asked how it is now, so "what happened since the last frame" is a comparison of two answers. At
60 frames a second that is a tap under 16 ms, which no thumb produces on a real button. The
keyboard does catch that case, because it gets real events.

- `index: number`: The port being read right now. Fixed for a handle pinned to a number; for one that
- `connected: boolean`: Whether there is a pad in that port right now.
- `id: string`: What the browser calls it, or empty with nothing connected.
- `mapping: string`: `'standard'`, or empty for a pad the browser did not recognise.
- `buttonCount: number`: How many buttons the pad says it has, `0` with nothing connected.
- `axisCount: number`: How many axes the pad says it has, `0` with nothing connected.
- `isDown: `
- `justPressed: `
- `justReleased: `
- `pressedAt: `
- `value: `
- `axis: `
- `leftStick: `
- `rightStick: `
- `deadzone: number`: The dead zone this handle uses, 0 to 1.
- `setDeadzone: `
- `rawButton: `
- `rawButtonValue: `
- `rawJustPressed: `
- `rawJustReleased: `
- `rawAxis: `
- `canRumble: boolean`: Whether the browser offers vibration for this pad, so an options screen can grey out its
- `rumble: `
- `stopRumble: `
- `onConnect: `
- `onDisconnect: `

#### TGamepadAxisName (type, from `nacatamalon`)

```ts
type TGamepadAxisName: 'leftX' | 'leftY' | 'rightX' | 'rightY'
```

A stick axis of the standard mapping. `X` grows to the right and `Y` grows downwards, which
is what the browser reports and also how this engine measures the screen: pushing the stick up
gives a negative `leftY`, the same sign a sprite needs to move up.

#### TGamepadButtonName (type, from `nacatamalon`)

```ts
type TGamepadButtonName: 'a' | 'b' | 'x' | 'y' | 'lb' | 'rb' | 'lt' | 'rt' | 'back' | 'start' | 'ls' | 'rs' | 'up' | 'down' | 'left' | 'right' | 'guide'
```

A button of the W3C standard mapping, by the name this engine gives it.

The names are the Xbox labels because that is how the specification itself defines the mapping:
"the bottom button of the right cluster" is index 0, and every pad that reports
`mapping: 'standard'` has agreed to put its bottom face button there.

Worth knowing before the first bug report: on a Nintendo or an 8BitDo pad the printed letters
sit in the other order, so the button under the player's thumb when they press `'a'` is the one
labelled B on the plastic. That is the pad's labelling, not a mistake here: the position is what
the standard mapping promises, and the position is what a game cares about.

A closed list of seventeen, so a typo is caught when the game is compiled.

#### TGamepadInfo (type, from `nacatamalon`)

```ts
type TGamepadInfo
```

What a pad says about itself when it arrives or leaves.

`mapping` is the field worth reading: `'standard'` means the button names line up with the
plastic, and anything else (usually an empty string) is a pad the browser did not recognise, one
of those cheap USB copies of a SNES or Mega Drive controller, whose buttons land wherever the
manufacturer put them. Those are still perfectly playable through `rawButton` and `rawAxis`; what
does not work on them are the names, and this is how a game finds that out instead of quietly
reading the wrong button.

- `index: number`: The port it is plugged into, the same number `useGamepad(port)` takes.
- `id: string`: What the browser calls it, something like `'Xbox Wireless Controller (…)'`.
- `mapping: string`: `'standard'` when the names can be trusted, usually empty otherwise.
- `buttonCount: number`
- `axisCount: number`

#### TGamepadListener (type, from `nacatamalon`)

```ts
type TGamepadListener: (info: TGamepadInfo) => void
```

Told when a pad arrives in a port, or leaves it.

#### TGamepadTarget (type, from `nacatamalon`)

```ts
type TGamepadTarget: number | 'first'
```

Which pad a handle reads: a port number, or the first one connected.

The difference is not fussiness. The browser owns the port, not the player: a pad unplugged
and plugged back in usually lands in a different one, so a single player game nailed to port 0
goes silent with nothing in the console to say why. Following the first pad is what such a game
actually means; a number is what local multiplayer means, where "player two" has to keep meaning
one particular controller.

#### TInputMapHandle (type, from `nacatamalon`)

```ts
type TInputMapHandle
```

Remapping: what a game's controls screen is built on. Returned by `useInputMap`.

- `list: `
- `bindings: `
- `defaults: `
- `bind: `
- `unbind: `
- `clear: `
- `reset: `
- `conflicts: `
- `capture: `
- `overrides: `
- `load: `
- `save: `

#### TKeyboard (type, from `nacatamalon`)

```ts
type TKeyboard
```

What a game asks the keyboard, once per frame, from inside an update.

- `isDown: `
- `justPressed: `
- `justReleased: `
- `pressedAt: `

#### TKeyName (type, from `nacatamalon`)

```ts
type TKeyName: string
```

A key, spelled the way the browser reports it in `KeyboardEvent.key`: space is `' '`, letters
are lowercase (`'f'`), and named keys keep their names (`'ArrowLeft'`, `'Escape'`, `'Enter'`).
`'Space'` is accepted for the space bar too.

An alias of `string`, so any literal still fits without a cast. What it buys is the name showing
up in a signature, which says what kind of string is expected.

#### TPointerButtonName (type, from `nacatamalon`)

```ts
type TPointerButtonName: 'main' | 'middle' | 'right' | 'back' | 'forward'
```

A button of the mouse, by the name this engine gives it. A finger is `'main'`.

`'main'` and not `'left'`: on a mouse set up for the left hand the main button is the right one,
and a finger is neither. `'back'` and `'forward'` are the two on the side of some mice.

#### TPointerDragInfo (type, from `nacatamalon`)

```ts
type TPointerDragInfo: TPointerInfo & { startX, startY, deltaX, deltaY, zone }
```

What a drag listener is told: everything a pointer listener is, and where the drag began.

`target` and `hits` are what is under the thing being dragged, never the thing itself or its
pieces: what it would be dropped on, which is what `onDragEnd` wants to know. A drag the system
takes back (a gesture of the browser's) ends over nothing, with `target` `null`.

- `startX: number`: Where the press began, as a place in the world through the camera as it is now: the point
- `startY: number`
- `deltaX: number`: How far the pointer is from where the press began, in the world: `worldX - startX`. What a
- `deltaY: number`
- `zone: TSprite | TText | TNineSlice | TBox | null`: The drop zone (`useDropZone`) under the pointer that accepts what is being dragged, or `null`:
- `screenX: number`: Where on the screen, in the game's pixels from the top-left corner. Ignores any camera.
- `screenY: number`
- `worldX: number`: The same point as a place in the world, through the camera of the scene that asked for the
- `worldY: number`
- `movementX: number`: How far the mouse moved since the last event, in pixels of the page rather than of the game,
- `movementY: number`
- `button: TPointerButtonName`: Which button: `'main'` (the left, or a finger), `'middle'` (the wheel pressed), `'right'`,
- `pointerId: number`: Which pointer this is: each finger on a touch screen has its own, the same from the moment it
- `pointerType: TPointerType`: What it is: `'mouse'`, `'touch'` (a finger) or `'pen'`. A game can show its on-screen buttons
- `time: number`: When it happened, in seconds on the page's clock: the moment the finger touched or the button
- `target: TSprite | TText | TNineSlice | null`: The sprite, text or nine-slice on top under the pointer, or `null` over empty space.
- `hits: ReadonlyArray<TSprite | TText | TNineSlice>`: Every sprite, text and nine-slice under the pointer, the one on top first.

#### TPointerDragListener (type, from `nacatamalon`)

```ts
type TPointerDragListener: (info: TPointerDragInfo) => void
```

A function that is told about a drag.

#### TPointerHandle (type, from `nacatamalon`)

```ts
type TPointerHandle
```

The mouse and touch input a scene reads, returned by `usePointer`.

- `onDown: `
- `onUp: `
- `onMove: `
- `onWheel: `
- `pick: `
- `isDown: `
- `justPressed: `
- `justReleased: `
- `pressedAt: `
- `position: `
- `touches: `
- `isPressing: `
- `lock: `
- `unlock: `
- `isLocked: `

#### TPointerInfo (type, from `nacatamalon`)

```ts
type TPointerInfo
```

What a pointer listener is told: where the mouse or finger is, which button, and what is under it.

- `screenX: number`: Where on the screen, in the game's pixels from the top-left corner. Ignores any camera.
- `screenY: number`
- `worldX: number`: The same point as a place in the world, through the camera of the scene that asked for the
- `worldY: number`
- `movementX: number`: How far the mouse moved since the last event, in pixels of the page rather than of the game,
- `movementY: number`
- `button: TPointerButtonName`: Which button: `'main'` (the left, or a finger), `'middle'` (the wheel pressed), `'right'`,
- `pointerId: number`: Which pointer this is: each finger on a touch screen has its own, the same from the moment it
- `pointerType: TPointerType`: What it is: `'mouse'`, `'touch'` (a finger) or `'pen'`. A game can show its on-screen buttons
- `time: number`: When it happened, in seconds on the page's clock: the moment the finger touched or the button
- `target: TSprite | TText | TNineSlice | null`: The sprite, text or nine-slice on top under the pointer, or `null` over empty space.
- `hits: ReadonlyArray<TSprite | TText | TNineSlice>`: Every sprite, text and nine-slice under the pointer, the one on top first.

#### TPointerListener (type, from `nacatamalon`)

```ts
type TPointerListener: (info: TPointerInfo) => void
```

A function that is told about a pointer event.

#### TPointerLockOptions (type, from `nacatamalon`)

```ts
type TPointerLockOptions
```

How the mouse is captured, for `usePointer().lock`.

- `raw?: boolean`: The mouse's own movement, without the system's acceleration, where the browser offers it.

#### TPointerTouch (type, from `nacatamalon`)

```ts
type TPointerTouch
```

A pointer that is pressing right now, a finger on the screen or a mouse button held, as
`usePointer().touches()` lists them.

- `pointerId: number`
- `pointerType: TPointerType`
- `screenX: number`: Where it is now, in the game's pixels from the top-left corner, ignoring any camera.
- `screenY: number`
- `worldX: number`: The same point through the camera of the scene that asked.
- `worldY: number`

#### TPointerType (type, from `nacatamalon`)

```ts
type TPointerType: 'mouse' | 'touch' | 'pen'
```

What a pointer is: a mouse, a finger on a touch screen, or a pen.

#### TPointerWheelInfo (type, from `nacatamalon`)

```ts
type TPointerWheelInfo: TPointerInfo & { deltaY, deltaX }
```

What a wheel listener is told: everything a pointer listener is, and how far the wheel turned.

In pixels whichever way the browser counted them (some mice count lines, some pages), so one
notch of an ordinary mouse wheel is around a hundred. Every turn since the last frame is added
together: a quick spin is one call with a large number, not several small ones a frame apart.

- `deltaY: number`: How far the wheel turned. Positive is turning it towards you, which scrolls a page down and
- `deltaX: number`: Sideways, from a tilting wheel or a trackpad. Positive is to the right.
- `screenX: number`: Where on the screen, in the game's pixels from the top-left corner. Ignores any camera.
- `screenY: number`
- `worldX: number`: The same point as a place in the world, through the camera of the scene that asked for the
- `worldY: number`
- `movementX: number`: How far the mouse moved since the last event, in pixels of the page rather than of the game,
- `movementY: number`
- `button: TPointerButtonName`: Which button: `'main'` (the left, or a finger), `'middle'` (the wheel pressed), `'right'`,
- `pointerId: number`: Which pointer this is: each finger on a touch screen has its own, the same from the moment it
- `pointerType: TPointerType`: What it is: `'mouse'`, `'touch'` (a finger) or `'pen'`. A game can show its on-screen buttons
- `time: number`: When it happened, in seconds on the page's clock: the moment the finger touched or the button
- `target: TSprite | TText | TNineSlice | null`: The sprite, text or nine-slice on top under the pointer, or `null` over empty space.
- `hits: ReadonlyArray<TSprite | TText | TNineSlice>`: Every sprite, text and nine-slice under the pointer, the one on top first.

#### TPointerWheelListener (type, from `nacatamalon`)

```ts
type TPointerWheelListener: (info: TPointerWheelInfo) => void
```

A function that is told the wheel turned.

#### TRumbleOptions (type, from `nacatamalon`)

```ts
type TRumbleOptions
```

How hard and how long to shake the pad. Everything is optional: `rumble()` on its own is a short
generic thump, which is what most calls want.

- `duration?: number`: Seconds, default 0.2, never more than 5: past that the browser cuts it without saying so.
- `strong?: number`: The heavy motor, 0 to 1, default 1. The one felt as a thud.
- `weak?: number`: The light motor, 0 to 1, default 1. The one felt as a buzz.
- `delay?: number`: Seconds to wait before starting, default 0.

#### TSpriteEvents (type, from `nacatamalon`)

```ts
type TSpriteEvents
```

What a sprite or a text can react to by itself: in `createSprite`'s or `createText`'s options, or
connected later with `listen`. Every one receives where the pointer is and what is under it. A text
reacts anywhere inside its block, the gaps between letters included.

- `onPointerOver?: TPointerListener`: The pointer comes over the sprite.
- `onPointerOut?: TPointerListener`: The pointer stops being over it, or leaves the game.
- `onPointerMove?: TPointerListener`: The pointer moves while over it.
- `onPointerDown?: TPointerListener`: A button is pressed, or a finger touches, over it.
- `onPointerUp?: TPointerListener`: A button is released, or a finger lifts, over it.
- `onClick?: TPointerListener`: Pressed and released without leaving it. The cursor turns into a hand over it.
- `onDragStart?: TPointerDragListener`: Pressed with the main button (or a finger) and moved far enough not to be a click: a few
- `onDrag?: TPointerDragListener`: The pointer that started the drag moved, wherever it is: over the sprite or not, in the game
- `onDragEnd?: TPointerDragListener`: The pointer that dragged it let go, with `target` and `hits` saying what it was let go over.

#### TUseActionsOptions (type, from `nacatamalon`)

```ts
type TUseActionsOptions
```

What the action hooks accept.

- `device?: TActionDevice`: Which devices to listen to. `'any'` (the default) is the keyboard and every pad, which is one

#### TUseDraggableOptions (type, from `nacatamalon`)

```ts
type TUseDraggableOptions
```

How `useDraggable` moves what it is given.

- `axis?: 'both' | 'x' | 'y'`: Which way it can go: `'both'`, or only along `'x'` (a slider) or `'y'`.
- `grid?: number`: Steps it keeps to, in pixels: it lands on multiples of this. `0` moves freely.
- `bounds?: { x, y, width, height }`: A rectangle it does not leave, measured where its position is: in its parent's space for an
- `dropOn?: string | typeOperator`: The tags of what it may be dropped on, one or several (`useTag`, or the tags of a box in a
- `snapBack?: boolean`: Goes back to where the drag began when it is let go anywhere it may not be dropped: off every
- `enabled?: boolean`: Whether it can be dragged from the start. `enable()` lets it later.
- `onDragStart?: (info: TPointerDragInfo) => void`: It began to move.
- `onDrag?: (info: TPointerDragInfo) => void`: It moved, already where the pointer took it.
- `onDragEnd?: (info: TDraggableDropInfo) => void`: It was let go, already where it stays (or back, with `snapBack`).

#### TUseDropZoneOptions (type, from `nacatamalon`)

```ts
type TUseDropZoneOptions
```

What a drop zone accepts, and what it is told.

- `accepts?: string | typeOperator`: The tags of what it accepts, one or several: what is dragged carries one of them, or an
- `enabled?: boolean`: Whether it takes things from the start. `enable()` lets it later.
- `onEnter?: TDropZoneListener`: Something it accepts is dragged over it.
- `onLeave?: TDropZoneListener`: It is dragged off it again, let go, or the drag was taken back.
- `onDrop?: TDropZoneListener`: It was let go over it. Called after what was dragged hears `onDragEnd`, so what is done here

#### TUseGamepadOptions (type, from `nacatamalon`)

```ts
type TUseGamepadOptions
```

What `useGamepad` accepts.

- `deadzone?: number`: Stick dead zone, 0 to 1. Default `0.2`: inside the value Xbox pads themselves suggest, and

### Store

State that outlives a scene, with optional persistence.

#### applyStoreDocs (function, from `nacatamalon/files`)

```ts
applyStoreDocs(docs: typeOperator) => void
```

Installs what a project's store files say: over the stores that exist, and as stores of their own
for the ones nothing has declared.

Both halves matter and they are not the same. Over an existing store it is a layer: the code
declared the floor and the file lands on top of it key by key, so a field the file does not
mention keeps what the code gave it. For a key no code has declared it builds the store outright,
which is what lets a tool add state to a project with no file to write and no module to import.

A store built that way is held as waiting for its code: if the file that declares it turns up
later, `createGameStore` takes over that very object instead of making a second one. That is the
ordinary order inside an editor, where the stores of a project are read when it opens and the
code that declares one only runs when a scene reaches for it.

It can be called at any point in a start-up, which is the property to keep: a packaged game
imports its stores while the modules evaluate, long before this runs, and an editor does it the
other way round.

- `docs: typeOperator`: The files, already read with `parseStoreDoc`.

#### createGameStore (function, from `nacatamalon`)

```ts
createGameStore(config: TGameStoreConfig<S, A>) => TGameStore<S, A>
```

Makes a store: data of the game that outlives a scene (lives, coins, options, a pet), with a way to
be told when it changes and, optionally, to be saved and loaded.

A signal says that something happened; a store holds what is still true. Make it once, in a
file of its own, and import it wherever it is needed: like a signal, it belongs to the page, not to
a scene or a game.

- Read: `store.state.hunger`, anywhere.
- Change: an action, or `store.set((s) => { s.hunger -= 1; })`. The state is changed in place.
- React: `useStore(store, selector, handler)` in a scene, `store.subscribe(...)` anywhere else.
Everyone listening is told straight away, inside the `set` that changed it.
- Keep it: `persist` with `localStorageAdapter()`, `indexedDbAdapter(name)` or an adapter of your
own, then `load()` when the game starts.

- `config: TGameStoreConfig<S, A>`: Its `key`, its starting `state`, its `actions`, and how it saves itself (`persist`).

#### getGameStore (function, from `nacatamalon`)

```ts
getGameStore(key: string) => TGameStore<Record<string, unknown>, unknown> | null
```

One store by its key, or `null`.

This is what an object's store link resolves through, and the whole reason it can: a behaviour
reaches state it did not define without an `import` naming a file it cannot know the path of.

- `key: string`: The store's name, as `createGameStore` was given it.

#### indexedDbAdapter (function, from `nacatamalon`)

```ts
indexedDbAdapter(dbName: string, storeName: string) => TStorePersistence<S>
```

Saves a store in the browser's IndexedDB: for bigger saves than `localStorage` holds, or several
save slots.

Every store using the same `dbName` shares one table (`storeName`), each under its own key. The state
is stored as it is, without turning it into text first. The database is opened on each operation;
the browser keeps the connection, so the adapter itself holds nothing.

Unlike `localStorageAdapter`, a failure here rejects, and the store warns about it and carries on.

The state's type defaults to `any` on purpose: written in place (`adapter: localStorageAdapter()`)
the adapter cannot know the store's state yet, and `unknown` would not fit it. The store's own
`state` is what decides the type.

- `dbName: string`: The database, shared by every store that names it.
- `storeName: string`: The table inside it. Default `'stores'`.

#### localStorageAdapter (function, from `nacatamalon`)

```ts
localStorageAdapter() => TStorePersistence<S>
```

Saves a store in the browser's `localStorage`, as JSON under the store's key.

Right for small state that is always on: options, a score table, a pet. `localStorage` holds a few
megabytes per site and every write is synchronous; for bigger saves use `indexedDbAdapter`.

A failure (storage full, disabled by the browser, a save that is not valid JSON) is warned about and
never thrown: the game carries on, as if there were no save.

The state's type defaults to `any` on purpose: written in place (`adapter: localStorageAdapter()`)
the adapter cannot know the store's state yet, and `unknown` would not fit it. The store's own
`state` is what decides the type.

#### parseStoreDoc (function, from `nacatamalon/files`)

```ts
parseStoreDoc(raw: unknown, src: string) => TStoreDoc
```

Reads a store file, whatever state it is in.

Total and it never throws, the same as the scene reader and for the same reason: a project
that refused to open because one row of one file was malformed is a project nobody can repair.
A row it cannot make sense of is left out and said out loud; everything else opens.

- `raw: unknown`: The parsed JSON.
- `src: string`: Where it came from, named in anything it has to report.

#### storeOf (function, from `nacatamalon`)

```ts
storeOf(self: TGameObject, key?: string) => TGameStore<S, A> | null
```

The store an object was given: how a behaviour reaches state it did not define.

This is what the link on an object is for. A behaviour attached through a tool cannot import
the file that declares a store, because it does not know where that file is, or even that it
exists; so the object names the store and the behaviour asks the object. It is also what makes
state reachable from a scene put together entirely by hand in an editor, where there is no code
to write an import in.

Pass a `key` when the object was given more than one. With no key it hands back the only one it
has, and says so when there are several, because picking one of them would be a guess that works
until somebody adds the second.

It is a plain function and not a hook on purpose: it registers nothing, cleans up nothing and is
told which object to look at, so it can be called anywhere, including from inside an update.

Hands back `null` rather than a stand-in that does nothing, because the shape of a store is the
game's and there is no neutral state to invent. The warning says which of the two went wrong:
the object has no link at all, or it has one to a store this project does not have.

- `self: TGameObject`: The object, as a behaviour is handed it.
- `key?: string`: Which of its stores, for an object that was given more than one.

#### useStore (function, from `nacatamalon`)

```ts
useStore(store: TGameStore<S, A>, selector: TStoreSelector<S, T>, handler: TStoreListener<T>, equals?: (a: T, b: T) => boolean) => void
```

Reacts from a scene when one value of a store changes, and stops by itself when that part of the
scene goes away. Nothing to disconnect by hand.

The selector picks the value; the handler runs only when it changes, straight away, inside the change
that caused it, with the new value and the one before. A value that decays every frame but is
selected as "is it low?" only calls the handler when the answer flips.

Pick values, not objects: the store's state is changed in place, so `(s) => s.inventory` is the
same object every time and never looks changed. `(s) => s.inventory.length` does.

The handler is not called when the scene starts: the scene can read `store.state` right there. It
keeps listening while the scene is paused, like `useSignal`.

- `store: TGameStore<S, A>`: The store, made with `createGameStore`.
- `selector: TStoreSelector<S, T>`: Picks the value to watch.
- `handler: TStoreListener<T>`: What to do when it changes.
- `equals?: (a: T, b: T) => boolean`: When two values count as the same. Default `Object.is`.

#### useStoreLink (function, from `nacatamalon`)

```ts
useStoreLink(key: string) => void
```

Says that this object uses one of the game's stores.

The important word is uses. The store is not in the object and cannot be: it outlives every
scene, and one living inside an object would die with its scene and be two the moment a second
object named it. So what is kept here is the name, exactly as a behaviour is kept by name rather
than as a function.

Two things come of it that an ordinary import cannot give. A behaviour attached in an editor can
be handed the state through the scene itself (`storeOf(self)`), so a scene put together entirely
by hand can wire behaviour to state with no file to write. And the tree becomes able to answer
"what touches this state?", which nothing else could.

Called by the scene reader for a store component; call it yourself when you would rather say it
in code than in the document.

- `key: string`: The store's name, as `createGameStore` was given it.

#### TGameStore (type, from `nacatamalon`)

```ts
type TGameStore
```

Data of the game that outlives a scene: lives, coins, the options, a pet. Made with
`createGameStore`, once, in a file of its own, and imported wherever it is needed.

`state` is plain JSON, which is what gets saved. Everything else (`actions`, `set`, `subscribe`...)
is how the game reads and changes it.

- `key: string`: The name it is saved under.
- `state: S`: The live state. Read it anywhere; change it through an action or `set`, so whoever listens is
- `actions: A`: The actions given to `createGameStore`.
- `set: TStoreSet<S>`: Changes the state in place and tells everyone listening, straight away.
- `get: TStoreGet<S>`: The live state, the same object as `state`.
- `subscribe: (listener: () => void) => () => void`: Starts listening. With only a listener, it is called after every change. With a selector, only
- `save: `
- `load: `
- `reset: `

#### TGameStoreConfig (type, from `nacatamalon`)

```ts
type TGameStoreConfig
```

What `createGameStore` is given.

- `key: string`: The name it is saved under. Different stores need different keys.
- `state: S`: The state a new game starts with, and what `reset()` goes back to. Plain JSON only.
- `actions?: TStoreActionsFactory<S, A>`: The store's actions. Optional: a store can also be changed with `set` directly.
- `persist?: TStorePersist<NoInfer<S>>`: Saving and loading. Without it, the store lives only while the page is open.

#### TStoreActionsFactory (type, from `nacatamalon`)

```ts
type TStoreActionsFactory: (set: TStoreSet<S>, get: TStoreGet<S>) => A
```

Builds a store's actions from its `set` and `get`.

A function and not an object, so the actions can use `set` and `get`, and so a big store can be put
together from several files: each file returns some actions and the store spreads them into one.
actions: (set, get) => ({ ...careActions(set, get), ...lifeActions(set, get) }),

#### TStoreDoc (type, from `nacatamalon/files`)

```ts
type TStoreDoc
```

A store as a file: the state a game starts with, written down.

This is the half of a store that a tool can make. `createGameStore` is code, and code is what
a person writes; this is the same store said in data, so that an editor can add a field, change
a starting value and show what a project's state even is, none of which is possible when the only
definition is a module somebody has to open.

The two halves layer, they do not compete: what the code declares is the floor and what the file
says lands on top of it, key by key. A field the file does not mention keeps the value the code
gave it, which is what makes a store that gains a field cost no migration at all.

- `format: 'nacatamalon-store'`
- `version: 2`
- `key: string`: The store's name: the key `createGameStore` was given, and the key its saved game is written
- `fields: TStoreField[]`: The starting state, one row per field.
- `persist?: TStorePersistDoc`: How it saves itself, when this file is the only thing defining it.

#### TStoreField (type, from `nacatamalon/files`)

```ts
type TStoreField
```

One authored entry of a store's starting state: the name it takes in the state, its type and its
value.

A list rather than an object, for two reasons that only show up later: a tool can keep the rows
in the order somebody put them in, and a type survives a value that happens to look like another
one (nothing tells `0` from a number meant as a number, and `{ x, y }` from a pair meant as a
place, once the type is gone).

- `name: string`
- `type: TStoreFieldType`
- `value: TStoreFieldValue`

#### TStoreFieldType (type, from `nacatamalon/files`)

```ts
type TStoreFieldType: 'number' | 'string' | 'boolean' | 'vec2' | 'vec3' | 'color'
```

What a store field can hold.

A short, flat list, and deliberately so. That a store's state is plain JSON is what holds up
the saving, the tools and the whole idea of writing a game down, and a free-form field in a form
would be the first place where somebody puts a `Map` into it and finds out three features later.
A typed row cannot express one.

It says `'boolean'` where core's format says `'bool'`, because `TScriptField` in this engine
already says `'boolean'`: being consistent inside one engine is worth more than matching a
format that belongs to the other one, whose files are told apart by their own name anyway.

#### TStoreFieldValue (type, from `nacatamalon/files`)

```ts
type TStoreFieldValue: number | string | boolean | { x, y } | { x, y, z } | TColor
```

The value a field holds, by its type.

#### TStoreGet (type, from `nacatamalon`)

```ts
type TStoreGet: () => S
```

Reads a store's live state.

#### TStoreListener (type, from `nacatamalon`)

```ts
type TStoreListener: (value: T, previous: T) => void
```

Told that a selected value changed: the new value and the one before.

#### TStorePersist (type, from `nacatamalon`)

```ts
type TStorePersist
```

How a store saves itself.

- `mode: 'auto'`, the default: the store saves on its own after it changes, at most once every
`throttle` seconds. A value changed on every frame still saves regularly.
- `mode: 'manual'`: the store only saves when `save()` is called. A save point: the game lives in
memory and is only written when the player reaches the typewriter.

In both modes `load()` reads what was saved; call it once when the game starts.

- `adapter: TStorePersistence<S>`: Where to save: `localStorageAdapter()`, `indexedDbAdapter(name)` or one of your own.
- `mode?: 'auto' | 'manual'`: Default `'auto'`.
- `throttle?: number`: In `'auto'`, the shortest time between two saves, in seconds. Default `0.25`.

#### TStorePersistDoc (type, from `nacatamalon/files`)

```ts
type TStorePersistDoc
```

How a store saves itself, as a file can say it: the adapter named rather than built, since
JSON cannot hold a function.

Only read for a store the document is the only thing defining. A store its own code declared
keeps its own saving, because saving is behaviour and behaviour lives in code.

- `adapter: 'localStorage' | 'indexedDb'`
- `mode?: 'auto' | 'manual'`
- `throttle?: number`: How long the trailing throttle waits, in seconds. A file of version 1 said it in milliseconds,
- `dbName?: string`: The database's name, for `indexedDb`. Ignored by the other one.

#### TStorePersistence (type, from `nacatamalon`)

```ts
type TStorePersistence
```

Where a store is saved to and loaded from. Two functions, both asynchronous, both keyed by the
store's `key`.

The engine ships `localStorageAdapter` and `indexedDbAdapter`, but any storage fits by implementing
these two: a server, an external database, a file in a desktop wrapper. `save` receives the live
state, which is plain JSON, so it can be sent as it is.

- `load: `
- `save: `

#### TStoreSelector (type, from `nacatamalon`)

```ts
type TStoreSelector: (state: S) => T
```

Picks one value out of a store's state, to be told only when that value changes.

Pick values, not objects: the state is changed in place, so `(s) => s.inventory` returns the same
object every time and never looks changed. `(s) => s.inventory.length` does.

#### TStoreSet (type, from `nacatamalon`)

```ts
type TStoreSet: (change: (state: S) => void) => void
```

Changes a store: receives the live state and changes it in place.
set((s) => { s.hunger -= 1; });

The state is changed, not replaced, the same way a sprite is moved by changing its `transform`: no
copy per change, even for a value that changes on every frame.

### State

The state an object keeps while it lives, and code that runs when it changes.

#### useData (function, from `nacatamalon`)

```ts
useData(initial: T) => [TDataRecord<T>, TDataSetter<T>]
```

State that lives with a piece of the scene, as `[data, setData]`.

Read it as `data.value`, change it with the setter, and have something react to it with
`useWatch(fn, [data])`. Two things far apart can share one: whoever makes it can hand it to
whatever it builds, and each of them watches it without knowing about the others.

Change it through the setter, not with `data.value = x`. Writing to the field works and is
read back correctly, but nothing is told about it, so a `useWatch` on it will not run.

It is also what gets saved. A scene written down carries this state and gives it back when the
scene is opened again, which is what makes a saved game a saved game and not a fresh start. Keep
in it the kind of thing a file can hold: numbers, text, lists and plain objects. A texture or a
function is not something a file can keep.

Call it in the scene body, like the rest of the hooks.

- `initial: T`: What it holds before anything changes it.

#### useWatch (function, from `nacatamalon`)

```ts
useWatch(fn: () => void, deps: typeOperator) => void
```

Runs your code on the first frame, and again whenever one of the things you listed changes.

What goes in the list is the thing itself (`[lives, texture]`), not a reading of it: those
objects are changed in place and never swap identity, so there would be nothing to compare. The
engine keeps a count of how many times it has announced a change on each one, and this compares
those counts once a frame.

The other side of that: a change the engine was not told about does not count. `setLives(2)`
is announced and `lives.value = 2` is not, exactly like React's rule about not assigning to
state. Anything a game moves by hand, a transform above all, is changed in place all the time and
is never announced, so it cannot be watched. Passing one is refused when the scene starts rather
than accepted into a callback that would never run again.

A store counts as changed on any `set` or action, whatever it touched. To hear one value of it,
straight away and even in pause, use `useStore`; to draw from several things at once, this.

The first run happens whether or not anything changed, so a scene does not need to write its
starting state twice: once in the body and once in the watch.

It runs in the same pass as `useUpdate` and in the order written, so a scene in pause does not
watch, the same way it does not update. A change made while it was paused is seen on the first
frame after it resumes. If something has to be heard even in pause, that is what `useStore` and
`useSignal` are for.

Two changes to the same thing in one frame are one run: what is compared is where things ended
up, once a frame, not every step they took.

- `fn: () => void`: What to run. It takes nothing: read the things you listed.
- `deps: typeOperator`: What to watch. Changing what is in this list later has no effect: it is read once.

#### TDataRecord (type, from `nacatamalon`)

```ts
type TDataRecord
```

State that belongs to one piece of the scene, handed back as an object you read `.value` from.

An object and not the value itself, because the body of a scene runs once: a plain value read
there would be the value it had at that moment, for ever, no matter what happened afterwards.
The object never changes identity, so reading `.value` later, inside `useUpdate` or a click,
always gives what it holds now.

- `value: T`

#### TDataSetter (type, from `nacatamalon`)

```ts
type TDataSetter: (next: T | (previous: T) => T | void) => void
```

How that state is changed. Three forms, and they are interchangeable:

- the next value: `setLives(2)`;
- a function that returns the next value: `setLives((n) => n - 1)`;
- a function that changes it in place and returns nothing: `setHero((h) => { h.x += 1; })`.

The third is why the function may return nothing: when it does, what it was given is taken as
already changed. That is the same shape a store's `set` has, so one habit covers both, while the
returning form stays there for numbers and strings, where changing in place is not possible.

Whichever form is used, the change is announced, which is what `useWatch` is listening for.

#### TWatchDep (type, from `nacatamalon`)

```ts
type TWatchDep: object
```

Something `useWatch` can be given to watch: state from `useData`, a store from `createGameStore`,
or an asset from one of the `useLoad…` hooks.

Not a number or a string. Those are read once, where the list is written, and can never change
afterwards, so watching one is always a mistake and is refused rather than accepted quietly.

### Signals

Named event channels, and the events an object sends up to whoever holds it.

#### createGameSignal (function, from `nacatamalon`)

```ts
createGameSignal() => TGameSignal<T>
```

Makes a signal: a way for one part of the game to say "this happened" and for any other part to
react, without the two knowing about each other.

A button does not need to know about the screen that lights up, and the screen does not need to
know about the button: both know the signal. That is what keeps them apart, so either can be
changed, removed or duplicated without touching the other.

Make it once, in a file of its own, and import it wherever it is needed. It belongs to the page,
not to a game, so it can be fired from outside the canvas too (a web page button, a tool).

- `emit(value)` tells everyone listening, straight away.
- `useSignal(signal, handler)` listens from a scene, and stops by itself when the scene goes.
- `connect(handler)` listens from anywhere else, and returns the function that stops.

A signal carries events, not state: nothing is kept after `emit`. What should still be true later
(a score, a level reached) belongs in the game's state, not here.

#### onReport (function, from `nacatamalon`)

```ts
onReport(object: TBox, name: string, handler: TReportHandler<T>) => () => void
```

Hears what a piece reports to whoever placed it: a pack's button pressed, a behaviour's box out of
lives. The piece sends it with `useReport`; it climbs from there to the first object that listens,
so listening on a whole copy hears any part of it.

For pieces made of data, a pack or a behaviour, which cannot be handed a function. A component of
your own code takes a callback in its props instead (`useSpawn(Enemy)({ onDied })`); something the
whole game hears is a signal.

Stops when the object goes away, and, called while something is being built, when that something
does. The function it returns stops it earlier.

- `object: TBox`: What to listen on: the report reaches it from itself or from anything inside it.
- `name: string`: The report, as the piece sending it named it.
- `handler: TReportHandler<T>`: What to do, given what came with it and the object that sent it.

#### useReport (function, from `nacatamalon`)

```ts
useReport(name: string) => (payload: T) => boolean
```

Lets a piece report to whoever placed it, and gives back the function that sends the report: a
pack's button saying it was pressed, a behaviour saying its box is out of lives. It climbs to the
first object above that listens (`onReport`, or `on` in `createPack`) and stops there. Answers
whether anybody heard it.

For pieces made of data, a pack's script or a behaviour, which cannot be handed a function. A
component of your own code takes a callback in its props instead; something the whole game hears
is a signal.

- `name: string`: What the report is called. The one who listens uses the same name.

#### useSignal (function, from `nacatamalon`)

```ts
useSignal(signal: TGameSignal<T>, handler: TSignalHandler<T>) => void
```

Listens to a signal from a scene, and stops listening by itself when that part of the scene goes
away. Nothing to disconnect by hand.

Call it in the scene body, or in the body of something created with `useSpawn`: then it stops when
that object is destroyed. It keeps listening while the scene is paused, because a signal is the
game telling itself something, and a paused level may still need to hear it.

- `signal: TGameSignal<T>`: The signal, made with `createGameSignal`.
- `handler: TSignalHandler<T>`: What to do each time it fires. Receives what the signal carries.

#### TGameSignal (type, from `nacatamalon`)

```ts
type TGameSignal
```

A channel one part of the game fires and any other part listens to, without either knowing the
other exists. Made with `createGameSignal`.

- `emit: `
- `connect: `
- `size: number`: How many are listening. Useful to spot a listener that was never disconnected.

#### TReportHandler (type, from `nacatamalon`)

```ts
type TReportHandler: (payload: T, from: TBox) => void
```

What a handler connected with `onReport` is told: what came with the report, and the object that
sent it (the button itself, inside the copy a game placed).

#### TSignalHandler (type, from `nacatamalon`)

```ts
type TSignalHandler: (payload: T) => void
```

A function that is told a signal fired, with whatever the signal carries.

### Sprites

The 2D drawable and its atlas.

#### createBackdrop (function, from `nacatamalon`)

```ts
createBackdrop(options: TBackdropOptions) => TSprite
```

A strip of picture that repeats endlessly across the world and moves with the camera only so much:
the far layers of a platformer, clouds, hills, a city skyline, as the 16-bit consoles drew them.

One sprite, laid far wider than any level and repeated, with `parallax`: nothing to move every
frame, and it is there wherever the camera goes. It moves in whole pixels.

- `options: TBackdropOptions`: The picture, its `parallax`, and where its top is.

#### createSprite (variable, from `nacatamalon`)

```ts
createSprite: TCreateSprite
```

Puts a picture in the scene: an image from `useLoadTexture`, a frame of a sheet, or a plain
coloured rectangle when it has neither.

It is placed by its anchor, its middle unless you say otherwise, so `transform: { x: 160, y: 120 }`
centres it there. Everything about it is a plain field you can change at any time
(`hero.transform.x += 1`, `hero.tint = red`), and the next frame shows it.

#### parseAtlasDoc (function, from `nacatamalon/files`)

```ts
parseAtlasDoc(value: unknown, src: string) => TAtlasDoc
```

Reads an `.atlas` file, already parsed from JSON, and checks all of it at once.

All of it and at once, rather than whatever a sprite happens to touch, because tools read these
files as much as games do: an editor has to say "this file is broken, here" while its author is
looking at it. Every message names `src`, since a project has many sheets and this is the last
place that knows which one it was.

- `value: unknown`: The file's contents, parsed from JSON.
- `src: string`: Where it came from, for the messages.

#### TAtlasDoc (type, from `nacatamalon/files`)

```ts
type TAtlasDoc
```

What an `.atlas` file holds: which image, how it is cut, and the named runs its frames make.

Cut one of two ways and never both: a regular `grid`, or a `packed` table of rectangles, which
is what a packing tool writes.

- `format: number`
- `kind: 'atlas'`
- `texture: string`: The image, relative to the `.atlas` file.
- `grid?: TAtlasGridSpec`
- `packed?: Record<string, TAtlasPixelRect>`: Frame name to rectangle, in the order the frames are numbered.
- `sequences?: Record<string, TAtlasSequenceDoc>`
- `points?: Record<string, Record<string, { x, y }>>`: Named points of its frames, for `pointOf`, by the frame's number, or its name in a packed

#### TAtlasDocFrame (type, from `nacatamalon/files`)

```ts
type TAtlasDocFrame
```

One frame of a sheet as a document cuts it: the window into the image in 0-1, and its size in
pixels, with its name when the sheet is packed.

- `name?: string`
- `uvOffset: { x, y }`
- `uvScale: { x, y }`
- `width: number`
- `height: number`

#### TAtlasFrame (type, from `nacatamalon`)

```ts
type TAtlasFrame
```

The window into the image one frame occupies, in 0-1: what `atlasFrame` works out and what a
sprite ends up carrying.

- `uvOffset: { x, y }`
- `uvScale: { x, y }`

#### TAtlasFramePoints (type, from `nacatamalon`)

```ts
type TAtlasFramePoints: Record<string, TAtlasPoint>
```

The named points of one frame: `{ hand: { x: 14, y: 10 }, feet: { x: 8, y: 16 } }`.

#### TAtlasGridSpec (type, from `nacatamalon/files`)

```ts
type TAtlasGridSpec
```

How a sheet laid out in a regular grid is cut: the size of a cell, or how many there are, and
the gaps round and between them.

Give the cell size or the count on each axis, and the other is worked out from the image. That
is what lets the same file describe a sheet whose size is not a whole number of cells, a common
thing in sheets made by hand: whatever is left over at the edge is simply not a frame.

- `frameWidth?: number`: Width of one cell in pixels.
- `frameHeight?: number`: Height of one cell in pixels.
- `columns?: number`: Cells across. Worked out from the image when left out.
- `rows?: number`: Cells down. Worked out from the image when left out.
- `margin?: number`: Empty pixels round the whole sheet. Default `0`.
- `spacing?: number`: Empty pixels between two cells. Default `0`.
- `count?: number`: How many cells are frames, counting row by row, for a last row that is not full.

#### TAtlasPixelRect (type, from `nacatamalon/files`)

```ts
type TAtlasPixelRect
```

One frame of a packed sheet, in the sheet's pixels.

- `x: number`
- `y: number`
- `w: number`
- `h: number`

#### TAtlasPoint (type, from `nacatamalon`)

```ts
type TAtlasPoint
```

A place on a frame: its pixels from the frame's top-left corner, measured as `anchor` is, so
`{ x: 14, y: 10 }` is the top-left corner of that pixel. It may be outside the frame.

- `x: number`
- `y: number`

#### TAtlasSequenceDoc (type, from `nacatamalon/files`)

```ts
type TAtlasSequenceDoc
```

A named run of frames: which ones, how fast, and what comes after.

- `frames: number | string[]`: The frames to play in order: numbers in a grid, names in a packed sheet, mixed freely.
- `fps?: number`: Frames per second. Default `12`.
- `loop?: boolean`: Starts again at the end. Default `true`; `false` rests on the last frame.
- `repeat?: number`: How many extra times it runs after the first: `2` is three times, then it ends. Given, it decides. A negative one, which older files have, is for ever. As a tween's `repeat`.
- `yoyo?: boolean`: Each time it runs again it comes back the other way, the ends shown once. As a tween's `yoyo`.
- `next?: string`: The run to play when this one ends: a punch that drops back to standing. Only means something

#### TBackdropOptions (type, from `nacatamalon`)

```ts
type TBackdropOptions: Pick<TSpriteOptions, 'key' | 'texture' | 'atlas' | 'frame' | 'tint' | 'smooth'> & { parallax, y, height, tileY, zIndex }
```

What a backdrop shows and where.

- `parallax: { x, y }`: How much it moves with the camera: `{ x: 0.2, y: 0 }`, clouds far away that slide slowly by.
- `y?: number`: Where its top is, in the world: with `parallax.y` at `0`, where it sits on the screen. `0` by
- `height?: number`: How tall it is. Its picture's height by default; with `tileY`, ignored.
- `tileY?: boolean`: Repeats down as well as across, filling everything: a sky, a cave wall. `false` by default.
- `zIndex?: number`: Where it is drawn among the rest. Behind everything by default.

#### TCreateSprite (type, from `nacatamalon`)

```ts
type TCreateSprite: (options: { transform, anchor, pivot, tint, visible, zIndex, smooth, parallax, blend, tags, material, uniforms } & TSpriteEvents & { width, height, texture, key, atlas, frame, uvOffset, uvScale, flipX, flipY, tile, tileOffset } & { width, height }) => TSprite & { width, height }
```

The two ways to call `createSprite`. Given a `width` and a `height` (a coloured rectangle, or a
picture drawn at a size of its own), the sprite that comes back has them as numbers, so
`paddle.width` can be used in sums straight away. Given neither, they stay optional: the size is
the picture's, and it is not known until the picture has loaded.

#### TSpriteOptions (type, from `nacatamalon`)

```ts
type TSpriteOptions: T2dOptions & { width, height, texture, key, atlas, frame, uvOffset, uvScale, flipX, flipY, tile, tileOffset }
```

What `createSprite` is asked for: a picture (`texture`, `key` or `atlas`), its size, where it
goes and how it looks. All of it is optional.

- `width?: number`: Omitted, it is drawn as wide as what it shows of its texture, and the sprite's `width` stays
- `height?: number`: Omitted, it is drawn as tall as what it shows of its texture, and the sprite's `height` stays
- `texture?: TTexture | string`: Its picture: a texture from `useLoadTexture`, or the key it was loaded under (`'hero'`), the way a
- `key?: string`: The key a texture was loaded under, for a sprite built where the record is not at hand.
- `atlas?: TSpriteAtlas`: A sheet from `createSpriteAtlas`. It supplies the image and, with `frame`, which part of it
- `frame?: number`: Which frame of `atlas` to show, numbered from 0. Ignored without an atlas. Default `0`.
- `uvOffset?: { x, y }`: Shows only part of the texture: where the window starts, in 0-1 across the image. Comes
- `uvScale?: { x, y }`: How big that window is, in 0-1: `{ x: 0.5, y: 1 }` is half the width, full height.
- `flipX?: boolean`: Shows the picture mirrored left to right, which is how one drawing covers a character facing
- `flipY?: boolean`: The same top to bottom.
- `tile?: boolean`: Tiles its picture across its size instead of stretching it: a wall of bricks, a strip of
- `tileOffset?: { x, y }`: With `tile`, where the pattern starts inside the sprite, in its picture's pixels: moving it
- `transform?: Partial<TTransform2d>`: Where it is, how it is turned and how big. Only what is given: the rest is no turn and a scale
- `anchor?: { x, y }`: Which point of it sits on its transform, in 0-1 of its own size: `{ x: 0.5, y: 0.5 }` is its
- `pivot?: { x, y }`: The point it turns and grows about, in 0-1 of its own size, when it is not the anchor: the
- `tint?: TColorOption`: Its colour, multiplied over what it draws: `'#ff6b1a'`, `0xff6b1a`, `'tomato'` or a `TColor`.
- `visible?: boolean`: Whether it is drawn. Default `true`. `false` makes it hidden from the start, which is how a set
- `zIndex?: number`: Which others it is drawn in front of, within its own scene: higher numbers go on top, and
- `smooth?: boolean`: Crisp (`false`) or blended (`true`) when scaled: `false` keeps pixel art crisp. Default: the
- `parallax?: { x, y }`: How much it moves with the camera: `1` (the default) as everything else, `0` not at all, and in
- `blend?: 'alpha' | 'additive'`: How it lands on what is behind it. `'alpha'` (the default) covers it as far as its alpha says.
- `tags?: string[]`: What it is, as `useTag` says it: `['wall']`. The same as adding them after.
- `material?: TSpriteMaterial`: An effect of its own, from `createMaterial`. Omitted is the built-in shader.
- `uniforms?: TUniformValues`: Its own values for that material's knobs, laid over the material's own.
- `onPointerOver?: TPointerListener`: The pointer comes over the sprite.
- `onPointerOut?: TPointerListener`: The pointer stops being over it, or leaves the game.
- `onPointerMove?: TPointerListener`: The pointer moves while over it.
- `onPointerDown?: TPointerListener`: A button is pressed, or a finger touches, over it.
- `onPointerUp?: TPointerListener`: A button is released, or a finger lifts, over it.
- `onClick?: TPointerListener`: Pressed and released without leaving it. The cursor turns into a hand over it.
- `onDragStart?: TPointerDragListener`: Pressed with the main button (or a finger) and moved far enough not to be a click: a few
- `onDrag?: TPointerDragListener`: The pointer that started the drag moved, wherever it is: over the sprite or not, in the game
- `onDragEnd?: TPointerDragListener`: The pointer that dragged it let go, with `target` and `hits` saying what it was let go over.

#### TSpriteTexture (type, from `nacatamalon`)

```ts
type TSpriteTexture
```

A picture that part of a scene is drawn into instead of the screen: a screen inside the game, a
monitor, a handheld's display.

It sits on the object whose contents it shows. Everything on that object and under it is drawn
into `texture` every frame, in the picture's own pixels, and anything else can show `texture` the
way it shows a loaded image. The object is where the picture's world ends: a camera asked for in
there belongs to the picture, and a lamp in there lights only the picture.

Plain data like every record, apart from `texture.gpu`, which is the renderer's.

- `type: 'sprite-texture'`
- `id: string`
- `width: number`: In pixels. Also how wide the world inside is, before any camera.
- `height: number`: In pixels. Also how tall the world inside is, before any camera.
- `sees: TSpriteTextureSees`: What is drawn into it. `'inside'`: only what is on its object and under it, a world of its own.
- `hide: TViewFilter | null`: What a view (`'world'`) leaves out of the world it sees, or `null`. Changed with `setViewFilter`.
- `only: TViewFilter | null`: What alone a view (`'world'`) takes in of the world it sees, or `null` for all of it.
- `background: TColor`: What the picture is cleared to every frame, behind everything drawn into it.
- `texture: TTexture`: The picture itself, ready from the first frame and found in the game's textures by its `key`.

#### TSpriteTextureOptions (type, from `nacatamalon`)

```ts
type TSpriteTextureOptions
```

What `createSpriteTexture` is asked for.

- `width: number`: In pixels, rounded down and at least 1.
- `height: number`: In pixels, rounded down and at least 1.
- `sees?: TSpriteTextureSees`: `'inside'`, the default: only what the component draws, a world of its own. `'3d'`: the
- `hide?: TViewFilter`: For a view (`'world'`): what of the world it leaves out. `hide` wins over `only`.
- `only?: TViewFilter`: For a view (`'world'`): what alone of the world it takes in.
- `background?: TColorOption`: What is behind everything drawn into it. Opaque black when left out. A transparent one lets
- `key?: string`: The name the picture is kept under, so a model can ask for it by name the way it asks for a

#### TSpriteTextureSees (type, from `nacatamalon`)

```ts
type TSpriteTextureSees: 'inside' | '3d' | 'world'
```

What a picture made by `createSpriteTexture` takes from outside itself: nothing, a world of its
own, or the 3D of the world around it.

### Tilemaps

Tile layers, their source atlas, and reading a tile back.

#### blocksBulletsAt (function, from `nacatamalon`)

```ts
blocksBulletsAt(map: TTilemap, column: number, row: number) => boolean
```

Whether anything in that cell stops a shot. Follows `solid` unless a tile says otherwise, which is
what lets water stop a tank and not a bullet.

- `map: TTilemap`: The map, as `createTilemap` or `getTilemap` gave it back.
- `column: number`: The cell's column, counting from 0.
- `row: number`: The cell's row, counting from 0.

#### cellAt (function, from `nacatamalon`)

```ts
cellAt(map: TTilemap, x: number, y: number) => { column, row }
```

Which cell of the map a point of the world falls in, wherever the map is: moved, scaled or turned,
by itself or by the objects it is in.

It answers for points outside the map too, with numbers below zero or past the edge: "off the
map to the left" is something a game needs to be able to tell, and clamping would say "the first
column" instead. `tileAt` and `solidAt` treat those as empty.

- `map: TTilemap`: The map, as `createTilemap` or `getTilemap` gave it back.
- `x: number`: A point in the world.
- `y: number`: A point in the world.

#### cellCorner (function, from `nacatamalon`)

```ts
cellCorner(map: TTilemap, column: number, row: number) => { x, y }
```

Where the top-left corner of a cell sits in the world, which is what a game needs to line
something up with the grid. Wherever the map is, as `cellAt` counts.

- `map: TTilemap`: The map, as `createTilemap` or `getTilemap` gave it back.
- `column: number`: The cell's column, counting from 0.
- `row: number`: The cell's row, counting from 0.

#### createTilemap (function, from `nacatamalon`)

```ts
createTilemap(options: TTilemapOptions) => TTilemap
```

Loads a map and draws it: one call per layer, however many cells it has.

A layer's cells go up as one batch of corners, each one already in its place and carrying the piece
of the sheet it shows. That is what makes one call possible: a sprite says "this picture, here",
and a thousand cells showing thirty different pieces cannot be said that way. A sprite per cell
would cost a place, a colour and a picture per tile, and stops being sensible somewhere around the
first map that is not tiny.

What comes back is `'loading'` and fills itself in; its layers start drawing once the file and its
sheet have arrived, so nothing has to be awaited. `useLoadProgress` counts it like any other asset.

A level generated in code has no file: give `doc` (what a `.tilemap` holds) and `atlas` (its
sheet, as an object) instead of `src`. It is ready at once when the sheet is, and everything else
works the same: `useTileBody2d`, `solidAt`, `setTile`, `useCameraFollow(hero, { bounds: map })`. It is in
no cache, so each call makes a map of its own, and it goes with what asked for it.

Call it in the body of a scene, like everything else. What it draws goes away with that scene.

- `options: TTilemapOptions`: The `.tilemap` file, or the map and its sheet in code, where it goes, and how it

#### getTilemap (function, from `nacatamalon`)

```ts
getTilemap(box: TBox) => TTilemap | null
```

The map an object carries, from the moment the scene is built, or `null` when it has none.

A map from a scene file is drawn by its object, but its layers only join the object once the
file has arrived, so looking for them among what it draws finds nothing while the scene is being
built. This hands back the map itself straight away, still loading, the same one the object will
draw: a behaviour keeps it and waits for `status` to be `'ready'` before reading tiles from it.
When an object has more than one map, it is the first.

- `box: TBox`: The object to ask about.

#### markLayerChanged (function, from `nacatamalon`)

```ts
markLayerChanged(layer: TTilemapLayer) => void
```

Tells the engine that a layer's cells were written directly, so its corners have to be built
again.

`setTile` says this for you, and for one cell at a time it is what you want. This is the other
case: writing a whole grid back in one go (restoring a level, loading a save) through `data`, where
going through `setTile` would mean one call per cell for an answer you already have.

The rebuild happens on the next frame and once, however many times this is said, so saying it
after every row costs nothing extra.

- `layer: TTilemapLayer`: The layer whose `data` was changed by hand.

#### parseTilemapDoc (function, from `nacatamalon/files`)

```ts
parseTilemapDoc(value: unknown, src: string) => TTilemapDoc
```

Reads a `.tilemap` file and says what is wrong with it, naming the file.

It throws where the project parser forgives, and the difference is who is looking: a project file
is settings, and a game with a silly window size should still run. A map is content, and a map
with a layer half the size of its grid cannot be drawn at all. Better to fail here, while the
author is looking at that file, than to read past the end of an array three layers down inside a
mesh builder.

- `value: unknown`: The file, as `JSON.parse` left it.
- `src: string`: Where the file came from, only so the message can say it.

#### setTile (function, from `nacatamalon`)

```ts
setTile(map: TTilemap, layer: number, column: number, row: number, id: number) => boolean
```

Puts an id into a cell, and says whether anything changed.

This is how a tile is destroyed: a cell is a number, so a brick turning to rubble is writing
another number, and a brick disappearing is writing `0`. The layer is drawn again on the next
frame, once, however many cells were changed.

- `map: TTilemap`: The map, as `createTilemap` or `getTilemap` gave it back.
- `layer: number`: Which of its layers, counting from 0.
- `column: number`: The cell's column, counting from 0.
- `row: number`: The cell's row, counting from 0.
- `id: number`: The tile to put there. `0` empties the cell.

#### solidAt (function, from `nacatamalon`)

```ts
solidAt(map: TTilemap, column: number, row: number) => boolean
```

Whether anything in that cell stops things moving, in any layer: a wall painted on the
treetops layer still stops the player.

- `map: TTilemap`: The map, as `createTilemap` or `getTilemap` gave it back.
- `column: number`: The cell's column, counting from 0.
- `row: number`: The cell's row, counting from 0.

#### solidAtPoint (function, from `nacatamalon`)

```ts
solidAtPoint(map: TTilemap, x: number, y: number) => boolean
```

Whether the point of the world falls on something solid. The short way to ask, for a game that
only wants to know whether it can walk there.

- `map: TTilemap`: The map, as `createTilemap` or `getTilemap` gave it back.
- `x: number`: A point in the world.
- `y: number`: A point in the world.

#### tileAt (function, from `nacatamalon`)

```ts
tileAt(map: TTilemap, layer: number, column: number, row: number) => number
```

The id in a cell of one layer. `0` for empty, and also for anything outside the map.

- `map: TTilemap`: The map, as `createTilemap` or `getTilemap` gave it back.
- `layer: number`: Which layer, numbered as the file wrote them.
- `column: number`: The cell's column, counting from 0.
- `row: number`: The cell's row, counting from 0.

#### tileInfoAt (function, from `nacatamalon`)

```ts
tileInfoAt(map: TTilemap, layer: number, column: number, row: number) => TTileDoc | undefined
```

What the tile in a cell means, or nothing when the cell is empty or off the map.

- `map: TTilemap`: The map, as `createTilemap` or `getTilemap` gave it back.
- `layer: number`: Which of its layers, counting from 0.
- `column: number`: The cell's column, counting from 0.
- `row: number`: The cell's row, counting from 0.

#### tileInfoAtPoint (function, from `nacatamalon`)

```ts
tileInfoAtPoint(map: TTilemap, layer: number, x: number, y: number) => TTileDoc | undefined
```

What the tile under a point of the world means, in one layer: the short way to ask, for a game
reading its own tiles. Whether a ladder is under the player, how hard a belt pushes, what a tile's
`props` say.

- `map: TTilemap`: The map, as `createTilemap` or `getTilemap` gave it back.
- `layer: number`: Which of its layers, counting from 0.
- `x: number`: A point in the world.
- `y: number`: A point in the world.

#### TTerrainDoc (type, from `nacatamalon/files`)

```ts
type TTerrainDoc
```

A ground painted as a whole (grass, water, a wall) whose edges and corners are chosen by what is
around each cell, instead of placed one by one.

Each of the sixteen tiles is the one to draw when the terrain covers a certain set of a cell's
corners: its place in `tiles` is that set, counted as the top-left corner 1, the top-right 2,
the bottom-left 4 and the bottom-right 8. So `tiles[15]` is the middle of the ground, `tiles[3]`
its top half (a cell along the bottom edge of a lake of it), `tiles[8]` the inside of a corner.
Sixteen tiles cover every way the ground can meet a cell, which is what makes painting it a
single gesture.

It is how the map is authored: an editor reads it to choose the tiles it writes into the
layers, and a game drawing the map never looks at it.

- `name: string`: What it is called, which is how it is chosen. Unique in the map.
- `tiles: number[]`: Sixteen tile ids, one for each set of corners the terrain can cover. `0` where the sheet has

#### TTileDoc (type, from `nacatamalon`)

```ts
type TTileDoc
```

What one tile id means. The grid holds numbers; this is the table those numbers point into.

A tile has no place and no identity: two bricks are two appearances of the same number, not two
things. Whatever needs identity (a chest, where the player starts) is a sprite instead. That is
the line this draws, and it is why destroying a tile is writing a number into a cell rather than
removing something.

- `frame?: number | string`: Which frame of the sheet to draw: its place in the count, or its name on a packed sheet.
- `anim?: string`: The name of a run of frames in the sheet to cycle: water, a torch. Exactly one of this and
- `solid?: boolean`: Stops things moving through it. Default `false`.
- `blocksBullets?: boolean`: Stops shots. Follows `solid` when it does not say, which is right for nearly every tile: water
- `becomes?: number`: What this tile turns into when it is destroyed, `0` for "nothing left".
- `flipX?: boolean`: Drawn turned left for right, or upside down: one picture serving both sides of a wall, or the
- `flipY?: boolean`
- `oneWay?: boolean`: A platform stood on from above and passed through from below and from the sides: the
- `ladder?: boolean`: Climbed with up and down. Combined with `oneWay` on the top rung, a character stands on the
- `slope?: [number, number]`: A floor that rises or falls across the cell: its height at the left edge and at the right
- `damage?: number`: How much harm touching it does, for spikes and lava. A value for the game, which decides what
- `push?: { x, y }`: Carries whatever stands on or in it along, in pixels per second: a conveyor belt, a current,
- `props?: Record<string, string | number | boolean>`: Anything else this game wants to know about the tile, which the engine carries and never reads:

#### TTileLayerOrder (type, from `nacatamalon`)

```ts
type TTileLayerOrder: 'under' | 'over' | number
```

Where a layer draws, next to everything else in the scene.

`'under'` and `'over'` mean under and over the characters, and they turn into reserved
numbers either side of zero. That step is the point: a map is drawn without knowing what numbers a
game gives its sprites, and a sprite that never mentions one is already at zero, so a map saying
"under" and "over" is right in any scene with nobody having agreed on a number first.

A plain number still works, because "above the characters but below the treetops" is a real thing
to want and only a number can say it.

#### TTilemap (type, from `nacatamalon`)

```ts
type TTilemap
```

A map: the sheet its numbers point into, how big its cells are, what the numbers mean, and its
layers.

From a file it comes back straight away, empty and `'loading'`, and fills itself in when the file
and its sheet arrive. Nothing has to be awaited, and `useLoadProgress` counts it like any other asset.
Made in code from a sheet that is there, it comes back `'ready'`.

- `type: 'tilemap-asset'`
- `src: string`: Where the file is. Empty for a map made in code, which has none.
- `status: TLoadStatus`
- `transform: TTransform2d`: Where the map's top-left corner sits. Shared by every layer: they sort apart, they do not move
- `atlas: TLoadedAtlas | null`: The sheet, loaded with the map. `null` until the file has said which one.
- `cell: number`: How big a cell is, in pixels. `0` until it has loaded.
- `width: number`: How many cells across and down. `0` until it has loaded.
- `height: number`
- `tiles: Record<number, TTileDoc>`: What each id means.
- `layers: TTilemapLayer[]`: Its layers, in the order the file wrote them.
- `tint: TColor`: Multiplies the colour of every layer.
- `smooth?: boolean`: How the sheet is read. Left out, the game's own setting decides.
- `clocks: Map<string, { elapsed, frame }>`: Where each cycling tile is in its run, by the name of the run.
- `destroyed: boolean`: Set by `destroy`, on it or on the object it belongs to, and never cleared.

#### TTilemapCodeDoc (type, from `nacatamalon`)

```ts
type TTilemapCodeDoc: Omit<TTilemapDoc, 'format' | 'kind' | 'atlas'> & Partial<Pick<TTilemapDoc, 'format' | 'kind' | 'atlas'>>
```

A map written in code: a `.tilemap` file's contents, without what only a file needs to say.

`cell`, `width`, `height`, `tiles` and `layers` are the file's, and are checked the same way.
`format` and `kind` may be left out, and `atlas` is not read: its sheet is given next to it.

#### TTilemapDoc (type, from `nacatamalon/files`)

```ts
type TTilemapDoc
```

What a `.tilemap` file holds: which sheet, how big the cells and the grid are, what the numbers
mean, and the layers.

The table of tiles is inside the map rather than in a file of its own. One file less to keep
in step while a first level is being built, and the id is already what ties a number in the grid
to what it means. When several levels need to share a table it comes back as an optional field
replacing `tiles`: something added to a format that already reads, not a rewrite.

- `format: number`
- `kind: 'tilemap'`
- `atlas: string`: Where the `.atlas` is whose frames the ids draw, relative to this file.
- `cell: number`: How big a cell is, in pixels. Square: a cell that is not is a different problem.
- `width: number`: How many cells across.
- `height: number`: How many cells down.
- `tiles: Record<number, TTileDoc>`: Id to what it means. `0` is always empty and is never written here.
- `layers: TTilemapLayerDoc[]`
- `terrains?: TTerrainDoc[]`: The grounds painted as a whole, for an editor. Left out when there are none.

#### TTilemapDocOptions (type, from `nacatamalon`)

```ts
type TTilemapDocOptions
```

A map written in code.

- `doc: TTilemapCodeDoc`: The map: its cell size, its grid, what each tile means and its layers. What is wrong with it
- `atlas: TSpriteAtlas | TLoadedAtlas`: The sheet its tiles come from, as an object: from `createSpriteAtlas`, or from `useLoadAtlas`.
- `sequences?: Record<string, TAnimationClip>`: The runs of frames its cycling tiles name, as a `.atlas` file's are. Only for a sheet from
- `src?: never`
- `key?: never`

#### TTilemapDrawOptions (type, from `nacatamalon`)

```ts
type TTilemapDrawOptions
```

How a map is placed and drawn, wherever it comes from.

- `transform?: Partial<TTransform2d>`: Where the map's top-left corner sits. What is left out takes its default (scale 1, no turn).
- `tint?: TColorOption`: Multiplies the colour of every layer. Default white.
- `smooth?: boolean`: Overrides the game's `smooth` for this map.

#### TTilemapFileOptions (type, from `nacatamalon`)

```ts
type TTilemapFileOptions
```

A map from a `.tilemap` file.

- `src: string`: Where the `.tilemap` file is.
- `key?: string`: What to cache it under, so two scenes share one map. Defaults to `src` as the page resolves
- `doc?: never`
- `atlas?: never`
- `sequences?: never`

#### TTilemapLayer (type, from `nacatamalon`)

```ts
type TTilemapLayer
```

One layer of a map, and the thing the renderer actually draws.

A layer and not the map, because the order things are drawn in is per drawable and a layer's whole
purpose is to sort on its own: the ground under the player, the treetops over them, both from one
map. Making the map one drawable would have meant teaching the sorter about an order inside an
order, when the one it has already does the job.

Its cells are split across two batches. The still one holds every cell whose tile shows a
fixed picture, and is built once. The moving one holds the cells whose tile cycles, and only their
corners are rewritten when the cycle turns. That split is what keeps a map with water cheap: a few
dozen cells go up again four times a second while the several thousand still ones sit there.

Those two batches are not here. They are handles to memory on a device, and this is a record:
plain data, and all of it writable to a file. They live in a side table instead, keyed by the
layer, which is the same arrangement a particle emitter's pool uses. See `layer_state.ts`.

- `type: 'tilemap'`
- `id: string`
- `name: string`
- `map: TTilemap`: The map this belongs to, so a layer on its own is still enough to find everything.
- `transform: TTransform2d`: Where the map's top-left corner sits: the map's own placement, by reference, not a copy.
- `worldTransform?: TTransform2d`: Where it ends up once everything above it has moved it, worked out once a frame while the tree
- `texture: TTexture | null`: The sheet its cells take their pictures from: the map's, by reference.
- `tint: TColor`: Multiplies the colour of the layer: the map's, by reference.
- `smooth?: boolean`: How the sheet is read. Left out, the game's own setting decides.
- `visible?: boolean`: Whether this layer is drawn. Omitted is drawn.
- `data: number[]`: The ids of its cells, row by row. `0` is empty.
- `order: TTileLayerOrder`: Where it draws. Read as `zIndex` by the same sort every drawable goes through.
- `zIndex: number`: Which number it sorts by. Worked out from `order` when the map loads, and kept on the layer
- `parallax?: { x, y }`: How much this layer moves with the camera: `1` as everything else, less for a layer further
- `destroyed: boolean`: Set by `destroy`, on it or on the object it belongs to, and never cleared.
- `material?: TSpriteMaterial`: An effect of its own, from `createMaterial`. Omitted is the built-in shader.
- `uniforms?: TUniformValues`: This layer's own values for that material's knobs, laid over the material's own.

#### TTilemapLayerDoc (type, from `nacatamalon/files`)

```ts
type TTilemapLayerDoc
```

One layer of the map: a flat grid of tile ids, row by row, and where it draws.

The order lives here and never on a tile: the same brick can be background in one layer and
foreground in another.

- `name: string`
- `order: TTileLayerOrder`: Where it draws. Default `0`, which is where the characters are.
- `data: number[]`: `width × height` ids, row by row from the top-left. `0` is empty.
- `parallax?: { x, y }`: How much the layer moves with the camera: `1` (left out) as everything else, less for a layer

#### TTilemapOptions (type, from `nacatamalon`)

```ts
type TTilemapOptions: TTilemapDrawOptions & TTilemapFileOptions | TTilemapDocOptions
```

What `createTilemap` is asked for: a `.tilemap` file by `src`, or a map written in code by `doc`
and `atlas`.

- `transform?: Partial<TTransform2d>`: Where the map's top-left corner sits. What is left out takes its default (scale 1, no turn).
- `tint?: TColorOption`: Multiplies the colour of every layer. Default white.
- `smooth?: boolean`: Overrides the game's `smooth` for this map.

### Particles

Emitters, their curves over a particle lifetime, and the document they serialize to.

#### useParticleCollider2d (function, from `nacatamalon`)

```ts
useParticleCollider2d(options: { shape, enabled }) => TParticleCollider2d
```

Makes this object solid to particles in a flat scene: sparks bounce off it and rain ends on it.

The shape is measured on the object, so it moves, turns and stretches with it. It only stops the
effects whose file says how they collide; every other effect goes straight through, which is what
smoke and glows want.

One per object. A staircase is one object per step, each with its own.

- `options: { shape, enabled }`: The shape, and whether it starts on.

#### useParticleCollider3d (function, from `nacatamalon`)

```ts
useParticleCollider3d(options: { shape, enabled }) => TParticleCollider3d
```

Makes this object solid to particles in three dimensions. The same thing as the flat one, with a
box, a ball or an endless floor.

- `options: { shape, enabled }`: The shape, and whether it starts on.

#### TEmissionDoc (type, from `nacatamalon/files`)

```ts
type TEmissionDoc
```

When and how many appear.

`rate` and `burst` are both here and they mean different things: `rate` is a fountain, so many a
second for as long as it runs, and `burst` is an explosion, all of them the moment the cycle
starts. An effect usually wants one and occasionally wants both, so neither can be said with the
other.

Both at zero is a perfectly good effect, not a mistake: it is one that something else fires
with `emitParticles`.

- `rate: number`: Per second, for as long as the cycle runs. `0` for an effect that only bursts.
- `burst: number`: How many appear at once when the cycle starts. `0` for one that only pours.
- `duration: number`: How long one cycle emits for, in seconds. `0` is forever: a torch rather than an explosion.
- `loop: boolean`: Start the cycle again when `duration` runs out. Means nothing when `duration` is `0`.

#### TEmitShape2d (type, from `nacatamalon`)

```ts
type TEmitShape2d: { kind } | { kind, radius, edge } | { kind, width, height } | { kind, length, angle }
```

Where a particle is born, in the emitter's own space.

Deliberately apart from which way it then moves, which is `direction` and `spread` below. A ring
of sparks flying outwards and a ring of rain falling straight down are the same birth area with
different velocities, and folding the two together makes the second one impossible to say.

#### TEmitShape3d (type, from `nacatamalon`)

```ts
type TEmitShape3d: { kind } | { kind, radius, edge } | { kind, size } | { kind, radius, angle }
```

Where a particle is born in three dimensions, in the emitter's own space.

The same split as the flat shapes, position only, with one exception: a cone also aims what it
emits, along its own wall, because a cone whose particles ignored it would be a disc.

#### TParticleAnimPace (type, from `nacatamalon`)

```ts
type TParticleAnimPace: 'life' | 'clip'
```

How a particle's run of frames is paced.

`'life'`, the default, stretches the run over each particle's own life: a spark that lives half
as long plays it twice as fast, and every particle shows the last frame as it dies, which is what
an explosion that ends in smoke wants. The run's `fps` and `loop` mean nothing there, since a life
always ends; a `repeat` and a `yoyo` say how many times it goes and which way, counted the way a
sprite counts them, and one that goes round for ever plays it once.

`'clip'` plays it at its own `fps`, exactly as `useSpriteAnimation` would, from the moment the
particle is born: for a fire that flickers or a coin that spins as it falls. One that does not
loop rests on its last frame until the particle dies; a particle that dies first cuts it short.

Neither tells anybody anything: a run's `events` and `next` belong to sprites.

#### TParticleBlend (type, from `nacatamalon`)

```ts
type TParticleBlend: 'alpha' | 'additive'
```

How a particle is laid over what is already there.

`alpha` covers, which is what smoke and dust want. `additive` adds light, which is the only
way fire, sparks and anything glowing read as bright: two overlapping embers should be brighter
than one, and under `alpha` they are merely nearer.

#### TParticleCollider2d (type, from `nacatamalon`)

```ts
type TParticleCollider2d
```

Something solid in a flat scene, as far as particles are concerned.

It belongs to the scene and not to any effect: every effect that says how it collides runs into
it. It is placed by the object it is on, so a moving platform takes its collider with it.

- `type: 'particle-collider-2d'`
- `id: string`
- `shape: TParticleColliderShape2d`
- `enabled: boolean`: Whether particles run into it. Turning it off keeps it, ready to turn on again.

#### TParticleCollider3d (type, from `nacatamalon`)

```ts
type TParticleCollider3d
```

Something solid in a scene in three dimensions, as far as particles are concerned. The same thing
as the flat one, one dimension up.

- `type: 'particle-collider-3d'`
- `id: string`
- `shape: TParticleColliderShape3d`
- `enabled: boolean`: Whether particles run into it. Turning it off keeps it, ready to turn on again.

#### TParticleColliderShape2d (type, from `nacatamalon`)

```ts
type TParticleColliderShape2d: { kind, width, height } | { kind, radius } | { kind }
```

The shapes a flat collider can take, measured on its object: the object's placement moves, turns
and stretches it.

`plane` is an endless floor along the object's own `y = 0`, solid below it on screen, which with
y growing downwards is the side where `y` is larger.

#### TParticleColliderShape3d (type, from `nacatamalon`)

```ts
type TParticleColliderShape3d: { kind, size } | { kind, radius } | { kind }
```

The shapes a collider in three dimensions can take, measured on its object.

`plane` is an endless floor through the object's own origin, solid below it, with +y up. Turn the
object and it becomes a slope or a wall.

#### TParticleCollision (type, from `nacatamalon`)

```ts
type TParticleCollision
```

What a particle does when it runs into something solid in the scene.

Only how it reacts, never what it runs into: the file is shared by every room that uses the
effect, so where the floor is belongs to the scene, which says it with colliders on its objects.
Sparks bounce and rain is gone, in any room.

- `mode: 'bounce' | 'die'`: `bounce` sends it back off the surface; `die` ends it there, which is what rain wants.
- `bounce: number`: How much of its speed into the surface it keeps on the way back, `0` to `1`.
- `friction: number`: How much of its speed along the surface it loses at each touch, `0` to `1`.

#### TParticleColorStop (type, from `nacatamalon`)

```ts
type TParticleColorStop
```

One stop of the colour a particle walks through as it ages. `t` is `0` at birth and `1` at death.

- `t: number`
- `color: TColor`
- `alpha: number`

#### TParticleOverrides (type, from `nacatamalon`)

```ts
type TParticleOverrides
```

What a scene may turn up or down about an effect without writing a second file.

The file says what the effect is, and it is shared: three torches are one document. These are
the handful of things one of those torches may disagree with it about.

It exists because every game eventually wants "the same explosion, half the size", and making
that a second file is what forks an effect into six near-copies nobody can keep in step.

- `rateScale?: number`: Multiplies how many come out a second.
- `sizeScale?: number`: Multiplies how big they are born.
- `speedScale?: number`: Multiplies how fast they set off.
- `lifeScale?: number`: Multiplies how long they live.

#### TParticleRange (type, from `nacatamalon`)

```ts
type TParticleRange: [number, number]
```

A pair of numbers a particle draws one value from when it is born.

Written as `[min, max]`, or as a bare number when every particle should get the same one.

#### TParticles3d (type, from `nacatamalon`)

```ts
type TParticles3d: Omit<TParticles, 'type' | 'transform' | 'worldTransform'> & { type, transform, worldMatrix }
```

An emitter in three dimensions: particles in world units, drawn as squares that always face the
camera.

Everything but its placement is the flat emitter's, field for field, and means the same thing:
the same file on the same terms, the same tint, the same verbs. What it does not share is where
it is, which here is a place in space with a turn of three angles, and the answer to where it
ended up is a matrix rather than a flat placement, exactly as it is for a model.

- `type: 'particles-3d'`
- `transform: TTransform3d`: Where the emitter is on its box. Particles are born here, turned and scaled by everything above it.
- `worldMatrix?: Float32Array`: Where it ended up once everything above it has moved it, or where it is when nothing does. Set

#### TParticles3dOptions (type, from `nacatamalon`)

```ts
type TParticles3dOptions: Omit<TParticlesOptions, 'transform'> & { transform }
```

What `createParticles3d` is asked for: the flat emitter's options, placed in three dimensions.

- `transform?: Partial<TTransform3d>`: Where it is. Particles are born here, turned and scaled by whatever is above it.

#### TParticleScaleStop (type, from `nacatamalon`)

```ts
type TParticleScaleStop
```

One stop of how big it is as it ages, as a multiple of the size it was born with.

- `t: number`
- `scale: number`

### Layout

How big something is and where it is: the size and rectangle of what is drawn flat, how a text is set and where its lines break, and points carried between an object and the world.

#### boundsOf (function, from `nacatamalon`)

```ts
boundsOf(thing: TMeasurable) => { x, y, width, height }
```

Where it sits and how big it is: the rectangle it covers, `x` and `y` its top-left corner, with its
anchor, its pivot and its own scale taken into account.

In the same place its `transform` is written, the box it hangs from, and not the world: two things
of one box line up with it whatever the box is doing, which is what laying out a HUD or a menu
needs. Unturned: a turned thing gives the rectangle it would cover straight.

- `thing: TMeasurable`: A sprite, a text or a panel.

#### letterAt (function, from `nacatamalon`)

```ts
letterAt(text: TText, at: { screenX, screenY }) => TTextLetter | null
```

The letter of a text under a point of the screen: which one was touched, of a word to spell, a
line of dialogue with words to pick, a field with a caret to place.

Seen as it is drawn: through its scene's camera, a text on the screen's own layer included,
turned, grown and placed by its pivot and everything it hangs under. A letter is its whole step
along its line and the line's height, so an `o` is touched above and below its ink, and two
letters touch. `null` on a space, in the spacing between two letters or two lines, outside the
text, or for one destroyed or with nothing drawn yet.

- `text: TText`: The text.
- `at: { screenX, screenY }`: A point of the screen: what the pointer gives, `info` itself.

#### localToWorld (function, from `nacatamalon`)

```ts
localToWorld(object: TSprite | TBox | TText | TNineSlice, point: TPoint2d) => TPoint2d
```

Where a point of an object is in the world: the way back from `worldToLocal`.

For anything that leaves an object from a spot on it: the muzzle of a gun that turns, the hand
that throws, the place on a ship where the flame comes out. Written once in the object's own
numbers, it stays right however the object and everything above it move, turn and grow.

- `object: TSprite | TBox | TText | TNineSlice`: The object the point belongs to, or a sprite, text or panel it draws.
- `point: TPoint2d`: A point measured on the object, from its own origin.

#### localToWorld3d (function, from `nacatamalon`)

```ts
localToWorld3d(object: TBox | TMesh, point: TPoint3d) => TPoint3d
```

`localToWorld` for a world with depth: where a point of a model is in space.

- `object: TBox | TMesh`: The object the point belongs to, or a model it draws.
- `point: TPoint3d`: A point measured on the object, from its own origin.

#### measureText (function, from `nacatamalon`)

```ts
measureText(text: TText) => TTextMeasure
measureText(text: string, options?: TMeasureTextOptions) => TTextMeasure
```

How a text on screen is set: the size of its block and the lines it is in, with its own font and
style. The same block `sizeOf` measures, before the text's scale.

- `text: TText`: A text from `createText`.

#### pointOf (function, from `nacatamalon`)

```ts
pointOf(sprite: TSprite, name: string) => { x, y } | null
```

Where a named point of the frame a sprite is showing is in the world: the hand a sword hangs from,
the feet dust rises at, the muzzle a shot leaves. Its sheet says where each one is in each frame,
so what follows it stays right when the art changes, with no table of offsets to keep in step.

Everything that moves the picture counts: its anchor and pivot, `flipX` and `flipY`, its size, its
scale and its turn, and every object above it. The camera does not, as in `localToWorld`, and so
neither does `parallax`: this is where it is placed, not where it is seen. In `useScreenSpace` the
answer is in screen pixels, as its placement is. With `tile` the frame is taken as stretched over
the whole sprite, not as each copy.

A point is a place and not a pixel, measured as `anchor` is: `{ x: 14, y: 10 }` is the top-left
corner of that pixel, and flipped it lands where that corner lands, so write `14.5` for its middle.

Ask it where it is used, each frame: the answer is for the frame showing now.

- `sprite: TSprite`: A sprite from a sheet with points: an `.atlas` with `points`, or `createSpriteAtlas({ points })`.
- `name: string`: The point's name.

#### sizeOf (function, from `nacatamalon`)

```ts
sizeOf(thing: TMeasurable) => { width, height }
```

How big it is, in game pixels, with its own scale: a text of 64 by 8 with `scaleX: 2` is 128 wide.

What it was given (`width`, `height`), or else what makes it that size: a sprite's picture or the
frame of its sheet, a text's letters. The text is measured as it is now, with the font it has, so
it can be read the moment it is made: a box behind a label, a coin after a score. Not yet loaded,
a picture or a font measures `0`.

Its own scale and not the boxes' above it, and not its turn: the size of what it says it is, in the
same place its `transform` is written. Where it sits as well is `boundsOf`.

- `thing: TMeasurable`: A sprite, a text or a panel.

#### worldToLocal (function, from `nacatamalon`)

```ts
worldToLocal(object: TSprite | TBox | TText | TNineSlice, point: TPoint2d) => TPoint2d
```

Where a point of the world falls on an object: measured from the object's own origin, along its
own turned and resized axes, which are the numbers its pictures are placed in.

What a hit needs once it has landed: where on the membrane the bite was, to dent it there; which
end of a paddle the ball touched; where on an on-screen button the finger is. The object's
placement is taken whole, with everything it hangs under, and the camera is not part of it: the
`worldX`/`worldY` the pointer gives are the world this means.

- `object: TSprite | TBox | TText | TNineSlice`: The object to measure from, or a sprite, text or panel it draws.
- `point: TPoint2d`: A point of the world.

#### worldToLocal3d (function, from `nacatamalon`)

```ts
worldToLocal3d(object: TBox | TMesh, point: TPoint3d) => TPoint3d
```

`worldToLocal` for a world with depth: where a point in space falls on a model, along its
own turned and resized axes.

- `object: TBox | TMesh`: The object to measure from, or a model it draws.
- `point: TPoint3d`: A point in space.

#### TMeasurable (type, from `nacatamalon`)

```ts
type TMeasurable: TSprite | TText | TNineSlice
```

Something drawn flat that `sizeOf` and `boundsOf` measure: a sprite, a text or a panel.

#### TMeasureTextOptions (type, from `nacatamalon`)

```ts
type TMeasureTextOptions: TTextStyle & { font }
```

What `measureText` is asked to measure with: a font, and the same size, spacing and width a text
on screen takes.

- `font?: TBitmapFont | TFont | string`: A font from `useLoadFont` or `useLoadBitmapFont`, or the key one was loaded under. A key is
- `fontSize?: number`: How big the letters are, in pixels.
- `align?: 'left' | 'center' | 'right'`: Where each line sits: inside `maxWidth` when there is one, inside the width of the longest
- `maxWidth?: number`: How wide a line can be, in pixels, before its words go on to the next one: a dialogue box, a
- `letterSpacing?: number`: Extra pixels between two characters. Default `0`.
- `lineSpacing?: number`: Extra pixels between two lines. Default `0`.

#### TTextLetter (type, from `nacatamalon`)

```ts
type TTextLetter
```

A letter of a text, as `letterAt` finds it under a point.

- `index: number`: Where it is in the text: `text.text[index]` is the letter, the first half of it for a
- `char: string`: The letter itself, whole.
- `line: number`: The line it is on, from 0: one per `\n`, and more where `maxWidth` broke one.

#### TTextMeasure (type, from `nacatamalon`)

```ts
type TTextMeasure
```

How big a text measured by `measureText` is, and the lines it is set in.

- `width: number`: How wide the block is: its longest line, or `maxWidth` when there is one.
- `height: number`: How tall the block is: its lines and the spacing between them.
- `lines: string[]`: What each line says: one per `\n`, and more where `maxWidth` broke one, without the spaces it

### Camera

The 3D camera and the 2D one, picking through them, and aiming one.

#### cameraLookAt (function, from `nacatamalon`)

```ts
cameraLookAt(camera: TCamera3d, target: { x, y, z }) => TCamera3d
```

Turns a camera to look at a point, from wherever it is standing.

It writes the camera's `rotationY` (left and right) and `rotationX` (up and down), and leaves its
roll alone. Call it in `useUpdate` to keep a moving thing in the middle of the view. A camera
turned by a quaternion is switched back to angles, since a quaternion would be read instead of
them and the camera would not move.

Straight up or straight down has no left or right to speak of, so there the camera keeps the
`rotationY` it had.

- `camera: TCamera3d`: The camera to turn.
- `target: { x, y, z }`: Where to look: anything with `x`, `y` and `z`, such as another object's placement.

#### screenToRay (function, from `nacatamalon`)

```ts
screenToRay(camera: TCamera3d | null, width: number, height: number, x: number, y: number) => TRay
```

The ray that goes into the world through a point of the screen: what is under the pointer.

It is worked out from the very view and projection the scene is drawn with, so the ray passes
through exactly what is drawn at that pixel. Test it against what can be picked (a sphere around
each object, a box, a plane) and the nearest hit is what was clicked; against the floor's plane, it
gives the point of the floor under the pointer, which is what dragging something across it needs.

`x` and `y` are in the game's pixels, the pointer's `screenX` and `screenY`, and `width` and
`height` are the game's size (`useGame().getWidth()`, `getHeight()`). With no camera it uses the
view the scene falls back to when it has none.

- `camera: TCamera3d | null`: The scene's camera, or `null`.
- `width: number`: The game's width, in pixels.
- `height: number`: The game's height, in pixels.
- `x: number`: The point, from the left.
- `y: number`: The point, from the top.

#### useCamera2d (function, from `nacatamalon`)

```ts
useCamera2d(options?: TCamera2dOptions) => TCamera2d
```

Gives the scene a camera, so its world can be bigger than the screen and scroll, turn and zoom.

Without one, everything is drawn where its `x` and `y` say on the screen, which is what a menu or
a single-screen game wants. With one, those same numbers become a place in a world, and the
camera decides which part of that world is on screen.

You get the camera back and move it by changing its numbers, usually every frame:

- `transform.x`/`transform.y`: the world point on the top-left corner of the screen. To keep
something in the middle, subtract half the screen from its position.
- `transform.rotation`: turns the view, in radians.
- `zoom`: `2` shows everything twice as big, `0.5` half as big.

The camera belongs to the whole scene, so it does not matter which part of the scene asks for it.
Asking twice replaces the first one. Inside a picture made by `createSpriteTexture` it belongs to
that picture instead, and the scene keeps its own.

Something that must stay still on screen while the world moves, a score or a health bar, has two
ways to do it: put it in its own scene started with `useScene().launch()`, or mark it with
`useScreenSpace()`.

- `options?: TCamera2dOptions`: Where the camera starts: `x`, `y`, `rotation` and `zoom`, all optional.

#### useCamera3d (function, from `nacatamalon`)

```ts
useCamera3d(options: TCamera3dOptions) => TCamera3d
```

Gives the scene a camera to look at its models through, and returns it.

Without one, models are drawn straight in the game's pixels, flat on, exactly the way sprites are
drawn without a 2D camera. With one, the scene is seen from wherever the camera is.

There are two ways of seeing and the choice is what the game looks like. `'orthographic'`, the
default, has no vanishing point and measures in the same pixels a sprite does: a model one
unit wide is one pixel wide, so a 3D piece can stand on a 2D board and pan and zoom with it.
`'perspective'` is the usual one, where things shrink with distance.

What comes back is the camera itself, so moving it is changing a number and it takes effect on
the next frame drawn. A scene has one, as it has one 2D camera; asking again gives back the one
it already has, and says so, rather than quietly replacing it. Inside a picture made by
`createSpriteTexture` it belongs to that picture instead, and the scene keeps its own.

- `options: TCamera3dOptions`: Where it starts, how it sees, and how far it can see.

#### useCameraFollow (function, from `nacatamalon`)

```ts
useCameraFollow(target: TCameraTarget | null, options: TCameraFollowOptions) => TCameraFollow
```

Makes the scene's camera follow something, the way a platformer's does: a box in the middle the
character moves inside freely (`deadzone`), never past the edges of the level (`bounds`), a little
ahead of where it goes (`lead`), and a shake for when something hits hard.

It moves the camera once every `useUpdate` and every body's move is over, in `useLateUpdate`, so it
reads where the character ended up and the two never shake against each other. It follows at once
and on whole pixels by default, as the consoles did; `smooth` makes it glide. It works at any
`zoom`; a camera turned with `rotation` is not one it can keep a level's edges out of.

With `rooms` the level is cut into rooms, as the action games of the consoles did: the camera stays
in the target's room and scrolls across when the target walks into the next. The scroll is the
bounds moving from one room to the other, so the dead zone and the rest keep working through it.

It uses the scene's camera from `useCamera2d`, or makes it. Everything it was told can be changed
on what it returns, at any time: another target for a cutscene, other bounds for another room.

- `target: TCameraTarget | null`: What to follow: an object, a sprite it draws, or a point the game moves. `null` to
- `options: TCameraFollowOptions`: How: see `TCameraFollowOptions`.

#### useFog (function, from `nacatamalon`)

```ts
useFog(options: TFogOptions) => TFog
```

Puts the scene in fog: its models fade into `color` between `near` and `far` from the camera.

The fog belongs to the scene, like its camera, so one is enough and a second is ignored with a
warning. It covers every model the scene draws, a material of your own included, and leaves the
2D alone. Set the game's background to the same colour and the far end of the world melts into
it instead of stopping at a wall, which is the whole trick.

- `options: TFogOptions`: What it fades into and over what distance.

#### useParallax (function, from `nacatamalon`)

```ts
useParallax(factor: { x, y }) => void
```

Makes this object, and everything it draws and holds, move with the camera only so much: `1` as
everything else, `0` not at all, as if pinned to the screen, and in between further away. A far
range of mountains is `{ x: 0.3, y: 0 }`: it slides slowly by and keeps its height.

Only how it is drawn and where the pointer touches it change; its placement stays what it says.
Something it draws with a `parallax` of its own keeps that. It moves in whole pixels, as the
layers of the old consoles did.

- `factor: { x, y }`: How much it moves with the camera, across and down.

#### useScreenSpace (function, from `nacatamalon`)

```ts
useScreenSpace(on: boolean) => void
```

Keeps what is created here fixed on the screen, even while the scene's camera moves.

It is how a health bar, a score or a pause button stays in its corner over a world that
scrolls. It covers everything this part of the scene creates and everything created under it,
however deep, so a whole HUD is marked once.

Positions inside it are screen pixels, measured from the top-left corner, whatever the camera
is doing. Called in the body of a scene, the whole scene ignores its camera.

The other way to get the same thing is a separate scene for the HUD, started with
`useScene().launch()`: a scene without a camera is always fixed to the screen, and it is drawn
over the scene that launched it.

- `on: boolean`: `false` undoes it. Default `true`.

#### useWorldOffScreen (function, from `nacatamalon`)

```ts
useWorldOffScreen(on: boolean) => void
```

Has the screen draw only what this scene pins to it (`useScreenSpace`): its world is seen through
views of it alone (`createSpriteTexture({ sees: 'world' })`).

For a split screen, where two views cover the whole screen and the world drawn underneath them
would never be seen and would cost a third time over. The scene runs as ever: everything in it
moves, sounds and is seen by its views, its particles included. Only what the screen draws of it
changes, and only while the game looks through its own cameras: a capture of the game is a picture
of the screen, its views and its HUD, while the editor's edit mode, a camera's preview and a capture
through another camera still show the world. A capture that names a camera, the scene's own
included, is through another one; one that names none, or `null`, is the game's. It is the scene's,
wherever in it it is called.

- `on: boolean`: `false` undoes it, for a game that leaves a split screen. Default `true`.

#### worldToScreen (function, from `nacatamalon`)

```ts
worldToScreen(camera: TCamera3d | null, width: number, height: number, point: { x, y, z }) => TScreenPoint
```

Where a point of the world is drawn on the screen: the other way round from `screenToRay`.

What puts a name over a character's head, a marker on something off in the distance, or a health
bar that follows a model: a sprite placed at the answer sits exactly over the point, because the
answer comes from the same view and projection the scene is drawn with.

- `camera: TCamera3d | null`: The scene's camera, or `null`.
- `width: number`: The game's width, in pixels.
- `height: number`: The game's height, in pixels.
- `point: { x, y, z }`: The point in the world.

#### TCamera2d (type, from `nacatamalon`)

```ts
type TCamera2d
```

The camera a scene looks at its 2D world through. Plain data: the renderer reads it every frame,
so changing a field is all it takes to move, turn or zoom the view.

`transform.x`/`y` is the world point that lands on the top-left corner of the screen, in
pixels, not its centre: following something means subtracting half the screen. Rotation and
zoom pivot on that same corner.

`zoom` magnifies: `2` shows everything twice as big, `0.5` half as big.

`type` exists for the compiler as much as for anyone reading the data: without it, any record
with a `transform` and a `zoom` would fit here by shape.

- `type: 'camera-2d'`
- `id: string`
- `transform: { x, y, rotation }`: Where the camera is and how it is turned. No scale: a camera does not stretch, and a field
- `zoom: number`: Magnification. `1` is none, `2` twice as big.
- `cullingCamera?: TCamera2d | null`: For finding out what a camera leaves out: another camera, which then decides what this one

#### TCamera2dOptions (type, from `nacatamalon`)

```ts
type TCamera2dOptions
```

What a camera can start with. Everything is optional.

- `transform?: Partial<{ x, y, rotation }>`: Where it is, as it is read afterwards (`camera.transform`): `x` and `y` are the world point on
- `zoom?: number`: Magnification. Default `1`.

#### TCamera3d (type, from `nacatamalon`)

```ts
type TCamera3d
```

The camera a scene looks at its 3D world through. Plain data, like the 2D one: the renderer reads
it every frame, so changing a field is all it takes to move, turn or zoom the view.

Two ways of seeing, and the choice is what the game is:

- `'perspective'` is the usual one: things get smaller with distance, and `fov` says how much is
in view.
- `'orthographic'` has no vanishing point and measures in the same pixels a sprite does: a
model one unit wide is one pixel wide. That is what lets a 3D piece stand on a 2D board and pan
and zoom with it, which is how the consoles of the era mixed the two.

Y points up here, the opposite of the 2D camera, because every model is built that way and
mirroring the axis would turn each one inside out. Unless it follows the 2D camera (`follow2d`):
then the 3D pieces live in the 2D world, y down, among the sprites.

- `type: 'camera-3d'`
- `id: string`
- `projection: 'perspective' | 'orthographic'`
- `transform: TTransform3d`: Where the camera is and how it is turned. No scale: a camera does not stretch.
- `fov: number`: How wide the view is, in degrees. Perspective only.
- `near: number`: Nothing closer than this is drawn, and nothing further than `far`.
- `far: number`
- `zoom: number`: Magnification. Orthographic only: `2` shows everything twice as big.
- `follow2d?: boolean`: Orthographic only: the 3D pieces are placed in the 2D world, as sprites are, and seen through
- `cullingCamera?: TCamera3d | null`: For finding out what a camera leaves out: another camera, which then decides what this one

#### TCamera3dOptions (type, from `nacatamalon`)

```ts
type TCamera3dOptions
```

What a 3D camera can start with. Everything is optional.

- `projection?: 'perspective' | 'orthographic'`: Default `'orthographic'`, which measures in pixels and lines up with the 2D.
- `transform?: Partial<Pick<TTransform3d, 'x' | 'y' | 'z' | 'rotation' | 'rotationX' | 'rotationY'>>`: Where it is and where it looks, as it is read afterwards (`camera.transform`): `x`, `y`, `z`
- `fov?: number`: In degrees. Default `60`. Perspective only.
- `follow2d?: boolean`: Orthographic only: the models placed in the 2D world and seen through the 2D camera. See
- `near?: number`: Default `0.1` and `1000`. An orthographic camera reads only `far`, and sees that far either
- `far?: number`
- `zoom?: number`: Default `1`. Orthographic only.

#### TCameraFollow (type, from `nacatamalon`)

```ts
type TCameraFollow
```

A camera following something: everything it was told, changeable at any time, and its tremor.

- `camera: TCamera2d`: The scene's camera, which this moves: the one it has now, so a `useCamera2d` before or after
- `target: TCameraTarget | null`: What it follows. `null` holds the camera where it is.
- `deadzone: { width, height }`
- `bounds: TTilemap | TRect | null`
- `lead: { x, y }`
- `smooth: number`
- `round: boolean`
- `rooms: TRect[] | { width, height } | null`
- `scroll: number`
- `room: TRect | null`: The room the camera is in, or scrolling into. `null` without rooms, or before the target has
- `scrolling: boolean`: `true` while the camera scrolls from one room to the next. Many games stop their characters
- `shake: (strength: number, seconds: number) => void`: Shakes the view, up to `strength` pixels either way, dying away over `seconds`: a hit, an
- `snap: () => void`: Puts the camera on its target at once, smoothing or not: after a respawn, a door, a cut.

#### TCameraFollowOptions (type, from `nacatamalon`)

```ts
type TCameraFollowOptions
```

How a camera follows.

- `deadzone?: { width, height }`: A box in the middle of the screen, in pixels, that the target moves inside without the camera
- `bounds?: TTilemap | TRect | null`: Where the camera may look, so it never shows past the edges of the level: a map, which is its
- `lead?: { x, y }`: How far ahead of the target it looks, in pixels: `{ x: 24, y: 0 }` shows more of what is to the
- `smooth?: number`: How long it takes to catch up, in seconds, rather than following at once: `0.2` glides. `0`, the
- `round?: boolean`: Whether the camera stays on whole pixels, so pixel art does not shimmer between two. `true` by
- `rooms?: TRect[] | { width, height } | null`: The level cut into rooms, as the action games of the consoles did: the camera keeps inside the
- `scroll?: number`: How long the scroll from one room to the next takes, in seconds. `0` cuts straight to it.
- `onRoom?: (room: TRect, from: TRect | null) => void`: Called when the target walks into another room, as the scroll starts: to bring back the

#### TCameraTarget (type, from `nacatamalon`)

```ts
type TCameraTarget: TBox | TSprite | TText | TNineSlice | { x, y }
```

What a camera can follow: an object, something it draws, or a point that the game moves.

#### TFog (type, from `nacatamalon`)

```ts
type TFog
```

The fog a scene is seen through: things fade into `color` as they get further from the camera.

It is what the Nintendo 64 is remembered by, and the way every game of the era hid how near it
stopped drawing. Worked out per corner and smeared across each triangle, like the light, which is
how that hardware did it too. Everything here is read every frame, so changing a field fades the
fog in, thickens it or turns it to dusk with nothing set up again.

- `type: 'fog'`
- `id: string`
- `color: TColor`: What things fade into. Usually the background, or the fog shows as a wall.
- `near: number`: How far from the camera the fog begins. Nearer than this is clear.
- `far: number`: How far from the camera it is complete. Further than this is all fog.
- `distance: boolean`: Whether things fade by how far they are from the camera (`near` to `far`). `false` leaves only
- `height: { enabled, top, bottom }`: Fog by height: below `top` things start to fade, and at `bottom` they are gone, however near.
- `enabled: boolean`: Whether it is drawn. Switching it off keeps the rest, so it can be switched back on.

#### TFogOptions (type, from `nacatamalon`)

```ts
type TFogOptions
```

What `useFog` is asked for. Everything has a default, so `useFog({ color })` is a fog.

- `color?: TColorOption`: What things fade into. Default black.
- `near?: number`: How far from the camera the fog begins. Default `10`.
- `far?: number`: How far from the camera it is complete. Default `100`.
- `distance?: boolean`: Whether things fade by how far they are from the camera. Default `true`.
- `height?: { top, bottom, enabled }`: Fog by height: it begins at `top` and is complete at `bottom` (default `-10` under `top`).
- `enabled?: boolean`: Whether it is drawn. Default `true`.

#### TRay (type, from `nacatamalon`)

```ts
type TRay
```

A ray in the world: where it starts, and the direction it goes in, one unit long.

- `origin: { x, y, z }`
- `direction: { x, y, z }`

#### TScreenPoint (type, from `nacatamalon`)

```ts
type TScreenPoint
```

Where a point of the world lands on the screen.

`x` and `y` are in the game's pixels, from the top-left corner, the same ones a sprite is placed
in. `depth` goes from 0 at the camera's near plane to 1 at its far one. `behind` says the point is
behind the camera, where `x` and `y` mean nothing and whatever was going to be drawn there should
not be.

- `x: number`
- `y: number`
- `depth: number`
- `behind: boolean`

### Masks

Keeping where something is drawn to a rectangle or to the shape of another object: hard, inverted or soft.

#### useMask (function, from `nacatamalon`)

```ts
useMask(options: TMaskOptions) => TMask
```

Makes a mask: what keeps objects, and everything under them, to a rectangle or to the shape of
another object, so nothing of them is drawn, or touched by the pointer, outside it. It masks
nothing until it is `add`ed to something, and can be added to as many objects as want it: a map
and its enemies, and not the HUD.

- `rect` keeps to a rectangle in the space of each object it is added to. Square to the screen it
costs nothing at all.
- `by` keeps to the shape of another object: everything that object and the ones under it draw,
sprites by their pictures, shapes, texts, added together. That object is not drawn itself
unless `showShape`, and it moves, turns, grows and is tweened like any other.
- `channel: 'luminance'` reads a black and white cut-out: white shows, black cuts.
- `invert` draws only outside the shape. `soft` lets the shape through as much as it is there, a
soft edge or a fade, and is the only kind that costs a picture of its own; the others cut hard,
in or out at half way, which is what pixel art wants.

An object has one mask. One mask inside another keeps to both: put each on its own object, one
inside the other. `zIndex` sorts what is masked as it always did. A mask goes away with the object
it was asked for in, and everything it masked is drawn whole again.

- `options: TMaskOptions`: What it keeps to (`rect` or `by`) and how.

#### TMask (type, from `nacatamalon`)

```ts
type TMask
```

A mask: what keeps the objects it is added to, and everything under them, to a rectangle or to
the shape of another object. Made with `useMask`, it masks nothing until it is `add`ed to
something, and everything it says can be changed at any moment, for all of them at once.

- `type: 'mask'`
- `id: string`
- `rect: TMaskRect | null`: The rectangle it keeps to, or `null`. A shape (`by`) wins over it.
- `by: TBox | null`: The object whose shape it keeps to, or `null`.
- `channel: TMaskChannel`
- `soft: boolean`
- `invert: boolean`
- `showShape: boolean`
- `enabled: boolean`
- `add: `
- `remove: `
- `has: `
- `boxes: typeOperator`: Every object it masks, in the order they were added.

#### TMaskChannel (type, from `nacatamalon`)

```ts
type TMaskChannel: 'alpha' | 'luminance'
```

Which part of a shape's picture says where it lets things through: its `'alpha'` (what is
transparent cuts), or its `'luminance'` (white shows, black cuts), for a cut-out painted in black
and white.

#### TMaskOptions (type, from `nacatamalon`)

```ts
type TMaskOptions
```

How a mask is made: `useMask`'s options. A mask keeps things to a `rect`, or to the shape of
another object (`by`), one of the two.

- `rect?: TMaskRect`: A rectangle to keep things to, in the space of each object the mask is added to. When it stays
- `by?: TBox`: The object whose shape keeps things in: everything it draws, and everything under it, added
- `channel?: TMaskChannel`: Which part of the shape's pictures says where it lets things through. `'alpha'` by default.
- `soft?: boolean`: `false`, the default, cuts hard: a pixel is in or out, at half way, which is what pixel art
- `invert?: boolean`: Draws only outside the shape instead of inside. `false` by default.
- `showShape?: boolean`: Draws the shape as well as masking with it: a window's frame that also keeps in what is
- `enabled?: boolean`: Whether it masks at all. `true` by default; `false` lets everything through for a while

#### TMaskRect (type, from `nacatamalon`)

```ts
type TMaskRect
```

A rectangle in the space of the object it masks: where its own `x: 0, y: 0` is, turned and grown
with it.

- `x: number`
- `y: number`
- `width: number`
- `height: number`

### Filters

Effects run over what one object, and everything under it, draws: an outline, a glow, a drop shadow, or any screen effect kept to that object.

#### dropShadow (function, from `nacatamalon/effects`)

```ts
dropShadow(options: { offset, radius, color, amount }) => TBuiltinPostEffect<{ offset, radius, color, amount }>
```

A shadow of everything an object draws, fallen a little way off behind it: a panel lifted off the
screen, a character over the ground. Made for a filter (`useFilter`); on the whole screen there is
nothing see-through for it to fall on.

- `options: { offset, radius, color, amount }`: `offset` how far it falls, in game pixels, as `{ x, y }` (down and to the right are

#### glow (function, from `nacatamalon/effects`)

```ts
glow(options: { color, radius, intensity }) => TBuiltinPostEffect<{ color, radius, intensity }>
```

A soft light of one colour around everything an object draws, behind it: a coin that shines, a
selected card, a ghost. Made for a filter (`useFilter`); on the whole screen there is nothing
see-through for it to shine into. For what is bright on the screen to glow, `bloom()`.

The object's shape is blurred at half its size and laid behind it in the colour, so the light is
wide and cheap.

- `options: { color, radius, intensity }`: `color` of the light; `radius` about how far it reaches, in game pixels;

#### outline (function, from `nacatamalon/effects`)

```ts
outline(options: { color, thickness }) => TBuiltinPostEffect<{ color, thickness }>
```

Draws a line of one colour around everything an object draws, outside it: one line around a
character made of pieces, not one per piece. Made for a filter (`useFilter`); on the whole screen
there is nothing see-through for it to draw around.

Read pixel by pixel, so a contour of `1` around pixel art is one game pixel, hard, with no corner
left out. What is half see-through counts as much as it is there.

- `options: { color, thickness }`: `color` of the line; `thickness` how many game pixels wide it is, up to 8.

#### useFilter (function, from `nacatamalon/effects`)

```ts
useFilter(options: TFilterOptions) => TFilter
```

Makes a filter: effects run over what an object, and everything under it, draws, before it is laid
on the screen. A contour around a character made of pieces, one contour and not one per piece; a
glow around a coin; a shadow under a panel; a television whose screen is a `crt()`. It filters
nothing until it is `add`ed to something, and can be added to as many objects as want it, each
filtered on its own.

- `effects` are the engine's own spread in (`outline()`, `glow()`, `dropShadow()`, and every
screen-wide one: `blur()`, `crt()`, `paletteMatch()`…), one written in place, or a file, in
order, each reading what the one before it gave, as `usePostProcess` takes them.
- They work on what the object draws, with room around it for what they draw beyond it. `rect`
gives them a part of the screen instead, in the object's space: the television's screen, which
a `crt()` then curves as a whole.
- An effect keeps what is see-through see-through, unless drawing around it is what it is for.

Each object filtered costs a picture the size of the screen and the effects run over its part:
for many enemies with the same contour, hang them from one object and filter that. An object has
one filter; one inside another runs on its own part first. A mask on the same object keeps the
filtered picture to it, glow and all. `zIndex` sorts what is filtered as it always did, and the
pointer touches it as it is, not as it looks. A filter goes away with the object it was asked for
in, and everything it filtered is drawn as it is again.

Its radii and widths are pixels of the screen, so they keep their size when the camera zooms. A
model inside one keeps its depth: the rest of the scene hides it and is hidden by it pixel by pixel,
and what an effect draws round it (a glow's halo) takes the depth of the nearest part of it, so the
halo of a part that is hidden is hidden too. The halo is laid over the solid models drawn right
after it as well; one drawn later still, past a sprite or a see-through model, is drawn over it.
An effect that moves pixels (a wave) is still tested at the depth of where they were. `crt()` and `lcd()` fill the part they are given, as
a screen does: give them a `rect`. An effect that reads its last frame (`motionBlur`, `phosphor`)
reads the one it is handed instead, since an object's part of the screen moves and changes size.

- `options: TFilterOptions`: The effects, and what part of the screen they work on.

#### TFilter (type, from `nacatamalon/effects`)

```ts
type TFilter
```

A filter: effects run over what one object, and everything under it, draws, before it is laid on
the screen. A contour around a character made of pieces, a glow around a coin, a shadow under a
panel. Made with `useFilter`, it filters nothing until it is `add`ed to something; each object it
is added to is filtered on its own, and everything it says can be changed at any moment, for all
of them at once.

- `type: 'filter'`
- `id: string`
- `effects: TPostEffect[]`: The effects, in order. Their `uniforms` are read every frame, so writing a number into one
- `rect: TFilterRect | null`: The part of the screen the effects work on, in each object's space, or `null` for what it draws.
- `padding: number | null`: Pixels of room around what the object draws, or `null` for what the effects ask.
- `enabled: boolean`
- `add: `
- `remove: `
- `has: `
- `boxes: typeOperator`: Every object it filters, in the order they were added.

#### TFilterOptions (type, from `nacatamalon/effects`)

```ts
type TFilterOptions
```

How a filter is made: `useFilter`'s options.

- `effects: typeOperator`: The effects, in order, each reading what the one before it gave: the engine's own spread in
- `rect?: TFilterRect`: The part of the screen the effects work on, in the space of each object the filter is on: a
- `padding?: number`: Pixels of room around what the object draws, for effects to draw into. Left out, each of the
- `enabled?: boolean`: Whether it filters at all. `true` by default; `false` draws the objects as they are for a while

#### TFilterRect (type, from `nacatamalon/effects`)

```ts
type TFilterRect
```

A rectangle in the space of the object a filter is on: where its own `x: 0, y: 0` is, turned and
grown with it.

- `x: number`
- `y: number`
- `width: number`
- `height: number`

### Lighting

Directional, point, spot and ambient lights, and the two kinds of shadow the era affords.

#### useAmbientLight (function, from `nacatamalon`)

```ts
useAmbientLight(options: { color, intensity }) => TAmbientLight
```

The level everything is lit to before any lamp reaches it.

It has no place and no direction, so it shades nothing: it only decides how dark the dark side is. A scene with one of these and nothing else is lit flat, which is a look and not a mistake.

One level for the whole scene: whichever object asks for it, it does not move with it. A scene
that asks for more than one keeps the last made, and says so: a cave can set its own on the way
in.

In a view (`createSpriteTexture` with `sees: 'world'`) it sets the level in all the view shows, and
the scene's lamps still light it unless the view brings lamps of its own: a minimap at
`intensity: 1` reads in full while the game around it is dark. In a picture that sees `'3d'`, it
sets the level of the room it shows.

- `options: { color, intensity }`: Its colour and how strong it is.

#### useAmbientLight2d (function, from `nacatamalon`)

```ts
useAmbientLight2d(options: { color, intensity }) => TAmbientLight2d
```

How bright a 2D world is where no lamp reaches, and in what colour, apart from any 3D light.
White at `0.2` by default. A scene says it once: with more than one, the last is the one used,
and that is said once.

- `options: { color, intensity }`

#### useBlobShadow (function, from `nacatamalon`)

```ts
useBlobShadow(options: TBlobShadowOptions) => TMesh
```

Puts a soft dark disc on the ground under something and keeps it there.

This is the cheap shadow, the one the consoles this engine is aimed at actually used: not a
shadow of the shape, a smudge under it. A game can afford fifty on hardware that could not afford
one real one, because it is not a feature of the renderer at all. It is a disc wearing a
shader that fades from the middle, drawn down the ordinary path with everything else: no extra
pass, no texture, nothing reserved.

It takes nothing from the lamps in the scene, on purpose. A shadow that took the light would
brighten as a lamp moved towards it, which is the opposite of what a shadow does.

It does not cast a real shadow of its own either, which matters the moment a scene has a light
that casts one: a flat disc lying on the floor would otherwise be drawn into the shadow map and
come back as a shadow of a shadow, a dark ring that nothing in the scene explains.

Call it in the scene body, next to the thing it goes under, and then forget it: it follows on its
own. It reads that thing's own placement, so the two belong together (either both loose in the
scene, or both inside the same group), which is what writing them next to each other already
gives you.

- `options: TBlobShadowOptions`: What it goes under, and how it looks.

#### useDirectionalLight (function, from `nacatamalon`)

```ts
useDirectionalLight(options: TLightOptionsBase & { shadowArea, shadowDistance }) => TDirectionalLight
```

A light from far away, like the sun.

It has a direction and no place: everything is lit by it the same, however far away it is. This is the one a scene should start with, because it is the one that shows the shape of things.

It shines along its own facing, so it is aimed by turning it, the way a camera is.

The light belongs to this object, so putting it inside something that moves means it moves with
it: a lamp carried by a character, headlights on a car. An object gives off as many as it asks
for, each placed by its own `transform`: a car's two headlights are two calls in the car, and the
lamps of a room two calls in the scene. An object of its own (`useSpawn`) is for a lamp that is a
thing of its own, one that breaks, swings or is switched off by itself.

A scene holds thirty-two at a time, and each model is lit by the eight of them that reach it most,
so a corridor of torches is lit all along. Past thirty-two they are dropped in the order they were
made, with a warning.

- `options: TLightOptionsBase & { shadowArea, shadowDistance }`: Its colour, how strong it is, and where it points.

#### useLight2d (function, from `nacatamalon`)

```ts
useLight2d(options: TLight2dOptions) => TLight2d
```

A lamp for a 2D world: it lights the sprites, texts, nine-slices, maps and particles around it,
with no normal map. It goes where the box holding it goes, so one made in the player's component is a
lantern the player carries.

A world with lamps starts dark, and `useAmbientLight2d` says how dark; once lit, it stays lit until
its scene ends, even with every lamp gone. A world can have as many lamps as it likes: what the
camera shows is lit by the eight nearest its middle that reach it, so a long level can have a torch
every few steps. The HUD (`useScreenSpace`) and what is left out with `useUnlit2d` are never lit.
It casts no shadow. Change what it gives back to move it, recolour it or dim it, from the next
frame.

- `options: TLight2dOptions`

#### usePointLight (function, from `nacatamalon`)

```ts
usePointLight(options: TLightOptionsBase & { range }) => TPointLight
```

A lamp: it has a place, shines every way, and fades out with distance.

`range` is how far it reaches; past that it lights nothing. A torch on a wall, a fire, a bulb.

The light belongs to this object, so putting it inside something that moves means it moves with
it: a lamp carried by a character, headlights on a car. An object gives off as many as it asks
for, each placed by its own `transform`: a car's two headlights are two calls in the car, and the
lamps of a room two calls in the scene. An object of its own (`useSpawn`) is for a lamp that is a
thing of its own, one that breaks, swings or is switched off by itself.

A scene holds thirty-two at a time, and each model is lit by the eight of them that reach it most,
so a corridor of torches is lit all along. Past thirty-two they are dropped in the order they were
made, with a warning.

- `options: TLightOptionsBase & { range }`: Its colour, how strong it is, and where it points.

#### useRectLight (function, from `nacatamalon`)

```ts
useRectLight(options: TLightOptionsBase & { range, width, height }) => TRectLight
```

A panel: a lamp shaped like a rectangle, which shines from its front only.

`width` and `height` are its size along its own X and Y, and `range` is how far it reaches from
the nearest point of the panel. It faces down unless turned, like a light in the ceiling, and is
aimed by turning it like a torch. A strip light, a window, a screen, a sign.

It does not cast shadows: a panel has no single point to look from.

The light belongs to this object, so putting it inside something that moves means it moves with
it: a lamp carried by a character, headlights on a car. An object gives off as many as it asks
for, each placed by its own `transform`: a car's two headlights are two calls in the car, and the
lamps of a room two calls in the scene. An object of its own (`useSpawn`) is for a lamp that is a
thing of its own, one that breaks, swings or is switched off by itself.

It is one of the thirty-two a scene holds, and one of the eight that light a model when it reaches it.

- `options: TLightOptionsBase & { range, width, height }`: Its colour, how strong it is, its size, and where it points.

#### useSpotLight (function, from `nacatamalon`)

```ts
useSpotLight(options: TLightOptionsBase & { range, angle, penumbra }) => TSpotLight
```

A torch: a lamp that only shines inside a cone.

`angle` is half the width of the cone and `penumbra` is how soft its edge is, from a hard rim at `0` to a beam that fades all the way in at `1`. Like a sun it is aimed by turning it.

The light belongs to this object, so putting it inside something that moves means it moves with
it: a lamp carried by a character, headlights on a car. An object gives off as many as it asks
for, each placed by its own `transform`: a car's two headlights are two calls in the car, and the
lamps of a room two calls in the scene. An object of its own (`useSpawn`) is for a lamp that is a
thing of its own, one that breaks, swings or is switched off by itself.

A scene holds thirty-two at a time, and each model is lit by the eight of them that reach it most,
so a corridor of torches is lit all along. Past thirty-two they are dropped in the order they were
made, with a warning.

- `options: TLightOptionsBase & { range, angle, penumbra }`: Its colour, how strong it is, and where it points.

#### useUnlit2d (function, from `nacatamalon`)

```ts
useUnlit2d(on: boolean) => void
```

Leaves what is created here out of the scene's 2D lights: it keeps its own colours however dark
the world is, and still moves with the camera, unlike the HUD. For what gives off its own light:
a fire, a neon sign, the glass of a lamp, the eyes of something in the dark, the sparks of a
torch (particles are lit like the rest, so dust in a cave stays dark).

It covers everything this part of the scene creates and everything created under it, however
deep. In a scene with no 2D lights it changes nothing. Each lit scene that has something left out
draws it through one view more, of the fifteen a frame has for scenes; with none left, it is drawn
lit, with the rest of the scene. Set it with this hook, not by writing the box's `unlit2d`: the hook
is what tells the scene to keep a view for it.

- `on: boolean`: `false` undoes it. Default `true`.

#### TAmbientLight (type, from `nacatamalon`)

```ts
type TAmbientLight
```

Light with no source: the level everything is lit to before any lamp reaches it.

It has no place and no direction, so it never shades anything: it only decides how dark the dark
side is. A scene with one of these and nothing else is flat, which is a look and not a mistake.

- `type: 'ambient'`
- `id: string`
- `color: TColor`
- `intensity: number`

#### TAmbientLight2d (type, from `nacatamalon`)

```ts
type TAmbientLight2d
```

How bright a scene's 2D world is where no lamp reaches, as `useAmbientLight2d` makes it. With more
than one, the last is the one used. A world with lamps and none of these is black where they do
not reach; one with neither keeps its own colours, until it has had one: then it stays lit until
its scene ends. The HUD (`useScreenSpace`) and what is left out (`useUnlit2d`) are never lit, and the
scene's 3D ambient light has nothing to do with it.

- `type: 'ambient-2d'`
- `id: string`
- `color: TColor`
- `intensity: number`

#### TBlobShadowOptions (type, from `nacatamalon`)

```ts
type TBlobShadowOptions
```

What `useBlobShadow` can be asked for. Only the thing it goes under is needed.

- `target: TMesh`: What it goes under. Every frame it moves to wherever that is.
- `groundY?: number`: The height of the ground it lies on. Default `0`.
- `radius?: number`: How wide it is, in the same units as everything else in the scene. Default `0.5`.
- `color?: TColorOption`: What colour it is. Default black: a shadow is darkness, faded by `opacity` and not tinted.
- `opacity?: number`: How dark it is in the middle, `0` to `1`. Default `0.5`.
- `softness?: number`: How soft the rim is, from a cut edge at `0` to a fade that starts at the centre at `1`. Default `0.4`.
- `followHeight?: boolean`: Whether it grows and fades as the thing above it rises. Default `false`, which is a shadow of
- `zIndex?: number`: Draw order within the scene. Default `0`.

#### TDirectionalLight (type, from `nacatamalon`)

```ts
type TDirectionalLight: TLightBase & { type, shadowArea, shadowDistance }
```

A light from far away, like the sun: it has a direction and no position, and it reaches
everything with the same strength.

- `type: 'directional'`
- `shadowArea?: number`: How far, in world units, the shadows reach. Default `12`.
- `shadowDistance?: number`: How far back along its own beam it sits when it looks at the scene. Default `20`.
- `id: string`
- `color: TColor`
- `intensity: number`
- `ambient: number`
- `transform: TTransform3d`: Where it is and which way it faces. Lights shine along their own -Z.
- `castShadow?: boolean`: Whether this is the light that casts shadows. Default `false`.
- `shadowBias?: number`: How far the shadow test is pushed away from the surface, towards the light, in the world's
- `shadowStrength?: number`: How dark it goes, `0` to `1`. Default `1`. Lower leaves the caster's light partly on.

#### TLight (type, from `nacatamalon`)

```ts
type TLight: TDirectionalLight | TPointLight | TSpotLight | TRectLight | TAmbientLight | TLight2d | TAmbientLight2d
```

Any light. A box holds one, and a scene holds as many as it has boxes with one.

#### TLight2d (type, from `nacatamalon`)

```ts
type TLight2d
```

A lamp of a 2D world, as `useLight2d` makes it: a circle of light over the sprites, texts, maps and
particles around it, with no normal map. It goes where its box goes, and its box's scale does not
change how far it reaches. What a camera shows is lit by the eight nearest its middle.

`radius` is in pixels of the world. `falloff` is how it fades towards the edge: `1` evenly, `2`
dimmer sooner, `0` not at all, a circle with a hard edge.

- `type: 'point-2d'`
- `id: string`
- `color: TColor`
- `intensity: number`
- `transform: TTransform3d`
- `radius: number`
- `falloff: number`

#### TLight2dOptions (type, from `nacatamalon`)

```ts
type TLight2dOptions
```

What a 2D lamp is: its colour, how strong, where, how far it reaches and how it fades.

- `color?: TColorOption`: The colour of its light, white by default.
- `intensity?: number`: How strong it is. `1` by default; `0` switches it off.
- `transform?: { x, y }`: Where it is from the box holding it, in pixels.
- `radius?: number`: How far it reaches, in pixels of the world. `128` by default; `0` or less lights nothing.
- `falloff?: number`: How it fades towards the edge: `1` evenly, `2` dimmer sooner, `0` not at all, a circle with a

#### TLightBase (type, from `nacatamalon`)

```ts
type TLightBase
```

What every light has: a colour, how strong it is, and how much of it reaches the parts it does
not shine on directly.

`ambient` is that last one: light bounces off everything in a real room, so a surface facing away
from the lamp is dim and not black. Without it a scene looks like it is in space.

- `id: string`
- `color: TColor`
- `intensity: number`
- `ambient: number`
- `transform: TTransform3d`: Where it is and which way it faces. Lights shine along their own -Z.
- `castShadow?: boolean`: Whether this is the light that casts shadows. Default `false`.
- `shadowBias?: number`: How far the shadow test is pushed away from the surface, towards the light, in the world's
- `shadowStrength?: number`: How dark it goes, `0` to `1`. Default `1`. Lower leaves the caster's light partly on.

#### TLightOptionsBase (type, from `nacatamalon`)

```ts
type TLightOptionsBase
```

Options every light with a place in the world shares.

- `color?: TColorOption`
- `intensity?: number`: How strong it is. Default `1`.
- `ambient?: number`: How much of it reaches what it does not shine on. Default `0.35`.
- `transform?: Partial<Pick<TTransform3d, 'x' | 'y' | 'z' | 'rotationX' | 'rotationY'>>`: Where it is and which way it faces, as it is read afterwards (`light.transform`): `x`, `y`,
- `castShadow?: boolean`: Whether this is the one light that casts shadows. Default `false`. See `TLight`.
- `shadowBias?: number`: How far the shadow test is pushed off the surface, towards the light, in the world's units.
- `shadowStrength?: number`: How dark it goes, `0` to `1`. Default `1`.

#### TPointLight (type, from `nacatamalon`)

```ts
type TPointLight: TLightBase & { type, range }
```

A lamp: it has a place, shines every way, and fades out by `range`.

- `type: 'point'`
- `range: number`
- `id: string`
- `color: TColor`
- `intensity: number`
- `ambient: number`
- `transform: TTransform3d`: Where it is and which way it faces. Lights shine along their own -Z.
- `castShadow?: boolean`: Whether this is the light that casts shadows. Default `false`.
- `shadowBias?: number`: How far the shadow test is pushed away from the surface, towards the light, in the world's
- `shadowStrength?: number`: How dark it goes, `0` to `1`. Default `1`. Lower leaves the caster's light partly on.

#### TRectLight (type, from `nacatamalon`)

```ts
type TRectLight: TLightBase & { type, range, width, height }
```

A panel: a lamp shaped like a rectangle, which shines from its front only.

`width` and `height` are its size, along its own X and Y. It shines along its own -Z, like a sun
and a torch, and fades out by `range`, measured from the nearest point of the panel. A ceiling
light, a window, a screen, a sign.

- `type: 'rect'`
- `range: number`
- `width: number`
- `height: number`
- `id: string`
- `color: TColor`
- `intensity: number`
- `ambient: number`
- `transform: TTransform3d`: Where it is and which way it faces. Lights shine along their own -Z.
- `castShadow?: boolean`: Whether this is the light that casts shadows. Default `false`.
- `shadowBias?: number`: How far the shadow test is pushed away from the surface, towards the light, in the world's
- `shadowStrength?: number`: How dark it goes, `0` to `1`. Default `1`. Lower leaves the caster's light partly on.

#### TSpotLight (type, from `nacatamalon`)

```ts
type TSpotLight: TLightBase & { type, range, angle, penumbra }
```

A torch: a lamp that only shines inside a cone.

`angle` is half the width of that cone and `penumbra` is how soft its edge is, from a hard rim at
`0` to a beam that fades all the way to the middle at `1`.

- `type: 'spot'`
- `range: number`
- `angle: number`
- `penumbra: number`
- `id: string`
- `color: TColor`
- `intensity: number`
- `ambient: number`
- `transform: TTransform3d`: Where it is and which way it faces. Lights shine along their own -Z.
- `castShadow?: boolean`: Whether this is the light that casts shadows. Default `false`.
- `shadowBias?: number`: How far the shadow test is pushed away from the surface, towards the light, in the world's
- `shadowStrength?: number`: How dark it goes, `0` to `1`. Default `1`. Lower leaves the caster's light partly on.

### Geometry

The shapes models are made of.

#### createGeometry (function, from `nacatamalon`)

```ts
createGeometry(data: TGeometryData) => TGeometry
```

Makes a shape of your own, corner by corner: a terrain from a height map, a trail, a ramp, a
star pushed out into a solid, anything the shape hooks do not make. Hand it to `createMesh`.

Only `positions` has to be there. Without `indices`, every three corners are a triangle; without
`normals`, the facings are worked out from the triangles, smooth, or one per triangle with `flat`;
without `uvs`, every corner reads the picture's top left; without `colors`, white. `uvs` can also
be worked out by name, `'box'` for a rock or a building, `'planar'` for a terrain, `'cylinder'`
or `'sphere'`, with `uvScale` to lay the picture by the units the corners are given in rather than once over it.

It belongs to the object that made it. Unlike a shape hook's, which every object asking for the
same size shares, this one is the object's own: it goes when the object goes, and a mesh elsewhere
still wearing it then draws nothing. Change it while the game runs with `updateGeometry`.

A shape made this way cannot be written in a scene document, since it is not a recipe but the
corners themselves: make it in code, in the body of the object that wears it.

- `data: TGeometryData`: Its corners, and whatever else it has of them.

#### getGeometry (function, from `nacatamalon`)

```ts
getGeometry(key: string) => TGeometry | null
```

Resolves the name a shape is kept under, the same string a model's `geometry` field holds, to the
shape itself, or `null` when nothing has been loaded under it.

A model looks this up on its own, which was enough while a model was the only thing that could
name a shape. A collider names one too (`{ shape: 'hull', geometry: 'crate' }`), and that
happens outside the engine, in whichever package does the simulating, so the lookup has to be
reachable rather than re-derived against the running game's insides by every one of them.

It answers while a scene is being built, which is when a collider is made, and that is the whole
of what it is for: there is no running game to ask outside of that, and the answer is `null`.

- `key: string`: The name the shape is kept under.

#### updateGeometry (function, from `nacatamalon`)

```ts
updateGeometry(geometry: TGeometry, data: TGeometryData) => void
```

Changes a shape made by `createGeometry` while the game runs: a terrain being dug, water worked
out on this side, a trail behind a ship, a rope or a cloth. Give it the shape again, whole, as
`createGeometry` took it; what is left out is worked out again the same way, and picture places
asked for by name are worked out again from where the corners are now.

It is written over what the card already holds while it fits, and in new room when it grows past
it, with some to spare so a shape that grows a little each frame is not made again each frame.
Every mesh wearing it shows the change on the next frame. Call it anywhere, a `useUpdate` included.

Only for a shape of your own: one from a shape hook is shared by every object that asked for the
same size, so changing it would change all of them, and it is refused.

- `geometry: TGeometry`: The shape `createGeometry` made.
- `data: TGeometryData`: Its corners now, and whatever else it has of them.

#### useCircleGeometry (function, from `nacatamalon`)

```ts
useCircleGeometry(options: TCircleGeometryOptions) => TGeometry
```

A flat disc facing up.

Built and put on the graphics card at once, so it is ready the moment you have it: nothing is
fetched and nothing is waited for. Hand it to `createMesh` as its `geometry`.

Asking for the same size twice gives back the same shape, so a hundred of them cost one shape and
a hundred placements.

- `options: TCircleGeometryOptions`: Its size, how smooth it is, and what to keep it under.

#### useConeGeometry (function, from `nacatamalon`)

```ts
useConeGeometry(options: TConeGeometryOptions) => TGeometry
```

A cone with a base.

Built and put on the graphics card at once, so it is ready the moment you have it: nothing is
fetched and nothing is waited for. Hand it to `createMesh` as its `geometry`.

Asking for the same size twice gives back the same shape, so a hundred of them cost one shape and
a hundred placements.

- `options: TConeGeometryOptions`: Its size, how smooth it is, and what to keep it under.

#### useCubeGeometry (function, from `nacatamalon`)

```ts
useCubeGeometry(options: TCubeGeometryOptions) => TGeometry
```

A cube, or any box shape: as wide, tall and deep as you ask.

Built and put on the graphics card at once, so it is ready the moment you have it: nothing is
fetched and nothing is waited for. Hand it to `createMesh` as its `geometry`.

Asking for the same size twice gives back the same shape, so a hundred of them cost one shape and
a hundred placements.

- `options: TCubeGeometryOptions`: Its size, how smooth it is, and what to keep it under.

#### useCylinderGeometry (function, from `nacatamalon`)

```ts
useCylinderGeometry(options: TCylinderGeometryOptions) => TGeometry
```

A tube with a lid and a base.

Built and put on the graphics card at once, so it is ready the moment you have it: nothing is
fetched and nothing is waited for. Hand it to `createMesh` as its `geometry`.

Asking for the same size twice gives back the same shape, so a hundred of them cost one shape and
a hundred placements.

- `options: TCylinderGeometryOptions`: Its size, how smooth it is, and what to keep it under.

#### useIcoSphereGeometry (function, from `nacatamalon`)

```ts
useIcoSphereGeometry(options: TIcoSphereGeometryOptions) => TGeometry
```

A ball whose triangles are all about the same size. The one to reach for when the surface is going to be lit or bent, because the globe-style ball has slivers at its poles and they show.

Built and put on the graphics card at once, so it is ready the moment you have it: nothing is
fetched and nothing is waited for. Hand it to `createMesh` as its `geometry`.

Asking for the same size twice gives back the same shape, so a hundred of them cost one shape and
a hundred placements.

- `options: TIcoSphereGeometryOptions`: Its size, how smooth it is, and what to keep it under.

#### useLatheGeometry (function, from `nacatamalon`)

```ts
useLatheGeometry(options: TLatheGeometryOptions) => TGeometry
```

A shape turned on a lathe: half an outline, its profile, spun about the upright axis into a solid,
as a turner shapes a chair leg or a potter a vase. A bottle, a glass, a vase, a chess piece, a
column, a barrel, a lamp, a bowling pin, in a handful of points.
 the profile        what it makes
  ●                    ___
   ●                  (   )
  ●         →          \ /
  ●                    | |
 ●                    /___\

Asking for the same profile twice gives back the same shape, so a shelf of a hundred bottles costs
one shape and a hundred placements. The profile goes from the bottom up; given the other way, the
shape is turned inside out and seen only from within.

- `options: TLatheGeometryOptions`: The profile, and how many faces round.

#### usePlaneGeometry (function, from `nacatamalon`)

```ts
usePlaneGeometry(options: TPlaneGeometryOptions) => TGeometry
```

A flat sheet lying like a floor, facing up. Cut it into squares to bend it later.

Built and put on the graphics card at once, so it is ready the moment you have it: nothing is
fetched and nothing is waited for. Hand it to `createMesh` as its `geometry`.

Asking for the same size twice gives back the same shape, so a hundred of them cost one shape and
a hundred placements.

- `options: TPlaneGeometryOptions`: Its size, how smooth it is, and what to keep it under.

#### useSphereGeometry (function, from `nacatamalon`)

```ts
useSphereGeometry(options: TSphereGeometryOptions) => TGeometry
```

A ball built as lines of latitude, the way a globe is drawn. Its squares bunch up at the poles, which is the price of a picture that wraps round it neatly.

Built and put on the graphics card at once, so it is ready the moment you have it: nothing is
fetched and nothing is waited for. Hand it to `createMesh` as its `geometry`.

Asking for the same size twice gives back the same shape, so a hundred of them cost one shape and
a hundred placements.

- `options: TSphereGeometryOptions`: Its size, how smooth it is, and what to keep it under.

#### useTorusGeometry (function, from `nacatamalon`)

```ts
useTorusGeometry(options: TTorusGeometryOptions) => TGeometry
```

A ring, like a doughnut.

Built and put on the graphics card at once, so it is ready the moment you have it: nothing is
fetched and nothing is waited for. Hand it to `createMesh` as its `geometry`.

Asking for the same size twice gives back the same shape, so a hundred of them cost one shape and
a hundred placements.

- `options: TTorusGeometryOptions`: Its size, how smooth it is, and what to keep it under.

#### TCircleGeometryOptions (type, from `nacatamalon`)

```ts
type TCircleGeometryOptions
```

What useCircleGeometry can be asked for. Everything is optional.

- `radius?: number`: Default `0.5`, so it is one across.
- `segments?: number`: How round it is. Default `32`.
- `key?: string`: What to keep it under. By default the size is the name, so two of different sizes never get

#### TConeGeometryOptions (type, from `nacatamalon`)

```ts
type TConeGeometryOptions
```

What useConeGeometry can be asked for. Everything is optional.

- `radius?: number`: Default `0.5`.
- `height?: number`: Default `1`.
- `segments?: number`: How round it is. Default `32`.
- `key?: string`: What to keep it under. By default the size is the name, so two of different sizes never get

#### TCubeGeometryOptions (type, from `nacatamalon`)

```ts
type TCubeGeometryOptions
```

What useCubeGeometry can be asked for. Everything is optional.

- `width?: number`: Across. Default `1`.
- `height?: number`: Up. Default `1`.
- `depth?: number`: Towards the viewer. Default `1`.
- `key?: string`: What to keep it under. By default the size is the name, so two of different sizes never get

#### TCylinderGeometryOptions (type, from `nacatamalon`)

```ts
type TCylinderGeometryOptions
```

What useCylinderGeometry can be asked for. Everything is optional.

- `radius?: number`: Default `0.5`.
- `height?: number`: Default `1`.
- `segments?: number`: How round it is. Default `32`.
- `key?: string`: What to keep it under. By default the size is the name, so two of different sizes never get

#### TGeometry (type, from `nacatamalon`)

```ts
type TGeometry
```

A shape on the graphics card: its corners, and the order they make triangles in.

It is an asset, like an image: built or loaded once, kept under a name, and shared by every model
that shows it. A hundred crates are a hundred placements over one of these.

The corners are eight numbers each, in one run: where the corner is, which way the surface faces
there, and which part of the picture it shows. One run rather than three, because the graphics
card reads a corner at a time and three separate runs would make it jump about for every one.

- `type: 'geometry'`
- `key: string`: What it is kept under. A shape asked for twice with the same size is the same one.
- `status: TLoadStatus`: A built shape is ready the moment you have it. One that came out of a file is `'loading'`
- `source: TGeometrySource | null`: How it was made, so it can be made again from a file. `null` for a shape built from raw
- `vertexCount: number`: How many corners, and how many triangle points.
- `indexCount: number`
- `bounds: { min, max } | null`: The box it fits inside, in its own units.
- `positions: Float32Array | null`: Where each corner is, kept on this side: the graphics card's copy cannot be read back.
- `indices: Uint16Array | Uint32Array | null`: Which corners make up each triangle, three at a time: the companion of `positions`, kept on
- `vertexBuffer: IBuffer | null`
- `indexBuffer: IBuffer | null`
- `indexType: 'uint16' | 'uint32'`: How wide each triangle point is. Small shapes use the narrow one, which is half the memory;
- `skinBuffer: IBuffer | null`: Which bones move each corner and how much, for a shape that came off a rig. `null` for
- `colorBuffer: IBuffer | null`: The colour painted on each corner, four bytes apiece (red, green, blue, and how opaque),
- `detailBuffer: IBuffer | null`: For each corner, the way across its surface the picture runs (its tangent, with which hand

#### TGeometryData (type, from `nacatamalon`)

```ts
type TGeometryData
```

A shape given corner by corner: what `createGeometry` makes and `updateGeometry` changes.

Every list is flat, a few numbers per corner one after another, and may be a plain array or a
typed one.

- `positions: ArrayLike<number>`: Where each corner is: `x, y, z`, three numbers a corner. The only list that has to be there.
- `indices?: ArrayLike<number>`: Which corners make each triangle, three a triangle, counter-clockwise as seen from the side
- `normals?: ArrayLike<number>`: Which way the surface faces at each corner: `x, y, z`, three numbers a corner. Left out, it is
- `uvs?: ArrayLike<number> | TUvProjection`: Where in the picture each corner is: `u, v`, two numbers a corner, `0, 0` the top left. Or the
- `uvScale?: number`: With `uvs` worked out by name, how many times the picture is laid every unit the corners are
- `colors?: ArrayLike<number>`: The colour painted on each corner: `r, g, b, a` from 0 to 1, four numbers a corner, multiplied
- `flat?: boolean`: Every triangle faces its own way, with hard edges between: the faceted look. The facings are

#### TGeometrySource (type, from `nacatamalon`)

```ts
type TGeometrySource: { kind, width, height, depth } | { kind, width, depth, widthSegments, depthSegments } | { kind, radius, segments } | { kind, radius, segments, rings } | { kind, radius, subdivisions } | { kind, radius, height, segments } | { kind, radius, height, segments } | { kind, radius, tube, radialSegments, tubularSegments } | { kind, profile, segments } | { kind, src, node, occurrence }
```

How a shape is made, when it was made rather than drawn by an artist.

A recipe and not the corners themselves, which is the whole point: a cube is a handful of numbers
of description or a few hundred of vertex data, and the description is what a person edits, what a
diff shows and what survives the engine changing how it builds a cube.

It is kept on the shape rather than worked out again from the key it was cached under. The key
does spell the recipe for every shape this engine builds (`cube:1:1:1`), but only until somebody
passes a key of their own, and then reading it back would be reading a name as if it were a fact.

Every field but `gltf`'s `src` may be left out, and leaving one out means its default, exactly as
calling `useCubeGeometry({})` does. So the smallest cube is `{ kind: 'cube' }`.

#### TIcoSphereGeometryOptions (type, from `nacatamalon`)

```ts
type TIcoSphereGeometryOptions
```

What useIcoSphereGeometry can be asked for. Everything is optional.

- `radius?: number`: Default `0.5`.
- `subdivisions?: number`: How many times each triangle is split. Default `2`. Each one costs four times as many.
- `key?: string`: What to keep it under. By default the size is the name, so two of different sizes never get

#### TLatheGeometryOptions (type, from `nacatamalon`)

```ts
type TLatheGeometryOptions
```

What useLatheGeometry can be asked for.

- `profile: ReadonlyArray<{ x, y }>`: Half the outline, from the bottom up: for each point, `x` is how far it is from the axis and
- `segments?: number`: How many faces round. Few is the faceted look of the time; many is round. Default `32`.
- `key?: string`: What to keep it under. By default the profile and the faces round are the name, so two the same

#### TPlaneGeometryOptions (type, from `nacatamalon`)

```ts
type TPlaneGeometryOptions
```

What usePlaneGeometry can be asked for. Everything is optional.

- `width?: number`: Across. Default `1`.
- `depth?: number`: Away. Default `1`.
- `widthSegments?: number`: How many squares across. Default `1`.
- `depthSegments?: number`: How many away. Default `1`.
- `key?: string`: What to keep it under. By default the size is the name, so two of different sizes never get

#### TSphereGeometryOptions (type, from `nacatamalon`)

```ts
type TSphereGeometryOptions
```

What useSphereGeometry can be asked for. Everything is optional.

- `radius?: number`: Default `0.5`, so it is one across.
- `segments?: number`: Around. Default `32`.
- `rings?: number`: From pole to pole. Default `16`.
- `key?: string`: What to keep it under. By default the size is the name, so two of different sizes never get

#### TTorusGeometryOptions (type, from `nacatamalon`)

```ts
type TTorusGeometryOptions
```

What useTorusGeometry can be asked for. Everything is optional.

- `radius?: number`: From the middle to the middle of the tube. Default `0.5`.
- `tube?: number`: How thick the tube is. Default `0.2`.
- `radialSegments?: number`: Around the ring. Default `32`.
- `tubularSegments?: number`: Around the tube. Default `16`.
- `key?: string`: What to keep it under. By default the size is the name, so two of different sizes never get

#### TUvProjection (type, from `nacatamalon`)

```ts
type TUvProjection: 'planar' | 'box' | 'cylinder' | 'sphere'
```

How a shape's picture places are worked out from where its corners are, rather than given.

- `'planar'`: seen from above, `x` across and `z` down: a terrain, a floor, a field.
- `'box'`: each triangle from the side it faces most, as the six faces of a box: a rock, a
building, a wall.
- `'cylinder'`: once round the upright axis and down its height: a trunk, a column, a barrel.
- `'sphere'`: round by longitude and down by latitude: a planet, a head.

### Animation

What moves over time: sprite frames, skeletons and their clips, and tweens.

#### getSpriteAnimation (function, from `nacatamalon`)

```ts
getSpriteAnimation(sprite: TSprite) => TSpriteAnimation | null
```

The animator a sprite already has, or `null` when nothing animates it.

A sprite from a scene file can arrive already moving: the file says which run it starts on, and
the scene started it. This is how a behaviour reaches that same animator to change the run,
instead of making a second one that fights the first for the same sprite every frame. If more
than one was made for a sprite, it is the last.

- `sprite: TSprite`: The sprite to ask about.

#### parseMotionDoc (function, from `nacatamalon/files`)

```ts
parseMotionDoc(raw: unknown, src: string) => TMotionDoc
```

Reads the contents of a `.motion` file, already parsed from JSON, into a motion.

It refuses what it could not play (another format, a track with no keys, a key with no time) with
an error that names the file. The keys of each track come back in order of time, and a curve with a
name the engine does not have is warned about and moved along in a straight line. Whether each
track's box and field exist is not known here: that is answered when it plays in a scene.

- `raw: unknown`: The file's JSON.
- `src: string`: Where it came from, for the errors.

#### useMotion (function, from `nacatamalon`)

```ts
useMotion(motion: TMotionDoc | TMotionFile, options: TMotionOptions) => TMotionHandle
```

Plays a motion on the scene this is called in: a `.motion` file of values moved over time by keys
(a camera sweeping the field, a title coming in), made in an editor's motion panel.

It moves on the scene's own clock, a frame at a time, so it stops when the scene is paused and
goes slower or faster with it. Each track finds its box by id, and by name when no box has that
id; a track whose box or field is not there is warned about once and moves nothing. On the way, it
plays the sounds, changes the scene and calls the marks it is played past.

A scene document plays one with a `motion` component; this is the same thing from code, for a
behaviour that decides when.

- `motion: TMotionDoc | TMotionFile`: A motion from `useLoadMotion`, or a `TMotionDoc` written in code.
- `options: TMotionOptions`: Whether it starts by itself, loops, and how fast.

#### useSkeletalAnimation (function, from `nacatamalon`)

```ts
useSkeletalAnimation(model: TAnimatable, options: TSkeletalAnimationOptions) => TSkeletalAnimation
```

Makes a placed model move: plays the movements its file came with, and eases between them.

A rigged model is a skin stretched over bones. A movement turns the bones, and the skin follows,
which is why one model can walk, run and fall over without three copies of it existing.

It moves the model `createModel` gave back, and only that one. Every model placed from a file
has bones of its own, so five enemies of one file can walk, jump and stand still each on their
own, each with an animator of its own.

It can be asked for a movement before the file has arrived, which is the ordinary case: the
model comes back still loading, so `play` is remembered and honoured the moment it shows up.

`fade` is what makes it look like a character rather than a demo. Asked to change movement over
half a second, it turns the bones from where one has them to where the other does; cutting
straight across snaps a leg from behind to in front in a single frame.

`only` plays a movement on part of the body, the rest carrying on with what it was doing: a
second animator on the same model, its bones named, shows over the first. The last one made goes
on top. With nothing playing it leaves those bones to the one below, and a movement that ends
hands them back, eased by its `fade`.

- `model: TAnimatable`: The placed model, from `createModel`.
- `options: TSkeletalAnimationOptions`: Which movement to start on, and how it plays.

#### useSpriteAnimation (function, from `nacatamalon`)

```ts
useSpriteAnimation(sprite: TSprite, options: TSpriteAnimationOptions) => TSpriteAnimation
```

Plays a sprite's pictures in order, so it walks, attacks or spins.

A sheet already holds every picture and a sprite already shows one of them: animating is
deciding which one, and when. That is all this does, once per frame, so the renderer never
learns that animation exists.

The sprite has to come from a sheet (`createSprite({ atlas })`), since that is where the frames
are. Call it while the scene is being built, like every hook, and keep what it returns to change
clips later.

- `sprite: TSprite`: The sprite to animate.
- `options: TSpriteAnimationOptions`: The runs it knows, which one to start with, and how fast.

#### useTween (function, from `nacatamalon`)

```ts
useTween() => TTweenStarter
```

Declares tweens: a number, or the fields of an object, going from one value to another over time,
so something can slide in, grow, fade or bounce without you writing the arithmetic for it every
frame.

`useTween()` animates nothing by itself: it gives you back a function that declares tweens. A
tween declared does not move either. It waits, and moves each time you call its `play()`, as many
times as you like: declared once while the scene is built, played on every hit.

- Played again once it is over, it makes the same trip again from the same start. Fields
moved `by` an amount move it again from wherever they are.
- `tween.chain(a, b, c)` plays them one after another, and `tween.all(a, b)` at the same
time. Both are tweens too, so one goes into the other. What goes into them is played by them:
you play the chain, not its steps.

What is animated is a plain number. The tween gives it to you every frame in `onUpdate` and you
put it wherever it belongs, which is why the same hook moves a sprite, widens a loading bar or
mixes two colours. Or it is given a `target` and the path to its fields, and moves them itself.

Tweens belong to the scene that declared them: they freeze while it is paused, they run in slow
motion when the game does, and they are gone when it leaves. There is nothing to clean up.

#### TAnimatable (type, from `nacatamalon`)

```ts
type TAnimatable
```

Anything with bones and movements for them, which is all `useSkeletalAnimation` reads: a model
placed from a file (`createModel`), or a body a plugin gives bones of its own.

`model.clips` are the movements it knows, by name, read every time so movements that arrive later
can be played at once; `model.status` says whether they have all arrived (left out, they have).
`skeletons` are the bones they turn: the animator writes each one's `pose`.

- `model: { clips, status }`
- `skeletons: typeOperator`

#### TAnimationClip (type, from `nacatamalon`)

```ts
type TAnimationClip
```

One named run of frames: which pictures, how fast, and whether it starts again at the end.

- `frames: typeOperator`: The frames of the sheet to show, in order. They may repeat and go backwards.
- `fps?: number`: Frames per second. Default `12`, which is the usual pace for hand-drawn movement.
- `loop?: boolean`: Whether it starts again at the end. Default `true`. A `repeat` says how many times instead.
- `repeat?: number`: How many extra times it runs after the first, as a tween's `repeat`: `2` shows it three
- `yoyo?: boolean`: Whether each time it runs again it comes back the other way, as a tween's `yoyo`: `[0, 1, 2, 3]`
- `events?: typeOperator`: Moments inside the run worth being told about: the picture a blow lands on, a footstep.
- `next?: string`: The run to play when this one ends, for one that does not loop: a punch that drops back to

#### TClipEvent (type, from `nacatamalon`)

```ts
type TClipEvent
```

Something that happens at a point inside a movement, rather than at its end: the moment a sword
becomes dangerous, a foot meets the ground, a spell leaves the hand.

It is a cue, not a consequence: the movement says when, and the game decides what that means.
That split is what lets the same swing be a hit, a parry or a miss without the animation knowing
which, and it is why nothing here names a function.

Used by both halves of the engine. What `at` counts differs, because the two kinds of movement
are measured differently, and each says so where it is taken.

- `at: number`: Where in the movement it happens. Its unit is the one the movement is measured in.
- `name: string`: What it is called, which is what the listener is handed.

#### TJointPose (type, from `nacatamalon`)

```ts
type TJointPose
```

Where one bone sits relative to the bone above it: a move, a turn and a size.

Kept as three separate things rather than one matrix because that is how animation writes it: a
clip turns a shoulder without touching where it is, and two poses can only be mixed sensibly one
part at a time.

- `t: [number, number, number]`
- `r: TQuat`
- `s: [number, number, number]`

#### TMotionAttachment (type, from `nacatamalon`)

```ts
type TMotionAttachment
```

A motion a box carries: its file and how it was asked to play. What a saved scene writes as a
`motion` component; never where it had got to.

- `type: 'motion'`
- `id: string`
- `file: TMotionFile`
- `autoplay: boolean`
- `loop: boolean | null`: `null` for what the file says.
- `speed: number`

#### TMotionDoc (type, from `nacatamalon`)

```ts
type TMotionDoc
```

A `.motion` file: values of a scene moved over time by keys, and what happens on the way. What an
editor's motion panel writes, and what `useMotion` (or a `motion` component) plays.

Movement placed by hand lives here rather than in a behaviour: a camera sweeping a field, a logo
coming in, a menu opening. Movement that answers the game (a ball's flight, an enemy chasing) is a
behaviour's, with `useTween` or the update.

- `format: 'nacatamalon-motion'`
- `version: number`
- `duration: number`: How long it lasts, in seconds. A key after it is never reached.
- `loop?: boolean`: Whether it starts again from the beginning once it ends. Default `false`.
- `tracks: TMotionTrack[]`
- `events: TMotionEvent[]`

#### TMotionEase (type, from `nacatamalon`)

```ts
type TMotionEase: typeOperator
```

The name of a curve a motion moves along between two keys: one of the engine's easing curves,
`'linear'` for the same speed all the way.

#### TMotionEvent (type, from `nacatamalon`)

```ts
type TMotionEvent: { t, kind, box, name, component } | { t, kind, scene, transition } | { t, kind, name }
```

Something that happens at a moment of a motion, once each time it is played past.

- `sound` plays a sound, music or song a box carries, by the box and the component's id.
- `scene` changes to another scene, covered by a transition if one is given.
- `mark` only has a name: a behaviour listens for it with `on(name, …)` on the motion it plays.

#### TMotionHandle (type, from `nacatamalon`)

```ts
type TMotionHandle
```

The controls of a motion playing on a box, handed back by `useMotion`.

- `play: `
- `pause: `
- `resume: `
- `stop: `
- `seek: `
- `on: `
- `onEnd: `
- `time: number`: Where it is, in seconds from the beginning.
- `duration: number`: How long it lasts, `0` until the file is here.
- `playing: boolean`

#### TMotionKey (type, from `nacatamalon`)

```ts
type TMotionKey
```

One key of a track: the value it has at a moment.

- `t: number`: When, in seconds from the start of the motion.
- `v: number | boolean`: What the value is then. A number moves towards the next key; `true` or `false` (whether
- `ease?: TMotionEase`: The curve of the stretch that arrives at this key, from the one before. Default `'linear'`.

#### TMotionOptions (type, from `nacatamalon`)

```ts
type TMotionOptions
```

How `useMotion` is asked to play.

- `autoplay?: boolean`: Starts as soon as the file is here. Default `false`.
- `loop?: boolean`: Starts again from the beginning once it ends. Default: what the file says.
- `speed?: number`: How fast it plays: `2` twice as fast, `0.5` at half speed. Default `1`.
- `id?: string`: Its id, the one a saved scene writes it under. Made up when not given.

#### TMotionTrack (type, from `nacatamalon`)

```ts
type TMotionTrack
```

One value moved over time: which box, which part of it, which field, and its keys.

A box is named the way the scene document names it, by its id, so the track and the box an editor
shows are the same one whatever the box is called. The name is kept beside it for reading and as a
second way in: a copy of a reusable box has another id each time it is placed.

- `box: string`: The box, by its id in the scene document. Empty for a track of a screen effect (`post`).
- `name?: string`: Its name, for reading, and to find it by when no box has that id.
- `component?: string`: One of its components, by id, when the field is the component's (a sprite's tint, a camera's
- `post?: string`: One of the game's screen effects (its post chain), by its name, when the field is the
- `path: string`: The field, as a path of dots: `x`, `scaleY`, `rotation` on the box; `zoom` or `transform.x` on
- `keys: TMotionKey[]`: In order of time, at least one.

#### TMotionTransition (type, from `nacatamalon`)

```ts
type TMotionTransition
```

The cover a `scene` event changes under: one of the engine's transitions, by name, how long it
takes in seconds, and what that one is given in code. A file that says `ms`, as they did before, is
read in milliseconds. The five whose names say cross or push go from one scene to the other with
no cover between, so they take no colour.

Each field past `duration` belongs to the transitions named beside it, and a file that gives one to
another is refused: a `direction` on a fade would be a choice somebody made that does nothing.

- `type: 'fade' | 'wipe' | 'iris' | 'pixelate' | 'dissolve' | 'crossfade' | 'push' | 'crossWipe' | 'crossIris' | 'crossDissolve'`
- `duration?: number`: Seconds, covering and uncovering together, or crossing. Left out, what the transition takes in
- `color?: TColor`: Every one that covers: what the screen is covered with. Black unless the file says.
- `ease?: TMotionEase`: Every one: the curve each half moves along, or the one run of one that crosses, by the name a
- `direction?: TWipeDirection`: A wipe and a cross wipe: which way the edge travels. A push: which way both scenes travel.
- `center?: { x, y }`: An iris: where it closes on, in screen fractions, `{ x: 0, y: 0 }` the top left corner. A
- `blocks?: number`: Pixelate: how many blocks across the short side of the screen at the worst of it, 2 or more.
- `mask?: string`: A dissolve and a cross dissolve, which need it: the picture whose greys say the order the
- `softness?: number`: A dissolve and a cross dissolve: how soft the edge of the shape is, from `0`, hard, to `1`.

#### TSkeletalAnimation (type, from `nacatamalon`)

```ts
type TSkeletalAnimation
```

The handle on a model's movement: what it is doing, and how to change it.

- `play: `
- `pause: `
- `stop: `
- `setSpeed: `
- `setLoop: `
- `onEnd: `
- `onRepeat: `
- `onEvent: `
- `playing: boolean`
- `clip: string | null`
- `time: number`: How far into it, in seconds.

#### TSkeletalAnimationOptions (type, from `nacatamalon`)

```ts
type TSkeletalAnimationOptions
```

What to ask a model's bones to do.

- `play?: string`: Which movement to start on. Left out, it takes the first the file has.
- `loop?: boolean`: Whether a movement goes round for ever. Default `true`. A `repeat` says how many times instead.
- `repeat?: number`: How many extra times a movement runs after the first, as a tween's `repeat`: `2` is three
- `yoyo?: boolean`: Whether each time a movement runs again it comes back the other way, as a tween's `yoyo`: a
- `speed?: number`: How fast, as a multiple. Default `1`.
- `fade?: number`: How long to take easing into a new movement, in seconds. Default `0`, which is a cut.
- `only?: typeOperator`: Plays only on these bones, each with all that hangs from it, by the names the file gives them,
- `clips?: Readonly<Record<string, TSkeletalClipOptions>>`: How each movement plays, by its name in the file: whether it goes round, how many times, what

#### TSkeletalClip (type, from `nacatamalon`)

```ts
type TSkeletalClip
```

One named movement of a skeleton: walking, running, being hit.

Named apart from a sprite's `TAnimationClip`, which is a run of pictures. The two are the same
idea in the two halves of the engine and share nothing at all.

It is plain data and holds nothing that is running. Two characters playing the same clip at
different moments share this and keep their own time, which is what makes a crowd cheap.

- `type: 'clip'`
- `name: string`
- `duration: number`: How long it lasts, in seconds: the latest key any of its runs reaches.
- `channels: TSkeletalChannel[]`
- `samplers: TSkeletalSampler[]`

#### TSkeletalClipOptions (type, from `nacatamalon`)

```ts
type TSkeletalClipOptions
```

How one movement of a model plays: what its sprite twin, `TAnimationClip`, says on the clip, said
here because a glTF file has nowhere to put it. The format says what moves and when, and
nothing about whether it goes round or what any moment of it means. Somebody has to decide, once
for each movement, and this is where.

Left out, a field is the animator's own (`loop`, `repeat`, `yoyo` of the options).

- `loop?: boolean`: Whether it goes round for ever. A `repeat` says how many times instead.
- `repeat?: number`: How many extra times it runs after the first: `2` is three times, then it ends, telling
- `yoyo?: boolean`: Whether each time it runs again it comes back the other way.
- `events?: typeOperator`: Moments inside it worth being told about: the instant a blow lands, a foot meets the ground, a
- `next?: string`: The movement to play when this one ends, eased into by the animator's `fade`: an attack that

#### TSkeleton (type, from `nacatamalon`)

```ts
type TSkeleton
```

The bones of a model, and how they are standing right now.

It comes out of a file that was rigged: the bones themselves, which bone hangs from which, how
they stand at rest, and what it takes to undo that rest position. Animation writes `pose` and
nothing else; the graphics card reads `jointMatrices` and nothing else. That line down the middle
is what keeps playing an animation, posing a skeleton and drawing it three separate problems.

Bones are a flat list, parents before their children, so working out where each one ended up
is one pass from the front with nothing to recurse into.

- `type: 'skeleton'`
- `key: string`: What it is kept under: the model's name, plus which of its skeletons this is.
- `parentIndex: Int32Array`: The bone each one hangs from, `-1` for one that hangs from nothing.
- `jointNames: typeOperator`: What the file calls each bone, `''` for one it does not name.
- `bindPose: TJointPose[]`: How it stands when nothing is playing.
- `pose: TJointPose[]`: How it stands now. The only thing an animation of the whole body writes.
- `laid: () => TJointPose[] | null`: How it stands with what animators of part of the body lay over `pose`, or `null` for a skeleton
- `inverseBindMatrices: Float32Array`: What undoes the rest position, 16 numbers a bone.
- `jointMatrices: Float32Array`: What the graphics card reads, 16 numbers a bone. Worked out from `pose` each frame.
- `rootMatrices: Float32Array | null`: What sits above the bones that hang from nothing, 16 numbers a bone, or `null` when nothing
- `posedOn: number`: Which frame `jointMatrices` was last worked out on, so the pieces of a model, which share its

#### TSpriteAnimation (type, from `nacatamalon`)

```ts
type TSpriteAnimation
```

The controls for one animated sprite, returned by `useSpriteAnimation`.

- `play: `
- `pause: `
- `stop: `
- `setSpeed: `
- `playing: boolean`: Whether a run is going right now. False while it is paused or stopped, and once a clip that
- `clip: string | null`: Which run is on, or `null` if none ever started.
- `frame: number`: Which frame of the sheet is showing.
- `onEnd: `
- `onRepeat: `
- `onEvent: `

#### TSpriteAnimationOptions (type, from `nacatamalon`)

```ts
type TSpriteAnimationOptions
```

What `useSpriteAnimation` is asked for.

- `clips: Readonly<Record<string, TAnimationClip>>`: The runs this sprite knows, by name: walking, attacking, dying.
- `play?: string`: Which one to start with. Left out, the sprite stays on the frame it was created with.
- `speed?: number`: A multiplier on every clip's speed: `2` twice as fast, `0.5` half. Default `1`.

#### TTweenFields (type, from `nacatamalon`)

```ts
type TTweenFields: conditional
```

The fields of an object a tween can move, as a path to each: `{ transform: { x: 200 }, tint: { a: 0 } }`.
Only the ends that are numbers can be named; everything on the way is optional.

#### TTweenHandle (type, from `nacatamalon`)

```ts
type TTweenHandle
```

A tween, declared: the controls to play it, as many times as you like, and to hear how it goes.
Nothing moves until `play()`.

What a tween inside a `tween.chain` or a `tween.all` hears is still told (its `onEnd` when its
step ends), but only the chain or the group is played, paused or stopped: those calls on what is
inside it are an error.

- `play: `
- `pause: `
- `resume: `
- `stop: `
- `finish: `
- `reset: `
- `onEnd: `
- `onRepeat: `
- `onStop: `
- `playing: boolean`: Whether it is playing: from `play` until it ends or is stopped, waiting out its `delay` or

#### TTweenOptions (type, from `nacatamalon`)

```ts
type TTweenOptions
```

One trip of a number: where it starts, where it ends, how long it takes and what to do with the
value along the way. Declared by `useTween`'s function, and moved by its `play()`.

A tween animates a number, not an object, and that is on purpose: `onUpdate` hands you the
value and you decide where it goes. Into an `x`, into a `width`, into the amount of one colour
mixed into another. Anything made of numbers can be animated without the tween knowing it exists.

When what moves is a field of an object (a sprite's `x`, its `tint.a`), `TTweenTargetOptions`
says it shorter, with no `onUpdate` to write.

- `from: number`: The value it starts at.
- `to: number`: The value it ends at.
- `duration: number`: How many seconds one trip takes.
- `ease?: TEaseFn`: The shape of the movement: a function that takes how far along the trip is, from `0` to `1`,
- `delay?: number`: Seconds to wait before it starts moving. Default `0`.
- `repeat?: number`: How many extra times it runs after the first trip, so `1` means it goes twice. Default `0`.
- `loop?: boolean`: Whether it goes round for ever, until it is stopped or finished. `repeat` is for a number of times.
- `yoyo?: boolean`: Whether each repeat comes back the other way, there and back. Default `false`.
- `onUpdate: (value: number) => void`: Called every frame with the value right now. This is where you put it to use.

#### TTweenStarter (type, from `nacatamalon`)

```ts
TTweenStarter(options: TTweenOptions) => TTweenHandle
TTweenStarter(options: TTweenTargetOptions<T>) => TTweenHandle
```

Declares one tween and hands back its controls. This is what `useTween` gives you, and you can
call it as many times as you like: while the scene is being built, or later from a key press.
What it declares moves when it is played.

- `options: TTweenOptions`

#### TTweenTargetOptions (type, from `nacatamalon`)

```ts
type TTweenTargetOptions
```

One trip of the fields of an object: the object, where its fields end or how far they go, and how
long they take.

`target` is the thing itself (the sprite) and `to` the path down to each number, not the object
that holds the numbers. The path is followed again every frame, so a `tint` swapped for a new one
halfway is the one that goes on fading.

- `to` says where each field ends. Played again, it makes the same trip from the same start.
- `by` says how far each field goes. Played again, it goes that far again from wherever the
field is: a knockback, a jump, a step along.

- `target: T`: What is moved: a sprite, a text, a box, any object with numbers in it.
- `to?: TTweenFields<T>`: Where each field ends, by its path from `target`. It needs this, `by`, or both, for different
- `by?: TTweenFields<T>`: How far each field goes from where it starts, by its path from `target`.
- `from?: TTweenFields<T>`: Where each field starts. A field left out starts where it is the first time the tween moves,
- `duration: number`: How many seconds one trip takes.
- `ease?: TEaseFn`: The shape of the movement, as in `TTweenOptions`. Default `linear`.
- `delay?: number`: Seconds to wait before it starts moving. Default `0`.
- `repeat?: number`: How many extra times it runs after the first trip. Default `0`.
- `loop?: boolean`: Whether it goes round for ever, until it is stopped or finished. `repeat` is for a number of times.
- `yoyo?: boolean`: Whether each repeat comes back the other way, there and back. Default `false`.

### Materials

Surfaces, custom shaders, and the file format shaders are written in.

#### parseShaderFile (function, from `nacatamalon/shader`)

```ts
parseShaderFile(source: string, src: string) => TParsedShader
```

Reads a shader file: comments that say what it is, and plain shader code that says what it does.

The header is read from the whole file, deliberately outside the language halves. What the
file is for and what knobs it has are one fact about the file, and a fact written twice is two
facts that can drift apart.

A file stays valid code in its own language, so an editor colours it and a card could compile it
with the engine's own preamble in front. That is the point of a comment-based header: nothing here
invents a format that only this engine can open.

- `source: string`: The text of the file.
- `src: string`: Where it came from, used only so an error can name it.

#### parseShaderSource (function, from `nacatamalon/shader`)

```ts
parseShaderSource(source: string, src: string) => TParsedShader
```

Reads a shader file with the reader its extension asks for: a `.shader` is a graph of nodes, as
JSON, and anything else is WGSL with a header. Both give the same shape, so nothing after this
knows which it was.

It is the loader's own reader, open to tools: an editor that lets a material pick a shader needs
the knobs it declares, whichever of the two ways it was written.

- `source: string`: The file's text.
- `src: string`: Its path: the extension decides how it is read, and warnings name it.

#### TCreateMaterial (type, from `nacatamalon`)

```ts
type TCreateMaterial: (options: TMaterialShaderOptions & { shader, texture, key, tint, emissive, emissiveTexture, occlusion, specular, shininess, alpha, transparent, smooth, wrap, maps, vertexSnap, affine, cull, alphaCutoff, lighting, steps } & { uniforms }) => TShaderHalf & { shader, texture, tint, emissive, emissiveTexture, occlusion, specular, shininess, alpha, transparent, smooth, wrap, maps, vertexSnap, affine, cull, alphaCutoff, lighting, steps } & { uniforms }
```

The ways to call `createMaterial`: which kind of material comes back follows from `shader`, so a
model's material can only be handed to a model and a sprite's only to a sprite.

Given `uniforms` (which `compileShader` always gives), the material's `uniforms` is typed as
there, so a parameter is animated with `material.uniforms.glow = 1` and no `?? {}`.

Plain overloads and no type parameter, so it is called from inside a function of your own that
takes its options as a type parameter, or with options typed as any kind of material, as readily
as with the options written in place; an option it does not have is still refused there. Options
that could be either kind give a material that could be either, to narrow by its `shader`.

#### TMaterial (type, from `nacatamalon`)

```ts
type TMaterial: TSpriteMaterial | TMeshMaterial
```

A surface, a shader, or both, that one or many things can be drawn with.

Two shapes and not one with half its fields unused, because a field that means nothing half the
time is a field that lies. Which one you have is written on `shader`, so asking narrows it.

A material is an atomic value: nothing caches it and nothing looks it up by name. Two things
share one by being handed the same one.

#### TMaterialMap (type, from `nacatamalon`)

```ts
type TMaterialMap
```

One extra picture a material carries for its shader to read, besides the picture it is drawn
with: a reflection, a mask, a fine detail, a ripple.

The shader reads it by name, `sampleMap_<name>(uv)`, with the same wrap and filtering choices the
main picture has.

- `texture: TTexture`: The picture. One still loading reads as white until it lands; what wears it is drawn meanwhile.
- `wrap?: TTextureWrap | { u, v }`: What the picture does past its edge. Default `'repeat'`.
- `smooth?: boolean`: How it is read between pixels. Omitted, the material's own `smooth` decides.

#### TMaterialOcclusion (type, from `nacatamalon`)

```ts
type TMaterialOcclusion
```

How much of the light from all around reaches each part of a model's surface: a crease, an
armpit, the gap between two pieces, where little of the room can see in.

Its picture's red is how open a place is, white wide open and black shut in. It darkens only the
light from all around (the scene's `ambient`), never a lamp's or the sun's: a crease turned to the
sun is lit, and the same crease in the shade is darker than the open surface beside it. It is what
a model file calls its occlusion, the shading its author saw in the modelling program, and one
picture with the roughness and metalness in its other channels serves as it is.

- `texture: TTexture`: The picture, read in its red. One still loading reads as white, wide open, until it lands.
- `strength?: number`: How much of it is applied, `0` none to `1` all. Default `1`.
- `uv?: 'uv' | 'uv2'`: Which picture coordinate it is laid out by: `'uv'`, the one the colour picture uses, or
- `wrap?: TTextureWrap | { u, v }`: What the picture does past its edge, one word for both ways or one each: `'repeat'`,

#### TMaterialOptions (type, from `nacatamalon`)

```ts
type TMaterialOptions: TSpriteMaterialOptions | TMeshMaterialOptions
```

What `createMaterial` is asked for. `shader` decides which of the two you are making.

#### TMaterialShader (type, from `nacatamalon`)

```ts
type TMaterialShader: 'sprite2d' | 'mesh3d' | 'post'
```

Which family of built-in shader a material plugs into, and so what its hooks are handed.

`'sprite2d'` is handed a colour already sampled and already tinted, and the flat square's place on
it. `'mesh3d'` is handed a surface and everything the light knew about it, and may also move the
corner before any of that happens. `'post'` is handed the finished frame.

A screen-wide effect takes the same hooks as a sprite, on purpose: a CRT over one sprite and a
CRT over the whole picture are the same arithmetic, so they should be the same file.

#### TMaterialShaderOptions (type, from `nacatamalon`)

```ts
type TMaterialShaderOptions
```

The shader half of what a material can be asked for, which both kinds share.

Writing the source here is the quick way in, for an effect that lives with the scene that uses it.
`effect` is the other way: a file, which several scenes can share and which an editor can open.
Giving both is a contradiction, and the file wins.

- `name?: string`: A name, which is what a warning about this material will call it.
- `fragment?: string`: The fragment hook, in WGSL.
- `fragmentGlsl?: string`: The same hook in GLSL, without which the effect is WebGPU only: on WebGL2 the material draws
- `vertex?: string`: The vertex hook, in WGSL: `fn vertex(pos: vec3<f32>, ctx: VertContext) -> vec3<f32>`, where the
- `vertexGlsl?: string`: The same, in GLSL, without which the corners are not moved on WebGL2. `compileShader` gives
- `uniforms?: TUniformValues`: The knobs, with the values they start at. Their kinds are read off these, and fixed from then
- `effect?: TShader | string`: A file from `useLoadShader`, or the name one was loaded under.

#### TMeshMaterial (type, from `nacatamalon`)

```ts
type TMeshMaterial: TShaderHalf & { shader, texture, tint, emissive, emissiveTexture, occlusion, specular, shininess, alpha, transparent, smooth, wrap, maps, vertexSnap, affine, cull, alphaCutoff, lighting, steps }
```

A material for models: a surface, and optionally a shader over it.

Here the surface is the material's, because that is what a hundred crates want to share. They
are one shape and one surface and a hundred placements, and a colour per crate would be a
parameter nobody asked for.

That is the other half of the split: in three dimensions the material owns the look, in two the
object does.

- `shader: 'mesh3d'`
- `texture: TTexture | null`: The picture on it, or `null` for a plain surface of its tint.
- `tint: TColor`: Multiplies the picture. White leaves it alone, and is the whole colour without one.
- `emissive: TColor`: Light it gives off by itself, which no lamp has to reach. Black is none.
- `emissiveTexture?: TTexture | null`: A picture the glow comes through, multiplied by `emissive`: white where it glows, black where
- `occlusion?: TMaterialOcclusion | null`: Where the light from all around is kept out of its creases. See `TMaterialOcclusion`.
- `specular: TColor`: The colour of its shine. Black is a surface that does not shine at all.
- `shininess: number`: How tight the shine is: higher is a smaller, harder highlight.
- `alpha: number`: How solid it is, `0` to `1`. Multiplies the tint's own.
- `transparent?: boolean`: Drawn as see-through even at full `alpha`: for a shader that works out its own alpha, or a
- `smooth?: boolean`: How the picture is read between pixels. Omitted, the game decides.
- `wrap?: TTextureWrap | { u, v }`: What the picture does past its edge: `'repeat'` (the default) tiles it, `'mirror'` tiles it
- `maps?: Record<string, TMaterialMap>`: Extra pictures for its shader to read, by name, up to four: `sampleMap_<name>(uv)` in the
- `vertexSnap?: boolean | number`: Its corners land on a coarse grid of the screen, the way the PlayStation drew them: a model
- `affine?: boolean`: Its picture is stretched straight across the screen, without correcting for depth, the way the
- `cull?: TCullMode`: Which side of its triangles is thrown away: `'back'`, `'front'` or `'none'` (see
- `alphaCutoff?: number`: Cuts its picture out instead of blending it: a pixel whose alpha is below this number is not
- `lighting?: TLightingLook`: The lighting look it was made with, kept so it can be written down and made again. Left out
- `steps?: typeOperator`: The steps it was made with, for the same reason. Left out when it has none.
- `id: string`
- `type: 'material'`
- `name: string | null`: A name for reading in a warning. Never looked up by: a material is a value, not a resource.
- `fragment: string | null`: The fragment hook in WGSL, which is what WebGPU compiles.
- `fragmentGlsl: string | null`: The same hook written again in GLSL, which is what WebGL2 compiles.
- `vertex: string | null`: The vertex hook in WGSL. Only a 3D material has one: a sprite is a flat square.
- `vertexGlsl: string | null`: The same, in GLSL.
- `uniforms: TUniformValues | null`: The knobs, or `null` when there is no shader of your own to turn.
- `uniformSig: TUniformSignature | null`: What kind each knob is. Fixed once, because the compiled shader was built against it.
- `effect: TShader | null`: The file this came from, or `null` when the source was written inline.

#### TMeshMaterialOptions (type, from `nacatamalon`)

```ts
type TMeshMaterialOptions: TMaterialShaderOptions & { shader, texture, key, tint, emissive, emissiveTexture, occlusion, specular, shininess, alpha, transparent, smooth, wrap, maps, vertexSnap, affine, cull, alphaCutoff, lighting, steps }
```

What `createMaterial` is asked for when the material is for models.

Everything about the surface is optional and defaults to a plain matt white one, so a material
that only carries an effect says only that.

- `shader: 'mesh3d'`: Which of the engine's shaders it is built on, its family: `'mesh3d'`, the one that lights
- `texture?: TTexture`: A picture for the surface, from `useLoadTexture`.
- `key?: string`: The name a picture was loaded under, for a material built where the picture is not at hand.
- `tint?: TColorOption`: Multiplies the picture, and is the whole colour without one. Default white.
- `emissive?: TColorOption`: Light it gives off by itself. Default none.
- `emissiveTexture?: TTexture`: A picture the glow comes through, multiplied by `emissive`: white where it glows, black where
- `occlusion?: TTexture | TMaterialOcclusion`: Where the light from all around is kept out of its creases, the shading a modelling program
- `specular?: TColorOption`: The colour of its shine. Default none, which is matt.
- `shininess?: number`: How tight that shine is. Default `32`.
- `alpha?: number`: How solid it is, `0` to `1`. Default `1`.
- `transparent?: boolean`: Drawn as see-through even at full `alpha`: for a shader that works out its own alpha, or a
- `smooth?: boolean`: Overrides the game's `smooth` for anything drawn with this.
- `wrap?: TTextureWrap | { u, v }`: What the picture does past its edge: `'repeat'` (the default for a model) tiles it, `'mirror'` tiles it
- `maps?: Record<string, TTexture | TMaterialMap>`: Extra pictures for the shader to read by name, up to four, each a texture or a texture with its
- `vertexSnap?: boolean | number`: Its corners land on a coarse grid of the screen, the way the PlayStation drew them: a model
- `affine?: boolean`: Its picture is stretched straight across the screen without correcting for depth, the way the
- `cull?: TCullMode`: Which side of its triangles is thrown away. `'back'` (the default) is a shape seen from
- `alphaCutoff?: number`: Cuts its picture out instead of blending it: a pixel whose alpha is below this number is not
- `lighting?: TLightingLook`: How the surface is lit, in place of the engine's light: `toonLighting()` (flat bands) or `unlitLighting()` (no
- `steps?: typeOperator`: What is done to the surface once it is lit, in order: `mangaDots()` (ink in the shadow),
- `name?: string`: A name, which is what a warning about this material will call it.
- `fragment?: string`: The fragment hook, in WGSL.
- `fragmentGlsl?: string`: The same hook in GLSL, without which the effect is WebGPU only: on WebGL2 the material draws
- `vertex?: string`: The vertex hook, in WGSL: `fn vertex(pos: vec3<f32>, ctx: VertContext) -> vec3<f32>`, where the
- `vertexGlsl?: string`: The same, in GLSL, without which the corners are not moved on WebGL2. `compileShader` gives
- `uniforms?: TUniformValues`: The knobs, with the values they start at. Their kinds are read off these, and fixed from then
- `effect?: TShader | string`: A file from `useLoadShader`, or the name one was loaded under.

#### TShaderHalf (type, from `nacatamalon`)

```ts
type TShaderHalf
```

What both kinds of material have: a shader of your own, and the knobs on it.

All four sources are `null` on a material that only describes a surface, which is every material
a game has until it asks for an effect.

- `id: string`
- `type: 'material'`
- `name: string | null`: A name for reading in a warning. Never looked up by: a material is a value, not a resource.
- `fragment: string | null`: The fragment hook in WGSL, which is what WebGPU compiles.
- `fragmentGlsl: string | null`: The same hook written again in GLSL, which is what WebGL2 compiles.
- `vertex: string | null`: The vertex hook in WGSL. Only a 3D material has one: a sprite is a flat square.
- `vertexGlsl: string | null`: The same, in GLSL.
- `uniforms: TUniformValues | null`: The knobs, or `null` when there is no shader of your own to turn.
- `uniformSig: TUniformSignature | null`: What kind each knob is. Fixed once, because the compiled shader was built against it.
- `effect: TShader | null`: The file this came from, or `null` when the source was written inline.

#### TSpriteMaterial (type, from `nacatamalon`)

```ts
type TSpriteMaterial: TShaderHalf & { shader, maps }
```

A material for flat things: a shader and nothing else.

The picture and the colour are not here, and that is the design. A sprite carries its own, and
carries them per sprite: they travel in the instance buffer next to its place and its size, so a
thousand sprites in one draw can be a thousand different colours. Putting the colour on something
shared would mean one of these per sprite to vary it, which is the opposite of what a material is
for. A sprite keeps its own texture and its own tint, and its material is only the effect over
the top.

- `shader: 'sprite2d'`
- `maps?: Record<string, TMaterialMap>`: Extra pictures for its shader to read, by name, up to four: `sampleMap_<name>(uv)` reads the
- `id: string`
- `type: 'material'`
- `name: string | null`: A name for reading in a warning. Never looked up by: a material is a value, not a resource.
- `fragment: string | null`: The fragment hook in WGSL, which is what WebGPU compiles.
- `fragmentGlsl: string | null`: The same hook written again in GLSL, which is what WebGL2 compiles.
- `vertex: string | null`: The vertex hook in WGSL. Only a 3D material has one: a sprite is a flat square.
- `vertexGlsl: string | null`: The same, in GLSL.
- `uniforms: TUniformValues | null`: The knobs, or `null` when there is no shader of your own to turn.
- `uniformSig: TUniformSignature | null`: What kind each knob is. Fixed once, because the compiled shader was built against it.
- `effect: TShader | null`: The file this came from, or `null` when the source was written inline.

#### TSpriteMaterialOptions (type, from `nacatamalon`)

```ts
type TSpriteMaterialOptions: TMaterialShaderOptions & { shader, maps }
```

What `createMaterial` is asked for when the material is for flat things.

There is nothing here about a picture or a colour, because those belong to the sprite. This is the
effect and its knobs, and nothing else.

- `shader?: 'sprite2d'`: Which of the engine's shaders it is built on, its family: `'sprite2d'`, the one that draws
- `maps?: Record<string, TTexture | TMaterialMap>`: Extra pictures for the shader to read by name, up to four, each a texture or a texture with its
- `name?: string`: A name, which is what a warning about this material will call it.
- `fragment?: string`: The fragment hook, in WGSL.
- `fragmentGlsl?: string`: The same hook in GLSL, without which the effect is WebGPU only: on WebGL2 the material draws
- `vertex?: string`: The vertex hook, in WGSL: `fn vertex(pos: vec3<f32>, ctx: VertContext) -> vec3<f32>`, where the
- `vertexGlsl?: string`: The same, in GLSL, without which the corners are not moved on WebGL2. `compileShader` gives
- `uniforms?: TUniformValues`: The knobs, with the values they start at. Their kinds are read off these, and fixed from then
- `effect?: TShader | string`: A file from `useLoadShader`, or the name one was loaded under.

#### TTextureWrap (type, from `nacatamalon`)

```ts
type TTextureWrap: 'repeat' | 'clamp' | 'mirror'
```

What a model's picture does past its edge.

A model's corners may ask for any part of the plane, not only the picture's own square: a ladder
of UVs from 0 to 20 across a field is how one small patch of grass covers a whole hillside, and it
is how nearly every level of the era was textured. `'repeat'` tiles the picture, `'mirror'` tiles
it flipped every other time so its seams meet themselves, and `'clamp'` stretches its last row and
column outwards.

Repeating is the default for a model, as it is in glTF. A sprite and a
map never repeat: they read one frame of a sheet, and the next frame over is not theirs to show.

#### TUniformSignature (type, from `nacatamalon`)

```ts
type TUniformSignature: Record<string, TUniformType>
```

What kind each of those parameters is.

Worked out once from the shape of the first values it was given, and fixed from then on: how
the numbers are laid out in memory depends on it, and the shader has already been compiled
against that layout.

#### TUniformType (type, from `nacatamalon`)

```ts
type TUniformType: 'f32' | 'vec2<f32>' | 'vec3<f32>' | 'vec4<f32>'
```

What kind a material's own parameter may be: one number, or two, three or four of them.

A small set on purpose. It covers what an effect is actually tuned with (a strength, a count, a
colour, an offset) and leaves out matrices and lists, which bring alignment rules that are all
cost and no use here. The one matrix a drawing needs is the one that puts it on screen, and that
already travels with the drawing rather than with its surface.

#### TUniformValues (type, from `nacatamalon`)

```ts
type TUniformValues: Record<string, number | number[]>
```

A material's own parameters, by name. One number, or a list of two to four.

Change one and the next frame shows it: the card is handed these again every draw, so animating a
strength is an assignment and nothing else.

### Shader composer

Writing a shader as typed values, and the node graph a .shader file holds.

#### compileShader (function, from `nacatamalon/shader`)

```ts
compileShader(asked: TShaderGraph<S>) => TCompiledShader<S>
```

Turns a composed graph into the fields a material takes, in both languages.

The result spreads straight into `createMaterial`, and whichever backend draws it takes the half
it speaks. Nothing at the call site names a backend, and that is the point: the values are
typed, so the second language is a second walk over the graph rather than a translation, and the
two halves cannot drift apart because nobody wrote either.

On the way it works out anything used twice only once, reads each leaf the way its stage offers
it (and refuses one read where it does not exist), gathers every parameter into `uniforms`, and
writes each noise function once.

The family comes back in the type, so the result spreads into a material for sprites or for
models and the material knows which it is.

A sprite shader needs `color` and has no vertex stage. A model shader takes `color`, `position`
or both, and a stage left out keeps the engine's own. `color` may be a `vec3` (alpha 1) or a
`vec4`; `position` is a `vec3`.

- `asked: TShaderGraph<S>`

#### compileShaderGraph (function, from `nacatamalon/shader`)

```ts
compileShaderGraph(doc: TShaderGraphDoc, src: string) => TParsedShader
```

Compiles a `.shader` file into what a material needs: its hooks in both languages, its
parameters and their types. The same shape reading a `.wgsl` gives, so nothing that uses a
shader file can tell the two apart.

It writes no shader code of its own. It turns the file into the same values the composer
functions build and hands them to `compileShader`, so a graph drawn on a canvas and the same
graph written in code cannot drift, and every check the composer functions make applies to a
wired graph too.

Only what reaches an output is built: a node nothing is wired to adds nothing, so a canvas with a
few loose nodes on it still compiles. A loop is caught and named. An output left unwired keeps
the engine's own for that stage, so a new file is valid and its material draws with the engine's
shader until something is connected.

- `doc: TShaderGraphDoc`: The graph, from `parseShaderGraph`.
- `src: string`: Where it came from, for the error messages.

#### composerAbs (variable, from `nacatamalon/shader`)

```ts
composerAbs: (a: TComposerInput) => TComposerNode
```

The value without its sign, lane by lane.

#### composerAdd (variable, from `nacatamalon/shader`)

```ts
composerAdd: TComposerBinaryOp
```

Adds lane by lane, a number spreading over a vector (`composerAdd(color, 0.1)` lightens every
channel). Two vectors of different sizes are an error.

#### composerAtan2 (function, from `nacatamalon/shader`)

```ts
composerAtan2(y: TComposerInput, x: TComposerInput) => TComposerNode
```

The angle of the point `(x, y)` around the origin, in radians from -π to π, lane by lane: `0`
pointing along +x, growing towards +y. `y` comes first, as in every language that has it.

It is what turns a place into "how far round": a swirl, rays from a sun, a ring that fills like a
clock, a pattern wrapped around a centre. Taken around the middle of a sprite,
`composerAtan2(uv.y - 0.5, uv.x - 0.5)`; around a model's axis, from the corner's own position.
Divide by `2π` and add `0.5` for a turn from 0 to 1.

- `y: TComposerInput`: The second coordinate. A number spreads over a vector given as `x`.
- `x: TComposerInput`: The first coordinate.

#### composerCarry (function, from `nacatamalon/shader`)

```ts
composerCarry() => TComposerNode
```

What the vertex stage carried to this pixel (`vec4`): the graph's `carry`, worked out at each
corner and smeared across the triangle. How high a wave lifted the water here, for foam on its
crests; a light of the graph's own worked out at the corners, the look of the era; where the point
was in the model's own space, for bands or a fill line that stay on the model as it moves. A `carry`
smaller than four numbers is filled out with zeros, and none at all reads as zeros. Models only,
colour stage.

#### composerCellularNoise (function, from `nacatamalon/shader`)

```ts
composerCellularNoise(pos: TComposerNode) => TComposerNode
```

Cell noise (`f32`): the distance to the nearest of a scatter of random points, which gives
cells, cracks or scales. Takes a `vec2` or a `vec3`.

- `pos: TComposerNode`: Where to read it: a `vec2` or a `vec3`.

#### composerCelShade (function, from `nacatamalon/shader`)

```ts
composerCelShade(value: TComposerNode, steps: TComposerInput) => TComposerNode
```

Cuts a gradient into `steps` flat bands, the cartoon look. Put the light through it for hard
lighting, or any other gradient. Same type in as out; more steps is smoother.

- `value: TComposerNode`: The gradient to cut.
- `steps: TComposerInput`: How many bands. Default `3`.

#### composerClamp (function, from `nacatamalon/shader`)

```ts
composerClamp(value: TComposerInput, lo: TComposerInput, hi: TComposerInput) => TComposerNode
```

Keeps `value` between `lo` and `hi`, lane by lane. Numbers as bounds spread over `value`.

- `value: TComposerInput`: What to keep inside.
- `lo: TComposerInput`: The lowest it may be.
- `hi: TComposerInput`: The highest it may be.

#### composerCos (variable, from `nacatamalon/shader`)

```ts
composerCos: (a: TComposerInput) => TComposerNode
```

Cosine of each lane, in radians.

#### composerCross (function, from `nacatamalon/shader`)

```ts
composerCross(a: TComposerNode, b: TComposerNode) => TComposerNode
```

A vector at right angles to two `vec3`s.

- `a: TComposerNode`: One `vec3`.
- `b: TComposerNode`: The other.

#### composerDistance (function, from `nacatamalon/shader`)

```ts
composerDistance(a: TComposerNode, b: TComposerNode) => TComposerNode
```

How far apart two points of the same size are (`f32`).

- `a: TComposerNode`: One point.
- `b: TComposerNode`: The other.

#### composerDiv (variable, from `nacatamalon/shader`)

```ts
composerDiv: TComposerBinaryOp
```

Divides lane by lane, a number spreading over a vector.

#### composerDot (function, from `nacatamalon/shader`)

```ts
composerDot(a: TComposerNode, b: TComposerNode) => TComposerNode
```

The dot product of two vectors of the same size (`f32`): how much they point the same way, and
the heart of any lighting sum.

- `a: TComposerNode`: One vector.
- `b: TComposerNode`: The other, the same size.

#### composerEmissive (function, from `nacatamalon/shader`)

```ts
composerEmissive() => TComposerNode
```

The light the material gives off by itself (`vec3`), so a graph can rebuild the engine's own
look exactly. Models only, colour stage.

#### composerEnvUv (function, from `nacatamalon/shader`)

```ts
composerEnvUv() => TComposerNode
```

Where the surface's reflection of the world lands on a round picture of its surroundings (`vec2`):
read a map there and the model looks like chrome, the reflection the consoles of the era made with
a second texture. Use it as the coordinate of `composerMapSample`. Models only, colour stage.

#### composerFbm (function, from `nacatamalon/shader`)

```ts
composerFbm(pos: TComposerNode, octaves: number) => TComposerNode
```

Layers of noise, each twice as fine and half as strong as the one before (`f32`): the detailed,
natural kind, for terrain, smoke or lava. `octaves` is written into the shader, so it is a small
fixed number (4 unless you say). Takes a `vec2` or a `vec3`.

- `pos: TComposerNode`: Where to read it: a `vec2` or a `vec3`.
- `octaves: number`: How many layers. Default `4`.

#### composerFloat (function, from `nacatamalon/shader`)

```ts
composerFloat(value: number) => TComposerNode
```

A constant number. The only kind of constant: vectors are made with `composerVec2`,
`composerVec3` and `composerVec4`, which wrap their plain numbers in this on their own.

- `value: number`: The number.

#### composerFloor (variable, from `nacatamalon/shader`)

```ts
composerFloor: (a: TComposerInput) => TComposerNode
```

The largest whole number not above each lane.

#### composerFract (variable, from `nacatamalon/shader`)

```ts
composerFract: (a: TComposerInput) => TComposerNode
```

What is left after the whole part (`x - floor(x)`), lane by lane: the basis of anything that
repeats or tiles.

#### composerFresnel (function, from `nacatamalon/shader`)

```ts
composerFresnel(power: TComposerInput) => TComposerNode
```

The rim term, from 0 where the surface faces the camera to 1 where it turns away (`f32`): the
start of a rim light, a hologram or a force field. A higher `power` pulls the rim in to the
silhouette.

It reads which way the surface faces and where the camera is, so it only exists in a model's
colour stage, and the compiler says so anywhere else.

- `power: TComposerInput`: How tightly the rim hugs the silhouette. Default `3`.

#### composerFrontFacing (function, from `nacatamalon/shader`)

```ts
composerFrontFacing() => TComposerNode
```

Which side of the triangle is being drawn (`f32`): `1` the side facing the camera, `0` the other.
Only ever `0` on a material that keeps the other side (`cull: 'front'` or `'none'`): mixed by it, a
liquid seen from below takes another colour, or a leaf is lighter underneath. Models only, colour
stage.

#### composerLayerUv (function, from `nacatamalon/shader`)

```ts
composerLayerUv() => TComposerNode
```

Where this pixel is across the whole of a map's layer, from `0` to `1` (`vec2`): a shine that
crosses the map rather than each tile on its own. On a sprite, the same as `composerLocalUv()`, so
one material does for both. Sprites and map layers only.

#### composerLength (function, from `nacatamalon/shader`)

```ts
composerLength(a: TComposerNode) => TComposerNode
```

How long a vector is (`f32`).

- `a: TComposerNode`: The vector.

#### composerLight (function, from `nacatamalon/shader`)

```ts
composerLight() => TComposerNode
```

How much light reaches this point, and of what colour (`vec3`).

It is handed over on its own so a graph can light things its own way, in hard bands or with
a rim. The engine's own look is the surface times this, plus `composerEmissive`. The light is
worked out at the corners and blended in between, as the consoles of the era did. Models only,
colour stage.

#### composerLocalUv (function, from `nacatamalon/shader`)

```ts
composerLocalUv() => TComposerNode
```

Where this pixel is across what is drawn, from `0` to `1` (`vec2`): the whole sprite however its
frames change, one tile of a map, one letter of a text or one piece of a nine-slice. Where a mask
is read so a sprite crumbles in its own shape and not the sheet's. It turns over with the picture
when the sprite or the tile is flipped. Sprites and map layers only.

#### composerMapSample (function, from `nacatamalon/shader`)

```ts
composerMapSample(name: string, coord: TComposerNode) => TComposerNode
```

Reads one of the material's extra maps by name, at any coordinate (`vec4`): a reflection, a mask, a
detail, a ripple. The material carries the picture under the same name in its `maps`; one it does
not carry reads white. Colour stage only. On a sprite or a map's layer, read it at
`composerLocalUv()` for a mask that stays put while the frames change.

- `name: string`: The map's name, as the material's `maps` gives it: lowercase first, letters and digits.
- `coord: TComposerNode`: Where to read it, in `0`-`1`.

#### composerMax (variable, from `nacatamalon/shader`)

```ts
composerMax: TComposerBinaryOp
```

The larger of two values, lane by lane. A number spreads over a vector.

#### composerMin (variable, from `nacatamalon/shader`)

```ts
composerMin: TComposerBinaryOp
```

The smaller of two values, lane by lane. A number spreads over a vector.

#### composerMix (function, from `nacatamalon/shader`)

```ts
composerMix(a: TComposerInput, b: TComposerInput, t: TComposerInput) => TComposerNode
```

Blends from `a` to `b` by `t`: 0 gives `a`, 1 gives `b`. `a` and `b` are the same size, and `t`
is either a number or that size too.

- `a: TComposerInput`: What `0` gives.
- `b: TComposerInput`: What `1` gives.
- `t: TComposerInput`: How far from `a` to `b`.

#### composerMod (variable, from `nacatamalon/shader`)

```ts
composerMod: TComposerBinaryOp
```

The remainder the way shader authors expect it, `x - y * floor(x / y)`, which always takes the
sign of `y`. Built from the pieces above, because WGSL's own `%` keeps the sign of `x` instead
and the two backends would disagree on negative numbers. A number as `y` spreads over `x`.

#### composerMul (variable, from `nacatamalon/shader`)

```ts
composerMul: TComposerBinaryOp
```

Multiplies lane by lane, a number spreading over a vector (`composerMul(color, 2)` doubles the
brightness; two vectors multiply lane by lane).

#### composerNormalize (function, from `nacatamalon/shader`)

```ts
composerNormalize(a: TComposerNode) => TComposerNode
```

The same direction, one unit long. Vectors only.

- `a: TComposerNode`: The vector.

#### composerOcclusion (function, from `nacatamalon/shader`)

```ts
composerOcclusion() => TComposerNode
```

How open this place is to the light from all around (`f32`): 1 wide open, 0 shut in a crease,
from the material's `occlusion`, and 1 with none. It is already in `composerLight()`; this is it on
its own, to dim a shine inside the creases or gather dirt in them. Models only, colour stage.

#### composerOneMinus (function, from `nacatamalon/shader`)

```ts
composerOneMinus(a: TComposerInput) => TComposerNode
```

`1 - x`, lane by lane: the opposite of a mask or of a falloff, in one step of a pipe.

- `a: TComposerInput`: The value.

#### composerPipe (function, from `nacatamalon/shader`)

```ts
composerPipe(seed: TComposerInput, steps: TComposerStep[]) => TComposerNode
```

Passes a value through a list of steps, left to right, so a long expression reads in the order
it happens instead of from the inside out.

`composerPipe(x, composerMul(2), composerAdd(1), composerSin)` reads "take x, double it, add one,
then the sine", and is exactly `composerSin(composerAdd(composerMul(x, 2), 1))`. Any one-operand
function is a step as it is, and the two-operand ones that put the value on the left
(`composerAdd`, `composerMul`, `composerPow`...) become one when given a single operand. For
anything else, a small function `(n) => ...` is a step too.

- `seed: TComposerInput`: The value to start with.
- `steps: TComposerStep[]`: What to do to it, in order.

#### composerPow (variable, from `nacatamalon/shader`)

```ts
composerPow: TComposerBinaryOp
```

`base` to the power of `exp`, lane by lane. A number as `exp` spreads over `base`.

#### composerReflect (function, from `nacatamalon/shader`)

```ts
composerReflect(i: TComposerNode, n: TComposerNode) => TComposerNode
```

Bounces the direction `i` off a surface facing `n`. Both vectors of the same size.

- `i: TComposerNode`: The direction that arrives.
- `n: TComposerNode`: The way the surface faces.

#### composerResolution (function, from `nacatamalon/shader`)

```ts
composerResolution() => TComposerNode
```

The game's size in pixels (`vec2`), which the engine writes for every material. For keeping a
pattern square, or for effects measured in pixels.

#### composerSaturate (function, from `nacatamalon/shader`)

```ts
composerSaturate(a: TComposerInput) => TComposerNode
```

Keeps each lane between 0 and 1, in one step of a pipe. Short for `composerClamp(x, 0, 1)`.

- `a: TComposerInput`: The value.

#### composerScreenPos (function, from `nacatamalon/shader`)

```ts
composerScreenPos() => TComposerNode
```

Where on the screen the surface is, in the game's pixels (`vec2`): `(0, 0)` at the top left, the
game's width and height at the bottom right, whatever the size of the window. A dot screen, a
hatch or a dither laid over a model lines up with the screen through this rather than sliding
with the model. Models only, colour stage.

#### composerShine (function, from `nacatamalon/shader`)

```ts
composerShine() => TComposerNode
```

The highlight the lamps put on the surface (`vec3`), already shadowed: the shine a material's
`specular` and `shininess` ask for. The built-in look adds it on top of the lit colour. Models
only, colour stage.

#### composerSign (variable, from `nacatamalon/shader`)

```ts
composerSign: (a: TComposerInput) => TComposerNode
```

The sign of each lane: -1, 0 or +1.

#### composerSimplexNoise (function, from `nacatamalon/shader`)

```ts
composerSimplexNoise(pos: TComposerNode) => TComposerNode
```

Smooth noise at a position (`f32`, 0 to 1): the usual source of anything organic, like clouds,
flowing water or a dissolve mask. Takes a `vec2` or a `vec3`. Scale the position to change how
busy it is, and add `composerTime()` to move it.

- `pos: TComposerNode`: Where to read it: a `vec2` or a `vec3`.

#### composerSin (variable, from `nacatamalon/shader`)

```ts
composerSin: (a: TComposerInput) => TComposerNode
```

Sine of each lane, in radians.

#### composerSmoothstep (function, from `nacatamalon/shader`)

```ts
composerSmoothstep(edge0: TComposerInput, edge1: TComposerInput, value: TComposerInput) => TComposerNode
```

A soft cut: eases from 0 to 1 as `value` goes from `edge0` to `edge1`. Numbers as edges spread
over `value`.

- `edge0: TComposerInput`: Where the ramp starts.
- `edge1: TComposerInput`: Where it ends.
- `value: TComposerInput`: What is ramped.

#### composerSqrt (variable, from `nacatamalon/shader`)

```ts
composerSqrt: (a: TComposerInput) => TComposerNode
```

Square root of each lane.

#### composerStep (function, from `nacatamalon/shader`)

```ts
composerStep(edge: TComposerInput, value: TComposerInput) => TComposerNode
```

A hard cut: 0 where `value` is below `edge`, 1 from there on, lane by lane. A number as `edge`
spreads over `value`.

- `edge: TComposerInput`: Where the cut is.
- `value: TComposerInput`: What is cut.

#### composerSub (variable, from `nacatamalon/shader`)

```ts
composerSub: TComposerBinaryOp
```

Subtracts lane by lane, a number spreading over a vector.

#### composerSurface (function, from `nacatamalon/shader`)

```ts
composerSurface() => TComposerNode
```

The colour the engine would have drawn here (`vec4`): the picture, already read and already
tinted (and on a model, painted by its corners' colour), before any light. Where most colour effects start. Colour stage only.

#### composerSwizzle (function, from `nacatamalon/shader`)

```ts
composerSwizzle(value: TComposerNode, mask: string) => TComposerNode
```

Picks components of a vector by their letters (`'bgr'`, `'xy'`). The number of letters, one to
four, is the size of the result, and a single letter gives a plain number.

The letters come from one set, `xyzw` or `rgba`, never both in one mask, and each must exist in
the value it reads: both are checked here rather than left to the card.

- `value: TComposerNode`: The vector to read.
- `mask: string`: One to four letters from `xyzw` or `rgba`.

#### composerTextureSample (function, from `nacatamalon/shader`)

```ts
composerTextureSample(coord: TComposerNode) => TComposerNode
```

Reads the material's own picture, tinted, at any coordinate (`vec4`). What waves, pixelation, a
colour split or a blur are made of: `composerSurface()` is this read at the fragment's own
coordinate. Colour stage only.

- `coord: TComposerNode`: Where to read the picture, in `0`-`1`.

#### composerTime (function, from `nacatamalon/shader`)

```ts
composerTime() => TComposerNode
```

Seconds since the game started (`f32`), which the engine writes for every material. Anything that
moves on its own is driven by this, with no code per frame.

#### composerUniform (function, from `nacatamalon/shader`)

```ts
composerUniform(name: string, value: number | number[]) => TComposerNode
```

A parameter with a name, which the game can change while it runs.

What `value` looks like sets its type (a number, or a list of two, three or four) and is what it
starts at. `compileShader` gathers every parameter of the graph into the material's `uniforms`,
so writing `material.uniforms.<name>` moves it on the next frame. `time` and `resolution` are
taken: the engine writes those, and they are read with `composerTime` and `composerResolution`.

- `name: string`: What the game calls it: `material.uniforms.<name>`.
- `value: number | number[]`: What it starts at. A number, or a list of two, three or four.

#### composerUv (function, from `nacatamalon/shader`)

```ts
composerUv() => TComposerNode
```

The texture coordinate (`vec2`). There in every stage; in a model's vertex graph it is the
corner's own.

#### composerUv2 (function, from `nacatamalon/shader`)

```ts
composerUv2() => TComposerNode
```

A second picture coordinate (`vec2`): the file's own, or the first again when it had none. For a
second picture laid over the first by coordinates of its own: baked light, a decal, a detail.
Models only, in either stage.

#### composerVec2 (function, from `nacatamalon/shader`)

```ts
composerVec2(args: TComposerInput[]) => TComposerNode
```

A vector of two. Two numbers, one `vec2`, or one number copied into both.

- `args: TComposerInput[]`: Two numbers or nodes, one `vec2`, or one number for both.

#### composerVec3 (function, from `nacatamalon/shader`)

```ts
composerVec3(args: TComposerInput[]) => TComposerNode
```

A vector of three, the everyday one for colours and directions. Three numbers, a `vec2` and a
number, one `vec3`, or one number copied into all three.

- `args: TComposerInput[]`: Three numbers or nodes, a smaller vector and the rest, or one number for all.

#### composerVec4 (function, from `nacatamalon/shader`)

```ts
composerVec4(args: TComposerInput[]) => TComposerNode
```

A vector of four, usually a colour with its alpha. Four numbers, a `vec3` and a number (colour
and alpha), one `vec4`, or one number copied into all four.

- `args: TComposerInput[]`: Four numbers or nodes, smaller vectors and the rest, or one number for all.

#### composerVertexBitangent (function, from `nacatamalon/shader`)

```ts
composerVertexBitangent() => TComposerNode
```

Which way down the surface the picture runs, in the model's own space (`vec3`). Models only,
vertex stage.

#### composerVertexColor (function, from `nacatamalon/shader`)

```ts
composerVertexColor() => TComposerNode
```

The colour painted on the model's corners (`vec4`), smeared across each triangle. Models only, in
either stage.

It is already part of `composerSurface`. On its own it is for using the paint as something other
than colour: how much a corner sways in the wind, or how much of a second picture shows through,
which is how the era blended grass into a dirt path without a second texture pass.

#### composerVertexNormal (function, from `nacatamalon/shader`)

```ts
composerVertexNormal() => TComposerNode
```

Which way the corner faces, in the model's own space (`vec3`). Moving a corner along it swells
the model. Models only, vertex stage.

#### composerVertexPosition (function, from `nacatamalon/shader`)

```ts
composerVertexPosition() => TComposerNode
```

Where the corner is, in the model's own space (`vec3`): the value a `position` graph moves, to
make it wobble, swell or jitter. Models only, vertex stage.

#### composerVertexTangent (function, from `nacatamalon/shader`)

```ts
composerVertexTangent() => TComposerNode
```

Which way across the surface the picture runs, in the model's own space (`vec3`). Models only,
vertex stage.

#### composerViewDir (function, from `nacatamalon/shader`)

```ts
composerViewDir() => TComposerNode
```

The direction from this point towards the camera, one unit long (`vec3`). What a rim or a shine
is worked out from. Models only, colour stage.

#### composerW (function, from `nacatamalon/shader`)

```ts
composerW(value: TComposerNode) => TComposerNode
```

The fourth component of a vector, as a number. Short for `composerSwizzle(v, 'w')`.

- `value: TComposerNode`: The vector.

#### composerWorldBitangent (function, from `nacatamalon/shader`)

```ts
composerWorldBitangent() => TComposerNode
```

Which way down the surface the picture runs, in the world (`vec3`): the normal crossed with the
tangent, turned over where the picture is mirrored. Models only, colour stage.

#### composerWorldNormal (function, from `nacatamalon/shader`)

```ts
composerWorldNormal() => TComposerNode
```

Which way the surface faces, in the world (`vec3`). Models only, colour stage; in a vertex graph
use `composerVertexNormal`.

#### composerWorldPos (function, from `nacatamalon/shader`)

```ts
composerWorldPos() => TComposerNode
```

Where this point of the surface is, in the world (`vec3`). Models only, colour stage.

#### composerWorldTangent (function, from `nacatamalon/shader`)

```ts
composerWorldTangent() => TComposerNode
```

Which way across the surface the picture runs, in the world (`vec3`, one long, square to the
normal). With the bitangent and the normal it is the frame a relief or a ripple moves the picture
in. Models only, colour stage.

#### composerX (function, from `nacatamalon/shader`)

```ts
composerX(value: TComposerNode) => TComposerNode
```

The first component of a vector, as a number. Short for `composerSwizzle(v, 'x')`.

- `value: TComposerNode`: The vector.

#### composerY (function, from `nacatamalon/shader`)

```ts
composerY(value: TComposerNode) => TComposerNode
```

The second component of a vector, as a number. Short for `composerSwizzle(v, 'y')`.

- `value: TComposerNode`: The vector.

#### composerZ (function, from `nacatamalon/shader`)

```ts
composerZ(value: TComposerNode) => TComposerNode
```

The third component of a vector, as a number. Short for `composerSwizzle(v, 'z')`.

- `value: TComposerNode`: The vector.

#### mangaDots (function, from `nacatamalon/shader`)

```ts
mangaDots(options: { size, fill, ink }) => TMangaDotsLook
```

Dots of ink in the shadow, the way printed comics shaded: the darker, the bigger. They are laid out
on the screen and not on the model, so they stay put as it turns. For a material's `steps`.

After `toonLighting` the dots go exactly where its bands went dark; with the engine's light they grow
smoothly into the dark.

- `options: { size, fill, ink }`: `size` how far apart the dots are, in game pixels (default `5`); `fill` how big a dot

#### parseShaderGraph (function, from `nacatamalon/shader`)

```ts
parseShaderGraph(source: unknown, src: string) => TShaderGraphDoc
```

Reads a `.shader` file and checks its shape.

It takes the text of the file or JSON already parsed, because both callers exist: a game loading
it has text in hand, and an editor holds a graph it has just changed and wants checked before it
writes it. A broken shape (a node with no id, a place that is not two numbers, an unknown family)
is refused here, naming the node. What the graph means (a kind of node nobody knows, a wire
into an input that does not exist, a loop) is `compileShaderGraph`'s, which knows the catalogue.

- `source: unknown`: The file's text, or its JSON.
- `src: string`: Where it came from, for the error messages.

#### rim (function, from `nacatamalon/shader`)

```ts
rim(options: { color, power, amount }) => TRimLook
```

A light along the edge that turns away from the camera, as if lit from behind. The edge goes
towards `color` and never past it, so it brightens without burning. For a material's `steps`.

- `options: { color, power, amount }`: `color`, what `getColor` takes, a `TColor` or `[r, g, b]` (default white); `power`

#### toonLighting (function, from `nacatamalon/shader`)

```ts
toonLighting(options: { bands, shadow, spot }) => TToonLook
```

The light cut into flat bands, the cartoon look: the surface is lit or it is not, with nothing
smeared between, and the shine is one hard spot. For a material's `lighting`.

It cuts the light every light of the scene gives (the sun, the lamps, the shadows they cast), and
only its brightness: each band keeps the colour of the light that made it, so a warm lamp gives
warm bands and a white light greys.

It also says which part it put in shadow, so a step after it (`mangaDots`) inks exactly there.

- `options: { bands, shadow, spot }`: `bands` how many steps of light (default `2`); `shadow` how bright the darkest one

#### unlitLighting (function, from `nacatamalon/shader`)

```ts
unlitLighting() => TUnlitLook
```

No light at all: the surface exactly as it is painted (its picture, its tint and what it gives off
itself), the same from every side and in every shadow. For a material's `lighting`: a sky, a
screen, a sign that glows, anything that is not lit but is a light.

#### TCompiledShader (type, from `nacatamalon/shader`)

```ts
type TCompiledShader
```

What `compileShader` gives back: exactly the fields `createMaterial` takes, so it is spread
straight in, `createMaterial({ ...compiled })`.

A stage left at the engine's own is missing, the same as not writing it in `createMaterial`.
`uniforms` holds every parameter found in the graph, ready to be changed while the game runs.

- `shader: S`
- `fragment?: string`: The colour hook in WGSL, missing when the graph has no `color`.
- `vertex?: string`: The vertex hook in WGSL, missing when the graph has no `position`.
- `fragmentGlsl?: string`: The same two hooks in GLSL, always, never on request.
- `vertexGlsl?: string`
- `uniforms: TUniformValues`

#### TComposerBinaryOp (type, from `nacatamalon/shader`)

```ts
type TComposerBinaryOp: (a: TComposerInput, b: TComposerInput) => TComposerNode
```

The two ways to call a two-operand node: both operands give a value, one gives a step for
`composerPipe`, with the value coming down the pipe as the first operand.

#### TComposerInput (type, from `nacatamalon/shader`)

```ts
type TComposerInput: TComposerNode | number
```

What an operand of a composer function may be: another value, or a plain number, which becomes a
constant. So `composerAdd(composerX(composerUv()), 0.5)` needs no `composerFloat(0.5)`.

#### TComposerNode (type, from `nacatamalon/shader`)

```ts
type TComposerNode
```

One value in a composed shader: what kind of value it is, and how to build it from the values it
depends on.

It is plain data, never a closure. `kind` picks how it is written out, `params` carries its
literal operands and `deps` the values it is made from. That is what lets a graph be written to
JSON and read back, the same rule every other piece of state in the engine follows.

Never built by hand: the `composer*` functions fill these fields consistently and check their
operands as they go.

- `kind: string`: How it is written out: `literal`, `input`, `uniform`, `texture`, `construct`, `swizzle`, `binop` or `call`.
- `type: TUniformType`: What it evaluates to. Every operation checks its operands against this.
- `deps: TComposerNode[]`: The values it is made from, in argument order.
- `params?: number | string[]`: Literal operands: a number, an operator, a swizzle mask or the name of the function called.
- `input?: TInputKind`: For a leaf, which value of the running shader it reads.
- `uniform?: { name, value }`: For a parameter, the name the shader reads it by and the value it starts at.
- `helpers?: THelperDef[]`: Functions this node needs written at the top of the shader.

#### TComposerStep (type, from `nacatamalon/shader`)

```ts
type TComposerStep: (value: TComposerNode) => TComposerNode
```

One stage of `composerPipe`: it takes the value flowing through and returns the next.

Every one-operand function already is one, and the two-operand ones become one when given a
single operand (`composerMul(2)`), so they chain without wrapping.

#### TGraphCommentDoc (type, from `nacatamalon/shader`)

```ts
type TGraphCommentDoc
```

A note on the canvas: a titled rectangle behind the nodes, for grouping part of a graph and
saying what it is for.

It has an array of its own rather than being a kind of node, because it is not one: no wires, no
value, no place in the graph. As a node, the compiler, the type check and the previews would each
need an exception for the one entry that is not part of the shader.

- `id: string`
- `text: string`: The note. May be empty while it is being written.
- `pos: [number, number]`: Top-left corner, in the same space as a node's `pos`.
- `size: [number, number]`: Width and height: a note is resized, a node is not.
- `color?: [number, number, number]`: An accent, `[r, g, b]` from 0 to 1. Left out, the editor picks.

#### TGraphEdgeDoc (type, from `nacatamalon/shader`)

```ts
type TGraphEdgeDoc
```

A wire: output `out` of node `from` feeds input `in` of node `to`.

By name rather than by position, so adding an input to a kind of node cannot quietly rewire
every file that uses it.

- `from: string`
- `out: string`
- `to: string`
- `in: string`

#### TGraphNodeDoc (type, from `nacatamalon/shader`)

```ts
type TGraphNodeDoc
```

One node in a saved graph: which kind of node it is, where it sits on the canvas, and its
literals.

It does not describe itself. Its inputs, outputs and code all come from the catalogue, looked
up by `type`, so a file stays small and a node learns something new when the catalogue changes
rather than when every file is rewritten.

`pos` belongs to the editor and is in the file on purpose, like a box's place in a scene: how a
graph is laid out is part of what it says to whoever reads it.

- `id: string`
- `type: string`: Which entry of the catalogue this is.
- `pos: [number, number]`: Where it sits on the canvas, `[x, y]`.
- `params?: Record<string, TParamValue>`: Its literals. One left out takes the catalogue's value.

#### TGraphTarget (type, from `nacatamalon/shader`)

```ts
type TGraphTarget: Exclude<TMaterialShader, 'post'>
```

The families a graph file can be for: the two that draw things, deliberately not every family a
material has.

`'post'` is missing because no node writes a screen-wide effect, so a graph claiming it would
compile to nothing. Spelling it as its own type makes that gap a compile error in the one place
that could open it, and the reader refuses the same value, so the two cannot disagree.

#### THelperDef (type, from `nacatamalon/shader`)

```ts
type THelperDef
```

A function a node needs written once at the top of the shader (the noise functions, for
instance), kept only once however many nodes ask for it.

Both languages ride on the node rather than living in a table per language looked up by name.
A helper exists because a node needs it, and keeping the two apart would let a graph ask for
a helper one language never defined: a failure that shows up as a shader that does not compile
in somebody's browser, rather than as a missing entry while writing it.

- `name: string`
- `wgsl: string`
- `glsl: string`

#### TInputKind (type, from `nacatamalon/shader`)

```ts
type TInputKind: 'uv' | 'time' | 'resolution' | 'surface' | 'worldNormal' | 'worldPos' | 'viewDir' | 'light' | 'emissive' | 'shine' | 'envUv' | 'vertexPosition' | 'vertexNormal' | 'vertexColor' | 'screenPos' | 'uv2' | 'worldTangent' | 'worldBitangent' | 'vertexTangent' | 'vertexBitangent' | 'frontFacing' | 'carry' | 'occlusion' | 'localUv' | 'layerUv'
```

A value a leaf reads from the running shader: a function argument (`uv`, `pos`), one of the
values the engine writes for every material (`time`), or what the model's colour hook is told
about the place it is colouring (`worldNormal`, `light`).

Each one is only there in some stages, and the compiler refuses a leaf read where it does not
exist (`light` in a vertex graph, say) instead of emitting code the card then rejects.

#### TLightingLook (type, from `nacatamalon`)

```ts
type TLightingLook: TToonLook | TUnlitLook
```

How a material's surface is lit: one of these, in place of the engine's own light.

#### TLook (type, from `nacatamalon/shader`)

```ts
type TLook: TLightingLook | TStepLook
```

Any look, as it is written down: plain data, so a scene document can keep it and a material can
be made from it again.

#### TMangaDotsLook (type, from `nacatamalon/shader`)

```ts
type TMangaDotsLook
```

Dots of ink in the shadow, the way printed comics shaded, as `mangaDots()` writes it down.

- `look: 'mangaDots'`
- `size: number`: How far apart the dots are, in game pixels.
- `fill: number`: How big a dot grows in full shadow, as a part of its cell. Past `0.5` they touch.
- `ink: number[]`: The colour of the ink, `[r, g, b]` from `0` to `1`.

#### TParamValue (type, from `nacatamalon/shader`)

```ts
type TParamValue: number | number[] | string
```

A literal a node keeps in its own body instead of receiving through a wire: a swizzle mask, a
number of octaves, a colour. Only what JSON keeps intact and an inspector can show a control for.

#### TRimLook (type, from `nacatamalon/shader`)

```ts
type TRimLook
```

A light along the edge that turns away from the camera, as `rim()` writes it down.

- `look: 'rim'`
- `color: number[]`: The colour the edge goes towards, `[r, g, b]` from `0` to `1`.
- `power: number`: How tightly it keeps to the silhouette: higher is thinner.
- `amount: number`: How far the edge goes towards the colour, `0` none to `1` all the way.

#### TShaderGraph (type, from `nacatamalon/shader`)

```ts
type TShaderGraph
```

What `compileShader` is handed: the family the shader is for, and up to three results.

`color` is what the colour hook returns and `position` is where the vertex hook moves a corner
to (models only), and `carry` is what the vertex hook hands on to the colour hook. Leave one out and
that stage keeps the engine's own.

The family is kept in the type, so what comes out is known to be a sprite's or a model's.

- `shader: S`
- `color?: TComposerNode`
- `position?: TComposerNode`
- `carry?: TComposerNode`: Up to four numbers worked out at each corner and handed to the colour stage, which reads them
- `lighting?: TLook`: How a model's surface is lit, in place of the engine's light: `toonLighting()` or `unlitLighting()`. With
- `steps?: typeOperator`: What is done to a model's surface once it is lit, in order: `mangaDots()`, `rim()`. Models only.

#### TShaderGraphDoc (type, from `nacatamalon/shader`)

```ts
type TShaderGraphDoc
```

A `.shader` file: a shader drawn as nodes on a canvas.

It sits next to a hand-written `.wgsl` and next to a graph composed in code, and all three end
up as the same thing, so a material cannot tell which one it was given. The file holds no shader
source in any language, only nodes and wires as plain JSON.

`target` means what `@shader` means at the top of a `.wgsl`: it picks the hooks, so it is not a
preference but part of what gets compiled.

- `format: number`
- `kind: 'shadergraph'`
- `target: TGraphTarget`
- `nodes: TGraphNodeDoc[]`
- `edges: TGraphEdgeDoc[]`
- `comments?: TGraphCommentDoc[]`: The notes on the canvas. A graph written by hand or by a tool has none, and then the field is

#### TStepLook (type, from `nacatamalon`)

```ts
type TStepLook: TMangaDotsLook | TRimLook
```

Something done to a surface once it is lit: as many as wanted, in order.

#### TToonLook (type, from `nacatamalon/shader`)

```ts
type TToonLook
```

Light cut into flat bands, the cartoon look, as `toonLighting()` writes it down.

- `look: 'toon'`
- `bands: number`: How many bands of light, from the darkest to the full.
- `shadow: number`: How bright the darkest band is, `0` black to `1` as lit as the rest.
- `spot: number`: Where the shine is cut into one hard spot, `0` everywhere it shines to `1` nowhere.

#### TUnlitLook (type, from `nacatamalon/shader`)

```ts
type TUnlitLook
```

No light at all: the surface as it is painted, as `unlitLighting()` writes it down.

- `look: 'unlit'`

### Post-processing

The chain that runs over the finished frame: colour grading and palettes, the tube and the cable, blur, bloom, distortions and the rest.

#### ascii (function, from `nacatamalon/effects`)

```ts
ascii(options: { size, ownColor, ink, background, brightness, glyphs }) => TBuiltinPostEffect<{ size, ownColor, ink, background, brightness }>
```

Draws the frame in letters: it is cut into cells, and each cell becomes a letter as dense as the
picture is bright there, a space in the dark and `@` in the light, in the picture's own colour.

The letters are chosen after everything is drawn, so lights, gradients and reflections come
through on their own. They are 5 × 5 pictures carried in the shader, so nothing has to be loaded.
A `bloom()` after it makes them glow.

`glyphs` says which letters, from the darkest to the brightest: `' .oO0@'`, `' ░▒▓█'`, or as many
as wanted. Any printable ASCII character, `' '` to `'~'`, and the blocks `░ ▒ ▓ █`; one it cannot
draw is left out with a warning. They are written into the shader when the effect is made, so
they are an option and not a knob: other letters are another `ascii()`.

- `options: { size, ownColor, ink, background, brightness, glyphs }`: `size` is the side of a cell in game pixels, rounded to a multiple of `6` so every

#### bloom (function, from `nacatamalon/effects`)

```ts
bloom(options: { threshold, intensity, radius }) => TBuiltinPostEffect<{ threshold, intensity, radius }>
```

Makes bright parts of the picture glow into what is around them, the soft light of the last
consoles of the era.

Only what is brighter than `threshold` glows. That part is taken at half the game's size, blurred
at a quarter, and added back over the sharp picture, so the glow is wide and cheap.

- `options: { threshold, intensity, radius }`: `threshold` from `0` (everything glows) to `1` (nothing does); `intensity` how

#### blur (function, from `nacatamalon/effects`)

```ts
blur(options: { radius, amount }) => TBuiltinPostEffect<{ radius, amount }>
```

Blurs the whole picture: a pause menu over the game, being out of focus, being underwater.

It is worked out at half the game's size, across and then down, and blown back up smoothly, so
a wide blur costs about what a narrow one does.

- `options: { radius, amount }`: `radius` is about how far a pixel spreads, in game pixels; `amount` mixes it with

#### colorAdjust (function, from `nacatamalon/effects`)

```ts
colorAdjust(options: { brightness, contrast, saturation, gamma, hue, tint }) => TBuiltinPostEffect<{ brightness, contrast, saturation, gamma, hue, tint }>
```

Brightness, contrast, saturation, gamma, a turn of the hue and a tint, in one pass.

Every knob starts where it changes nothing, so it is safe to put in a chain and drive one number
at a time: `saturation: 0` is black and white, `tint: [1, 0.8, 0.6]` is an old photograph, a `hue`
of `180` swaps every colour for its opposite. They are applied in that order: gamma, contrast,
brightness, saturation, hue, tint.

- `options: { brightness, contrast, saturation, gamma, hue, tint }`: `hue` is in degrees; the rest are multipliers, `1` being unchanged. `tint` is any

#### crt (function, from `nacatamalon/effects`)

```ts
crt(options: TCrtOptions) => TBuiltinPostEffect<TCrtUniforms>
```

Makes the frame look as if it were on a CRT television: lines, a phosphor mask, the bulge and the
glow of the glass, and the small faults of a real tube. Each part is a number, and a part at `0` is
not there at all. `POST_EFFECTS.CRT_PRESETS` has four tubes to start from.

One line per row of the game, not of the screen. The lines follow the game's own pixels, the
way a console's picture sat on the tube, so a game at 320 × 224 has 224 of them however large the
canvas is drawn. When a game row is only one real pixel tall (`pixelRatio: 1`, the pixel-art
default) a line cannot be drawn inside it, so alternate rows are darkened instead. At two or more
real pixels per row each line gets the shape of a beam, and a bright one widens into the gap.

The mask is the coloured grid of the tube itself and works on real pixels, so it only looks
like a mask at a `pixelRatio` of 3 or more; below that, `maskScale` makes it coarser, or leave it
off. That is why it starts switched off.

The glow is worked out at half the game's size, so it costs a little whatever size the canvas
is. `noise`, `flicker` and `interlace` move every frame and start off, so a still scene stays still.

It belongs last in a chain: a palette or a dither decides which colours exist, and the tube
is what those colours are shown on.

It already holds a glow, noise, a vignette, a flicker and a colour split of its own: laid over
`bloom`, `grain`, `oldFilm` or `rgbSplit`, that part is done twice (see `POST_BUILTINS`).

- `options: TCrtOptions`: How strong each part is; see `TCrtOptions`.

#### distort (function, from `nacatamalon/effects`)

```ts
distort(options: { center, radius, bulge, twist }) => TBuiltinPostEffect<{ center, radius, bulge, twist }>
```

Bulges, pinches or twists a round patch of the picture: a lens, a black hole, a whirlpool.

Outside `radius` nothing moves, and the edge of the patch always meets the picture around it, so
the effect can be dropped anywhere without a seam.

- `options: { center, radius, bulge, twist }`: `center` from `{ x: 0, y: 0 }` (top left) to `{ x: 1, y: 1 }`; `radius` in game pixels; `bulge`

#### dither (function, from `nacatamalon/effects`)

```ts
dither(options: { levels, amount }) => TBuiltinPostEffect<{ levels, amount }>
```

Cuts each channel down to a few steps, hiding the bands with an ordered pattern.

This is what the machines of the era did, and doing it here rather than in the artwork means it
applies to everything at once: the sprites, the lighting, the gradients a shader made up.

It works in the frame's own colours, with no trip through linear light and back. That is
deliberate: the hardware it imitates had no notion of linear light, and a physically tidy version
of this bands in the wrong places.

- `options: { levels, amount }`: `levels` is steps per channel, `amount` how much of the pattern to mix in.

#### glitch (function, from `nacatamalon/effects`)

```ts
glitch(options: { slices, offset, split, amount, rate, seed }) => TBuiltinPostEffect<{ slices, offset, split, amount, rate, seed }>
```

Tears the picture into horizontal bands and throws some of them sideways, with the colours coming
apart where they land: a signal failing, a save file corrupting, a boss arriving.

The bands are chosen again `rate` times a second, so it jumps rather than slides, which is what
a broken signal does. `amount` is the share of bands that move; at `0` it is gone.

Its `split` is a colour split of its own: over `rgbSplit` the colours come apart twice (see
`POST_BUILTINS`).

- `options: { slices, offset, split, amount, rate, seed }`: `slices` is how many bands; `offset` and `split` are in game pixels; `seed` gives a

#### godrays (function, from `nacatamalon/effects`)

```ts
godrays(options: { center, threshold, density, decay, intensity }) => TBuiltinPostEffect<{ center, threshold, density, decay, intensity }>
```

Shafts of light streaming out from a point past whatever stands in front of it: sun through
trees, a window in a dark hall, a holy relic.

The bright parts are taken at half the game's size, smeared outwards from `center` in a second
pass at the same size, and added back over the sharp picture. Only what is brighter than
`threshold` casts rays, so a dark silhouette in front of a bright sky is what makes them.

- `options: { center, threshold, density, decay, intensity }`: `center` from `{ x: 0, y: 0 }` (top left) to `{ x: 1, y: 1 }`, and it may lie off screen; `density`

#### grain (function, from `nacatamalon/effects`)

```ts
grain(options: { amount, size, animated }) => TBuiltinPostEffect<{ amount, size, animated }>
```

Sprinkles the picture with grain, new every frame unless told to hold still.

`size` is in game pixels, so the grain is as chunky as the art around it rather than finer than
anything else on screen.

- `options: { amount, size, animated }`: `amount` is how strong; `size` how big a grain is; `animated` `false` to freeze it.

#### halftone (function, from `nacatamalon/effects`)

```ts
halftone(options: { size, rotation, mode, ownColor }) => TBuiltinPostEffect<{ size, rotation, mode, ownColor }>
```

Prints the picture the way a comic or a newspaper did: dots that grow where it is dark
(`mode: 'dots'`, the default), or lines laid over each other as it darkens (`mode: 'lines'`).

The ink is black, or the picture's own colour as `ownColor` goes to `1`. The paper is white.

- `options: { size, rotation, mode, ownColor }`: `size` is one dot, or the gap between lines, in game pixels; `rotation`

#### lcd (function, from `nacatamalon/effects`)

```ts
lcd(options: { grid, ghosting, tint }) => TBuiltinPostEffect<{ grid, ghosting, tint }>
```

The screen of a handheld: a grid between the pixels, a tint the panel could not help, and pixels
that were slow to change, so anything moving left a ghost behind it.

The grid is one real pixel wide around each game pixel, so it needs a `pixelRatio` of 2 or more
to have somewhere to go; at `1` there is no gap to draw and it is left out, and at `2` only the
corner of each pixel is darkened, so the picture keeps most of its light. The ghost reads what
the effect showed last frame, which costs two pictures the size of the canvas while it runs.

Its `ghosting` is a trail of its own: with `phosphor` too, a thing moving leaves two (see
`POST_BUILTINS`).

- `options: { grid, ghosting, tint }`: `grid` and `ghosting` from `0` to `1`; `tint` multiplies every colour, and is any

#### lutGrade (function, from `nacatamalon/effects`)

```ts
lutGrade(options: { amount }) => TBuiltinPostEffect<{ amount }>
```

Looks every colour up in a grading table and mixes towards what it says.

This is grading, not limiting. A table moves colours about (warmer, colder, more contrast, a
green night); a palette or a dither take colours away. They compose, and in one order only:
grade first, limit last. The other way round grades colours the machine could not show and
then picks a neighbour for them, which throws away the whole point of the grade.

- `options: { amount }`: `amount` is how far to go, from the frame untouched at `0` to the table's word at `1`.

#### mosaic (function, from `nacatamalon/effects`)

```ts
mosaic(options: { size }) => TBuiltinPostEffect<{ size }>
```

Turns the picture into big square blocks, each the colour at its middle.

The consoles of the era had this in hardware, set from one register, and it was how a game
dissolved into a room or out of a hit. `size` is in game pixels and is meant to be animated: from
`1`, which is nothing, up to whatever is coarse enough and back.

- `options: { size }`: `size` is how many game pixels wide one block is.

#### motionBlur (function, from `nacatamalon/effects`)

```ts
motionBlur(options: { velocity, samples }) => TBuiltinPostEffect<{ velocity, samples }>
```

Smears the whole picture along one direction, as if it moved during the exposure.

- `options: { velocity, samples }`: `velocity` is the length and direction of the smear in game pixels, as `{ x, y }`;

#### ntsc (function, from `nacatamalon/effects`)

```ts
ntsc(options: { bleed, crawl, artifacts }) => TBuiltinPostEffect<{ bleed, crawl, artifacts }>
```

The picture as it came down a composite cable: colour smeared sideways, a crawl of dots along
edges, and rainbows where fine detail confused the set.

Brightness and colour travelled mixed in one signal, and the television could only part them
roughly. So brightness stays sharp and colour bleeds over several pixels (`bleed`), the colour
that leaks back into brightness crawls with each frame (`crawl`), and sharp edges in brightness
are taken for colour (`artifacts`). Artists of the era drew for it: dithered stripes that a cable
blended into a solid colour, which is why some old art only looks right through this.

- `options: { bleed, crawl, artifacts }`: Each from `0` (a clean cable) to about `1`.

#### oldFilm (function, from `nacatamalon/effects`)

```ts
oldFilm(options: { sepia, grain, scratches, flicker, vignette }) => TBuiltinPostEffect<{ sepia, grain, scratches, flicker, vignette }>
```

An old film reel: sepia, grain, a scratch that comes and goes, a flicker and dark edges, all
stepping at twenty-four frames a second whatever the game runs at.

It already holds a grain, a vignette and a flicker of its own: over `grain` that part is done
twice (see `POST_BUILTINS`).

- `options: { sepia, grain, scratches, flicker, vignette }`: Each part from `0` (absent) to about `1`.

#### paletteMatch (function, from `nacatamalon/effects`)

```ts
paletteMatch(options: { dither }) => TBuiltinPostEffect<{ dither }>
```

Replaces every colour with the nearest one a palette actually holds.

This is the effect that makes a frame look like a particular machine rather than merely like an
old one: a NES palette is fifty-four specific colours, and no amount of cutting channels into
steps will land on them.

A small `dither` shakes each pixel by less than the gap between neighbours before the search, so a
slow gradient breaks into a mix of two palette entries instead of a hard edge.

With no palette, or one of a single colour, it gives back what it was handed. A palette that
has not arrived yet must cost you the palette and not the picture.

Its `dither` is a dither of its own: after `dither` the picture is shaken twice (see
`POST_BUILTINS`).

- `options: { dither }`: `dither` is how much to shake before matching. Zero matches exactly.

#### phosphor (function, from `nacatamalon/effects`)

```ts
phosphor(options: { persistence }) => TBuiltinPostEffect<{ persistence }>
```

A tube's phosphor, which kept glowing a moment after the beam had gone: bright things moving fast
leave a short trail.

It reads what it showed last frame and keeps whichever is brighter, the new picture or the old
one dimmed by `persistence`. That costs two pictures the size of the canvas while it runs. Put it
before `crt`, so the trail is of the picture and not of the lines and the mask.

A trail of its own: with `lcd`'s `ghosting` too, a thing moving leaves two (see `POST_BUILTINS`).

- `options: { persistence }`: `persistence` from `0` (none) to just under `1` (a long trail).

#### posterize (function, from `nacatamalon/effects`)

```ts
posterize(options: { levels }) => TBuiltinPostEffect<{ levels }>
```

The same cut as `dither`, with nothing to hide the seams.

It exists as the other half of the pair rather than as a lesser version: hard bands are a look,
and it is the one that shows what the dithering is actually doing when you put the two side by
side.

The same as `dither` with `amount: 0`: one or the other, not both (see `POST_BUILTINS`).

- `options: { levels }`: `levels` is steps per channel.

#### rgbSplit (function, from `nacatamalon/effects`)

```ts
rgbSplit(options: { red, green, blue }) => TBuiltinPostEffect<{ red, green, blue }>
```

Reads the red, green and blue of each pixel from three slightly different places.

Each offset is in game pixels, so a split of one pixel stays one pixel wide however large the
canvas is. Small and still, it is a cheap lens; large and shaken, it is a hit.

- `options: { red, green, blue }`: `red`, `green` and `blue` are where each channel is read from, as `{ x, y }`.

#### shockwave (function, from `nacatamalon/effects`)

```ts
shockwave(options: { center, radius, amplitude, wavelength, brightness }) => TBuiltinPostEffect<{ center, radius, amplitude, wavelength, brightness }>
```

A ring that pushes the picture outwards as it passes, from an explosion, a landing, a spell.

It does not grow on its own: `radius` is where the ring is now, and the game moves it. That keeps
one effect good for any number of blasts, at any speed, with any easing.

- `options: { center, radius, amplitude, wavelength, brightness }`: `center` is where it starts, from `{ x: 0, y: 0 }` (top left) to `{ x: 1, y: 1 }`; `radius`,

#### tiltShift (function, from `nacatamalon/effects`)

```ts
tiltShift(options: { focus, band, feather, radius }) => TBuiltinPostEffect<{ focus, band, feather, radius }>
```

Keeps a band of the picture sharp and blurs above and below it, which makes a whole scene look
like a model on a table.

- `options: { focus, band, feather, radius }`: `focus` is where the sharp band sits, from `0` (top) to `1`; `band` how far it

#### usePostProcess (function, from `nacatamalon/effects`)

```ts
usePostProcess(options: TPostProcessOptions<U>) => TPostEffect<U>
```

Adds a full-screen effect: a shader that reads the finished frame and gives back what to show.

A material changes one thing; this changes the picture. That is what a palette reduction or a
dither has to be, because "what colours may this frame use" is a question about the screen and not
about any sprite on it.

The chain belongs to the game; the entry belongs to the scene. Opening a pause menu over a
level does not change how the screen looks, so effects do not stack up as scenes do. But the scene
that installed one takes it away when it closes.

Order is the order you ask, and order is the picture: each effect reads what the one before it
produced. Grade first and limit last, or you will be grading colours the machine cannot show.

Everything but whether it is switched on is read every frame, so writing a number into the
`uniforms` of what this gives back animates the effect with nothing reinstalled.

- `options: TPostProcessOptions<U>`: What the effect is, and what to set its knobs to.

#### wave (function, from `nacatamalon/effects`)

```ts
wave(options: { amplitude, frequency, speed, axis }) => TBuiltinPostEffect<{ amplitude, frequency, speed, axis }>
```

Slides each line of the picture sideways by a moving wave: heat over sand, a room under water,
a dream.

It moves whole lines, one game row at a time, which is how the consoles did it: by changing
the scroll of the next line while the screen was being drawn. A smooth version looks like a
modern shader; this one looks like the hardware. `axis: 'columns'` moves columns up and down instead.

- `options: { amplitude, frequency, speed, axis }`: `amplitude` is how far a line moves, in game pixels; `frequency` how many waves fit

#### zoomBlur (function, from `nacatamalon/effects`)

```ts
zoomBlur(options: { center, reach, samples }) => TBuiltinPostEffect<{ center, reach, samples }>
```

Smears the picture towards a point, as if the camera rushed at it: speed, a dash, an impact.

- `options: { center, reach, samples }`: `center` from `{ x: 0, y: 0 }` (top left) to `{ x: 1, y: 1 }`; `reach` is how much of the way

#### TBuiltinPostEffect (type, from `nacatamalon/effects`)

```ts
type TBuiltinPostEffect
```

One of the engine's own effects, ready to be spread into `usePostProcess`.

Both halves are required here, unlike a game's own effect. These are the engine's, and an
engine effect that ran on one card and not the other would be a hole in the promise that the two
backends draw the same thing, rather than the honest degradation a hand-written WGSL file gets.

- `name: string`
- `fragment: string`
- `fragmentGlsl: string`
- `uniforms: U`
- `uniformSig: TUniformSignature`
- `passes?: typeOperator`: Its steps before `fragment`, each in both languages, for the same reason the hook is.
- `history?: boolean`: Whether it reads what it showed last frame.
- `reach?: `

#### TCrtOptions (type, from `nacatamalon/effects`)

```ts
type TCrtOptions
```

What `crt()` takes. Every part is a number, and a part at `0` is not there at all.

- `scanlines?: number`: How dark the gap between two lines of the picture is, from `0` to `1`.
- `mask?: number`: How strong the coloured grid of the tube is, from `0` to `1`.
- `maskType?: 'aperture' | 'slot' | 'shadow'`: `'aperture'` (vertical stripes), `'slot'` (staggered cells, a common TV) or `'shadow'` (dots in
- `maskScale?: number`: How many real pixels one stripe of the mask is wide. `1` is the finest the screen can show.
- `curvature?: number`: How much the glass bulges.
- `vignette?: number`: How much darker the corners are.
- `sharpness?: number`: How sharply one game pixel turns into the next along a line: `0` is a soft blend, `1` hard steps.
- `beam?: number`: How much a bright line widens into the gap around it, the way a stronger beam did.
- `brightness?: number`: What the whole picture is multiplied by, to win back what the lines and the mask darken.
- `linearBlend?: boolean`: `true` (the default) works the lines and the glow out in light rather than in the stored
- `halation?: number`: The glow that bright parts throw across the glass.
- `convergence?: number`: How far the red and blue beams miss the green one, in game pixels.
- `noise?: number`: Moving grain.
- `flicker?: number`: A faint flicker and a slow band rolling down the screen.
- `interlace?: boolean`: `true` draws odd and even lines on alternate frames.
- `cornerRadius?: number`: How round the corners of the tube are, as a share of the screen.
- `bezel?: TColorOption | [number, number, number]`: The colour around the glass: what fills the corners the bulge leaves, the plastic of the set.

#### TCrtUniforms (type, from `nacatamalon/effects`)

```ts
type TCrtUniforms
```

The knobs of the effect `crt()` returns, as `effect.uniforms` holds them: the same names as
`TCrtOptions`, each a plain number. `maskType` is `0` aperture, `1` slot, `2` shadow;
`linearBlend` and `interlace` are `1` for `true` and `0` for `false`.

- `scanlines: number`
- `mask: number`
- `maskType: number`
- `maskScale: number`
- `curvature: number`
- `vignette: number`
- `sharpness: number`
- `beam: number`
- `brightness: number`
- `linearBlend: number`
- `halation: number`
- `convergence: number`
- `noise: number`
- `flicker: number`
- `interlace: number`
- `cornerRadius: number`
- `bezel: number[]`

#### TEffectField (type, from `nacatamalon/effects`)

```ts
type TEffectField
```

One setting of an effect or a look, declared so a form can be made of it: what it is called, what
kind of value it takes and between what, and what it starts at.

- `key: string`: Its name: the knob's (a key of the effect's `uniforms`), or, for one marked `rebuild`, the option
- `label?: string`: What a form calls it. The key, in words, when left out.
- `description?: string`: A line about what it does, for a form to show on hover.
- `type: 'number' | 'boolean' | 'string' | 'select' | 'color' | 'vec2'`: How the value is written: `'color'` is `[r, g, b]` from `0` to `1`, `'vec2'` is `[x, y]`. A
- `default: number | string | boolean | number[]`: What it is when nobody has set it: exactly what the effect starts at.
- `min?: number`
- `max?: number`
- `step?: number`
- `options?: typeOperator`: `'select'` only: what may be chosen.
- `suggestions?: typeOperator`: `'string'` only: values worth offering, which may still be typed over.
- `angle?: true`: `'number'` only: a turn, kept in radians like every one in the engine, which a form may show
- `rebuild?: true`: Written into the effect when it is made, not a knob: changing it makes the effect again, so it

#### TPostChain (type, from `nacatamalon/effects`)

```ts
type TPostChain: TPostChainEntry[]
```

The whole chain, in order.

An array rather than a bag of named things, for the reason the input map shares: order is the
meaning here. Each effect reads what the one before it produced, so grading before limiting and
limiting before grading are two different pictures, not two spellings of one.

#### TPostChainEntry (type, from `nacatamalon/effects`)

```ts
type TPostChainEntry
```

One effect as a project writes it down.

An entry names its effect one of two ways, and that split is the whole design:

- `builtin` is a key the engine already knows. There is no file to fetch and no path that can
point at nothing, so a project that only reduces its colours needs no `shaders/` folder at all.
- `effect` is a path to a `.shader` (or `.wgsl`) file, the code of an effect of the game's own: the
same word a material and `usePostProcess` use for it. A project written before says `shader`,
and is read the same.

The arithmetic the engine knows is a key; the look you invented is a file.

- `effect?: string`: Where the effect is written. Left out, or empty, when `builtin` names one instead.
- `builtin?: string`: A key from `POST_BUILTINS`, the engine's own effects. Wins over `effect` if somebody wrote both.
- `uniforms?: TUniformValues`: Values laid over whatever the file or the built-in starts its knobs at.
- `options?: Record<string, string | number | boolean>`: A built-in's settings that are written into it when it is made, rather than knobs (its fields
- `palette?: string`: A `.palette` file whose colours this effect matches against.
- `lut?: string`: A grading table: a `.cube`, or a strip image. Never both this and `palette`.
- `enabled?: boolean`: `false` keeps the entry in the chain, in its place, without running it. Default `true`.
- `name?: string`: What the editor calls it in its list. Defaults to the file's own name.

#### TPostEffect (type, from `nacatamalon/effects`)

```ts
type TPostEffect
```

An effect the game has installed: a shader that reads the finished frame and gives back what
should be shown instead.

This record is what crosses into the backend, by reference and never copied into something
new. The compiled shader is cached against this object's identity, so building a fresh one each
frame would mean compiling each frame and never once finding the cache.

Everything except `enabled` is read every frame, so changing a number in `uniforms` animates the
effect with nothing reinstalled: the same rule a material's knobs follow.

It holds the palette or the table as the asset, not as an uploaded picture, because a file
lands after the effect was installed. Keeping the picture here would mean keeping the `null` it
was at the moment the effect was made.

- `type: 'post'`
- `id: string`
- `name: string | null`: What a warning about it will call it, and what the editor lists. Never an identity.
- `fragment: string | null`: `fn effect(color: vec4<f32>, uv: vec2<f32>) -> vec4<f32>`, and anything it calls.
- `fragmentGlsl: string | null`: The same hook in GLSL, without which the effect is WebGPU only and degrades to nothing.
- `uniforms: U`: The knobs. Read every frame, so writing into this is how a game drives an effect.
- `uniformSig: TUniformSignature`: What kind each one is. Fixed when the effect is made: the block's layout depends on it.
- `passes?: typeOperator`: The steps run before `fragment`, in order, for an effect that needs more than one read of the
- `history?: boolean`: Whether it reads what it showed last frame, through `sampleHistory`. It costs two pictures the
- `palette: TPalette | null`: The colours this effect matches against, for one that reduces to a palette.
- `lut: TLut | null`: The grading table this effect looks colours up in.
- `mask?: TTexture`: The picture a transition reads through `sampleMask`. Only a transition carries one, and it
- `outgoing?: 'take' | 'taken' | 'keep'`: For a transition that crosses, what to do with the picture of the scene going out, which its
- `source: TPostSource`
- `reach?: `
- `enabled: boolean`: Whether it runs. A switched-off effect stays in the chain where it is, so turning it back
- `effect: TShader | null`: The file it came from, so it can be filled in when that lands.

#### TPostPass (type, from `nacatamalon/effects`)

```ts
type TPostPass
```

One step an effect takes before its own hook, for an effect that cannot be done in one read of
the frame: a blur, a glow, a halo.

A pass is written exactly like an effect, `fn effect(color, uv)`, and shares the effect's knobs.
What changes is what it reads and where it writes: `sampleTexture` gives the pass before it (the
first one gets the effect's input), and it writes a picture of its own at `scale` times the
game's size. The effect's own hook runs last, at full size, and reads the last pass the same way,
with `sampleInput` still giving it the frame the effect was handed.

`scale` is measured against the game and not the canvas on purpose: a halo at half of a 320 × 224
game costs the same whatever size the canvas is shown at, and looks the same too.

- `fragment: string`: The pass's hook, in WGSL: `fn effect(color: vec4<f32>, uv: vec2<f32>) -> vec4<f32>`.
- `fragmentGlsl: string | null`: The same in GLSL. An effect with a pass that lacks it is WebGPU only, as a whole.
- `scale?: number`: The size of the picture it writes, against the game's. Default `1`.

#### TPostProcessOptions (type, from `nacatamalon/effects`)

```ts
type TPostProcessOptions
```

What `usePostProcess` is asked for.

There are three ways to say what the effect is, and they are the same three a material has: write
it here, name a file, or spread one of the engine's own.

- `name?: string`: A name, which is what a warning about this effect will call it, and what an editor lists.
- `fragment?: string`: The hook, in WGSL: `fn effect(color: vec4<f32>, uv: vec2<f32>) -> vec4<f32>`.
- `fragmentGlsl?: string`: The same hook in GLSL, without which the effect is WebGPU only.
- `effect?: TShader | string`: A file from `useLoadShader`, or the name one was loaded under. Wins over `fragment`.
- `uniforms?: U`: The knobs, and what they start at. Laid over whatever a file or a built-in says.
- `uniformSig?: TUniformSignature`: What kind each knob is, when it is already known.
- `passes?: typeOperator`: Steps run before `fragment`, for an effect that needs more than one read of the frame. Ignored
- `history?: boolean`: Whether the effect reads what it showed last frame, through `sampleHistory`. Default `false`.
- `palette?: TPalette | null`: The colours to match against, from `useLoadPalette`.
- `lut?: TLut | null`: The grading table, from `useLoadLut`. Never set alongside `palette`.
- `reach?: `
- `enabled?: boolean`: Whether it runs at all. Default `true`.

### Color & palettes

Colour values, indexed palettes and grading tables: the indexed-colour half of the retro look.

#### consoleColor (function, from `nacatamalon`)

```ts
consoleColor(color: TColorOption, levels: number) => TColor
```

A colour as a console could show it: each channel moved to the nearest of the steps that machine
had, `POST_EFFECTS.COLOR_LEVELS.genesis` (8) for a Mega Drive, `POST_EFFECTS.COLOR_LEVELS.snes` (32) for a SNES.

Why it matters under `dither`: the effect leaves a colour that is on one of its steps flat, and
puts its pattern on everything in between, because in between is what a gradient is. A tile tinted
`'#e8423f'` sits between steps, so it comes out checkered; `consoleColor('#e8423f', POST_EFFECTS.COLOR_LEVELS.genesis)` is `#db4949`, on the steps, and comes out flat, while a sky's gradient still
gets the pattern. A screen effect cannot make that choice: it sees pixels, not which ones were
meant to be flat.

Takes what `getColor` takes, or a colour already made. Alpha is left as it is, and the colour given
is never changed.

- `color: TColorOption`: A colour: a hex number or string, any CSS colour, or a `TColor`.
- `levels: number`: Steps per channel, from `POST_EFFECTS.COLOR_LEVELS` or any number from `2` up.

#### fromHsl (function, from `nacatamalon`)

```ts
fromHsl(h: number, s: number, l: number, a: number) => TColor
```

Converts HSL values to a `TColor`.

Useful for animated effects that cycle hue over time (rainbow cascades, palette
shifts, hit-flashes): it runs as pure arithmetic, so it is safe to call inside
`useUpdate` every frame without performance concerns. Prefer it to
`fromCss('hsl(...)')` per frame: that one parses a string first, which is work
this does not do.

- `h: number`: Hue in degrees [0, 360).
- `s: number`: Saturation in [0, 1]. Defaults to `1` (fully saturated).
- `l: number`: Lightness in [0, 1]. Defaults to `0.5` (mid tone).
- `a: number`: Alpha in [0, 1]. Defaults to `1` (fully opaque).

#### getColor (function, from `nacatamalon`)

```ts
getColor(input: TColorOption) => TColor
```

A colour as the engine keeps it, `{ r, g, b, a }` from `0` to `1`, from a hex number, a hex string,
any CSS colour, or a `TColor`. Always a new one, a `TColor` given included: a copy of it.

Every option that takes a colour reads it through here, so `tint: '#ff6b1a'` and
`tint: getColor('#ff6b1a')` are the same thing, and whatever is made keeps a colour of its own.
One colour kept in a constant can tint many texts, and fading one of them (`who.tint.a = 0.3`)
fades that one: not the others, nor the constant.

- `input: TColorOption`: A colour: a hex number, a hex string, any CSS colour, or a `TColor`.

#### lerpColor (function, from `nacatamalon`)

```ts
lerpColor(a: TColor, b: TColor, t: number) => TColor
```

Linearly interpolates two colors channel by channel (straight RGBA, not gamma
corrected): `t = 0` returns `a`, `t = 1` returns `b`. The built-in for color fades
and tinted hit-flashes: feed it the eased value from a tween's `onUpdate`. GLSL
calls this `mix`.

- `a: TColor`: The colour at `0`.
- `b: TColor`: The colour at `1`.
- `t: number`: How far from `a` to `b`.

#### useColor (function, from `nacatamalon`)

```ts
useColor(input: TColorInput) => TColor
```

Converts any color format to a normalized `TColor` object.
Accepts hex numbers (`0xff0000`), hex strings (`"#ff0000"`, `"#f00"`),
and any valid CSS color string (`"skyblue"`, `"rgb(255,0,0)"`, `"hsl(0,100%,50%)"`).

- `input: TColorInput`: A colour: a hex number, a hex string, or any CSS colour.

#### TColor (type, from `nacatamalon`)

```ts
type TColor
```

RGBA color with components normalized to 0-1 for WebGPU.

- `r: number`
- `g: number`
- `b: number`
- `a: number`

#### TColorInput (type, from `nacatamalon`)

```ts
type TColorInput: number | TCssNamedColor | string & { … }
```

Accepted input formats for `useColor`/`getColor`.

`TCssNamedColor | (string & {})` is a TS trick: it gives editor autocomplete for
the named CSS colors while still accepting any other string (hex, `rgb()`,
`hsl()`, ...) without a type error: `string & {}` stops TS from widening the
union to plain `string`, which would otherwise discard the literal suggestions.

#### TColorOption (type, from `nacatamalon`)

```ts
type TColorOption: TColorInput | TColor
```

Any colour an option takes: what `getColor` reads (`'#ff6b1a'`, `0xff6b1a`, `'tomato'`, `'rgb(…)'`)
or a `TColor` already made. What it becomes is always a `TColor`, which is what is read back.

#### TCssNamedColor (type, from `nacatamalon`)

```ts
type TCssNamedColor: 'aliceblue' | 'antiquewhite' | 'aqua' | 'aquamarine' | 'azure' | 'beige' | 'bisque' | 'black' | 'blanchedalmond' | 'blue' | 'blueviolet' | 'brown' | 'burlywood' | 'cadetblue' | 'chartreuse' | 'chocolate' | 'coral' | 'cornflowerblue' | 'cornsilk' | 'crimson' | 'cyan' | 'darkblue' | 'darkcyan' | 'darkgoldenrod' | 'darkgray' | 'darkgreen' | 'darkgrey' | 'darkkhaki' | 'darkmagenta' | 'darkolivegreen' | 'darkorange' | 'darkorchid' | 'darkred' | 'darksalmon' | 'darkseagreen' | 'darkslateblue' | 'darkslategray' | 'darkslategrey' | 'darkturquoise' | 'darkviolet' | 'deeppink' | 'deepskyblue' | 'dimgray' | 'dimgrey' | 'dodgerblue' | 'firebrick' | 'floralwhite' | 'forestgreen' | 'fuchsia' | 'gainsboro' | 'ghostwhite' | 'gold' | 'goldenrod' | 'gray' | 'green' | 'greenyellow' | 'grey' | 'honeydew' | 'hotpink' | 'indianred' | 'indigo' | 'ivory' | 'khaki' | 'lavender' | 'lavenderblush' | 'lawngreen' | 'lemonchiffon' | 'lightblue' | 'lightcoral' | 'lightcyan' | 'lightgoldenrodyellow' | 'lightgray' | 'lightgreen' | 'lightgrey' | 'lightpink' | 'lightsalmon' | 'lightseagreen' | 'lightskyblue' | 'lightslategray' | 'lightslategrey' | 'lightsteelblue' | 'lightyellow' | 'lime' | 'limegreen' | 'linen' | 'magenta' | 'maroon' | 'mediumaquamarine' | 'mediumblue' | 'mediumorchid' | 'mediumpurple' | 'mediumseagreen' | 'mediumslateblue' | 'mediumspringgreen' | 'mediumturquoise' | 'mediumvioletred' | 'midnightblue' | 'mintcream' | 'mistyrose' | 'moccasin' | 'navajowhite' | 'navy' | 'oldlace' | 'olive' | 'olivedrab' | 'orange' | 'orangered' | 'orchid' | 'palegoldenrod' | 'palegreen' | 'paleturquoise' | 'palevioletred' | 'papayawhip' | 'peachpuff' | 'peru' | 'pink' | 'plum' | 'powderblue' | 'purple' | 'rebeccapurple' | 'red' | 'rosybrown' | 'royalblue' | 'saddlebrown' | 'salmon' | 'sandybrown' | 'seagreen' | 'seashell' | 'sienna' | 'silver' | 'skyblue' | 'slateblue' | 'slategray' | 'slategrey' | 'snow' | 'springgreen' | 'steelblue' | 'tan' | 'teal' | 'thistle' | 'tomato' | 'turquoise' | 'violet' | 'wheat' | 'white' | 'whitesmoke' | 'yellow' | 'yellowgreen' | 'transparent' | 'currentcolor'
```

Standard CSS Level 4 named colors. Listed as a string-literal union purely so
editors autocomplete them as `useColor`/`getColor` arguments: `fromCss` (and the
browser's CSS parser it relies on) accepts any of these plus the rest of the CSS
color syntax (`rgb()`, `hsl()`, hex, etc.) regardless of this list.

### Pixels

Pictures generated in code instead of on a canvas: procedural art, textures for shaders, and pictures that change while the game runs.

#### blitPixels (function, from `nacatamalon/pixels`)

```ts
blitPixels(target: TPixels, source: TPixels, x: number, y: number, options: { flipX, flipY }) => TPixels
```

Lays one picture over another, the way a sprite is drawn over a background: a transparent pixel
of `source` leaves what was there, an opaque one replaces it, and one in between mixes the two.

It is how a sprite is put together from parts (a car from a body, wheels and a driver) or stamped
many times over a bigger picture.

- `target: TPixels`: The picture drawn on.
- `source: TPixels`: The picture drawn.
- `x: number`: Where `source`'s top-left corner lands in `target`.
- `y: number`: Where `source`'s top-left corner lands in `target`.
- `options: { flipX, flipY }`: `flipX` and `flipY` mirror `source` as it is drawn.

#### checkColorLimit (function, from `nacatamalon/pixels`)

```ts
checkColorLimit(pixels: TPixels, options: TColorLimitOptions) => TColorLimitCell[]
```

Says which parts of a picture use more colours than they may, as the machines of the era limited
them: three and transparent in a NES sprite's 8 by 8, four in a Game Boy's.

It only looks. Nothing is warned about or changed: what comes back is for a test to check, or for
whoever is drawing to read and correct.

- `pixels: TPixels`: The picture.
- `options: TColorLimitOptions`: How many colours, and in what size of part: see `TColorLimitOptions`.

#### clonePixels (function, from `nacatamalon/pixels`)

```ts
clonePixels(pixels: TPixels) => TPixels
```

A copy of a picture that can be painted on without touching the original.

- `pixels: TPixels`: The picture to copy.

#### createPixels (function, from `nacatamalon/pixels`)

```ts
createPixels(width: number, height: number, fill?: TColorOption) => TPixels
```

A new picture to paint on in code, transparent or filled with one colour: what to use instead of a
`<canvas>` whenever a picture has to be generated rather than drawn.

Drawn art (characters, tiles, backgrounds) is made in a pixel-art editor and loaded with
`useLoadTexture`. This is for the pictures nobody draws by hand: procedural variations, placeholders,
noise and masks for a shader, a picture that changes while the game runs, or art made by an agent
that has no image editor. A canvas only exists in a browser; this runs the same there, on the
native runtime and in a test.

It lives in memory only: paint it with `fillRect`, `drawLine`, `fillGradient` and the rest, then
make it a texture with `createPixelTexture`. It needs no game, so a build script or a test can make
one too.

- `width: number`: In pixels, at least `1`.
- `height: number`: In pixels, at least `1`.
- `fill?: TColorOption`: The colour every pixel starts as. Left out, fully transparent.

#### createPixelTexture (function, from `nacatamalon/pixels`)

```ts
createPixelTexture(source: TPixels | TLoadedPixels, paint: TPixelPaint, options?: TPixelTextureOptions) => TTexture
createPixelTexture(source: TPixels | TLoadedPixels, options?: TPixelTextureOptions) => TTexture
```

Turns a picture into a texture a sprite, a model's material or a shader shows like any image loaded
from a file.

It is how a game turns a generated picture into something it can show, with no image file and no
page: a procedural sky, a rock texture from noise for a model, a mask for a shader, a placeholder
while the real art is drawn. Art someone drew is loaded with `useLoadTexture` instead. It works the
same in the browser and on the native runtime, because nothing here draws with the page. Use it
instead of a `<canvas>`: a canvas only exists in a browser.

The picture can be:

- One painted in code (`createPixels`). The texture is ready at once and keeps reading from the
picture: paint on it again and call `updatePixelTexture` to show the change (a minimap filling in,
a floor with a crater in it). `paint`, if given, is applied to it first.
- A drawn picture loaded to be processed (`useLoadPixels`). The texture comes back at once,
still loading, the way one from `useLoadTexture` does, and whatever shows it appears when the file
has arrived. `paint` then receives a copy of the picture, so the loaded one, shared by everyone
who loaded the file, is never changed: a palette swap, a white silhouette for a hit, an outline.

Asked again for a `key` it already made at the same size, it gives that texture back with the new
picture uploaded into it, so a scene that restarts does not pile up textures.

A scene document can name a texture made here but cannot carry it, since there is no file to fetch:
make it again under the same `key` before loading the document.

- `source: TPixels | TLoadedPixels`: The picture: one from `createPixels`, or one from `useLoadPixels`.
- `paint: TPixelPaint`: Changes the picture before it is uploaded. With a loaded picture it gets a copy.
- `options?: TPixelTextureOptions`: `key` to keep it by name.

#### drawCircle (function, from `nacatamalon/pixels`)

```ts
drawCircle(pixels: TPixels, cx: number, cy: number, radius: number, color: TColorOption) => TPixels
```

Draws the outline of a circle, one pixel thick, with no gaps and no pixel drawn twice.

- `pixels: TPixels`: The picture.
- `cx: number`: Its centre, from the left.
- `cy: number`: Its centre, from the top.
- `radius: number`: In pixels.
- `color: TColorOption`: Its colour.

#### drawEllipse (function, from `nacatamalon/pixels`)

```ts
drawEllipse(pixels: TPixels, cx: number, cy: number, rx: number, ry: number, color: TColorOption) => TPixels
```

Draws the outline of an ellipse, one pixel thick and with no gaps. With both radii equal it is
`drawCircle`, pixel for pixel.

- `pixels: TPixels`: The picture.
- `cx: number`: Its centre, from the left.
- `cy: number`: Its centre, from the top.
- `rx: number`: Half its width, in pixels.
- `ry: number`: Half its height, in pixels.
- `color: TColorOption`: Its colour.

#### drawLine (function, from `nacatamalon/pixels`)

```ts
drawLine(pixels: TPixels, x0: number, y0: number, x1: number, y1: number, color: TColorOption) => TPixels
```

Draws a line one pixel thick between two points, with the stepped edge of the era and no
smoothing: each pixel is either on the line or not.

- `pixels: TPixels`: The picture.
- `x0: number`: Where it starts, from the left.
- `y0: number`: Where it starts, from the top.
- `x1: number`: Where it ends, from the left.
- `y1: number`: Where it ends, from the top.
- `color: TColorOption`: Its colour.

#### drawText (function, from `nacatamalon/pixels`)

```ts
drawText(pixels: TPixels, text: string, x: number, y: number, options: TDrawTextOptions) => TPixels
```

Writes text into a picture: the name on a sign, a number on a door, a label on a crate. Once it is
in the pixels it is part of the art, and turns, scales and lights with whatever shows it.

It lays the text out exactly as `createText` does, with the same `fontSize`, `align`, `maxWidth`
and spacing, and works with either kind of font: a bitmap font is copied pixel for pixel, and a vector
font is drawn from its outlines at the size asked for.

With no font it needs nothing at all: the font the engine carries is already inside it, so a
build script or a test can write text too. A loaded font has to have arrived. One loaded in an
earlier scene and named by its key is ready; in the scene that loads it, draw in a `useWatch` on
the font, which runs again once it is here, and send the change on with `updatePixelTexture`.
Until then this draws nothing and says nothing.

- `pixels: TPixels`: The picture.
- `text: string`: What to write. `\n` starts a new line.
- `x: number`: Where it goes, from the left: the block's top-left corner unless `anchor` says another.
- `y: number`: Where it goes, from the top.
- `options: TDrawTextOptions`: The font, the colour, the size and the spacing.

#### fillChecker (function, from `nacatamalon/pixels`)

```ts
fillChecker(pixels: TPixels, size: number, a: TColorOption, b: TColorOption) => TPixels
```

Fills the picture with a checkerboard: the first texture every 3D scene is tried with, because it
shows at a glance whether a surface is stretched, mirrored or seen from the wrong side.

- `pixels: TPixels`: The picture.
- `size: number`: How many pixels wide one square is.
- `a: TColorOption`: The colour of the top-left square.
- `b: TColorOption`: The other colour.

#### fillCircle (function, from `nacatamalon/pixels`)

```ts
fillCircle(pixels: TPixels, cx: number, cy: number, radius: number, color: TColorOption) => TPixels
```

Fills a circle. Its edge is stepped, one pixel or none, the way a circle drawn by hand on a grid
looks, and it is exactly the edge `drawCircle` draws for the same radius.

Painted with a transparent colour, it cuts a round hole: the crater in a destructible floor.

- `pixels: TPixels`: The picture.
- `cx: number`: Its centre, from the left.
- `cy: number`: Its centre, from the top.
- `radius: number`: In pixels. `0` is a single pixel.
- `color: TColorOption`: What it is filled with.

#### fillDither (function, from `nacatamalon/pixels`)

```ts
fillDither(pixels: TPixels, x: number, y: number, width: number, height: number, a: TColorOption, b: TColorOption, amount: number) => TPixels
```

Fills a rectangle with two colours in the ordered pattern of the era: a shadow or a slope of light
on a picture with few colours, or a see-through patch when one of them is transparent.

The dots are laid out by where they are on the picture, so two patches side by side meet without a
seam, and in the same places the `dither()` effect and `fillGradient` put theirs.

- `pixels: TPixels`: The picture.
- `x: number`: The rectangle's left edge.
- `y: number`: Its top edge.
- `width: number`: How wide, in pixels.
- `height: number`: How tall, in pixels.
- `a: TColorOption`: The colour it is made of when `amount` is `0`.
- `b: TColorOption`: The colour it is made of when `amount` is `1`.
- `amount: number`: How much of `b`, from `0` to `1`, in sixteen steps. Default `0.5`, one and one.

#### fillEllipse (function, from `nacatamalon/pixels`)

```ts
fillEllipse(pixels: TPixels, cx: number, cy: number, rx: number, ry: number, color: TColorOption) => TPixels
```

Fills an ellipse: a circle stretched to `rx` across and `ry` down, for bodies, shadows under a
character, a lake seen from above. Its edge is stepped and exactly the one `drawEllipse` draws
for the same radii. With both radii equal it is `fillCircle`, pixel for pixel.

- `pixels: TPixels`: The picture.
- `cx: number`: Its centre, from the left.
- `cy: number`: Its centre, from the top.
- `rx: number`: Half its width, in pixels. `0` is a vertical line.
- `ry: number`: Half its height, in pixels. `0` is a horizontal line.
- `color: TColorOption`: What it is filled with.

#### fillGradient (function, from `nacatamalon/pixels`)

```ts
fillGradient(pixels: TPixels, options: TFillGradientOptions) => TPixels
```

Fills the picture with a gradient, smooth or cut into the flat bands of a sky from the era, with an
ordered dither between the bands if you want it.

- `pixels: TPixels`: The picture.
- `options: TFillGradientOptions`: The colours, the direction and how it is stepped.

#### fillNoise (function, from `nacatamalon/pixels`)

```ts
fillNoise(pixels: TPixels, options: TFillNoiseOptions) => TPixels
```

Fills the picture with smooth noise: clouds, rock, water, a height map for terrain, or a texture
for a shader to read.

It tiles without a seam: the right edge runs on into the left one and the bottom into the top,
so a model can repeat it (`wrap: 'repeat'`) with no visible joins. For that, the blobs are fitted
to a whole number across the picture, so `scale` is followed as closely as that allows.

- `pixels: TPixels`: The picture.
- `options: TFillNoiseOptions`: Which noise, how big and between which colours.

#### fillPolygon (function, from `nacatamalon/pixels`)

```ts
fillPolygon(pixels: TPixels, points: typeOperator, color: TColorOption) => TPixels
```

Fills a polygon: a ramp, a slope of ground, a roof, a shard. A pixel is painted when its middle is
inside the outline or on it, so the edge is stepped, one pixel or none, a shape and its mirror image
are painted alike (a ramp up and a ramp down), and two polygons that share a side leave no gap
along it. An outline that crosses itself leaves the parts it wraps twice empty, as a star drawn in
one stroke does.

- `pixels: TPixels`: The picture.
- `points: typeOperator`: Its corners in order, around either way, in pixels from the top-left corner. Fewer
- `color: TColorOption`: What it is filled with.

#### fillRect (function, from `nacatamalon/pixels`)

```ts
fillRect(pixels: TPixels, x: number, y: number, width: number, height: number, color: TColorOption) => TPixels
```

Fills a rectangle with one colour, cut to the picture's edges.

- `pixels: TPixels`: The picture.
- `x: number`: Its left edge.
- `y: number`: Its top edge.
- `width: number`: How wide, in pixels.
- `height: number`: How tall, in pixels.
- `color: TColorOption`: What it is filled with.

#### getPixel (function, from `nacatamalon/pixels`)

```ts
getPixel(pixels: TPixels, x: number, y: number) => TColor
```

Reads one pixel. Outside the picture it is fully transparent black.

Reading the picture is also how a game asks questions of it: whether a pixel of a destructible
floor is still solid, which colour a minimap shows somewhere.

- `pixels: TPixels`: The picture.
- `x: number`: From the left edge, in pixels.
- `y: number`: From the top edge, in pixels.

#### mapPixels (function, from `nacatamalon/pixels`)

```ts
mapPixels(pixels: TPixels, fn: (color: TColor, x: number, y: number) => TColor) => TPixels
```

Works out every pixel again from what it is and where it is: for anything the other functions do
not draw. A recolour, a shading by height, a pattern of your own.

- `pixels: TPixels`: The picture.
- `fn: (color: TColor, x: number, y: number) => TColor`: Given each pixel's colour and position, returns what it becomes.

#### mirrorPixels (function, from `nacatamalon/pixels`)

```ts
mirrorPixels(pixels: TPixels, axis: 'x' | 'y', options?: TMirrorPixelsOptions) => TPixels
```

Copies one half of a picture onto the other, turned round: draw half of something symmetric, and
this draws the rest.

`'x'` copies the left half onto the right, `'y'` the top half onto the bottom. What the half
written over had is replaced, transparent pixels included. On an odd size the middle row or column
stays as it is.

- `pixels: TPixels`: The picture, changed in place.
- `axis: 'x' | 'y'`: Which way: `'x'` left onto right, `'y'` top onto bottom.
- `options?: TMirrorPixelsOptions`: The part of the picture: see `TMirrorPixelsOptions`.

#### outlinePixels (function, from `nacatamalon/pixels`)

```ts
outlinePixels(pixels: TPixels, color: TColorOption, options?: TOutlinePixelsOptions) => TPixels
```

Draws a border one pixel wide around everything that is not fully transparent. It goes on the
transparent pixels around the shape, never over the shape, and is worked out from the picture as
it was, so it never outlines itself.

The picture does not grow: a shape touching the edge has no outline on that side, so draw it with
a pixel of room around it.

- `pixels: TPixels`: The picture, changed in place.
- `color: TColorOption`: The border's colour.
- `options?: TOutlinePixelsOptions`: Its corners, and the part of the picture: see `TOutlinePixelsOptions`.

#### pixelsFromRows (function, from `nacatamalon/pixels`)

```ts
pixelsFromRows(rows: typeOperator, palette: TPixelPalette, options?: TPixelRowsOptions) => TPixels
```

A picture drawn as rows of characters, one a pixel, and a palette saying what colour each one is:
the way pixel art is written in code. A grid reads in a diff, is edited by hand, and is far
harder to get wrong than working out the corners of a `fillRect` for every patch of colour.

Top row first, every row as wide. `'.'` and `' '` are left transparent unless the palette names
them. A character missing from the palette, or a row of another width, throws, saying which row
and column.

It needs no game, so a test or a build script can draw one too. Make it a texture with
`createPixelTexture`; several frames for an animation are `sheetFromRows`.

- `rows: typeOperator`: The picture, top row first, one character a pixel.
- `palette: TPixelPalette`: What colour each character is.
- `options?: TPixelRowsOptions`: `scale`, to draw each character as a bigger square.

#### setPixel (function, from `nacatamalon/pixels`)

```ts
setPixel(pixels: TPixels, x: number, y: number, color: TColorOption) => TPixels
```

Sets one pixel. Outside the picture it does nothing.

- `pixels: TPixels`: The picture.
- `x: number`: From the left edge, in pixels.
- `y: number`: From the top edge, in pixels.
- `color: TColorOption`: What it becomes, replacing what was there.

#### shadeWithRamp (function, from `nacatamalon/pixels`)

```ts
shadeWithRamp(pixels: TPixels, ramps: typeOperator | typeOperator, options?: TShadeWithRampOptions) => TPixels
```

Gives flat colours a side lit and a side in shadow: each pixel on the edge of a shape that faces
the light moves one step up its ramp, and each one on the edge facing away moves one step down.
What is in between stays, and so does a pixel with an edge on both sides.

A ramp is a list of colours from darkest to lightest, and a colour in none of them is left alone,
so skin and cloth each shade along their own. The shape is everything that is not fully transparent.
Alpha stays as it was.

- `pixels: TPixels`: The picture, changed in place.
- `ramps: typeOperator | typeOperator`: One ramp, or a list of them, each colour as anything `getColor` reads. Colours are
- `options?: TShadeWithRampOptions`: Where the light comes from, and the part of the picture: see

#### sheetFromRows (function, from `nacatamalon/pixels`)

```ts
sheetFromRows(frames: Readonly<Record<string, typeOperator>> | typeOperator, palette: TPixelPalette, options?: TPixelRowsOptions) => TPixelSheet
```

Several frames drawn as rows, as `pixelsFromRows` draws one, laid side by side in one picture:
a sheet ready to be cut by `createSpriteAtlas`.

Given by name (`{ stand, walkA, walkB }`), `frames` says which number each one is, so a sprite's
animations name them instead of counting: `{ walk: { frames: [hero.frames.walkA, hero.frames.walkB] } }`. Given as a list, they are numbered in its order. Every frame is the same
size, and one that is not throws, naming it.

By name, they are laid in the order JavaScript keeps an object's names, which is the order they
were written in except that names which are whole numbers (`{ 2: … }`) come first. Frames that are
numbered are a list.

- `frames: Readonly<Record<string, typeOperator>> | typeOperator`: Each frame's rows, by name or in order.
- `palette: TPixelPalette`: What colour each character is, shared by every frame.
- `options?: TPixelRowsOptions`: `scale`, to draw each character as a bigger square.

#### snapToPalette (function, from `nacatamalon/pixels`)

```ts
snapToPalette(pixels: TPixels, palette: typeOperator, options?: TSnapToPaletteOptions) => TPixels
```

Moves every pixel to the nearest colour of a palette, by the same measure `paletteMatch` uses for
the screen. Alpha stays as it was, and what is fully transparent is left alone.

It is `paletteMatch` for one picture, while it is being made: a generated sprite that really has
only those colours, whatever it was drawn with.

- `pixels: TPixels`: The picture, changed in place.
- `palette: typeOperator`: The colours, as anything `getColor` reads: `BUILTIN.PALETTES.nes`, or a list of
- `options?: TSnapToPaletteOptions`: The part of the picture: see `TSnapToPaletteOptions`.

#### swapColors (function, from `nacatamalon/pixels`)

```ts
swapColors(pixels: TPixels, pairs: typeOperator) => TPixels
```

Replaces colours with others, exactly: every pixel that is one of the `from` colours, alpha
included, becomes its `to`. Every other pixel is left alone.

It is the palette swap of the consoles of the era: one drawn enemy, and the red, blue and gold
versions made from it, with no extra picture drawn. Swaps are worked out from the picture as it
was, so swapping red for blue and blue for red at once trades them rather than turning both blue.

- `pixels: TPixels`: The picture, changed in place.
- `pairs: typeOperator`: Each colour to replace, and what replaces it.

#### updatePixelTexture (function, from `nacatamalon/pixels`)

```ts
updatePixelTexture(texture: TTexture, region?: TPixelRegion) => void
```

Shows what has been painted on a picture since its texture was made or last updated.

Only the part asked for is sent to the graphics card, so a game that changes a little of a big
picture every frame (a crater, a revealed patch of map) passes `region` and stays cheap. Left out,
the whole picture is sent.

- `texture: TTexture`: A texture `createPixelTexture` made.
- `region?: TPixelRegion`: The rectangle that changed, in the picture's pixels. It is cut to the picture.

#### useLoadPixels (function, from `nacatamalon/pixels`)

```ts
useLoadPixels(options: TUseLoadPixelsOptions) => TLoadedPixels
```

Loads a drawn picture as pixels, to process it in code: a palette swap, a white silhouette for
a hit, an outline, a collision mask taken from the drawing, a level read from an image.

To just show a drawn picture, use `useLoadTexture`: that is the normal way, and faster, because
the picture goes straight to the graphics card. This is for when the game has to read or change
the drawing first. It reads PNG files, decoded in plain JavaScript with no canvas, so it works the
same in the browser and on the native runtime.

It hands back a record at once, still loading, the way every loader does; `useLoadProgress` counts it.
Give it straight to `createPixelTexture` with a function that changes it: the texture appears by
itself when the file arrives, and the function works on a copy. Once `status` is `'ready'`, its
`pixels` can be read as well (with `getPixel`) but not painted on: the picture is shared by every
scene that loads the same file.

- `options: TUseLoadPixelsOptions`: Where the file is, and the name to keep it under.

#### TColorLimitCell (type, from `nacatamalon/pixels`)

```ts
type TColorLimitCell
```

A part of a picture using more colours than it may.

- `x: number`: Where it starts, in pixels of the picture.
- `y: number`
- `width: number`
- `height: number`
- `colors: string[]`: The colours it uses, as `#rrggbb`, in the order they are met.

#### TColorLimitOptions (type, from `nacatamalon/pixels`)

```ts
type TColorLimitOptions
```

What `checkColorLimit` is asked to check.

- `colors: number`: How many colours each part may use. Fully transparent pixels do not count, and any other is
- `cell?: number | { width, height }`: The size of the parts: one number for a square, or `{ width, height }`. Left out, the whole

#### TDrawTextOptions (type, from `nacatamalon/pixels`)

```ts
type TDrawTextOptions: TMeasureTextOptions & { color, anchor, smooth }
```

How `drawText` writes: the font, the colour, and the same size and spacing a text on screen takes.
The same font and style measured by `measureText` are what it draws.

- `color?: TColorOption`: What the letters are painted in. Default white. A bitmap font's own colours are multiplied by
- `anchor?: { x, y }`: Which point of the block of text lands on `x, y`, from `0, 0` (its top-left corner, the
- `smooth?: boolean`: For a vector font: soft edges (the default) or hard ones, every pixel in or out, for a
- `font?: TBitmapFont | TFont | string`: A font from `useLoadFont` or `useLoadBitmapFont`, or the key one was loaded under. A key is
- `fontSize?: number`: How big the letters are, in pixels.
- `align?: 'left' | 'center' | 'right'`: Where each line sits: inside `maxWidth` when there is one, inside the width of the longest
- `maxWidth?: number`: How wide a line can be, in pixels, before its words go on to the next one: a dialogue box, a
- `letterSpacing?: number`: Extra pixels between two characters. Default `0`.
- `lineSpacing?: number`: Extra pixels between two lines. Default `0`.

#### TFillGradientOptions (type, from `nacatamalon/pixels`)

```ts
type TFillGradientOptions
```

What `fillGradient` is asked for.

- `from?: TColorOption`: The colour it starts at. With `to`, a gradient between two colours.
- `to?: TColorOption`: The colour it ends at.
- `colors?: typeOperator`: Several colours, evenly spaced, in place of `from` and `to`: a sunset from deep blue through
- `direction?: 'down' | 'right'`: `'down'` (default) runs from the top edge to the bottom one; `'right'` from left to right.
- `bands?: number`: How many flat steps it is cut into, the way a sky was drawn when a game had few colours to
- `dither?: boolean`: Breaks the line between two steps with the ordered pattern the `dither()` effect uses, so the
- `region?: TPixelRegion`: Only this rectangle is painted, and the gradient runs across it. Left out, the whole picture.

#### TFillNoiseOptions (type, from `nacatamalon/pixels`)

```ts
type TFillNoiseOptions
```

What `fillNoise` is asked for.

- `seed?: number`: Which noise. The same seed always gives the same picture. Default `1`.
- `scale?: number`: How big its blobs are, in pixels. Default `8`.
- `octaves?: number`: How many layers of finer detail are added, each half the size and half as strong. Default `1`;
- `from?: TColorOption`: The colour of the lowest values. Default black.
- `to?: TColorOption`: The colour of the highest values. Default white.

#### TLoadedPixels (type, from `nacatamalon/pixels`)

```ts
type TLoadedPixels
```

A picture file loaded as pixels, to process in code: what `useLoadPixels` hands back.

`pixels` is `null` until `status` is `'ready'`. Read it as much as you like (a collision mask, a
level drawn as an image); to change it, hand the record to `createPixelTexture` with a function,
which paints on a copy. The picture is shared by everyone who loaded the same file, so painting
on it directly would change it for all of them.

- `type: 'loaded-pixels'`
- `key: string`: What it is cached under in this game. The `src` unless a `key` was given.
- `src: string`: Where the file comes from.
- `status: TLoadStatus`
- `pixels: TPixels | null`: The picture, once it has arrived.

#### TMirrorPixelsOptions (type, from `nacatamalon/pixels`)

```ts
type TMirrorPixelsOptions
```

What `mirrorPixels` can be told.

- `region?: TPixelRegion`: Mirrors only this part of the picture, about its own middle: one frame of a sheet. What falls

#### TOutlinePixelsOptions (type, from `nacatamalon/pixels`)

```ts
type TOutlinePixelsOptions
```

What `outlinePixels` can be told.

- `corners?: boolean`: Fills the corners too, so a diagonal edge gets a thicker, closed border. Default `false`: only
- `region?: TPixelRegion`: Keeps it to this part of the picture, which is looked at as if it were the whole picture: on a

#### TPixelPaint (type, from `nacatamalon/pixels`)

```ts
type TPixelPaint: (pixels: TPixels) => TPixels | void
```

What `createPixelTexture` hands a picture to before uploading it: change it in place, or return a
picture of your own.

#### TPixelPalette (type, from `nacatamalon/pixels`)

```ts
type TPixelPalette: Record<string, TColorOption>
```

What each character of a drawing is: one character to one colour, as anything `getColor` reads
(`'#f80058'`, `'pink'`, `0xf80058`, `{ r, g, b, a }`), or `'transparent'`.

`'.'` and `' '` are nothing unless the palette says otherwise, so a drawing does not have to name
its gaps.

#### TPixelRegion (type, from `nacatamalon/pixels`)

```ts
type TPixelRegion
```

A rectangle of a picture, in its pixels, from its top-left corner.

- `x: number`
- `y: number`
- `width: number`
- `height: number`

#### TPixelRowsOptions (type, from `nacatamalon/pixels`)

```ts
type TPixelRowsOptions
```

How a drawing written as rows is laid into pixels, for `pixelsFromRows` and `sheetFromRows`.

- `scale?: number`: Each character drawn as a square this many pixels across. A whole number, `1` by default.

#### TPixels (type, from `nacatamalon/pixels`)

```ts
type TPixels
```

A picture held in memory, to paint on in code and then hand to `createPixelTexture`: the engine's
replacement for drawing on a `<canvas>`, which only exists in a browser. Drawn art is loaded with
`useLoadTexture` instead; this is for pictures that are generated.

Four bytes a pixel (red, green, blue, alpha, each `0` to `255`), row after row, with row `0` at the
top: the same way round as an image file and as the game's own `y`. Nothing in it touches the
graphics card, the page or a game, so the same picture can be painted in a scene, in a test, in a
build script or on the native runtime and comes out byte for byte the same.

`data` is yours to read and write directly when the painting functions are not enough; it is
`width × height × 4` long and never replaced.

- `type: 'pixels'`
- `width: number`
- `height: number`
- `data: Uint8Array`

#### TPixelSheet (type, from `nacatamalon/pixels`)

```ts
type TPixelSheet
```

A sheet of frames drawn from rows, side by side in one picture.

- `pixels: TPixels`: Every frame, left to right, in the order they were given.
- `columns: number`: How many frames there are, which is what `createSpriteAtlas` asks for as `columns`.
- `frames: Record<string, number>`: Which frame each name is, counted from `0`, for a sprite's animations: `[hero.frames.walkA, hero.frames.walkB]`. Frames given as a list have no names, and this is empty.
- `frameWidth: number`: The size of one frame, in pixels, after `scale`.
- `frameHeight: number`

#### TPixelTextureOptions (type, from `nacatamalon/pixels`)

```ts
type TPixelTextureOptions
```

What `createPixelTexture` is asked for.

- `key?: string`: A name, so the texture outlives the scene that made it and another scene can find it with

#### TShadeWithRampOptions (type, from `nacatamalon/pixels`)

```ts
type TShadeWithRampOptions
```

What `shadeWithRamp` can be told.

- `light?: { x, y }`: Where the light comes from, as a direction on the picture: `{ x: -1, y: -1 }` is the top left,
- `region?: TPixelRegion`: Only this part of the picture, which is looked at as if it were the whole picture: one frame of

#### TSnapToPaletteOptions (type, from `nacatamalon/pixels`)

```ts
type TSnapToPaletteOptions
```

What `snapToPalette` can be told.

- `region?: TPixelRegion`: Only this part of the picture.

#### TUseLoadPixelsOptions (type, from `nacatamalon/pixels`)

```ts
type TUseLoadPixelsOptions
```

What `useLoadPixels` is asked for.

- `src: string`: Where the PNG is.
- `key?: string`: What to keep it under, so another scene can reach the same one. Defaults to `src` as the page

### Audio

Sound, music in layers, zones and the listener.

#### parseSongDoc (function, from `nacatamalon/files`)

```ts
parseSongDoc(raw: unknown, src: string) => TSongDoc
```

Reads the contents of a `.song` file, already parsed from JSON, into a song.

It refuses what it could not play (no tempo, a bar that is not two whole numbers, a voice the
console does not have) with an error that names the file. The notes themselves are not checked
here: `renderSong` reads them and says where each mistake is, without refusing the song.

- `raw: unknown`: The file's JSON.
- `src: string`: Where it came from, for the errors.

#### renderSfx (function, from `nacatamalon`)

```ts
renderSfx(options: TSfxOptions, sampleRate: number) => Float32Array
```

Makes a sound effect from a preset, the way the 8-bit consoles made theirs: a coin, a jump, a hit,
an explosion; and for a game with people in it, a crowd, a cheer with clapping, a referee's
whistle, a wooden bat on a ball. A seed is one take on the preset, always the same one; another
seed, another take. `crowd` and `cheer` are long, for under a scene: `length` makes them as long as
needed.

Nothing of the browser is used, so the same options give the same samples anywhere.

- `options: TSfxOptions`: Which preset, and how it differs from it.
- `sampleRate: number`: Samples per second. Default `44100`.

#### renderSong (function, from `nacatamalon`)

```ts
renderSong(doc: TSongDoc, sampleRate: number, options: { transpose, samples }) => TRenderedSong
```

Turns a song into sound: one track per voice, of samples between `-1` and `1`, all the same length
so they play together as layers and can be mixed into one.

It needs nothing of the browser, so the same song sounds the same in a game, in the editor that
wrote it, and in a test that measures it. Given the same song it gives back the same samples,
every time.

Whatever in the text could not be read is in `errors`, with where it was, and the rest is played:
a wrong note costs that note, not the song. What could not be read in its instruments is in
`instrumentErrors`.

An instrument is the song's own when it has one of that name, and otherwise the engine's
(`BUILTIN.MUSIC.INSTRUMENTS`); `nacatamalon:` in front of a name always means the engine's.

- `doc: TSongDoc`: The song.
- `sampleRate: number`: Samples per second. Default `44100`.
- `options: { transpose, samples }`: `transpose`: semitones up (or down, below zero) for every voice but the noise and

#### songVoicesOf (function, from `nacatamalon`)

```ts
songVoicesOf(chip: TSongChip | undefined) => typeOperator
```

The voices of a song's console.

- `chip: TSongChip | undefined`: The song's `chip`. Default `'nes'`.

#### soundOf (function, from `nacatamalon`)

```ts
soundOf(box: TBox, name: string) => THandle | null
```

The handle of a sound a box carries, found by its name: the component's id, or its file (`crowd`,
`/sfx/crowd.wav`, or the key it was loaded under). For music, any of its layers' files; for a song,
the `.song`. `null` when the box carries none by that name.

What lets a behaviour play a sound laid out in the scene, where a person can choose its file, its
volume and whether it loops, instead of naming the file in code:
registerScript('title-screen', (self) => {
    useUpdate(() => {
        if (pressed) soundOf<TSongHandle>(self, 'title_theme')?.stop();
    });
});

The scene builds a box's components in order, so a sound written after the behaviour is not there
yet while the behaviour starts: ask for it when it is played.

- `box: TBox`: The box that carries the sound.
- `name: string`: The component's id, or the file it plays.

#### useAudio (function, from `nacatamalon`)

```ts
useAudio() => TAudioHandle
```

The game's volumes: the general one and one per channel, plus a way to silence everything.

A channel is a group of sounds that share a volume. `'sfx'` and `'music'` are there from the
start, and any other name works the moment a sound uses it. This is what the sliders of an
options screen move, and the numbers a game saves in its store so they are still there next time.

Volumes go from `0` (silent) to `1` (as recorded); above `1` is allowed and can distort.

#### useAudioListener (function, from `nacatamalon`)

```ts
useAudioListener(options: { enabled }) => TAudioListener
```

Puts the game's ears on this object: every sound placed in the world is heard from where it is,
and in a 3D scene, facing the way it faces.

Without it the game is heard from the camera, which is right until the camera is not where the
player is. A game seen from behind its character wants the ears on the character: a bird to the
character's left is then heard on the left, wherever the camera has swung round to.

One per object. With several switched on the first one is used, and that is said once; switch one
off with `enabled` to hand over to the next, and in the end back to the camera.

- `options: { enabled }`: Whether it starts on. Default on.

#### useMusic (function, from `nacatamalon`)

```ts
useMusic(options: TMusicOptions) => TMusicHandle
```

Plays one piece of music recorded in several files (its layers, or stems) that sound together,
and hands back the controls for it.

What it is for: music that changes with the game without starting over: the drums come in for
a fight, the tune goes muffled under water. Not for: a single recording (that is `useSound`), or
a tune written as notes in a `.song` (that is `useSong`, which can also drop a voice, change key and
speed up).

The layers are the same tune played by different instruments, one file each and all the same
length. They always play together and in time; what the game changes is how loud each one is.
That is how music follows the place without ever starting over: walk into the water and the tune
goes muffled, start a fight and the drums come in, and the melody never loses its place.

It belongs to the channel `'music'` unless it says otherwise, so an options screen moves it with
the rest. Like every sound, it stops with the scene or the object that asked for it.

With `loopStart` it has an intro that plays once, and with `bpm` the game can follow its beat.

Music laid out in a scene document (a `music` component) is made by this same hook; a behaviour
gets its controls with `soundOf(box, name)`. In a scene shown rather than played (an editor's
viewport), `autoplay` waits for the game to play.

- `options: TMusicOptions`: The layers by name, each with the volume it starts at, and the piece's volume,

#### useSong (function, from `nacatamalon`)

```ts
useSong(song: TSongDoc | TSongFile | TSongAccompaniment, options: TSongOptions) => TSongHandle
```

Plays a song written as notes (a `.song` file, or one written in code) with the engine's 8-bit
synthesizer, and hands back the controls.

What it is for: chiptune music the game can change while it plays: faster for the last inning,
in another key, with a voice dropped. Not for: a recorded file (that is `useSound`, or
`useMusic` for one recorded in layers).

It does not load the file: `useLoadSong({ src })` does, and its result is given here,
`useSong(useLoadSong({ src: '/music/title.song' }))`.

The song is turned into sound here, in the game, as it plays: a moment ahead of what is heard, one
voice of an 8-bit console at a time, the way the console played it. So a whole tune weighs a few
kilobytes instead of megabytes, starts at once and holds almost nothing while it plays, and what a
recording cannot do, this can: it can speed up for the last inning, change key to build
tension and come back, and drop a voice without a file per voice. Each change is heard at
once, carrying on from the beat it was on.

The file says where the loop goes back to, so a song can have an intro that plays once. It is in
the channel `'music'` unless it says otherwise, and it stops with the scene or the object that
asked for it.

A song can be sung over: `accompany(vocal, song)` from `nacatamalon/speech` hands it here with a
vocal part, which is made with it as it plays, in a voice of its own (`vocal`), at its tempo and in
its key.

A song laid out in a scene document (a `song` component, chosen in an editor) is made by this same
hook; a behaviour gets its controls with `soundOf(box, name)`. In a scene shown rather than played
(an editor's viewport), `autoplay` waits for the game to play.

- `song: TSongDoc | TSongFile | TSongAccompaniment`: A song from `useLoadSong`, a `TSongDoc` written in code, or either with a vocal part
- `options: TSongOptions`: Its volume, channel, each voice's volume, tempo and key, and whether it starts on

#### useSound (function, from `nacatamalon`)

```ts
useSound(source: TSoundSource, options: TSoundOptions) => TSoundHandle
```

Plays one audio file (`.wav`, `.mp3`, `.ogg`), or an effect made from a preset, and hands back
the controls for it.

What it is for: sound effects (a jump, a coin, a bat hitting the ball), an ambience that loops
(rain, a crowd, an engine), and music that is a single recording. Not for: music in layers that
come in and out (that is `useMusic`), or a tune written as notes in a `.song` (that is `useSong`).

It loads the file itself when given `{ src }`, so one line is enough:
`useSound({ src: '/sfx/jump.wav' })`.

Called in the body of a scene, like every hook; the controls are used whenever, from an update, a
click, a signal. When the scene stops or the object that asked for it is destroyed, its sound
stops: a level's music does not survive the level.

A one-off (the default) can sound on top of itself, which is what effects need: two coins picked
up at once are heard twice. A looping sound is a single thing that `pause`, `resume` and `stop`
act on: music, rain, an engine.

With `spatial` it is heard from where the object that asked for it is, and follows it; with `zone`
it fills an area around the object instead, the way birds fill a meadow. Starting and stopping
can fade, and so can the volume.

Pausing the game or the scene does not stop a sound, because a menu over a frozen level often
wants the music to carry on. To silence something on pause, listen with `useScene().onPaused` or
`useGame().onPaused` and decide for each sound.

A sound laid out in a scene document (a `sound` component, chosen in an editor) is made by this same
hook; a behaviour gets its controls with `soundOf(box, name)`. In a scene shown rather than played
(an editor's viewport), `autoplay` waits for the game to play.

- `source: TSoundSource`: A clip from `useLoadAudio`, `{ src }` to load it here, `{ sfx }` to make an
- `options: TSoundOptions`: Volume, looping, speed, channel, and placing it in the world: at a point, facing a

#### TAudioHandle (type, from `nacatamalon`)

```ts
type TAudioHandle
```

The volumes of a game, and the way to silence it. Returned by `useAudio`.

A channel is a group of sounds that share a volume: `'sfx'` and `'music'` are there from the
start, and any other name works the moment a sound uses it. This is what an options screen with a
slider per kind of sound is made of, and what a game saves in its store.

- `setVolume: `
- `getVolume: `
- `stopAll: `
- `setOffset: `
- `getOffset: `

#### TAudioListener (type, from `nacatamalon`)

```ts
type TAudioListener
```

Where the game is heard from, put on an object: its ears.

Without one the ears are in the camera, which is right for most games. A game seen from behind
its character wants them on the character instead, or a bird on the left of the character is
heard on whichever side of the camera it happens to be.

- `type: 'audio-listener'`
- `id: string`
- `enabled: boolean`: Off, the ears go back to the next in line, and in the end to the camera.

#### TAudioMeasure (type, from `nacatamalon`)

```ts
type TAudioMeasure
```

What a piece of sound measures: what tells you, without hearing it, whether it is too loud, too
quiet, or broken.

- `duration: number`: In seconds.
- `peak: number`: The loudest sample, `0` to `1`, and the same in decibels below full scale.
- `peakDb: number`
- `rms: number`: How loud it is on average, `0` to `1`, and in decibels.
- `rmsDb: number`
- `clipped: number`: How many samples reached full scale, where a speaker can go no further and the sound distorts.

#### TBeatListener (type, from `nacatamalon`)

```ts
type TBeatListener: (beat: number, bar: number) => void
```

What a beat is heard as: which beat of the bar (from `1`) and which bar of the tune (from `1`).

#### TFmOperator (type, from `nacatamalon`)

```ts
type TFmOperator
```

One operator of an FM voice: a sine wave with its own envelope, which either is heard or bends
the operators after it, as its voice's `algorithm` says.

- `multiple?: number`: Its frequency as a multiple of the note's: `1` the note, `2` an octave up, `0.5` one down.
- `detune?: number`: A hair off its multiple, in cents, for a richer, beating sound. Default `0`.
- `level?: number`: How strong it is, `0` to `1`: how loud when it is heard, how much it bends when it bends
- `attack?: number`: Seconds to come in, seconds to fall to `sustain` (`0` to `1`), and seconds to die away once
- `decay?: number`
- `sustain?: number`
- `release?: number`

#### TFmPatch (type, from `nacatamalon`)

```ts
type TFmPatch
```

An FM voice of the Genesis: four operators joined one of eight ways.

- `algorithm: number`: How the four operators are joined, `0` to `7`, as the console numbers them: `0` each bends the
- `feedback?: number`: How much the first operator bends itself, `0` to `7`: grit, up to a buzz. Default `0`.
- `operators: [TFmOperator, TFmOperator, TFmOperator, TFmOperator]`

#### TMusicAttachment (type, from `nacatamalon`)

```ts
type TMusicAttachment
```

A piece of music in layers attached to one object: which files, and how loud each is meant to
be. What it keeps is the intention, never how far into the tune it had got.

- `type: 'music'`
- `id: string`
- `layers: TMusicLayer[]`
- `volume: number`
- `channel: string`
- `autoplay: boolean`
- `fadeIn: number`: Seconds, `0` for none: see the options.
- `fadeOut: number`
- `loopStart: number`: `0` for both is the whole tune.
- `loopEnd: number`
- `bpm: number | null`: `null` when nobody said, and then it has no beats to count.
- `beatsPerBar: number`
- `firstBeat: number`

#### TMusicHandle (type, from `nacatamalon`)

```ts
type TMusicHandle
```

Controls a piece of music made with `useMusic`.

- `play: `
- `stop: `
- `pause: `
- `resume: `
- `setVolume: `
- `setLayer: `
- `getLayer: `
- `onBeat: `
- `position: number | null`: How far into the music the player is hearing now, in seconds, or `null` while it is stopped:
- `beat: number | null`: How far into the music the player is hearing now, in beats from its first (`firstBeat`), or
- `latency: number`: How long a sound started now takes to be heard, in seconds: the device's delay and the
- `positionAt: `
- `playing: boolean`

#### TMusicLayer (type, from `nacatamalon`)

```ts
type TMusicLayer
```

One layer, as the object carries it.

- `name: string`
- `clip: TAudioClip`
- `volume: number`
- `pan?: number`: Where between left (`-1`) and right (`1`) it is heard. Absent, in the middle.

#### TMusicLayerOptions (type, from `nacatamalon`)

```ts
type TMusicLayerOptions: { src, key } | { clip } & { volume }
```

One layer of a piece of music: a file, or a clip already loaded, and how loud it starts.

- `volume?: number`: `0` silent, `1` as recorded. Default `1`.

#### TMusicOptions (type, from `nacatamalon`)

```ts
type TMusicOptions
```

How a piece of music in layers plays.

- `id?: string`: The same name the saved scene keeps. Made up when nobody says.
- `layers: Record<string, TMusicLayerOptions>`: The layers, by name: the same tune played by different instruments, each in its own file and
- `volume?: number`: The volume of the whole piece. Default `1`.
- `channel?: string`: Default `'music'`.
- `autoplay?: boolean`: Starts as soon as every layer is ready, with no `play()`. Default `false`. In a scene coming in with a
- `fadeIn?: number`: Seconds that `autoplay` takes to come up from silence. Default `0`, at full volume at once.
- `fadeOut?: number`: Seconds it takes to go down to silence when what asked for it goes away: its scene stops, its
- `loopStart?: number`: Where the loop goes back to, in seconds into the tune. Everything before it is an intro that
- `loopEnd?: number`: Where the loop turns back, in seconds into the tune. Default `0`, the end of the files.
- `bpm?: number`: The tune's tempo in beats per minute, which is what lets `onBeat` count it. A file does not
- `beatsPerBar?: number`: How many beats a bar has. Default `4`.
- `firstBeat?: number`: Seconds into the file where the first beat falls, for a file with silence or a pickup before

#### TRenderedSong (type, from `nacatamalon`)

```ts
type TRenderedSong
```

A song turned into sound: one track per voice, all the same length so they can play together.

- `sampleRate: number`
- `length: number`: In samples, every voice's track this long. Rounded up to whole bars, so a loop lands on one.
- `duration: number`: In seconds.
- `loopSample: number`: Where the loop goes back to, in samples and in seconds: what `loopStart` wants.
- `loopStart: number`
- `voices: Partial<Record<TSongVoiceName, Float32Array>>`
- `echo?: Float32Array`: The SNES's echo, a track of its own beside the voices, for a song that has it.
- `notes: TSongNote[]`: Every note, in the order the voices were read, for whoever wants to draw them.
- `errors: TSongError[]`: Whatever could not be read. What could is still played.
- `instrumentErrors: TSongInstrumentError[]`: Whatever in its instruments could not be read. The rest of each instrument is still used.

#### TSampledSound (type, from `nacatamalon`)

```ts
type TSampledSound
```

A sound made in code: one channel of samples from -1 to 1, and how many of them make a second.

For what no preset makes and no file holds: an engine whose note is worked out from the
revolutions, a tone built from the level's seed. The samples are copied when the sound is made, so
changing the array afterwards changes nothing; make another sound for another tone. Any rate works,
the audio engine converts it, and the era's own were low: 22050 or 11025 is plenty for a buzz.

Nothing is written to a saved scene for it, since there is no file to name, the same as `{ sfx }`.

- `samples: Float32Array`
- `sampleRate: number`
- `key?: string`: What it is called where sounds are listed (an editor, a log of what the game played). Default

#### TSfxOptions (type, from `nacatamalon`)

```ts
type TSfxOptions
```

What sound effect to make, and how it differs from the preset as it comes.

- `preset: TSfxPreset`
- `seed?: number`: Each seed is a slightly different take on the same preset: the same seed, the same sound.
- `pitch?: number`: Higher or lower than the preset, as a multiple: `2` is an octave up. Default `1`.
- `length?: number`: How long it lasts, in seconds. Default, what the preset lasts.
- `volume?: number`: How loud it peaks, `0` to `1`. Default `0.8`.

#### TSfxPreset (type, from `nacatamalon`)

```ts
type TSfxPreset: 'coin' | 'jump' | 'hit' | 'explosion' | 'crowd' | 'cheer' | 'whistle' | 'bat'
```

The kinds of sound effect `renderSfx` makes: `coin`, `jump`, `hit`, `explosion`; `crowd` (people
murmuring), `cheer` (an ovation with clapping), `whistle` (a referee's) and `bat` (wood on a ball).

#### TSongAccompaniment (type, from `nacatamalon`)

```ts
type TSongAccompaniment
```

A song with something sung or played over it, made into sound with it as it plays: what
`accompany` from `nacatamalon/speech` hands to `useSong`. The song is played as it always is, and
what goes over it comes in a voice of its own, at the song's tempo and in its key, following them
when they change.

- `type: 'song-accompaniment'`
- `song: TSongFile | TSongDoc`: The song played under it.

#### TSongAttachment (type, from `nacatamalon`)

```ts
type TSongAttachment
```

A song attached to one object: which file, and how it was asked to play.

- `type: 'song'`
- `id: string`
- `file: TSongFile`
- `volume: number`
- `channel: string`
- `autoplay: boolean`
- `fadeIn: number`: Seconds, `0` for none: see the options.
- `fadeOut: number`
- `voices: Partial<Record<TSongVoiceName, number>>`
- `pan: Partial<Record<TSongVoiceName, number>>`: Where each voice is heard, left (`-1`) to right (`1`), only those changed from the file's.
- `tempo: number | null`: `null` for what the file says.
- `transpose: number`

#### TSongBuiltinInfo (type, from `nacatamalon`)

```ts
type TSongBuiltinInfo
```

One song the engine has: what it is, who wrote it, which console it is arranged for, and what a
game might play it for.

- `key: string`: What a game asks for it by, after `nacatamalon:`: `useLoadSong({ src: 'nacatamalon:canon' })`.
- `label: string`
- `composer: string`
- `year: number`: When it was written, near enough.
- `chip: TSongChip`
- `description: string`
- `uses: string[]`

#### TSongChip (type, from `nacatamalon`)

```ts
type TSongChip: 'nes' | 'snes' | 'genesis'
```

Which console a song sounds like, and so which voices it has:

- `'nes'`, the default: `pulse1`, `pulse2`, `triangle`, `noise` and `dpcm`.
- `'snes'`: eight voices, `s1` to `s8`, each playing a sample (an instrument's `sample`), with
the console's grain, its envelopes and its echo.
- `'genesis'`: six FM voices, `fm1` to `fm6` (an instrument's `fm`), and the three squares and
noise of its second chip, `psg1` to `psg3` and `psgNoise`.

#### TSongDoc (type, from `nacatamalon`)

```ts
type TSongDoc
```

A song as the text of a `.song` file: the tempo, the bar, where it loops, and what each voice plays.

Small enough to write by hand and to read back, and played the same in the editor, in a browser
and on the desktop runtime, because it is turned into sound by the same function everywhere.

- `format: number`
- `tempo: number`: Beats per minute, a beat being the note the bar counts in (a quarter in 3/4).
- `meter: [number, number]`: How many beats a bar has, and which note is a beat: `[3, 4]` is a minuet's three quarters.
- `loop?: { bar }`: The bar the song goes back to when it reaches the end, counted from `1`. Everything before it
- `voices: Partial<Record<TSongVoiceName, TSongVoice>>`
- `instruments?: Record<string, TSongInstrument>`: The instruments its voices play with, by name.
- `chip?: TSongChip`: Which console it sounds like, and so which voices it has. Default `'nes'`.
- `echo?: { delay, feedback, volume, filter }`: The SNES's echo, which the instruments with `echo` go into: how far behind it comes (seconds,

#### TSongError (type, from `nacatamalon`)

```ts
type TSongError
```

Something wrong in the text of a voice, and where: the line and column inside that voice's
`notes`, both counted from `1`.

- `voice: TSongVoiceName`
- `line: number`
- `column: number`
- `message: string`

#### TSongHandle (type, from `nacatamalon`)

```ts
type TSongHandle
```

Controls a song made with `useSong`.

- `play: `
- `stop: `
- `pause: `
- `resume: `
- `setVolume: `
- `setVoice: `
- `getVoice: `
- `setPan: `
- `pan: Record<TSongVoiceName, number>`: Where each voice is heard, left (`-1`) to right (`1`). Written, a voice moves there at once, so
- `setTempo: `
- `setTranspose: `
- `tempo: number`: The beats per minute it plays at now.
- `position: number | null`: How far into the song the player is hearing now, in seconds, or `null` while it is stopped.
- `beat: number | null`: How far into the song the player is hearing now, in beats from its start, or `null` while it
- `latency: number`: How long a sound started now takes to be heard, in seconds: the delay the device reports on
- `positionAt: `
- `onBeat: `
- `errors: typeOperator`: What in the notes could not be read. Empty until the song is ready, and when it is right.
- `notes: typeOperator`: The notes it plays, every voice's, in the order they start: each with its `voice`, its
- `playing: boolean`

#### TSongInstrument (type, from `nacatamalon`)

```ts
type TSongInstrument
```

How a note sounds over its life, the way an 8-bit tracker describes it: lists of numbers read one
a frame (a sixtieth of a second), from the moment the note starts.

Each list is numbers separated by spaces. Past its end it keeps its last number, unless a `|`
marks where it starts again: `'15 12 | 9 8'` falls to 9 and then goes 8 9 8 9…, `'| 0 4 7'`
repeats from the start for as long as the note lasts.

- `volume?: string`: How loud, `0` to `15` a frame: `'15 12 9 6 3 0'` is a pluck, `'15 14 13 12 | 11'` a note
- `duty?: string`: The colour of a square wave a frame: `0` 12.5 %, `1` 25 %, `2` 50 %, `3` 75 %. Square voices
- `arpeggio?: string`: Semitones added a frame, for the chords an 8-bit console plays one note at a time:
- `pitch?: string`: Semitones added a frame, fractions allowed, for a note that bends as it starts: a kick that
- `vibrato?: { speed, depth, delay }`: A wobble in pitch: how many times a second, how far in semitones, and how long the note is
- `slide?: number`: How long, in seconds, each note takes to glide from the note before it in its voice.
- `adsr?: { attack, decay, sustain, release }`: How a sample or an FM voice comes in and goes out: seconds to full, seconds to fall to
- `echo?: boolean`: Whether its notes go into the song's `echo`. SNES voices only.
- `root?: number`: The note a sample of the game sounds as it was recorded, on a SNES voice: `60` is middle C,
- `loop?: number`: Where, in seconds, a sample of the game starts again once it reaches its end, on a SNES voice,
- `fm?: TFmPatch`: The four operators of a Genesis FM voice.
- `sample?: string`: The recording the `dpcm` voice plays: a sample the engine has (`'nacatamalon:kick'`: see

#### TSongInstrumentError (type, from `nacatamalon`)

```ts
type TSongInstrumentError
```

Something wrong in an instrument, and where: which instrument, which of its fields, and for a
list, which of its words, counted from `1`.

- `instrument: string`
- `field: TSongInstrumentList | 'vibrato' | 'slide' | 'sample' | 'adsr' | 'fm' | 'root' | 'loop'`
- `position?: number`: The word of the list, from `1`. Absent for a field that is not a list.
- `message: string`

#### TSongInstrumentInfo (type, from `nacatamalon`)

```ts
type TSongInstrumentInfo
```

One instrument the engine has: what it is called, what it is for, which voices it is meant for,
and the instrument itself.

- `key: string`: What the notes call it: `@lead`, or `@nacatamalon:lead` to say it is the engine's.
- `label: string`
- `description: string`
- `voices: typeOperator`: The voices it sounds right on. Any voice can play it.
- `instrument: TSongInstrument`

#### TSongInstrumentList (type, from `nacatamalon`)

```ts
type TSongInstrumentList: 'volume' | 'duty' | 'arpeggio' | 'pitch'
```

The lists of an instrument, by the name its field has.

#### TSongNote (type, from `nacatamalon`)

```ts
type TSongNote
```

One note a voice plays, worked out from its text: when, for how long, and which.

- `voice: TSongVoiceName`
- `start: number`: Counted in beats from the start of the song.
- `beats: number`
- `instrument?: string`: The instrument it is played with, by name, when it has one.
- `effects?: TSongNoteEffects`: What it does on its own, written after it in brackets.
- `pitch: number`: As a MIDI number: `60` is middle C, `69` the A of 440 Hz. A drum hit is `72`.

#### TSongNoteEffects (type, from `nacatamalon`)

```ts
type TSongNoteEffects
```

What one note does on its own, written after it in brackets.

- `slide?: number`: Seconds it takes to glide in from the note before it in its voice.
- `vibrato?: { speed, depth, delay }`
- `arpeggio?: number[]`: Semitones played one a frame, round and round.
- `volume?: number`: How loud, `0` to `15`, on top of what its instrument says.

#### TSongOptions (type, from `nacatamalon`)

```ts
type TSongOptions
```

How a song written as notes plays.

- `id?: string`: The same name the saved scene keeps. Made up when nobody says.
- `volume?: number`: The volume of the whole song. Default `1`.
- `channel?: string`: Default `'music'`.
- `autoplay?: boolean`: Starts as soon as the song is ready, with no `play()`. Default `false`. In a scene coming in with a
- `fadeIn?: number`: Seconds that `autoplay` takes to come up from silence. Default `0`, at full volume at once.
- `fadeOut?: number`: Seconds it takes to go down to silence when what asked for it goes away: its scene stops, its
- `voices?: Partial<Record<TSongVoiceName, number>>`: How loud each voice starts, `0` to `1`. A voice not named starts at `1`.
- `pan?: Partial<Record<TSongVoiceName, number>>`: Where between left (`-1`) and right (`1`) each voice is heard, only those not where the file
- `tempo?: number`: Beats per minute, instead of what the file says.
- `transpose?: number`: Semitones up, or down below zero, for every voice but the drums. Default `0`.

#### TSongSample (type, from `nacatamalon`)

```ts
type TSongSample
```

A recording, as its samples, for the `dpcm` voice.

- `samples: Float32Array`
- `sampleRate: number`
- `root?: number`: The note it sounds as it is, on a SNES voice. Default `60`, middle C.
- `loopStart?: number`: Where, in samples, it starts again once it reaches its end, so a note can be held. Absent, it

#### TSongSampleInfo (type, from `nacatamalon`)

```ts
type TSongSampleInfo
```

One of the engine's samples: its name, what it is, and how it is made.

- `key: string`: What an instrument's `sample` calls it, after `nacatamalon:`.
- `label: string`
- `description: string`

#### TSongVoice (type, from `nacatamalon`)

```ts
type TSongVoice
```

One voice of a song: what it plays, written as text, and how.

- `notes: string`: The notes, separated by spaces, read left to right:
- `volume?: number`: `0` silent, `1` as loud as this voice goes. Default `1`. The four voices are mixed so that all
- `duty?: 0.125 | 0.25 | 0.5 | 0.75`: How much of each cycle a square wave spends up, which is its colour: `0.125` thin and
- `pan?: number`: Where it is heard, from left (`-1`) to right (`1`). Default `0`, the middle. The Genesis put
- `instrument?: string`: The instrument its notes start with, by its name in the song's `instruments`. `@name` in the

#### TSongVoiceName (type, from `nacatamalon`)

```ts
type TSongVoiceName: 'pulse1' | 'pulse2' | 'triangle' | 'noise' | 'dpcm' | 's1' | 's2' | 's3' | 's4' | 's5' | 's6' | 's7' | 's8' | 'fm1' | 'fm2' | 'fm3' | 'fm4' | 'fm5' | 'fm6' | 'psg1' | 'psg2' | 'psg3' | 'psgNoise'
```

The voices of an 8-bit console: two square waves for the tune and the harmony, a triangle for the
bass, noise for the drums, and `dpcm`, which plays short recorded samples (a drum kit, a hit) at
the console's own grain.

#### TSoundAttachment (type, from `nacatamalon`)

```ts
type TSoundAttachment
```

One sound attached to one object: which clip, and how it was asked to play.

What it holds is the intention, never what is sounding. A scene saved while the music was
halfway through comes back ready to start, not halfway through: where a voice had got to is a
fact about this run, and a level is not.

It carries the clip itself rather than its name, the way a sprite carries its texture, so that
whoever writes the scene down can name the file and ask for it to be fetched again in one step.

- `type: 'sound'`
- `id: string`
- `clip: TAudioClip`
- `volume: number`
- `loop: boolean`
- `loopStart: number`: `0` for both is the whole file.
- `loopEnd: number`
- `rate: number`
- `pan: number`: Where between left (`-1`) and right (`1`) it is heard.
- `channel: string`
- `autoplay: boolean`
- `spatial: boolean`
- `refDistance: number | null`: `null` until somebody chooses one: then it is what the scene measures in.
- `maxDistance: number | null`
- `cone: TSoundCone | null`
- `zone: TSoundZone | null`
- `duck: TSoundDuck | null`

#### TSoundCone (type, from `nacatamalon`)

```ts
type TSoundCone
```

Where a sound is heard loudest: a speaker, a siren, a radio on a shelf. Straight ahead of its
object it plays at full volume, and further round it drops towards `outerVolume`.

Ahead is the way the object faces: its -Z in a 3D scene, the same way a camera and a lamp face, and
along its turn in a flat one (to the right when it is not turned).

- `inner: number`: How wide the loud part is, in degrees, from edge to edge.
- `outer: number`: Past this width, in degrees, it is at its quietest. Between the two it fades.
- `outerVolume: number`: How loud it is behind, from `0` (silent) to `1` (the same as ahead).

#### TSoundDuck (type, from `nacatamalon`)

```ts
type TSoundDuck
```

Another channel made quieter while a sound plays: the music going down under a cheer, a voice, an
alarm, and coming back up on its own once it is over.

It moves a volume of its own behind the channel, never the channel's: the one an options screen
set is still there when the music comes back.

- `channel: string`: The channel that goes down, usually `'music'`.
- `to: number`: How loud it is kept while this sounds, from `0` (silent) to `1` (untouched). When several
- `fade?: number`: How long it takes to go down, in seconds. Default `0.15`.
- `release?: number`: How long it takes to come back up once nothing holds it down. Default, the same as `fade`.

#### TSoundFade (type, from `nacatamalon`)

```ts
type TSoundFade
```

How a sound starts or stops.

- `fade?: number`: How long it takes to come in or go out, in seconds. Default `0`, at once.

#### TSoundHandle (type, from `nacatamalon`)

```ts
type TSoundHandle
```

Controls a sound made with `useSound`.

- `play: `
- `stop: `
- `pause: `
- `resume: `
- `setVolume: `
- `setLoop: `
- `setRate: `
- `setPan: `
- `pan: number`: Where between left (`-1`) and right (`1`) it is heard: written, it moves there at once, so a
- `playing: boolean`: Whether something of this sound is sounding right now.

#### TSoundOptions (type, from `nacatamalon`)

```ts
type TSoundOptions
```

How a sound plays.

- `id?: string`: The name this sound keeps in a saved scene, so that opening one and saving it again gives the
- `volume?: number`: `0` silent, `1` as recorded. Default `1`. This sound only: the channel and the general volume still apply.
- `loop?: boolean`: Repeats until it is stopped. Default `false`.
- `loopStart?: number`: Where the loop goes back to, in seconds into the file. Everything before it is an intro that
- `loopEnd?: number`: Where the loop turns back, in seconds into the file. Default `0`, the end of the file.
- `rate?: number`: How fast it plays, which also changes the pitch: `2` is twice as fast, an octave up. Default `1`.
- `pan?: number`: Where between left (`-1`) and right (`1`) it is heard. Default `0`, the middle. A sound placed
- `channel?: string`: Which volume group it belongs to: `'sfx'` (the default) or `'music'` are there from the start,
- `autoplay?: boolean`: Starts as soon as the file is ready, with no `play()`. Default `false`.
- `spatial?: boolean`: Places the sound in the world, where the object that asked for it is: it comes from a
- `refDistance?: number`: How far it can be heard at full volume. Placed sounds only. Measured the way the scene
- `maxDistance?: number`: Past this distance it is not heard at all: between `refDistance` and this it fades in a
- `cone?: TSoundCone`: Louder ahead of the object than behind it. Placed sounds only.
- `zone?: TSoundZone`: Fills an area instead of coming from a point: full volume inside, fading outside, and never
- `duck?: TSoundDuck`: Makes another channel quieter while this sounds: the music under a cheer.

#### TSoundSource (type, from `nacatamalon`)

```ts
type TSoundSource: TAudioClip | { src, key } | { sfx } | TSampledSound
```

What to play: a clip from `useLoadAudio`, just where the file is and let `useSound` load it, an
effect made on the spot from a preset (`{ sfx: { preset: 'coin' } }`, see `renderSfx`), or a sound
the game worked out itself, sample by sample (`{ samples, sampleRate }`).

#### TSoundZone (type, from `nacatamalon`)

```ts
type TSoundZone
```

An area a sound fills, the way a meadow is full of birds or a cave of dripping: inside, it plays
at full volume wherever you stand, and outside it fades as you walk away.

- `shape: TSoundZoneShape`: Measured on the object, so it moves, turns and grows with it.
- `fade: number`: How far outside the shape it takes to fade to silence. `0` stops dead at the edge.

#### TSoundZoneShape (type, from `nacatamalon`)

```ts
type TSoundZoneShape: TParticleColliderShape2d | TParticleColliderShape3d
```

The shape of an area a sound fills: the same shapes that stop particles. A box, a ball or an
endless floor in a 3D scene; a rectangle, a circle or an endless floor in a flat one. An endless
floor fills everything under it, which is what the water of a lake needs.

### Assets & loading

Every asset hook and the loader they report through, for progress bars and preloaders.

#### createSpriteAtlas (function, from `nacatamalon`)

```ts
createSpriteAtlas(options: TSpriteAtlasOptions) => TSpriteAtlas
```

Describes a sheet as a grid of frames, so many pictures can live in one image.

This is what lets a whole game draw with one texture: sprites sharing an image are drawn
together, and every change of image costs the renderer a break in the batch. A sheet is also
how a character is animated, since its frames are the same picture at different moments.

Nothing is loaded here and nothing is measured: a frame is a fraction of the image, so this
works the instant it is written, whether or not the image has arrived.

- `options: TSpriteAtlasOptions`: The image, and how many columns and rows of frames it holds.

#### parsePaletteDoc (function, from `nacatamalon/files`)

```ts
parsePaletteDoc(value: unknown, src?: string) => TPaletteDoc
```

Reads a `.palette`, whatever state it is in.

Total and never throws, like every other document this engine reads: a palette is edited by hand
and written by tools, so one bad colour drops that colour and keeps the rest. A palette that ends
up with nothing in it is still a palette, and an effect matching against an empty one gives back
what it was handed, which is the honest answer to "reduce this to no colours".

- `value: unknown`: Whatever `JSON.parse` gave back.
- `src?: string`: Where it came from, named in anything it has to report. Left out, its own name.

#### parseParticlesDoc (function, from `nacatamalon/files`)

```ts
parseParticlesDoc(value: unknown, src: string) => TParticlesDoc
```

Reads a `.particles` file.

Normalizes as it reads, and names the file in every refusal. What it refuses is only what has
no sensible answer: a kind of effect that is not one, a shape that is not one, a colour that
is not a colour, a lifetime of zero. Everything else falls back to something an author would
recognise, because an effect that loads slightly wrong can be seen and fixed, and one that does
not load at all takes the scene with it.

- `value: unknown`: Whatever `JSON.parse` gave back.
- `src: string`: Which file it came from, for the messages.

#### setSpriteFrame (function, from `nacatamalon`)

```ts
setSpriteFrame(sprite: TSprite, index: number) => void
```

Shows another frame of the sheet the sprite came from.

For a picture that changes without running: a chest that is open, a tile that is broken, a
character facing another way. What moves through frames by itself is `useSpriteAnimation`,
which is this called once per frame.

The same as `sprite.frame = index`: a sprite made with `createSprite` shows the frame its `frame`
says, and the sheet its `atlas` says, from the moment either is changed. This is that, written as
a call, for a sprite that may not have come from `createSprite`.

Putting another sheet on a sprite and asking for a frame is all it takes to swap what it shows:
walking to attacking, in two lines.

Does nothing to a sprite that came from no sheet, rather than throwing: asking a plain sprite
to show frame 3 is a mistake worth ignoring, not worth stopping a game for.

- `sprite: TSprite`: A sprite made with an `atlas`.
- `index: number`: Which frame of that sheet to show, counting from 0.

#### useLoadAtlas (function, from `nacatamalon`)

```ts
useLoadAtlas(options: TUseLoadAtlasOptions) => TLoadedAtlas
```

Loads a sheet written as a file: the image, how it is cut into frames, and the named runs of
frames over it.

The file-based twin of `createSpriteAtlas`. Slicing in code is fine while a sheet is used in
one place; a file is what lets the same slicing be shared by everything that reads the
sheet, and changed without touching any code, which is what an editor and an artist need.

Asking twice for the same sheet gives the same one back, already loaded, so scenes can help
themselves without arranging anything between them.

What comes back can be used right away: sprites made from it show their frame when it arrives,
and `useLoadProgress` counts it like an image.

- `options: TUseLoadAtlasOptions`: Where the `.atlas` file is, and the name to keep it under.

#### useLoadAudio (function, from `nacatamalon`)

```ts
useLoadAudio(options: TUseLoadAudioOptions) => TAudioClip
```

Loads a sound, and hands it back at once, still loading, for `useSound` to play.

It comes back straight away, empty, and fills itself in when the file arrives. Playing it before
then does nothing, so nothing has to check whether it is ready. Asking twice for the same key
gives the same sound, already loaded: load it once in a loading scene and use it in the level.

It can be watched with `useLoadProgress` to show a loading bar.

This is also what opens the game's sound, so a game that never loads or plays anything never does.

- `options: TUseLoadAudioOptions`: Where the file is, and the name to keep it under.

#### useLoadBitmapFont (function, from `nacatamalon`)

```ts
useLoadBitmapFont(options: TUseLoadBitmapFontOptions) => TBitmapFont
```

Loads a bitmap font: an image with the characters drawn in it, and a file saying where each one is.

You get the font back at once, still loading. Hand it to `createText` straight away: the text
appears by itself when the font arrives, and `useLoadProgress` counts it like any other asset.

Asking for the same font twice, in this scene or another, gives back the one already loaded.

- `options: TUseLoadBitmapFontOptions`: `json` and `atlas`, the two files of the font, and an optional `key`.

#### useLoadFont (function, from `nacatamalon`)

```ts
useLoadFont(options: TUseLoadFontOptions) => TFont
```

Loads a vector font: a `.ttf` or `.woff` file, the kind a computer has installed.

You get the font back at once, still loading. Hand it to `createText` straight away: the text
appears by itself when the font arrives, and `useLoadProgress` counts it like any other asset.

Unlike a bitmap font (`useLoadBitmapFont`), it looks sharp at any `fontSize`, and when the text is
scaled, turned or seen through a zoomed camera: each letter is kept as a distance field, drawn the
first time it is needed, and the edge is worked out again at whatever size it ends up on screen.
Accents, ñ and every other character the font has work with no list of characters to keep in sync.

Reads TrueType outlines (most `.ttf`, and `.woff` files made from them), with the font's own
kerning. Not yet: `.otf` files with cubic outlines, `.woff2`, and scripts whose letters change shape
by their neighbours (Arabic, the Indic scripts). Such a font ends as `'error'` with a warning saying
which.

Asking for the same font twice, in this scene or another, gives back the one already loaded.

- `options: TUseLoadFontOptions`: `src`, the font file, and optionally `key`, `size` and `chars`.

#### useLoadLut (function, from `nacatamalon/effects`)

```ts
useLoadLut(options: TUseLoadLutOptions) => TLut
```

Loads a colour grading table.

Both shapes a table comes in are read here and end up the same: a `.cube` is text written by a
grading tool, a strip is a picture of the same numbers, and which one you have is a fact about
where it came from rather than about what it does.

Handed back at once and filled in when the file lands. Until then an effect grading with it shows
the frame unchanged.

- `options: TUseLoadLutOptions`: Where the file is, and what to keep it under.

#### useLoadModel (function, from `nacatamalon`)

```ts
useLoadModel(options: TUseLoadModelOptions) => TGltfModel
```

Loads a model made somewhere else: something sculpted in Blender, bought in a pack, exported from
anywhere that writes glTF.

Both kinds of the format are read, and you do not have to know which you have. One is a
description in text with its numbers and pictures in files beside it, the other is all of it in a
single file, and which you were given is an accident of the button somebody pressed. The file is
asked, not its name.

You get the model back at once, still loading. Hand it to `createModel` straight away: it appears
by itself when the file arrives, and `useLoadProgress` counts it like any other asset.

A file is usually several pieces, each with its own colour or picture: a vehicle is a body, glass
and tyres. All of them come, and `createModel` draws them as one thing you move with one
placement.

Asking for the same model twice, in this scene or another, gives back the one already loaded, and
its shapes are on the graphics card once however many of it you put in the world.

- `options: TUseLoadModelOptions`: Where the model is, and how to read it.

#### useLoadMotion (function, from `nacatamalon`)

```ts
useLoadMotion(options: TUseLoadMotionOptions) => TMotionFile
```

Loads a motion: a `.motion` file of values moved over time by keys, made in an editor's motion
panel.

Handed back at once and filled in when it lands, like every other asset, and counted by a loading
scene the same way.

- `options: TUseLoadMotionOptions`: Where the file is, and what to keep it under.

#### useLoadPack (function, from `nacatamalon`)

```ts
useLoadPack(options: TUseLoadPackOptions) => TLoadedPack
```

Loads a pack: boxes and scenes somebody made in another project, with everything they need.

A pack is a folder (what installing a `.nacapack` writes) and this reads it: its manifest, and the
document of each box and scene it offers, with every picture and sound they use found inside the
pack's folder, wherever you serve it. Hand it to `createPack` to place what it offers.

The input actions its scripts read are added to the game when it lands, if the game does not have
them already. Its scripts are code, which a loader cannot bring: import the pack's `<name>.pack.ts`
once and they are registered.

Handed back at once and filled in when it lands, like every other asset, and `useLoadProgress` counts it.
Asking for the same pack twice, in this scene or another, gives back the one already loaded.

- `options: TUseLoadPackOptions`: Where the pack is, and what to keep it under.

#### useLoadPalette (function, from `nacatamalon/effects`)

```ts
useLoadPalette(options: TUseLoadPaletteOptions) => TPalette
```

Loads a set of colours for an effect to match a frame against.

Handed back at once and filled in when the file lands, like every other asset. Until then an
effect matching against it shows the frame unchanged, so a palette that is slow to arrive costs
you the palette and never the picture.

- `options: TUseLoadPaletteOptions`: Where the file is, and what to keep it under.

#### useLoadParticles (function, from `nacatamalon`)

```ts
useLoadParticles(options: TUseLoadParticlesOptions) => TParticlesFile
```

Loads an effect: what a puff of smoke or a shower of sparks is made of.

The file is the effect and it is shared: three torches in a level are one document, read once,
with one picture behind it. Each of them is its own emitter with its own particles, its own clock
and its own run of luck.

Handed back at once and filled in when it lands, like every other asset. Until then an emitter
following it draws nothing, which is what lets a scene appear before its effects do.

- `options: TUseLoadParticlesOptions`: Where the file is, and what to keep it under.

#### useLoadProgress (function, from `nacatamalon`)

```ts
useLoadProgress(assets?: typeOperator) => TLoadProgress
```

Keeps count of what a scene is loading, for a loading bar or to wait before changing scene.

- `useLoadProgress([hero, tiles])`: exactly those.
- `useLoadProgress()`: everything this scene asks for with `useLoadTexture`, including what its body
asks for after this line. Counted once the body is over.

The scene keeps drawing while it loads, so a loading bar made of sprites without a texture is on
screen from the first frame. Must be called inside a scene body, like every hook.

A clarification: If you use `useLoadProgress()` with no arguments, it will track all assets requested by the scene, including those requested after the call.
If you use with a specific list of assets, it will only track those and ignore any others requested by the scene.

- `assets?: typeOperator`: What to count. Left out, everything the scene asks for.

#### useLoadScene (function, from `nacatamalon`)

```ts
useLoadScene(options: TUseLoadSceneOptions) => TLoadedScene
```

Loads a scene laid out in the editor: a `.scene` file.

When it lands it is added to the game under the name written in it, which is not always the file's
(`cave.scene` may be the scene `Cave`): its `name` says which, and `change(cave.name)` starts it
like a scene written in code, and what it uses (its pictures, sounds, models) is asked for at once,
so it starts with all of it there.

Handed back at once and filled in when it lands, like every other asset, and `useLoadProgress` counts it
until what it uses has arrived too. Asking for the same file twice, in this scene or another, gives
back the one already loaded.

Which files a game has is for the game to say: a page cannot look inside a folder, so the editor
keeps the list in `src/scenes.ts`.

- `options: TUseLoadSceneOptions`: Where the file is, and what to keep it under.

#### useLoadShader (function, from `nacatamalon/shader`)

```ts
useLoadShader(options: TUseLoadShaderOptions) => TShader
```

Loads a shader written in a file, so an effect can live beside the game instead of inside it.

Hand what comes back to `createMaterial({ effect })`. It arrives still loading, and anything built
on it draws with the built-in shader until it lands: a scene is never held up waiting for an
effect, it simply gets the effect a moment late.

The file says which family it is for and what knobs it has, so the scene repeats neither.

A file ending in `.shader` is a shader drawn as nodes rather than written, and it is loaded the
same way: it brings both languages with it, so it never takes a `glslSrc`.

- `options: TUseLoadShaderOptions`: Where the file is, and what to keep it under.

#### useLoadSong (function, from `nacatamalon`)

```ts
useLoadSong(options: TUseLoadSongOptions) => TSongFile
```

Loads a song written as notes: a `.song` file, a few kilobytes where the same tune recorded would
be megabytes.

Handed back at once and filled in when it lands, like every other asset, and counted by a loading
scene the same way.

- `options: TUseLoadSongOptions`: Where the file is, and what to keep it under.

#### useLoadTexture (function, from `nacatamalon`)

```ts
useLoadTexture(options: TUseLoadTextureOptions) => TTexture
```

Loads an image to draw with, and hands it back at once, still loading: a sprite made with it
appears by itself when the file arrives, so nothing has to wait for it.

You can use this hook with a `useLoadProgress` to track its loading progress.

- `options: TUseLoadTextureOptions`: Where the image is, and the name to keep it under (`key`), which a later

#### useLoadTrackerSong (function, from `nacatamalon`)

```ts
useLoadTrackerSong(options: TUseLoadTrackerSongOptions) => TSongFile
```

Loads a song made in a NES tracker, FamiTracker or FamiStudio, from its text export, and plays it
on the engine's NES: the same song `useLoadSong` gives, for `useSong`, with its own tempo, key,
pan and beats. Its DPCM samples come with it, kept in memory.

Read when it is loaded, every time the game runs. What it cannot bring over (fine pitch, an
expansion chip's voices…) it leaves out and says so once. For a finished game, a `.song` made from
it once is quicker to load and says the same every time: the editor makes one when the export is
dropped on it, and `importTrackerText` (`nacatamalon/authoring`) does it in a script.

Handed back at once and filled in when it lands, like every other asset, and counted by a loading
scene the same way. A game that never calls it carries none of the reading.

- `options: TUseLoadTrackerSongOptions`: Where the export is, which of its songs, and what to keep it under.

#### TAsset (type, from `nacatamalon`)

```ts
type TAsset: TTexture | TLoadedAtlas | TBitmapFont | TFont | TAudioClip | TTilemap | TGeometry | TGltfModel | TShader | TParticlesFile | TSongFile | TMotionFile | TPalette | TLut | TLoadedPack | TLoadedScene | TLoadedPixels
```

Anything a game loads and keeps by key: what the `useLoad…` hooks and the shape hooks hand back.
Each one says what it is in its `type`, which is how `releaseAsset` knows what to let go of.

#### TAudioClip (type, from `nacatamalon`)

```ts
type TAudioClip
```

A sound asset, returned by `useLoadAudio` the moment it is asked for.

It is born `'loading'` with no length, and the loader fills it in place once the file has been
fetched and decoded. Anything holding it (a `useSound`) sees the change with no re-wiring, which
is why it is filled in rather than replaced.

Everything but `buffer` is plain data. `buffer` is the decoded sound inside the browser's audio
engine: runtime only, and `null` until it is ready, exactly like `gpu` in a texture.

- `type: 'audio'`
- `key: string`: What it is cached under in this game. The `src` unless a `key` was given.
- `src: string`: Where the file comes from.
- `duration: number`: How long it lasts, in seconds. `0` until it is `'ready'`.
- `status: TLoadStatus`
- `buffer: AudioBuffer | null`: The decoded sound. `null` while loading and after an error.

#### TBitmapFont (type, from `nacatamalon`)

```ts
type TBitmapFont
```

A bitmap font, returned by `useLoadBitmapFont` the moment it is asked for.

It is born `'loading'` and fills itself in once both its description and its image have arrived.
Texts made with it draw nothing until then, and appear on their own when it is ready.

- `type: 'bitmap-font'`
- `key: string`: What it is cached under in this game. The description's path unless a `key` was given.
- `src: string`: Where the description file comes from.
- `status: TLoadStatus`
- `texture: TTexture`: The image holding every character.
- `meta: TBitmapFontMeta | null`: The description, or `null` until it has arrived.

#### TBitmapFontGlyph (type, from `nacatamalon`)

```ts
type TBitmapFontGlyph
```

Where one character sits in a font's image, in pixels.

- `char: string`: The character, one per entry.
- `x: number`: Left edge in the image.
- `y: number`: Top edge in the image.
- `w: number`: Width. Every character of a font shares the font's `glyphHeight`.

#### TBitmapFontMeta (type, from `nacatamalon`)

```ts
type TBitmapFontMeta
```

A bitmap font's description file, exactly as it is written on disk.

- `name: string`
- `glyphHeight: number`: How tall every character is, in pixels of the image.
- `tracking: number`: Extra pixels of the image between two characters, added after each one.
- `baseline: number`: How far down from the top of a character its baseline is.
- `atlasWidth: number`
- `atlasHeight: number`
- `chars: TBitmapFontGlyph[]`

#### TChildEmitter (type, from `nacatamalon`)

```ts
type TChildEmitter
```

Another effect a particle sets off when it is born or when it dies: the sparks a firework leaves.

`src` is another `.particles`, relative to this one, never an effect written inline: a child is an
effect like any other, made, looked at and used again like one.

- `on: 'birth' | 'death'`: When in a particle's life it goes off.
- `src: string`: The other file, relative to this one.
- `count: number`: How many of the child's particles each one sets off.

#### TConsolePaletteName (type, from `nacatamalon`)

```ts
type TConsolePaletteName: 'nes' | 'gameBoy' | 'c64'
```

The machines whose colours the engine carries.

#### TFont (type, from `nacatamalon`)

```ts
type TFont
```

A vector font (`.ttf` or `.woff`), returned by `useLoadFont` the moment it is asked for.

It is born `'loading'` and fills itself in once the file has arrived and been read. Texts made with
it draw nothing until then, and appear on their own when it is ready.

Its letters are kept as distance fields in `texture`, drawn the first time each character is
needed, so the same font is sharp at any `fontSize`, scale, turn or camera zoom.

- `type: 'font'`
- `key: string`: What it is cached under in this game. Its path unless a `key` was given.
- `src: string`: Where the file comes from.
- `status: TLoadStatus`
- `size: number`: How many pixels tall one em of a letter is drawn in `texture`. More is finer detail in thin
- `texture: TTexture`: The image the letters are kept in. Grows, and is uploaded again, as new characters are drawn.
- `meta: TFontMeta | null`: What the file says, or `null` until it has been read.

#### TFontMeta (type, from `nacatamalon`)

```ts
type TFontMeta
```

What a vector font says about itself once it has been read, in its own units unless said otherwise.

- `name: string`: Its name as the file gives it ("Inter Bold"), or `''` when the file has none.
- `unitsPerEm: number`: How many of its units make the size of the font. A `fontSize` of 16 makes this many units 16 pixels.
- `ascender: number`: How far above the baseline its tallest letters reach.
- `descender: number`: How far below the baseline its letters reach. Negative, as fonts write it.
- `lineGap: number`: Extra space the font asks for between two lines.
- `atlasWidth: number`: The size of the image its letters are kept in, in pixels. It grows as new characters are drawn.
- `atlasHeight: number`

#### TGltfModel (type, from `nacatamalon`)

```ts
type TGltfModel
```

A model loaded from a file, and everything it takes to draw it.

You get it back at once, still loading, and the pieces appear in `parts` when the file arrives.
Hand it to `createModel` straight away: what it shows turns up by itself.

- `type: 'gltf'`
- `key: string`: What it is kept under in this game. The `src` unless a `key` was given.
- `src: string`
- `status: TLoadStatus`
- `parts: TGltfPart[]`: Its drawable pieces, in the order the file lists them. Empty until it arrives.
- `skeletons: TSkeleton[]`: The rigs it holds. Nearly always none or one, but a file is allowed more and a few do.
- `clips: Record<string, TSkeletalClip>`: The movements it came with, by name. Empty for a model nobody animated.

#### TGltfNodeInfo (type, from `nacatamalon`)

```ts
type TGltfNodeInfo
```

One piece of a glTF file's tree, said with the engine's words: the shape a tool turns into a box
when it brings a model in.

- `name: string`: The piece's name, which is also how a model asks for it. Empty when the file names nothing:
- `transform: TTransform3d`: Where it sits relative to the piece above it.
- `hasMesh: boolean`: Whether it has something to draw. One that does not only groups the ones below it.
- `skinned: boolean`: Whether a rig deforms its mesh. A rigged piece has to come in as one box with everything
- `children: TGltfNodeInfo[]`

#### TGltfPart (type, from `nacatamalon`)

```ts
type TGltfPart
```

One drawable piece of a model: a shape and the surface it wears.

A model file is one piece surprisingly rarely. A vehicle is a body, glass and tyres; a character
is skin, hair and clothes. Each of those is a piece here because each wears a different surface,
and a surface is the thing that cannot be shared: merging them would mean choosing one picture
for all of it.

- `name: string`: The name of the piece it came from, or `''` when it was never given one.
- `material: string`: The name of the surface it wears, or `''`. It is often the only thing telling two pieces of
- `geometry: TGeometry`
- `texture: TTexture | null`: The picture on it, or `null` for a plain coloured surface.
- `tint: TColor`
- `emissive: TColor`
- `emissiveTexture: TTexture | null`: The picture its glow comes through, or `null` when the glow is the same all over.
- `occlusion: TMaterialOcclusion | null`: Where the light from all around is kept out of its creases, as the file says, or `null` when
- `transparent: boolean`: Whether the file says it is seen through, whatever its colour's alpha: a picture with
- `doubleSided: boolean`: Whether the file says both sides of it are seen. Drawn with `cull: 'none'` unless the game
- `alphaCutoff: number | null`: The alpha below which its picture is cut out, when the file says it is (`alphaMode: 'MASK'`):
- `wrap: { u, v }`: What its picture does past its edge, as the file says: repeating, unless it says otherwise.
- `skeleton: TSkeleton | null`: The bones that move it, or `null` when nothing does.

#### TLoadable (type, from `nacatamalon`)

```ts
type TLoadable
```

Anything `useLoadProgress` can track: the one field every asset record shares, so the loader never
has to know whether it is looking at a texture, a sound or a font.

- `status: TLoadStatus`

#### TLoadedAtlas (type, from `nacatamalon`)

```ts
type TLoadedAtlas
```

A sheet described by a file: the image, how it is cut, and the runs of frames over it.

It is a sheet and a load at once. Once it is `'ready'` it is exactly what `createSprite`
and `useSpriteAnimation` take, and until then it is something `useLoadProgress` counts, like an
image.

- `type: 'atlas'`
- `key: string`: What it is cached under: the key given, or the path.
- `src: string`: Where the file is.
- `status: TLoadStatus`: How it is doing. A sheet that failed reports `'error'` and slices nothing.
- `texture: TTexture`: The image the file names, loaded with it.
- `columns: number`: How many frames across. `0` until the image has landed and the grid can be worked out.
- `rows: number`: How many frames down. `0` until then.
- `frames: number`: How many frames in total. `0` until then.
- `rects?: typeOperator`: The window of every frame, as the file cuts the image. Filled in with the grid.
- `names?: Record<string, number>`: Where each named frame of a packed sheet sits in the count, by name. A map's tile can name
- `sequences: Record<string, TAnimationClip>`: The runs the file declares, by name, ready for `useSpriteAnimation`.
- `points?: Readonly<Record<number, TAtlasFramePoints>>`: The named points of its frames, by frame number, for `pointOf`. Filled in with the grid.

#### TLoadedPack (type, from `nacatamalon`)

```ts
type TLoadedPack
```

A pack, as `useLoadPack` hands it back: what it offers, ready to be placed with `createPack`.

It is a definition and not something in the world. It has no position and no state of its own,
the same as a model file: each copy made from it with `createPack` has its own place and its own
settings, and the pack is not changed by any of them.

Handed back at once, still loading, and filled in when its manifest and the documents it offers
land. Until then `boxes` and `scenes` are empty.

- `type: 'pack'`
- `key: string`: What it is cached under: the key given, or the folder.
- `src: string`: The pack's folder, always ending in `/`. Everything in it is found from here.
- `status: TLoadStatus`: How it is doing. A pack that failed reports `'error'` and places nothing.
- `name: string`: Its name, from its manifest. Empty until it lands.
- `version: string`: Its own version, from its manifest.
- `exports: { boxes, scenes }`: What it offers, by name. Either list may be empty: a pack of assets offers neither.
- `boxes: Record<string, TSceneDoc>`: The documents of the boxes it offers, by name, with their paths already found in its folder.
- `scenes: Record<string, TSceneDoc>`: The documents of the scenes it offers, by name, the same way.
- `actions: TActionMap`: The input actions its scripts read, with the bindings they came with.

#### TLoadedScene (type, from `nacatamalon`)

```ts
type TLoadedScene
```

A scene document, as `useLoadScene` hands it back.

Handed back at once, still loading. When its file lands the scene is added to the game under the
name written in it, the same one the editor and `mainScene` use, and from then on `change(name)`
starts it like any scene written in code.

- `type: 'scene'`
- `key: string`: What it is cached under: the key given, or the file.
- `src: string`: Where the `.scene` file is.
- `status: TLoadStatus`: How it is doing. `'ready'` once it can be started and what it uses has arrived; `'error'` when
- `name: string`: The scene's name, from its document: what `change` starts it by. Empty until it lands.

#### TLoadProgress (type, from `nacatamalon`)

```ts
type TLoadProgress
```

How a batch of loads is doing, returned by `useLoadProgress`. Plain data, updated in place as each
asset settles, so a scene reads it in `useUpdate` to drive a progress bar or move on.

- `total: number`: How many assets the batch tracks.
- `loaded: number`: How many of them have settled, `'ready'` or `'error'`.
- `progress: number`: `loaded / total`, from 0 to 1. `1` for an empty batch.
- `done: boolean`: `true` once every asset has settled. An error counts as settled: waiting for it would be forever.

#### TLoadStatus (type, from `nacatamalon`)

```ts
type TLoadStatus: 'loading' | 'ready' | 'error' | 'released'
```

Where an asset is in its load. `'loading'` from the moment it is asked for, then exactly one
of `'ready'` (usable) or `'error'` (it will never be usable).

`'released'` is the end of an asset the game let go of with `releaseAsset`: whatever still
holds it is not drawn or played, and the same key loads afresh the next time it is asked for.

#### TLut (type, from `nacatamalon/effects`)

```ts
type TLut
```

A grading table, whatever shape its file had.

One layout, whichever it came from. A `.cube` is text and a strip is an image, but by the time
anything downstream looks at it there is only the strip: N slices side by side, each N by N. Red
runs across within a slice, green down it, and blue picks which slice. That is the layout every
engine uses, so a table exported for another one drops straight in.

- `type: 'lut'`
- `key: string`: What it is kept under in this game. The `src` unless a `key` was given.
- `src: string`
- `status: TLoadStatus`
- `size: number`: How many steps a side: a 16 table is 16 by 16 by 16. `0` until it lands.
- `gpu: ITexture | null`: The uploaded strip, `size * size` across and `size` down. `null` until it lands.

#### TMotionFile (type, from `nacatamalon`)

```ts
type TMotionFile
```

A `.motion` file, on its way in or already here.

- `type: 'motion-file'`
- `key: string`: What it is kept under in this game. The `src` unless a `key` was given.
- `src: string`: Empty for a motion written in code rather than fetched.
- `status: TLoadStatus`
- `doc: TMotionDoc | null`: `null` until it is `'ready'`, and after an error.

#### TPalette (type, from `nacatamalon/effects`)

```ts
type TPalette
```

A palette asset, handed back the moment it is asked for and filled in when the file lands.

Its picture is one row of pixels, one per colour, which is how the card reads it: an effect asks
how many there are and then for the nth, and neither question needs a second dimension.

- `type: 'palette'`
- `key: string`: What it is kept under in this game. The `src` unless a `key` was given.
- `src: string`
- `status: TLoadStatus`
- `name: string`: What the file called it, or an empty string until it lands.
- `colors: string[]`: The colours as written, in file order. Empty until it lands.
- `gpu: ITexture | null`: The uploaded row. `null` while loading and after an error.

#### TPaletteDoc (type, from `nacatamalon/files`)

```ts
type TPaletteDoc
```

A `.palette` file as it is written on disk.

Plain JSON, because a palette is the one part of this whole feature that is data rather than
arithmetic: everything else the engine can work out, and these are colours somebody chose.

- `format: number`
- `kind: 'palette'`
- `name: string`: What a person calls it. The file's name is its identity; this is only read.
- `colors: string[]`: The colours, as `#rrggbb`.

#### TParsedShader (type, from `nacatamalon/shader`)

```ts
type TParsedShader
```

What reading a shader file got out of it.

The same shape whichever way the file was authored, so whatever learns to write shader files next
plugs in here without anything downstream noticing.

- `shader: TMaterialShader`: Which family the file is for, from its `@shader` line, or `'sprite2d'` if it had none.
- `fragment: string | null`
- `vertex: string | null`
- `fragmentGlsl: string | null`
- `vertexGlsl: string | null`
- `uniforms: TUniformValues`: The knobs it declared, with the values it gave them.
- `uniformSig: TUniformSignature`: What kind each of those is, straight from the declaration rather than guessed.

#### TParticleColorStopInput (type, from `nacatamalon`)

```ts
type TParticleColorStopInput
```

One stop of the colour curve as it is written: the colour as text (`'#ffcc00'`) or as a colour
already worked out (`lerpColor(dusk, night, stage)`), and its opacity, which is the colour's own
when left out.

- `t: number`
- `color: string | TColor`
- `alpha?: number`

#### TParticlesBounds (type, from `nacatamalon`)

```ts
type TParticlesBounds
```

How far an effect reaches: a box in the emitter's own space, centred where it says, because a
fountain reaches up and dust reaches sideways.

Said, and not enforced. Nothing is stopped at its faces and nothing is left undrawn because of
it: an effect that outgrows its box has the wrong box. It is here so a tool can draw it and a
renderer could one day skip an effect that is nowhere near the screen. A flat effect leaves `z` at
zero on both.

- `center: { x, y, z }`: The middle of the box.
- `size: { x, y, z }`: Its whole size on each axis, not half of it.

#### TParticlesDoc (type, from `nacatamalon/files`)

```ts
type TParticlesDoc: TParticlesDoc2d | TParticlesDoc3d
```

A `.particles` file, read, for whichever dimension it says it is.

The dimension is the file's and not the emitter's, because it decides what every other number in
it means. An emitter is made for one of the two, and pointing it at the other is refused.

#### TParticlesDoc2d (type, from `nacatamalon/files`)

```ts
type TParticlesDoc2d
```

A flat `.particles` file, read: pixels, with +y down, like everything else in the plane.

Normalized as it is read, which is what lets everything downstream be a plain walk with no
guards: the curves come out sorted with their `t` inside `0` to `1`, a bare number has been
widened into a range, and a range somebody wrote backwards has been turned round rather than
refused.

- `format: number`
- `kind: 'particles-2d'`
- `texture: string | null`: The picture every particle shows, relative to the file rather than to the page.
- `atlas: string | null`: A sheet (`.atlas`) to take the picture from instead, relative to the file. Wins over
- `frame: number | string | null`: Which frame of that sheet, by its place or, in a packed sheet, its name. At most one of this
- `anim: string | null`: A run of the sheet's (its `sequences`), played by every particle from the moment it is born.
- `animPace: TParticleAnimPace`: How `anim` is paced. `'life'` stretches the run over each particle's life, so it ends as the
- `max: number`: How many may be alive at once. Asked for once and never grown.
- `blend: TParticleBlend`
- `worldSpace: boolean`: Whether a particle keeps living where it was born when the emitter moves on.
- `emission: TEmissionDoc`
- `shape: TEmitShape2d`
- `direction: number`: The middle of the direction they fly, in radians. `0` is to the right; with y downwards, up is `-π/2`.
- `spread: number`: How wide that fan is, in radians. A whole turn is every direction at once.
- `life: TParticleRange`: How long they live, in seconds.
- `speed: TParticleRange`: How fast they set off, in pixels a second.
- `size: TParticleRange`: How big they are born, in pixels.
- `spin: TParticleRange`: How fast they turn, in radians a second.
- `gravity: { x, y }`: A steady pull, in pixels a second squared. `+y` is down.
- `damping: number`: How much of its speed a particle sheds each second, `0` to `1`.
- `colorOverLife: TParticleColorStop[]`
- `sizeOverLife: TParticleScaleStop[]`
- `collision: TParticleCollision | null`: What it does when it runs into a collider in the scene, or `null` to pass through everything.
- `trail: TParticleTrail | null`: The tail each particle leaves, or `null` for none.
- `bounds: TParticlesBounds | null`: How far it reaches, or `null` when nobody has said. See `TParticlesBounds`.
- `children: TChildEmitter[]`: The effects its particles set off.
- `unsupported: Record<string, unknown>`: Whatever else the file declared, kept exactly as written and not acted on by this version.

#### TParticlesDoc3d (type, from `nacatamalon/files`)

```ts
type TParticlesDoc3d: Omit<TParticlesDoc2d, 'kind' | 'shape' | 'direction' | 'gravity'> & { kind, shape, direction, gravity }
```

A `.particles` file for three dimensions, read: world units, with +y up.

Everything but four fields reads as it does in the flat one. The four are the ones a dimension
changes the meaning of: where they are born, which way they fly, which way they fall, and the kind
that says so. Pixels and units differ by a factor of a hundred, so the two never share defaults.

- `kind: 'particles-3d'`
- `shape: TEmitShape3d`
- `direction: { x, y, z }`: The middle of the direction they fly, one unit long. Straight up unless the file says otherwise.
- `gravity: { x, y, z }`: A steady pull, in units a second squared. `+y` is up, so falling is negative.

#### TParticlesEffect (type, from `nacatamalon`)

```ts
type TParticlesEffect: TParticlesEffect2d | TParticlesEffect3d
```

Either of the two.

#### TParticlesEffect2d (type, from `nacatamalon`)

```ts
type TParticlesEffect2d: TParticlesEffectCommon & { kind, shape, direction, gravity }
```

A flat effect written in code instead of in a `.particles` file: the same fields, the same
meaning, and only `kind` has to be said. For an effect that depends on the game, like dust the
colour of the ground the car is on.

- `kind: 'particles-2d'`
- `shape?: TEmitShape2d`
- `direction?: number`
- `gravity?: { x, y }`

#### TParticlesEffect3d (type, from `nacatamalon`)

```ts
type TParticlesEffect3d: TParticlesEffectCommon & { kind, shape, direction, gravity }
```

The same, for an effect in three dimensions.

- `kind: 'particles-3d'`
- `shape?: TEmitShape3d`
- `direction?: { x, y, z }`
- `gravity?: { x, y, z }`

#### TParticlesFile (type, from `nacatamalon`)

```ts
type TParticlesFile
```

A `.particles` file, on its way in or already here.

Kept apart from the emitter that follows it, and that split is the whole reason this type
exists: three torches in a level are three emitters, each with its own particles, its own clock
and its own stream of chance, all reading one document. Folding the two together is what makes
three torches parse three files and upload three copies of the same picture.

Like a shader and unlike a texture, it keeps a list of who is waiting on it. A picture can be
handed to a sprite and that is enough, because the sprite hands it straight to the card. An effect
cannot: an emitter has to size its particles from the document, so when the bytes land they have
to reach everything already built against them.

- `type: 'particles-file'`
- `key: string`: What it is kept under in this game. The `src` unless a `key` was given.
- `src: string`
- `status: TLoadStatus`
- `doc: TParticlesDoc | null`: What the file said, or `null` until it lands.
- `texture: TTexture | null`: The picture, loaded through the game's own cache so two effects sharing one fetch it once.
- `atlas: TLoadedAtlas | TSpriteAtlas | null`: The sheet its particles take their frames from, which wins over `texture`: one loaded from
- `clip: TAnimationClip | null`: A run given in code (`anim: { frames, fps }`) instead of one named on the sheet. A file only
- `bound: TParticles | TParticles3d[]`: The emitters built on this file and not yet destroyed, filled in when it lands. Never written
- `children: TParticlesFile | null[]`: The files of the effects its particles set off, one per entry of `doc.children`, in the same

#### TParticleTrail (type, from `nacatamalon`)

```ts
type TParticleTrail
```

A tail drawn behind each particle, out of where it has just been.

It is more of the same particle and not a ribbon: copies of it, smaller and fainter towards the
end, which is how a trail was drawn in that era and needs nothing the draw does not already have.
`length` is in copies because that is what it costs: `max × length` more of them, set aside once.
Every copy shows the frame its particle shows now, not the one it showed when it was there.

- `length: number`: How many of its last places each particle keeps.
- `width: number`: How wide, against the particle's own size.
- `fade: boolean`: Whether it also fades towards the end, and not only narrows.

#### TShader (type, from `nacatamalon/shader`)

```ts
type TShader
```

A shader written in a file, on its way in or already here.

The file holds the hooks and the knobs and says which family it is for, so the scene that uses it
repeats none of that. One file can be shared by any number of materials, and is fetched once.

It keeps a list of who is following it, which is the one thing that makes it unlike a texture.
A texture is handed to a drawing and that is enough, because the drawing hands the texture
straight to the card. A shader is not: the card compiles from the source, so when the bytes
finally land they have to be poured into every material that was already built against it.

- `type: 'shader'`
- `key: string`: What it is kept under in this game. The `src` unless a `key` was given.
- `src: string`: Where the WGSL comes from.
- `glslSrc: string | null`: Where the GLSL comes from, when it was written in a second file instead of the same one.
- `status: TLoadStatus`
- `shader: TMaterialShader`: Which family the file declared, or `'sprite2d'` when it did not say.
- `fragment: string | null`: The fragment hook in WGSL, or `null` if the file defines only a vertex one.
- `fragmentGlsl: string | null`: The same hook in GLSL, or `null` when the author wrote no GLSL half.
- `vertex: string | null`: The vertex hook in WGSL.
- `vertexGlsl: string | null`: The same, in GLSL.
- `uniforms: TUniformValues`: The knobs the file declared, with the values it gave them.
- `uniformSig: TUniformSignature`: What kind each of those is.
- `bound: TShaderTarget[]`: Everything built on this file, which is filled in when it lands. Never written out.

#### TShaderTarget (type, from `nacatamalon/shader`)

```ts
type TShaderTarget: TMaterial | TPostEffect
```

Something a shader file can be poured into once it lands.

The two kinds have almost nothing else in common, and they do not need to: a file knows only that
whoever is waiting on it has somewhere to put a fragment hook, a vertex hook and a set of knobs.

#### TShading (type, from `nacatamalon`)

```ts
type TShading: 'auto' | 'smooth' | 'flat'
```

How the surfaces of a loaded model are made to face.

#### TSongFile (type, from `nacatamalon`)

```ts
type TSongFile
```

A `.song` file, on its way in or already here.

It holds the song as written, not as sound: every `useSong` playing it turns it into sound itself,
because each can be at its own tempo and in its own key.

- `type: 'song-file'`
- `key: string`: What it is kept under in this game. The `src` unless a `key` was given.
- `src: string`: Empty for a song written in code rather than fetched.
- `status: TLoadStatus`
- `doc: TSongDoc | null`: `null` until it is `'ready'`, and after an error.
- `samples: Map<string, TSongSample>`: The sound files its instruments' `sample` name, read, by that name. The engine's own samples are

#### TSpriteAtlas (type, from `nacatamalon`)

```ts
type TSpriteAtlas
```

One image holding many pictures in a grid, and the arithmetic to point at any of them.

A grid rather than a list of rectangles, because that is what a sprite sheet is and because it
needs no pixel sizes: the frames are fractions of the image, so an atlas works from the moment
it is described, before the image has even arrived.

Frames are numbered from 0, left to right and then top to bottom, the order a sheet is drawn in.

- `texture: TTexture`: The image itself. Every frame is a window into it.
- `columns: number`: How many frames across.
- `rows: number`: How many frames down.
- `frames: number`: How many there are in total, which is what a frame number has to stay under.
- `rects?: typeOperator`: The window of every frame, for a sheet that is not an even grid of the whole image: one read
- `points?: Readonly<Record<number, TAtlasFramePoints>>`: The named points of its frames, by frame number, for `pointOf`.

#### TSpriteAtlasOptions (type, from `nacatamalon`)

```ts
type TSpriteAtlasOptions
```

What `createSpriteAtlas` is asked for.

- `texture: TTexture`: The sheet, from `useLoadTexture`.
- `columns: number`: How many frames across.
- `rows?: number`: How many frames down. One for a single strip.
- `points?: Readonly<Record<number, TAtlasFramePoints>>`: Named points of its frames, by frame number, for `pointOf`: where the hand is in each one.

#### TTexture (type, from `nacatamalon`)

```ts
type TTexture
```

A texture asset, returned by `useLoadTexture` the moment it is asked for.

It is born `'loading'` with no size, and the loader fills it in place once the image has been
fetched, decoded and uploaded. Anything holding it (a sprite) sees the change on its next frame
with no re-wiring, which is the whole reason it is mutated rather than replaced.

Everything but `gpu` is plain data. `gpu` is a handle into the renderer's memory: runtime only,
meaningless to anyone but the backend that made it, and `null` until the upload.

- `type: 'texture'`
- `key: string`: What it is cached under in this game. The `src` unless a `key` was given.
- `src: string`: Where the image comes from.
- `width: number`: In pixels. `0` until it is `'ready'`.
- `height: number`: In pixels. `0` until it is `'ready'`.
- `status: TLoadStatus`
- `gpu: ITexture | null`: The uploaded texture. `null` while loading and after an error.

#### TUseLoadAtlasOptions (type, from `nacatamalon`)

```ts
type TUseLoadAtlasOptions
```

What `useLoadAtlas` is asked for.

- `src: string`: Where the `.atlas` file is.
- `key?: string`: What to remember it under, so another scene can ask for the same sheet. Defaults to `src` as

#### TUseLoadAudioOptions (type, from `nacatamalon`)

```ts
type TUseLoadAudioOptions
```

What `useLoadAudio` is asked for. Named fields rather than two bare strings, which would be too
easy to swap.

- `src: string`: Where the sound file is. Anything the browser can play: `.mp3`, `.ogg`, `.wav`.
- `key?: string`: What to cache it under, so another scene can reach the same sound. Defaults to `src` as the

#### TUseLoadBitmapFontOptions (type, from `nacatamalon`)

```ts
type TUseLoadBitmapFontOptions
```

Where a bitmap font comes from.

- `json: string`: The font's description file: where each character sits in the image.
- `atlas: string`: The image with every character drawn in it.
- `key?: string`: What to call it in this game. Default: the description's path as the page resolves it, so one

#### TUseLoadFontOptions (type, from `nacatamalon`)

```ts
type TUseLoadFontOptions
```

Where a vector font comes from, and how finely its letters are kept.

- `src: string`: The font file: a `.ttf` or a `.woff`.
- `key?: string`: What to call it in this game. Default: `src` as the page resolves it, so one file written two
- `size?: number`: How many pixels tall one em of a letter is kept, from 16 to 128. Default `48`, enough for most
- `chars?: string`: Characters to get ready while the font loads, instead of the first time a text shows them.

#### TUseLoadLutOptions (type, from `nacatamalon/effects`)

```ts
type TUseLoadLutOptions
```

What `useLoadLut` is asked for.

- `src: string`: Where the table is: a `.cube` file, or a strip image.
- `key?: string`: What to keep it under, so another scene can reach the same one. Defaults to `src` as the page

#### TUseLoadModelOptions (type, from `nacatamalon`)

```ts
type TUseLoadModelOptions
```

Where a model comes from, and how to read it.

- `src: string`: The model file: either kind, `.gltf` or `.glb`.
- `node?: string`: One named piece of the file and whatever hangs beneath it, instead of all of it. For taking
- `shading?: TShading`: How its surfaces are made to face. `'auto'`, the default, keeps what the file says.
- `textures?: boolean`: `false` loads no pictures, leaving each piece in its plain colour. Default `true`.
- `key?: string`: What to call it in this game. Default: `src`, or `src` and the piece when one is named, as

#### TUseLoadMotionOptions (type, from `nacatamalon`)

```ts
type TUseLoadMotionOptions
```

What `useLoadMotion` is asked for.

- `src: string`: Where the `.motion` file is.
- `key?: string`: What to keep it under, so another scene can reach the same one. Defaults to `src` as the page

#### TUseLoadPackOptions (type, from `nacatamalon`)

```ts
type TUseLoadPackOptions
```

What `useLoadPack` is asked for.

- `src: string`: The pack's folder: where `pack.json` is. With or without the last `/`.
- `key?: string`: What to keep it under, so another scene can reach the same one. Defaults to the folder as the

#### TUseLoadPaletteOptions (type, from `nacatamalon/effects`)

```ts
type TUseLoadPaletteOptions
```

What `useLoadPalette` is asked for.

- `src: string`: Where the `.palette` file is, or one of the engine's: `'nacatamalon:nes'`, `'nacatamalon:gameBoy'`
- `key?: string`: What to keep it under, so another scene can reach the same one. Defaults to `src` as the page

#### TUseLoadParticlesOptions (type, from `nacatamalon`)

```ts
type TUseLoadParticlesOptions
```

What `useLoadParticles` is asked for.

- `src: string`: Where the `.particles` file is.
- `key?: string`: What to keep it under, so another scene can reach the same one. Defaults to `src` as the page

#### TUseLoadSceneOptions (type, from `nacatamalon`)

```ts
type TUseLoadSceneOptions
```

What `useLoadScene` is asked for.

- `src: string`: Where the `.scene` file is, from the page: `scenes/cave.scene`.
- `key?: string`: What to keep it under, so another scene can reach the same one. Defaults to `src` as the page

#### TUseLoadShaderOptions (type, from `nacatamalon/shader`)

```ts
type TUseLoadShaderOptions
```

What `useLoadShader` is asked for.

- `src: string`: Where the shader is. Its WGSL half, which is the one that has to exist.
- `glslSrc?: string`: Where its GLSL half is, when it was written in a second file instead of after a `// @glsl`
- `key?: string`: What to keep it under, so another scene can reach the same one. Defaults to `src` as the page

#### TUseLoadSongOptions (type, from `nacatamalon`)

```ts
type TUseLoadSongOptions
```

What `useLoadSong` is asked for.

- `src: string`: Where the `.song` file is.
- `key?: string`: What to keep it under, so another scene can reach the same one. Defaults to `src` as the page

#### TUseLoadTextureOptions (type, from `nacatamalon`)

```ts
type TUseLoadTextureOptions
```

What `useLoadTexture` is asked for. Named fields rather than two positional strings, which would
be too easy to swap.

- `src: string`: Where the image is.
- `key?: string`: The name the game finds it by, here or in another scene: `createSprite({ texture: 'hero' })`.

#### TUseLoadTrackerSongOptions (type, from `nacatamalon`)

```ts
type TUseLoadTrackerSongOptions
```

What `useLoadTrackerSong` is asked for.

- `src: string`: Where the tracker's text export is: FamiTracker's (File, Export text) or FamiStudio's (File,
- `song?: number`: Which of its songs, from `0`, when the file holds several. The first by default.
- `key?: string`: What to keep it under, so another scene can reach the same one. Defaults to `src` as the page

### Tags

What something is, as many as it likes: finding the things of a kind, and colliding with them.

#### useTag (function, from `nacatamalon`)

```ts
useTag(name: string) => TTag
```

Gives the tag `name`, to put on things, take off, ask about and find: what something is,
`'enemy'`, `'coin'`, `'damageable'`, as many as it likes. Asking is getting: two pieces that ask
for `'enemy'` get the same tag, and nothing registers it.

Asked for in a body, like every hook; what it gives works whenever, from a frame or a callback.
It finds in the scene it was asked for in. Collisions are asked by tag too:
`useCollisionWith(enemies, ...)` or `useCollisionWith('enemy', ...)`.

- `name: string`: The tag.

#### TTag (type, from `nacatamalon`)

```ts
type TTag
```

A tag, as `useTag` gives it: one name, and what to do with everything that carries it.

The name is the tag: two pieces that ask for `'enemy'` get the same one, and nothing registers
it. What carries it carries it as data (`thing.tags`), so this and the field are never out of
step.

- `name: string`
- `add: `
- `remove: `
- `has: `
- `all: `
- `clear: `

#### TTaggable (type, from `nacatamalon`)

```ts
type TTaggable: TGameObject<TApi> | TSprite | TText | TNineSlice
```

What can carry a tag: a piece (a box) or something drawn on its own, a sprite, a text or a panel.

### Hitboxes

The parts of an object that can be touched, and what to do when two of them meet: hits, pickups and triggers without a physics engine.

#### collisionBetween (function, from `nacatamalon`)

```ts
collisionBetween(a: TCollisionTarget, b: TCollisionTarget) => boolean
```

Whether two things touch right now: a bullet and a brick, the player and a door, a sword and
whatever is in front of it. With no physics: just whether their rectangles share any area.

Each side can be an object, a sprite, a text, a panel, or a rectangle of the world
(`{ x, y, width, height }`, from its top-left corner). An object is measured by its hitboxes
(`useHitbox`) when it has any, and by what it draws when it has none. Everything is measured
where it is drawn: turned, grown and moved with everything above it. Edges that only meet do not
count, so two tiles side by side are not touching.

For many things against many, every frame, `useCollisionBetween` with components or tags says it
once.

- `a: TCollisionTarget`: One thing.
- `b: TCollisionTarget`: The other.

#### useCollisionBetween (function, from `nacatamalon`)

```ts
useCollisionBetween(a: A, b: B, callback: (a: TCollisionMatched<A>, b: TCollisionMatched<B>) => void) => () => void
```

Calls `callback` for every thing of kind `a` touching one of kind `b`, every frame they touch: a
rule of the scene, shots against enemies, the player against coins. A piece says itself what it
collides with with `useCollisionWith`; this is for the scene that knows both. Each kind is a
component, whose pieces come with their `api` typed, or a tag, as in `useCollisionWith`, and
physics bodies meet as they do there.

Run once every `useUpdate` of the scene is over, before `useLateUpdate`, measured as
`collisionBetween` measures. The two come in the order the tags were named. Once one is destroyed
it is not called with again, so a bullet hits one enemy and not the three behind it. A tag can be
named twice, `useCollisionBetween('enemy', 'enemy', ...)`, and each pair comes once. Stops when
what registered it goes away; not while its scene is paused.

- `a: A`: One kind: a component, or a tag.
- `b: B`: The other.
- `callback: (a: TCollisionMatched<A>, b: TCollisionMatched<B>) => void`: What to do with each pair, in that order.

#### useCollisionWith (function, from `nacatamalon`)

```ts
useCollisionWith(match: M, callback: (other: TCollisionMatched<M>) => void, options: TCollisionWithOptions) => () => void
```

Calls `callback` with each thing the object being built touches, of one kind: a bullet and the
enemies, the player and the coins. The piece says itself what it collides with, so a bullet and
an enemy written apart need no rule in the scene that knows them both.

The kind is a component or a tag. A component (`useCollisionWith(Enemy, ...)`) meets the pieces
`useSpawn` made from it, and gives each with its `api` typed. A tag (`'damageable'`, or what
`useTag` gives) meets everything carrying it, pieces of any kind and sprites, texts or panels
tagged on their own, so what comes is either: `'api' in thing` tells. A tag is also what a saved
scene and the editor can name.

Measured as `collisionBetween` measures: a piece by its hitboxes that are on, or by what it draws
when it has none; a sprite, text or panel by itself. Two pieces with physics bodies also touch
when the simulation says so, in 2D and in 3D, so the same line works whether a hitbox or a body
tells it. Run with the scene's other collisions, once its updates are over; not while it is
paused. Stops when the object goes away.

- `match: M`: What it collides with: a component, or a tag.
- `callback: (other: TCollisionMatched<M>) => void`: What to do with each one.
- `options: TCollisionWithOptions`: When to call: see `TCollisionWithOptions`.

#### useHitbox (function, from `nacatamalon`)

```ts
useHitbox(options: THitboxOptions) => THitbox
```

Gives this object a part that can be touched: what `useCollisionWith`, `useCollisionBetween` and
`collisionBetween` measure it by instead of its picture. What it is (`'enemy'`) is its tag
(`useTag`), not the hitbox's.

Apart from the picture, because that is how these games were made. The hero's hitbox is
smaller than his sprite, so a bullet that grazes his hair misses; a sword only hurts on the frames
of the swing. Left without a size it is the size of the picture, frame after frame, and it is
changed whenever the game likes: written to, it takes effect on the next comparison.

It is measured like the picture, in the object's own pixels, from the picture's centre: it turns,
grows and moves with the object. A sword that hurts beside a body that is hurt is a piece of its
own, with its own tag.

- `options: THitboxOptions`: Where it is and how big, and whether it is on.

#### TCollisionMatch (type, from `nacatamalon`)

```ts
type TCollisionMatch: string | TTag | (args: never[]) => unknown
```

What a collision is asked by: a tag (what `useTag` gives, or its name), which meets everything
carrying it, or a component, which meets the pieces `useSpawn` made from it.

#### TCollisionMatched (type, from `nacatamalon`)

```ts
type TCollisionMatched: conditional
```

What a collision asked by `M` is called with: a piece of that component, its `api` typed, for a
component; a piece, or a sprite, text or panel tagged on its own, for a tag.

#### TCollisionTarget (type, from `nacatamalon`)

```ts
type TCollisionTarget: TBox | TSprite | TText | TNineSlice | TRect
```

Anything `collisionBetween` can compare: an object, something it draws, or a rectangle of the world.

#### TCollisionWhen (type, from `nacatamalon`)

```ts
type TCollisionWhen: 'start' | 'touching' | 'end'
```

When `useCollisionWith` calls: on the first frame two things touch, every frame they do, or on the
first they no longer do.

#### TCollisionWithOptions (type, from `nacatamalon`)

```ts
type TCollisionWithOptions
```

What `useCollisionWith` takes besides what it collides with and what to do.

- `when?: TCollisionWhen`: When it calls: `'touching'` every frame the two touch (the default), `'start'` on the first,

#### THitbox (type, from `nacatamalon`)

```ts
type THitbox
```

The part of an object that can be touched. Made by `useHitbox`, and changed whenever the game
likes: smaller while crouching, off while blinking after a hit. What the object is (`'enemy'`) is
its tag, not the hitbox's: see `useTag`.

Measured like the object's picture, in its own pixels: it turns, grows and moves with it.

- `x: number`: How far its centre is moved from the centre of the picture, in the object's own pixels.
- `y: number`
- `width: number | undefined`: Its size, in the object's own pixels. `undefined` takes the picture's, frame after frame, so it
- `height: number | undefined`
- `enabled: boolean`: Off, nothing touches it: a character blinking after a hit, a sword between swings. `true` by

#### THitboxOptions (type, from `nacatamalon`)

```ts
type THitboxOptions
```

How a hitbox starts. All of it is optional: it begins as the picture.

- `x?: number`
- `y?: number`
- `width?: number`
- `height?: number`
- `enabled?: boolean`

#### TRect (type, from `nacatamalon`)

```ts
type TRect
```

A rectangle in the world, unturned: its top-left corner and its size.

- `x: number`
- `y: number`
- `width: number`
- `height: number`

### Physics

The body format the engine defines and the seam an adapter registers through. The simulation lives in the adapter.

#### box3dProvider (variable, from `nacatamalon/physics3d`)

```ts
box3dProvider: TPhysicsProvider
```

Moves the physics a scene declares in three dimensions, using box3d.

This is the adapter's half of the split: the engine keeps colliders, writes them down and reads
them back while deliberately being unable to move them, and registering this is what makes a
scene actually fall. Nothing here is needed to author physics: a tool opens, edits and saves
a scene with none of it installed.

It is handed everything a scene declares, whether that came from a file or from a line somebody
typed, because the engine records it and then asks. There is nothing here that only one of those
two paths goes through.
import { registerPhysicsProvider } from 'nacatamalon/extend';
import { box3dProvider } from 'nacatamalon/physics3d';

registerPhysicsProvider(box3dProvider);

- `createWorld: (world: TPhysicsWorld) => void`: Called once for the world a scene declares, and always before any body of that scene: the
- `createBody: (box: TBox, body: TPhysicsBody) => void`: Called for each object carrying a collider. `box` is the object that owns it: where it starts
- `watchContacts?: (box: TBox, told: (other: TBox, started: boolean) => void) => boolean`: Tells `told` from now on whenever the body of `box` starts or stops touching another, with the
- `hasBody?: (box: TBox) => boolean | undefined`: Whether `box` still has a body here: `true` or `false` when this provider simulates it, and

#### createPhysicsBody2d (function, from `nacatamalon`)

```ts
createPhysicsBody2d(options: { id, name, body, collider } & Partial<TPhysicsSurface>) => TPhysicsBody2d
```

A complete flat collider from a partial description.

The factory exists so that "a rectangular collider" is one call instead of six fields somebody can
forget: every record that reaches a document is complete by construction, which is what lets
the format promise that a scene means the same thing whoever simulates it.

- `options: { id, name, body, collider } & Partial<TPhysicsSurface>`: What is known: at least its shape. Everything left out takes its default.

#### createPhysicsBody3d (function, from `nacatamalon`)

```ts
createPhysicsBody3d(options: { id, name, body, collider, offset } & Partial<TPhysicsSurface>) => TPhysicsBody3d
```

A complete collider in three dimensions from a partial description: the sibling of
`createPhysicsBody2d`, filling in the same defaults.

- `options: { id, name, body, collider, offset } & Partial<TPhysicsSurface>`: What is known: at least its shape. Everything left out takes its default.

#### DEFAULT_RAY_DISTANCE (variable, from `nacatamalon/physics3d`)

```ts
DEFAULT_RAY_DISTANCE: 1000
```

How far a ray reaches when nothing says otherwise.

Finite rather than infinite because the backend takes a translation vector, not a direction:
there is no "infinity" to hand it. A kilometre is far beyond any level this engine's retro
scope builds, so it reads as unlimited while staying a number the solver can use.

#### getPhysicsWorld2d (function, from `nacatamalon/physics2d`)

```ts
getPhysicsWorld2d() => TPhysicsWorldHandle2d | null
```

The 2D physics world the running scene's document built, or `null` if it has none.

The seam between authored data and authored behaviour, and the 2D twin of the 3D adapter's
accessor of the same name. A collider is data: the document declares it and this provider
simulates it, no code on either side. Steering is not: a script attached to a box has no way
to name the world the scene root declared, because that world never passed through the
document. Without this the only reachable world is one the script opens itself, which is a
second world with its own gravity and its own bodies, colliding with nothing in the scene.

Pair it with `world.bodyOf(box)` to reach the box's own collider: the document built the
body, so the handle `addRect` returned went to this provider and never to the game.

`null` is the ordinary answer, not an error, and scripts must handle it: the editor's edit
viewport deserializes the same document with no provider registered (see `play.ts`), so a
script runs there with no world and must simply do nothing. That is what keeps the workspace
from simulating while you author in it.
registerScript('walker', (self) => {
    const world = getPhysicsWorld2d();
    if (!world) return;                          // editing, not playing
    useUpdate(() => {
        // Looked up per frame: the body is registered as the box is built, so at init it may
        // not exist yet.
        world.bodyOf(self)?.setLinearVelocity(120, 0);
    });
});

Call it during scene init, which is when a script runs, so this is the ordinary case. Outside
init there is no active game to compare against and the answer is always `null`.

#### getPhysicsWorld3d (function, from `nacatamalon/physics3d`)

```ts
getPhysicsWorld3d() => TPhysicsWorldHandle3d | null
```

The 3D physics world the running scene built, or `null` if it has none.

The seam between authored data and authored behaviour, and the twin of the 2D adapter's
accessor of the same name. A collider is data: the scene declares it and this provider
simulates it, with no code on either side. Steering is not, and neither is a character, a
ray or a trigger's answer, so this is the way back out.

Pair it with `world.bodyOf(box)` to reach an object's own collider: the engine built the body
and handed it here, so the handle `addBox` returned never went to the game.

`null` is an ordinary answer, not an error, and scripts must handle it: a tool that opens a
scene to edit it deserializes the same objects with no provider registered, so a script
runs there with no world and must simply do nothing. That is what keeps a workspace from
simulating while somebody is building in it.
registerScript('walker', (self) => {
    const world = getPhysicsWorld3d();
    if (!world) return;                          // editing, not playing
    useUpdate(() => {
        // Looked up per frame: the body is registered as the object is built, so at init it
        // may not exist yet.
        world.bodyOf(self)?.setLinearVelocity(0, 0, -4);
    });
});

Call it while the scene is being built, which is when a script runs, so this is the ordinary
case. Outside that there is no active game to compare against and the answer is always `null`.

#### installPhysics2d (function, from `nacatamalon/physics2d`)

```ts
installPhysics2d() => void
```

Installs this as what simulates the flat half of a scene's physics. The one line a game
writes, and the only one: everything else about physics is the engine's vocabulary, so a scene
says what it is made of and this makes it move.

It is a call and not something that happens on import, for three separate reasons, any one of
which would be enough. The engine declares itself free of side effects, so a bundler is allowed
to drop an import nothing is used from, and a game that worked in development would sit still
once built. A tool must be able to open a scene and NOT simulate it, which is what lets you build
a level in a workspace that holds still. And a game may install the solid half as well, which it
does the same way, by saying so.
import { installPhysics2d } from 'nacatamalon/physics2d';

installPhysics2d();

Installing twice does nothing the second time.

#### installPhysics3d (function, from `nacatamalon/physics3d`)

```ts
installPhysics3d() => void
```

Installs this as what simulates the solid half of a scene's physics. The one line a game
writes, and the only one: everything else about physics is the engine's vocabulary, so a scene
says what it is made of and this makes it move.

It is a call and not something that happens on import, for three separate reasons, any one of
which would be enough. The engine declares itself free of side effects, so a bundler is allowed
to drop an import nothing is used from, and a game that worked in development would sit still
once built. A tool must be able to open a scene and NOT simulate it, which is what lets you build
a level in a workspace that holds still. And a game may install the flat half as well, which it
does the same way, by saying so.
import { installPhysics3d } from 'nacatamalon/physics3d';

installPhysics3d();

Installing twice does nothing the second time.

#### loadBox3D (function, from `nacatamalon/physics3d`)

```ts
loadBox3D() => Promise<Box3DModule>
```

Loads (once) the Box3D WASM runtime and memoizes the promise, so every
world in a game shares a single instantiation instead of re-compiling
the module per scene. Uses the inline build, so the `.wasm` is embedded in the
bundle as base64, so it works in the browser with no Vite/loader config and in a
headless Node/Bun test with no separate file to resolve. The trade is a slightly
larger bundle, paid only by games that actually import the physics adapter.

#### loadRapier2D (function, from `nacatamalon/physics2d`)

```ts
loadRapier2D() => Promise<__module>
```

Load and initialize the Rapier2D WebAssembly module.

Memoizes the promise globally, so multiple calls return the same promise.
The WASM is loaded once per page, shared across all scenes and worlds.

`-compat` and not the plain package, and this is a portability decision rather than a
preference. `@dimforge/rapier2d` reaches its WebAssembly through an ESM import of the `.wasm`
file itself (`import * as wasm from './rapier_wasm2d_bg.wasm'`), which only works under a
bundler that implements WASM-ESM integration. Vite does, with `vite-plugin-wasm`, and that is
why this worked for as long as the editor was the only thing that ran it. An exported build is
bundled by something else, and there the import yields a namespace with no functions on it: the
failure is `rawintegrationparameters_new is not a function`, thrown from generated glue, at
which point nothing points back at how the module was loaded.

`-compat` is the same bindings with the WebAssembly carried inline and instantiated by an
explicit `init()`. It costs a larger JavaScript payload and buys one loading path that holds
under every bundler (including whatever a desktop shell uses), which matters more, because the
alternative is physics that works in the editor and is inert in the shipped game.

The `await` this adds is free: every caller already went through this promise.

#### rapier2dProvider (variable, from `nacatamalon/physics2d`)

```ts
rapier2dProvider: TPhysicsProvider
```

Moves the physics a scene declares, using Rapier2D.

This is the adapter's half of the split: the engine keeps colliders, writes them down and reads
them back while deliberately being unable to move them, and registering this is what makes a
scene actually fall. Nothing here is needed to author physics: a tool opens, edits and saves
a scene with none of it installed.

It is handed everything a scene declares, whether that came from a file or from a line somebody
typed, because the engine records it and then asks. There is nothing here that only one of those
two paths goes through.
import { registerPhysicsProvider } from 'nacatamalon/extend';
import { rapier2dProvider } from 'nacatamalon/physics2d';

registerPhysicsProvider(rapier2dProvider);

- `createWorld: (world: TPhysicsWorld) => void`: Called once for the world a scene declares, and always before any body of that scene: the
- `createBody: (box: TBox, body: TPhysicsBody) => void`: Called for each object carrying a collider. `box` is the object that owns it: where it starts
- `watchContacts?: (box: TBox, told: (other: TBox, started: boolean) => void) => boolean`: Tells `told` from now on whenever the body of `box` starts or stops touching another, with the
- `hasBody?: (box: TBox) => boolean | undefined`: Whether `box` still has a body here: `true` or `false` when this provider simulates it, and

#### useCharacterBody (function, from `nacatamalon/physics3d`)

```ts
useCharacterBody(options: TCharacterOptions) => TCharacterBody | null
```

Makes this object a walking character: a capsule moved by asking the world what is in the
way, and the thing that makes a room walkable.

It is the one part of physics that a scene cannot write down, and that is not an oversight. A
crate is a shape with a weight, and a file can say so. A character is a decision taken sixty
times a second about where a body is allowed to be: it climbs a step without jumping, walks up a
ramp and not up a wall, and stops dead against a crate instead of tipping over it. None of that
is a property of the object, so there is nothing for a scene to keep, and steering one with
forces gives you something that slides on slopes and falls over on its face.

Set `velocity` and it walks. Falling is applied for you, and `isOnGround` is the answer to
whether it may jump.

`null` when the scene has no physics world, which is what a tool opening a scene to edit it
looks like. Handle it the way you would handle `getPhysicsWorld3d()` coming back empty: do
nothing, because nothing is running.
const Player = () => {
    useTransform({ x: 0, y: 1, z: 0 });
    createMesh({ geometry: useSphereGeometry({ radius: 0.35 }), tint: BLUE });

    const player = useCharacterBody({ radius: 0.35, height: 1.8 });
    const keys = useKeyboard();
    useUpdate(() => {
        if (!player) return;
        player.velocity.x = keys.isDown('d') ? 4 : keys.isDown('a') ? -4 : 0;
        if (keys.isDown('Space') && player.isOnGround) player.velocity.y = 5;
    });
};

- `options: TCharacterOptions`

#### useCharacterBody2d (function, from `nacatamalon/physics2d`)

```ts
useCharacterBody2d(options: TCharacterOptions2d) => TCharacterBody2d
```

Makes this object a platformer's character: the game says how fast it goes, and the world stops it
at the map's walls, holds it on ledges and slopes, and lets it push what the simulation moves.

Moved by the game, not by physics. It does not slide, bounce or keep its speed: it goes at the
speed the game writes into `velocity`, every frame, and stops dead when the game writes nothing.
That is how the old platformers felt, and it still lives in the simulation, so a crate is pushed by
it, a bridge sags under it, and something falling lands on its head.

It is moved once every `useUpdate` of the scene is over, where the game set its speed, so what the
game decides this frame is seen this frame, and before hitboxes are compared and `useLateUpdate`
runs, so a camera follows it where it ended up.

What it reads from the map's tiles: walls (`solid`), ledges (`oneWay`, landed on from above,
`dropThrough()` to fall through), slopes (`slope`), ladders (`isOnLadder`, then the game sets
`climbing`), belts and currents (`push`, which carry it), harm (`damage`, which it reports and
never applies) and the tile underfoot (`ground`, for whatever the game wrote in its `props`).

Needs the scene's 2D world (`usePhysicsWorld2d`) and `installPhysics2d()`. Where nothing simulates,
as in a tool showing the scene, it is a character that never moves.

- `options: TCharacterOptions2d`: Its size, and how it falls, climbs and pushes.

#### useJoint2d (function, from `nacatamalon/physics2d`)

```ts
useJoint2d(a: TGameObject, b: TGameObject | null, joint: TJoint2d) => TJointHandle2d
```

Joins two objects' bodies, or one to a point of the world when `b` is `null`: the planks of a
bridge, a pendulum on its nail, a platform on a spring, a door on its hinge.

Both objects need a body (`usePhysicsBody2d`) in the scene's 2D world. Points are given where the
scene lays things out, in the world, so a bridge's planks are pinned where they meet. The two
joined bodies do not collide with each other unless the joint says `collide: true`.

A bridge wants three things to hang still: a little slack (planks a touch longer than the gap, so it
sags), a little `friction` in its hinges, and its ends pinned a pixel or two off the banks, since a
plank flush against a wall rubs on it and never settles.

The joint goes with whichever of its objects is destroyed first, and earlier with `destroy()`.
Call it while the scene is built; to join things later (a hook thrown mid-jump), keep the world
from `getPhysicsWorld2d()` and call its `addJoint`. Where nothing simulates, it joins nothing.

- `a: TGameObject`: One object.
- `b: TGameObject | null`: The other, or `null` for a fixed point of the world.
- `joint: TJoint2d`: How they are joined: see `TJoint2d`.

#### usePhysicsBody2d (function, from `nacatamalon`)

```ts
usePhysicsBody2d(options: TPhysicsBody2dOptions) => TPhysicsBody2d
```

Gives this object a flat shape to collide with.

It does two things, and that is the whole design. It writes down what the object is made of,
so a scene saved from code says it and comes back with it; and it hands that to whatever is
installed to simulate, so it moves. One way in, whether the scene was typed by a person or opened
from a file, which is what makes those two behave the same.

Where it is comes from the object, never from here: the object's own placement if it has one, and
otherwise the placement of what it draws, which is where you put it with `createSprite`.

With nothing installed to simulate, this is still worth calling: the shape is recorded, the scene
opens and saves without losing it, and it sits still. That is what a tool wants while somebody is
building a level.

- `options: TPhysicsBody2dOptions`: Its kind (`'dynamic'`, `'static'` or `'kinematic'`), its shape, and what it is

#### usePhysicsBody3d (function, from `nacatamalon`)

```ts
usePhysicsBody3d(options: TPhysicsBody3dOptions) => TPhysicsBody3d
```

Gives this object a shape in three dimensions to collide with. The sibling of
`usePhysicsBody2d`; see it for why declaring and simulating are one call.

`offset` is where the shape sits inside the object, which is what a model imported standing on
its own feet needs so that its collider is not buried half in the floor.

- `options: TPhysicsBody3dOptions`: Its kind (`'dynamic'`, `'static'` or `'kinematic'`), its shape, and what it is

#### usePhysicsWorld2d (function, from `nacatamalon`)

```ts
usePhysicsWorld2d(options: { id, gravity }) => TPhysicsWorld2d
```

Opens the flat simulation for this scene, and says how hard things fall.

Called in the scene's body, because gravity belongs to the scene and not to anything in it: a
scene seen from above has none and a platformer does. Called anywhere else it is recorded on that
object and nothing reads it, which is said out loud rather than ignored.

`gravity` is in pixels per second squared with +y pointing down, which is the way a sprite's
y axis grows, so earth-ish is about `900` and positive.

Like a collider, this is worth calling with nothing installed to simulate: the scene records what
it wants and comes back with it.

- `options: { id, gravity }`: How hard things fall (`gravity`, in pixels per second squared, +y down), and an

#### usePhysicsWorld3d (function, from `nacatamalon`)

```ts
usePhysicsWorld3d(options: { id, gravity }) => TPhysicsWorld3d
```

Opens the simulation in three dimensions for this scene. See `usePhysicsWorld2d`.

`gravity` is in metres per second squared with +y up, so earth is `-9.81`: the opposite sign
from the flat one, because there a placement's y grows downwards. The two disagree on purpose.

- `options: { id, gravity }`: How hard things fall (`gravity`, per second squared), and an `id` for a scene

#### useSoftBody2d (function, from `nacatamalon/physics2d`)

```ts
useSoftBody2d(options: TSoftBodyOptions2d) => TSoftBody2d
```

Makes the object being built a soft body in the scene's 2D world: something that squashes, wobbles
and rolls rather than keeping its shape. A blob of goo, a jelly, a ball of dough, a rope.

It is a ring of corners held to one another, and to the area they enclose, by springs. It falls,
lands on the map and on the other bodies, rolls down slopes and squashes against walls. The object
is kept at its middle, and `outline` says where its corners are. Give it a `polygon` made with
`createPolygon` and the corners of that are moved to the body's every frame, the picture stretching
with them.

A soft body rolls without turning its object: its turn is in where its corners are.

- `options: TSoftBodyOptions2d`: The shape it starts as and what it is made of: see `TSoftBodyOptions2d`.

#### useTileBody2d (function, from `nacatamalon`)

```ts
useTileBody2d(options: TCharacterShape2d) => TCharacterBody2d
```

Makes this object a platformer's character that walks on the scene's maps, with no physics engine:
the game says how fast it goes, and the map stops it at walls, holds it on ledges and slopes, lets
it climb ladders and carries it on belts.

The classic way, in pixels. It moves across, settles against walls, then up or down, settles
against the floor or the ceiling, the way the old platformers did, with nothing to load and the same
result on every machine at a fixed `step`. It knows the maps, and the objects laid out with a 2D
collider that never moves (a `'static'` body that is not a sensor: a floor, a wall, a platform made
of an object rather than of tiles), measured as the rectangle around them. For crates to push,
bridges that sag or anything else simulated, `useCharacterBody2d` from `nacatamalon/physics2d` gives the same
character in the 2D world, and changing between the two is changing this one line.

It is moved once every `useUpdate` of the scene is over, where the game set its speed, and before
hitboxes are compared and `useLateUpdate` runs. Slopes are measured under the middle of its feet,
and a block of any height on flat ground is a wall. A map can be moved and scaled, not turned.

- `options: TCharacterShape2d`: Its size, and how it falls.

#### TBox3dModule (type, from `nacatamalon/physics3d`)

```ts
type TBox3dModule: Box3DModule
```

The Box3D WebAssembly runtime, or `null`: the opaque handle every physics call
goes through. Excluded from any serialization exactly like a GPU handle: it is pure
runtime, reconstructed on load, never part of a save. Re-exported so the world
wrapper can type body/world ids off it without importing Embind internals.

#### TCharacterBody (type, from `nacatamalon/physics3d`)

```ts
type TCharacterBody
```

A kinematic character: the thing that separates "boxes that fall" from "somebody walks here".

A character is deliberately not a dynamic body. A dynamic capsule steered by forces fights
its own momentum and friction: it slides on slopes, tips over, skates after you let go of the
stick, and catches on the seam between two floor tiles. Every engine solves this the same way
and so does this one: the character is a capsule moved by queries against the world
(`b3World_CollideMover` + `b3SolvePlanes`, Box2D v3's mover primitives), resolved to a position
that does not overlap anything, then written onto the box's transform.

Set `TCharacterBody.velocity`, and the world advances it on its own fixed step. Gravity is applied for you (set `gravity: 0` for a
flyer), because a controller that falls is what "character" means to nearly everyone asking
for one.

- `velocity: { x, y, z }`: Metres per second, mutable in place: write into it from a `useUpdate` and the next
- `isOnGround: boolean`: Whether a surface flat enough to stand on is underfoot, decided from the contact planes of
- `setPosition: (x: number, y: number, z: number) => void`: Places the character without consulting the world: spawning, respawning, a cutscene. The
- `destroy: () => void`: Stops driving the object, which keeps whatever placement it last had.

#### TCharacterBody2d (type, from `nacatamalon`)

```ts
type TCharacterBody2d
```

A character of a platformer, moved by the game and stopped by the map: by its walls, ledges and
slopes, and, made with `useCharacterBody2d`, by everything else in the simulation, which it can
push. `useTileBody2d` and `useCharacterBody2d` both give one, so a game can change which it uses
by changing one line.

Every field but `velocity` and `climbing` is read only, and each says what happened in the last
move, the one after the updates of the previous frame.

- `velocity: { x, y }`: Pixels per second, written by the game from a `useUpdate`: `x` walks, a negative `y` jumps, and
- `climbing: boolean`: On a ladder, climbing: no gravity, ledges passed through, moved only by `velocity`. The game
- `isOnGround: boolean`: Standing on something: a floor, a ledge, a slope, a crate.
- `isOnCeiling: boolean`: Its head hit something on the way up.
- `wallSide: 'left' | 'right' | null`: Pressed against a wall, and on which side.
- `isOnOneWay: boolean`: Standing on a ledge (`oneWay`), which `dropThrough()` falls through.
- `isOnLadder: boolean`: In front of a ladder tile, or standing on top of one, which is when the game may set `climbing`:
- `ground: TTileDoc | undefined`: The tile underfoot, for what the game wrote on it (`ground?.props?.ice`), or `undefined` in the
- `damage: number`: The largest `damage` of the tiles it is touching, `0` for none. The game decides what it means.
- `dropThrough: () => void`: Falls through the ledge it is standing on. Nothing happens anywhere else.
- `setPosition: (x: number, y: number) => void`: Puts it somewhere without asking the world: spawning, respawning. It can put it inside a wall.
- `destroy: () => void`: Stops moving the object and takes the character out of the simulation.

#### TCharacterOptions (type, from `nacatamalon/physics3d`)

```ts
type TCharacterOptions
```

Everything a character needs beyond its world: the capsule it occupies, how it treats slopes,
and where that capsule sits inside its box.

`height` is the total height including the caps, the same convention `Collider3D`'s capsule
uses, so a 1.8 m person is `{ radius: 0.3, height: 1.8 }`.

- `radius: number`
- `height: number`
- `slopeLimit?: number`: The steepest surface that still counts as ground, in radians. Default ~50°. Anything steeper is a wall as far as `TCharacterBody.isOnGround` is
- `gravity?: number`: Downward acceleration in m/s², applied every step. Defaults to the world's own gravity
- `offset?: [number, number, number]`: Where the capsule's centre sits relative to the box's origin, in local units before scale
- `layer?: number`: The collision layer the character's presence proxy is on, and which layers may see it
- `collidesWith?: number`

#### TCharacterOptions2d (type, from `nacatamalon/physics2d`)

```ts
type TCharacterOptions2d: TCharacterShape2d & { maxSlope, pushes, mass, layer, collidesWith }
```

What a character in the simulation is: its size and how it falls, and how it climbs and pushes.

- `maxSlope?: number`: The steepest slope it walks up, in radians. About 50° by default; steeper ones it slides down.
- `pushes?: boolean`: Whether walking into something the simulation moves pushes it. `true` by default.
- `mass?: number`: How heavy it is: what it weighs on a bridge or a crate it stands on, and how heavy a thing it can
- `layer?: number`: Its collision layer and what it collides with, as any body's.
- `collidesWith?: number`
- `width: number`: The box it collides with, in the object's own pixels: usually narrower than its picture, as
- `height: number`
- `offset?: [number, number]`: Where the box's centre is from the object's position, in its own pixels. `[0, 0]` by default.
- `gravity?: number`: Pixels per second squared, pulling down. 900 by default (the world's gravity, for a character
- `maxFall?: number`: The fastest it falls, in pixels per second. `420` by default: seven pixels a frame at `step: 60`.

#### TCharacterShape2d (type, from `nacatamalon`)

```ts
type TCharacterShape2d
```

What a character is, whatever moves it: its size, where that sits on the object, and how it falls.

- `width: number`: The box it collides with, in the object's own pixels: usually narrower than its picture, as
- `height: number`
- `offset?: [number, number]`: Where the box's centre is from the object's position, in its own pixels. `[0, 0]` by default.
- `gravity?: number`: Pixels per second squared, pulling down. 900 by default (the world's gravity, for a character
- `maxFall?: number`: The fastest it falls, in pixels per second. `420` by default: seven pixels a frame at `step: 60`.

#### TCollider2d (type, from `nacatamalon`)

```ts
type TCollider2d: { shape, width, height } | { shape, radius } | { shape, vertices }
```

The shape a flat body collides with, centred on where the object is.

Centred and not cornered, because that is where this engine puts a picture too: a sprite's
`anchor` is its middle unless somebody says otherwise, so a shape the same size as the art lines
up with it without anybody working out an offset. An object drawn from its corner (`anchor` at
zero, which is how a floor is usually written) is the case where the two differ, and there the
placement is the corner and the shape is still around it.

The set is small and closed on purpose. It is what any 2D physics engine has in common, not a
copy of one library's list: the document has to outlive whatever is simulating it, and a shape no
backend can build would be a field that does nothing.

#### TCollider3d (type, from `nacatamalon`)

```ts
type TCollider3d: { shape, size } | { shape, radius } | { shape, radius, height } | { shape, geometry } | { shape, geometry }
```

The shape a body in three dimensions collides with, around the object's origin unless it carries
an `offset`.

#### TColliderOptions3d (type, from `nacatamalon/physics3d`)

```ts
type TColliderOptions3d: TColliderSurface3d & { offset }
```

What every `add*` takes beyond its own dimensions: the surface, plus where the shape sits
inside the object.

`offset` is in the box's local units before its scale, matching
`TPhysicsBody3d.offset`, and it exists for glTF, which puts a model's origin at its
feet while a collider is centred on that origin. Omitted means centred.

- `offset?: [number, number, number]`
- `bounce?: number`
- `friction?: number`
- `density?: number`
- `sensor?: boolean`
- `layer?: number`: Which collision layer this body is on (`0..15`) and which layers it collides with (a
- `collidesWith?: number`

#### TColliderSurface2d (type, from `nacatamalon/physics2d`)

```ts
type TColliderSurface2d
```

The non-geometric properties of a collider: how it responds to a contact, and whether it
resolves the contact at all.

- `bounce?: number`: How much approach speed survives as separation speed, 0..1.
- `friction?: number`
- `density?: number`
- `sensor?: boolean`: Make this a sensor: it detects overlaps but never resolves them, so things pass
- `layer?: number`: Which collision layer this body is on (`0..15`) and which layers it collides with (a
- `collidesWith?: number`

#### TColliderSurface3d (type, from `nacatamalon/physics3d`)

```ts
type TColliderSurface3d
```

Optional surface tuning for a collider: how a contact resolves, and whether it resolves at
all. `bounce` is how much it bounces (0 = dead, 1 = elastic), `friction` resists sliding,
`density` (kg/m³) sets the body's mass from its volume, and `sensor` detects overlaps
without resolving them, which is a trigger volume. All optional; omitted fields keep Box3D's
defaults.

The grouping mirrors the engine's `TPhysicsSurface`, which puts the same four together for the same
reason: they are the non-geometric half of a collider.

- `bounce?: number`
- `friction?: number`
- `density?: number`
- `sensor?: boolean`
- `layer?: number`: Which collision layer this body is on (`0..15`) and which layers it collides with (a
- `collidesWith?: number`

#### TJoint2d (type, from `nacatamalon/physics2d`)

```ts
type TJoint2d: { type, at, limits, motor, friction } | { type, length, atA, atB } | { type, length, frequency, damping, atA, atB } | { type, axis, at, limits, motor, friction } | { type } & { collide }
```

How two bodies are joined, or one body to a point of the world.

Every point is a point of the world where the scene lays the bodies out, not one measured on
either body: a bridge's planks are pinned where they meet, a pendulum where its nail is.

- `'hinge'`: pinned together at `at`, each free to turn about it: the planks of a bridge, a door, a
pendulum, a windmill with a `motor`. `limits` bounds the turn, in radians either side of where
it started.
- `'rope'`: never further apart than `length` (their distance at the start, by default), and free
to come closer: a hanging lamp, a grappling hook. Tied at `atA` and `atB`, their centres by default.
- `'spring'`: pulled back to `length` apart: a platform that bounces, a car's suspension. Said as
how many times a second it bounces, `frequency` (2 by default), and how soon it settles,
`damping` (0 bounces for ever, 1 settles without a bounce; 0.3 by default), so a heavy crate
and a light one bounce alike.
- `'slider'`: free to move along `axis` only, through `at` (b's centre by default), without
turning: a platform on a rail, a piston. `limits` bounds the travel, in pixels from where it
started.
- `'weld'`: held as they are now, as one: pieces that move together and still collide apart.

A hinge or a slider with `friction` slows down: its turning (or sliding) loses that much of itself
a second, whatever the bodies weigh, so it settles rather than swinging or rolling on for ever. `2`
calms a swing in a few seconds, `5` in about one. Nothing else in the world slows a joint down, so
without it a bridge sways as long as the game runs.

`collide` says whether the two bodies collide with each other; `false` by default, since joined
pieces usually touch, and two planks of a bridge pushing each other apart is not a bridge.

- `collide?: boolean`

#### TJointHandle2d (type, from `nacatamalon/physics2d`)

```ts
type TJointHandle2d
```

A joint once made: what lets it go before its bodies do.

- `destroy: () => void`: Lets the two bodies go: a rope cut, a hook let go of, a bridge's plank snapped off. A joint

#### TPhysicsBody (type, from `nacatamalon`)

```ts
type TPhysicsBody: TPhysicsBody2d | TPhysicsBody3d
```

Either kind of collider, told apart by `_type`.

Two types and not one with a flag, because their shapes do not overlap (a circle is not a sphere)
and everything reading them would otherwise have to unwrap one discriminant inside another.

#### TPhysicsBody2d (type, from `nacatamalon`)

```ts
type TPhysicsBody2d: { _type, id, name, body, collider } & TPhysicsSurface
```

A flat collider on an object: what it is made of, and how it is driven.

Where it is is not here. The object owns that, by the standing rule that a component carries
no placement of its own, which is also why moving the object moves its collider.

- `_type: 'physics-body-2d'`
- `id: string`
- `name: string`
- `body: TPhysicsBodyType`
- `collider: TCollider2d`
- `bounce: number`: Bounciness: how much of the speed it arrived with it leaves with, `0` to `1`.
- `friction: number`
- `density: number`: Weight comes from this and the shape's size, and is never set directly.
- `sensor: boolean`: Notices overlaps without settling them, so things pass straight through: a checkpoint, a
- `layer: number`: Which layer this body is on, `0` to `15`. In code it can be said by the name the game gave
- `collidesWith: number`: A mask of the layers this body collides with: `1 << n` per layer, so `PHYSICS.ALL_LAYERS` is

#### TPhysicsBody2dOptions (type, from `nacatamalon`)

```ts
type TPhysicsBody2dOptions: { id, name, body, collider } & Partial<Omit<TPhysicsSurface, 'layer' | 'collidesWith'>> & TPhysicsLayerOptions
```

What a flat body is made from: how it moves, its shape, and how it feels to touch. `id` and
`name` are only for keeping the identity a scene document gave it.

- `id?: string`
- `name?: string`
- `body: TPhysicsBodyType`
- `collider: TCollider2d`
- `layer?: TPhysicsLayer`
- `collidesWith?: number | typeOperator`

#### TPhysicsBody3d (type, from `nacatamalon`)

```ts
type TPhysicsBody3d: { _type, id, name, body, collider, offset } & TPhysicsSurface
```

A collider in three dimensions. See `TPhysicsBody2d` for why the placement is not here.

- `_type: 'physics-body-3d'`
- `id: string`
- `name: string`
- `body: TPhysicsBodyType`
- `collider: TCollider3d`
- `offset: [number, number, number]`: Where the shape sits inside the object, before its scale.
- `bounce: number`: Bounciness: how much of the speed it arrived with it leaves with, `0` to `1`.
- `friction: number`
- `density: number`: Weight comes from this and the shape's size, and is never set directly.
- `sensor: boolean`: Notices overlaps without settling them, so things pass straight through: a checkpoint, a
- `layer: number`: Which layer this body is on, `0` to `15`. In code it can be said by the name the game gave
- `collidesWith: number`: A mask of the layers this body collides with: `1 << n` per layer, so `PHYSICS.ALL_LAYERS` is

#### TPhysicsBody3dOptions (type, from `nacatamalon`)

```ts
type TPhysicsBody3dOptions: { id, name, body, collider, offset } & Partial<Omit<TPhysicsSurface, 'layer' | 'collidesWith'>> & TPhysicsLayerOptions
```

The same for a body in space.

- `id?: string`
- `name?: string`
- `body: TPhysicsBodyType`
- `collider: TCollider3d`
- `offset?: [number, number, number]`
- `layer?: TPhysicsLayer`
- `collidesWith?: number | typeOperator`

#### TPhysicsBodyHandle2d (type, from `nacatamalon/physics2d`)

```ts
type TPhysicsBodyHandle2d
```

Handle to a physics body registered in the world.

- `onEnter: `
- `onExit: `
- `applyImpulse: (x: number, y: number) => void`: Apply an instantaneous impulse (momentum change) at the body's center.
- `applyForce: (x: number, y: number) => void`: Pushes it steadily for this frame: ask every frame for as long as it should push (a fan, a
- `setLinearVelocity: (vx: number, vy: number) => void`: Set the linear velocity directly.
- `setAngularVelocity: (w: number) => void`: Set the angular velocity (rotation speed).
- `setLayers: (layer: TPhysicsLayer, collidesWith?: number | typeOperator) => void`: Moves the body to another collision layer, and changes what it collides with, while the game
- `destroy: () => void`: Takes the body out of the simulation and leaves the object where it is, still drawn and

#### TPhysicsBodyHandle3d (type, from `nacatamalon/physics3d`)

```ts
type TPhysicsBodyHandle3d
```

A runtime handle to one simulated body. Runtime only: the body lives in WASM
memory, so nothing here is serializable (the serializable form is the mesh's own
transform, which this body writes into).

- `onEnter: `
- `onExit: `
- `applyImpulse: (x: number, y: number, z: number) => void`: Applies an instantaneous impulse (mass·velocity) at the body's center, waking
- `applyForce: (x: number, y: number, z: number) => void`: Applies a continuous force (mass·acceleration) at the body's center. Unlike an impulse
- `applyTorque: (x: number, y: number, z: number) => void`: Applies a continuous torque about the body's center, the rotational sibling of
- `setLinearVelocity: (x: number, y: number, z: number) => void`: Sets the body's velocity outright, overriding whatever the solver had. This is how a
- `setAngularVelocity: (x: number, y: number, z: number) => void`: Sets the body's spin outright, the rotational sibling of `setLinearVelocity`.
- `setLayers: (layer: TPhysicsLayer, collidesWith?: number | typeOperator) => void`: Moves the body to another collision layer, and changes what it collides with, while the game
- `destroy: () => void`: Removes this body from the simulation, freeing its WASM memory. The object stops being

#### TPhysicsBodyType (type, from `nacatamalon`)

```ts
type TPhysicsBodyType: 'static' | 'dynamic' | 'kinematic'
```

How a body is driven, and what it lets happen to it.

`'static'` never moves and nothing can push it: walls, the ground, the level. `'dynamic'` is
driven by gravity, forces and contacts, and is what "a physics object" usually means.
`'kinematic'` is driven by you and pushes others without being pushed back: a moving platform, a
lift, a boss on rails.

#### TPhysicsLayer (type, from `nacatamalon`)

```ts
type TPhysicsLayer: number | string
```

A collision layer, by its number (`0` to `15`) or by the name the game gave it in
`physicsLayers` (`'enemy'`).

#### TPhysicsLayerOptions (type, from `nacatamalon`)

```ts
type TPhysicsLayerOptions
```

How a body says what layer it is on and what it collides with: by number, as the format keeps
them, or by the names the game gave its layers (`physicsLayers` in `createGame` or the project).
`collidesWith` is a mask (`1 << n`) or a list of layers: `['world', 'enemy']`.

- `layer?: TPhysicsLayer`
- `collidesWith?: number | typeOperator`

#### TPhysicsSurface (type, from `nacatamalon`)

```ts
type TPhysicsSurface
```

What a collider is made of: how a contact settles, and whether it settles at all.

Every field is required, and that is the point. This is the written-down truth of a scene, and
an absent value would mean "whatever the adapter does", which would make the meaning of a document
depend on who reads it. A scene that behaves differently under a different backend is exactly the
coupling this format exists to prevent.

- `bounce: number`: Bounciness: how much of the speed it arrived with it leaves with, `0` to `1`.
- `friction: number`
- `density: number`: Weight comes from this and the shape's size, and is never set directly.
- `sensor: boolean`: Notices overlaps without settling them, so things pass straight through: a checkpoint, a
- `layer: number`: Which layer this body is on, `0` to `15`. In code it can be said by the name the game gave
- `collidesWith: number`: A mask of the layers this body collides with: `1 << n` per layer, so `PHYSICS.ALL_LAYERS` is

#### TPhysicsWorld (type, from `nacatamalon`)

```ts
type TPhysicsWorld: TPhysicsWorld2d | TPhysicsWorld3d
```

Either simulation's settings. A scene has at most one, and it lives on the scene's root:
gravity is a property of the scene and not of anything in it (a scene seen from above has none, a
platformer does), and the root is an object like any other, so it needs no new home.

#### TPhysicsWorld2d (type, from `nacatamalon`)

```ts
type TPhysicsWorld2d
```

The flat simulation's settings for one scene.

`gravity` is in pixels per second squared with +y pointing DOWN, because that is the way a
sprite's y axis grows and the simulation writes straight onto a placement. So earth-ish gravity is
about `900`, positive: the opposite sign from three dimensions. The disagreement is deliberate,
because turning 2D into metres would put a pixels-per-metre factor into every flat game and every
push, and buy a tidiness no author benefits from, since no scene mixes the two.

- `_type: 'physics-world-2d'`
- `id: string`: Its own id, like every other component. A world has no name (neither does a camera), and it
- `gravity: { x, y }`

#### TPhysicsWorld3d (type, from `nacatamalon`)

```ts
type TPhysicsWorld3d
```

The simulation's settings in three dimensions. `gravity` is in metres per second squared with
+y UP, so earth is `{ x: 0, y: -9.81, z: 0 }`. See `TPhysicsWorld2d` for why the two
disagree on purpose.

- `_type: 'physics-world-3d'`
- `id: string`
- `gravity: { x, y, z }`

#### TPhysicsWorldHandle2d (type, from `nacatamalon/physics2d`)

```ts
type TPhysicsWorldHandle2d
```

The physics world interface returned by usePhysicsWorld.

- `addCircle: (box: TGameObject, options: { type } & TColliderSurface2d & { radius }) => TPhysicsBodyHandle2d`: Register a circular rigid body on an object.
- `addRect: (box: TGameObject, options: { type } & TColliderSurface2d & { width, height }) => TPhysicsBodyHandle2d`: Register a rectangular rigid body on an object.
- `addPolygon: (box: TGameObject, options: { type } & TColliderSurface2d & { vertices }) => TPhysicsBodyHandle2d`: Register a polygonal rigid body on an object.
- `bodyOf: (box: TGameObject) => TPhysicsBodyHandle2d | null`: The body already registered for `box`, or `null` if it has none.
- `addCharacter: (box: TGameObject, options: TCharacterOptions2d) => TCharacterBody2d`: Makes `box` a platformer's character in this world: what `useCharacterBody2d` does for the
- `addSoftBody: (box: TGameObject, options: TSoftBodyOptions2d) => TSoftBody2d`: Makes `box` a soft body in this world: what `useSoftBody2d` does for the object being built.
- `addJoint: (a: TGameObject, b: TGameObject | null, joint: TJoint2d) => TJointHandle2d`: Joins two objects' bodies, or one to a point of the world when `b` is `null`, at any moment:

#### TPhysicsWorldHandle3d (type, from `nacatamalon/physics3d`)

```ts
type TPhysicsWorldHandle3d
```

The world a scene declared, and the factory for bodies in it. Deliberately narrow (box + sphere for now); more shapes and queries land
when a concrete case needs them, not before.

- `addBox: (box: TGameObject, opts: { type, size } & TColliderOptions3d) => TPhysicsBodyHandle3d`: Adds a box collider whose full extents are `size` (metres, halved internally to
- `addSphere: (box: TGameObject, opts: { type, radius } & TColliderOptions3d) => TPhysicsBodyHandle3d`: Adds a sphere collider of `radius` (metres), positioned from where the object is.
- `addCapsule: (box: TGameObject, opts: { type, radius, height } & TColliderOptions3d) => TPhysicsBodyHandle3d`: Adds a capsule collider standing on Y: a cylinder with hemispherical caps, and what a
- `addHull: (box: TGameObject, opts: { type, geometry } & TColliderOptions3d) => TPhysicsBodyHandle3d`: Adds the convex hull of a geometry's vertices: the collider for a prop whose silhouette
- `addMesh: (box: TGameObject, opts: { type, geometry } & TColliderOptions3d) => TPhysicsBodyHandle3d`: Adds the geometry's actual triangles, concave and all. This is level geometry:
- `addCharacter: (box: TGameObject, opts: TCharacterOptions) => TCharacterBody`: Adds a kinematic character: a capsule moved by queries rather than by forces, and the
- `raycast: (origin: { x, y, z }, direction: { x, y, z }, opts?: TRaycastOptions) => TRaycastHit | null`: Fires a ray and returns the closest thing it hits, or `null` for a clean miss, and the
- `bodyOf: (box: TGameObject) => TPhysicsBodyHandle3d | null`: The body already registered for the box with this `id`, or `null` if it has none.

#### TPhysicsWorldOptions2d (type, from `nacatamalon/physics2d`)

```ts
type TPhysicsWorldOptions2d
```

World initialization options.

- `gravity?: { x, y }`

#### TPhysicsWorldOptions3d (type, from `nacatamalon/physics3d`)

```ts
type TPhysicsWorldOptions3d
```

Options for the world. `gravity` is metres/second² (the engine's 3D unit is one
metre); it defaults to Earth on -Y.

- `gravity?: { x, y, z }`

#### TRapierModule (type, from `nacatamalon/physics2d`)

```ts
type TRapierModule: query
```

Type of the initialized Rapier2D module.

#### TRaycastHit (type, from `nacatamalon/physics3d`)

```ts
type TRaycastHit
```

What a ray found: which object, where, and how far along.

`box` is the very object handed to `addBox`/`addSphere` and the rest, so a hit is immediately
usable ("move this", "damage that") without a lookup table of your own.

- `box: TGameObject`
- `point: { x, y, z }`: Where the ray met the surface, in world metres.
- `normal: { x, y, z }`: The surface normal there, unit length and pointing out of the shape.
- `distance: number`: Metres from `origin` to `point`.

#### TRaycastOptions (type, from `nacatamalon/physics3d`)

```ts
type TRaycastOptions
```

How far a ray reaches and what it is allowed to hit.

`collidesWith` is a layer bitmask read exactly like a body's own (see the engine's `TPhysicsSurface`)
so a ground check that should ignore enemies, or a cursor pick that should ignore scenery,
is the same vocabulary the colliders are already authored in rather than a second one.

- `maxDistance?: number`: Metres. Defaults to `DEFAULT_RAY_DISTANCE`.
- `collidesWith?: number`: Which layers the ray can hit. Defaults to all of them.

#### TSoftBody2d (type, from `nacatamalon/physics2d`)

```ts
type TSoftBody2d
```

A soft body in the scene's 2D world, as `useSoftBody2d` gives it: where its corners are, and ways to
move it.

- `outline: ReadonlyArray<{ x, y }>`: Where each corner is now, measured from the object's place, which the body keeps at its middle.
- `velocity: { x, y }`: How fast it is going as a whole, in pixels per second.
- `push: (x: number, y: number) => void`: Pushes every corner this frame, in pixels per second each second whatever it weighs, as gravity
- `kick: (x: number, y: number) => void`: Adds to its speed at once, in pixels per second: a hop, a blow.
- `destroy: () => void`: Takes it out of the world. Destroying its object does it too.

#### TSoftBodyOptions2d (type, from `nacatamalon/physics2d`)

```ts
type TSoftBodyOptions2d: TSoftBodyShape2d & { softness, damping, keepShape, mass, friction, bounce, pinned, polygon }
```

What a soft body is made of.

- `softness?: number`: How quickly it springs back into shape, in times a second: `5` by default, a jelly. Lower is
- `damping?: number`: How soon its wobbling dies away: `1` by default, settling without bouncing back and forth;
- `keepShape?: boolean`: Holds on to the shape it started as, and not only to its area and its sides, so it squashes
- `mass?: number`: How much it weighs in all, spread over its corners. `1` by default.
- `friction?: number`: How much it grips what it touches: `0.5` by default, `0` slides like ice, `1` rolls rather than
- `bounce?: number`: How much it bounces off what it hits: `0` by default.
- `pinned?: number[]`: Corners held where they start: the ends of a rope, the corner of a flag on its pole. Counted
- `polygon?: TPolygon`: The shape that draws it: its corners are moved to the body's every frame, its picture stretching

#### TSoftBodyShape2d (type, from `nacatamalon/physics2d`)

```ts
type TSoftBodyShape2d: { shape, radius, corners } | { shape, points } | { shape, to, corners }
```

The shape a soft body starts as, measured from its object's place.

- `'disk'`: a ring of `corners` round a circle, holding its area: a blob, a jelly, a ball of goo.
- `'polygon'`: a closed outline of the corners given, holding its area: any blob that is not round.
- `'rope'`: a line of `corners` from the object's place to `to`, which stretches and folds: a rope,
a vine, a tail. It holds no area.

### Project

The project file: the game window, the main scene and the input map.

#### gameOptionsFromProject (function, from `nacatamalon/files`)

```ts
gameOptionsFromProject(settings: TProjectSettings) => TGameOptions
```

Turns a project's settings into what `createGame` takes.

The one place that translation happens, so that the day there is an editor with a play button
and an exported build, both start the game down the same path and cannot drift apart.

`mainScene` is not part of it: which scene starts is `createGame`'s second call, not an option.

For editors and other tools. A game written only in code does not need it: it passes its
options straight to `createGame`. This exists so a tool can save those options as a file and a
game made with that tool can read them back.

- `settings: TProjectSettings`: The project's settings, from `parseProject`.

#### parseProject (function, from `nacatamalon/files`)

```ts
parseProject(value: unknown) => TProjectSettings
```

Reads a `project.json`, whatever state it is in.

Total and never throws: a field that is missing, of the wrong shape or plain nonsense falls back
to the default, and the game still runs. A project file is written by tools and edited by hand, so
treating it as a promise would mean a typo takes the whole game down.

What it does say out loud is a file that is not a project of this engine at all, or one written by
a newer version: those are warned about once, because carrying on quietly there would look like
the settings simply being ignored.

For editors and other tools. A game written only in code does not need it: it passes its
options straight to `createGame`. This exists so a tool can save those options as a file and a
game made with that tool can read them back.

- `value: unknown`: Whatever `JSON.parse` gave back.

#### TProjectSettings (type, from `nacatamalon/files`)

```ts
type TProjectSettings
```

A game's own settings: everything that is true of the game, not of one scene.

It lives on disk as `project.json` at the root of a project. The fields mirror what `createGame` takes wherever they can, so starting a game from a
project is a translation and not a second set of decisions that can drift.

This is the format of that file: the editor writes it (its `ProjectSettings` is this type),
and a game, the editor's play window and an exported build all read it through `parseProject`.

For editors and other tools. A game written only in code does not need it: it passes its
options straight to `createGame`. This exists so a tool can save those options as a file and a
game made with that tool can read them back.

- `format: 'nacatamalon-project'`
- `version: 1`
- `name: string`: What the game is called.
- `width: number`: The game window in pixels: how much it draws, not how big it looks. Kept small by default
- `height: number`
- `background: TColor`: What the frame is cleared to.
- `renderer: TRendererType`: Which backend draws: `'AUTO'`, or one named on purpose.
- `msaa: 1 | 4`: Anti-aliasing: `1` is off, which is the aliased look of the era.
- `smooth: boolean`: Default filtering: `false` is crisp pixel art, `true` blends. A sprite can still choose.
- `pixelSnap?: TPixelSnap`: Which pixels what is drawn lands on: `'screen'`, `'game'` or `'off'`. Absent, it follows
- `pixelAspect?: number`: How wide a game pixel is painted against how tall: absent is `1`, square; `8 / 7` for the NES
- `pixelRatio: number | 'device'`: How many real pixels each game pixel is drawn with: `1` (the default, and the one for pixel
- `scaling: TCanvasScaling`: How the canvas is fitted into the page.
- `keep: TCanvasKeep`: What is kept when it is fitted.
- `fullscreenScaling: TCanvasScaling`: How the canvas is fitted once the player asks for full screen. `'integer'` by default. See
- `align?: TCanvasAlign`: Where the game sits in its page, over what the page's CSS asks. Left out, the page decides.
- `step: TGameStep`: How the game's time moves forward: `'display'`, one update per picture the screen shows, or
- `pauseOnBlur: boolean`: Whether the game pauses itself when the page loses focus.
- `seed?: number`: The seed of the game's random numbers, or absent for a different run every time.
- `actions: TActionMap`: The input map: the named actions and what each one listens to. In the same file as
- `actionsPersist: 'none' | 'localStorage' | 'indexedDb'`: Whether the player's own controls survive a reload, and where they are kept. `'none'` by
- `contextMenu: boolean`: Whether a right click over the game opens the browser's menu. `false` by default: the right
- `physicsLayers: string[]`: Names for the physics layers, in order: the first is layer 0. Empty when the game says its
- `post: TPostChain`: The full-screen effect chain: the effects applied to the finished frame, in order (a
- `mainScene: string`: Which scene the game starts on, by name. Empty means nobody has decided yet: better than
- `banner: boolean`: Whether the startup line is printed in the console. `true` by default.
- `splash: boolean | 'always'`: Whether the game opens with "Made with NacatamalOn": `true` in the published game,

### Capture

Photos of the game taken by the game: a region or a box, with or without screen effects, and what each photo shows.

#### useCapture (function, from `nacatamalon`)

```ts
useCapture() => TCapture
```

A camera for the game itself: takes photos of what the player sees, the whole screen or a part of
it, for a photography game, an album, a "spot the difference", a sticker of the boss beaten. Call it
in a scene's body or a behaviour's, and take a photo at any moment after (it resolves the next
frame, drawn once more for it, so it is for taking a picture, not for every frame).

A photo is pixels, plus what came out in it (`boxes`): each box shown in the frame, how much of it
(`seen`, 0 to 1) and where, for scoring what the player took a picture of. To show it, make the
print in the scene's body (`createPixels`, `createPixelTexture`, a sprite with it), then copy each
photo into it and `updatePixelTexture`; a build script saves one with `encodePng`.

- `region`: a rectangle of the screen in the game's pixels, or a box (or its name), the part of the
screen it covers, measured even hidden: a viewfinder that is never drawn.
- `hide`: boxes left out (the HUD, the touch pad), by box or by name.
- `only`: the only boxes in it, on a transparent background: one thing alone.
- `width`, `height`: the photo's size, by whole pixels, never smoothed.
- `effects: false`: under the screen effects, as the scene is drawn.

Works while the game is paused. Two photos asked for at once are taken one after the other.

#### TCapture (type, from `nacatamalon`)

```ts
type TCapture: (options?: TPhotoOptions) => Promise<TPhoto>
```

Takes a photo (from `useCapture`).

#### TPhoto (type, from `nacatamalon`)

```ts
type TPhoto: TPixels & { boxes }
```

A photo: its pixels, which show (`createPixelTexture`) or save (`encodePng`) as any picture
painted in code, and what came out in it, most seen first.

- `boxes: typeOperator`
- `type: 'pixels'`
- `width: number`
- `height: number`
- `data: Uint8Array`

#### TPhotoBox (type, from `nacatamalon`)

```ts
type TPhotoBox
```

Something that came out in a photo: how much of it, and where. For telling what a player took a
picture of: the bird is in it, most of it, in the middle.

Each sprite, text and panel shown is one, measured by its rectangle turned as it is drawn; a box with
hitboxes is one, measured by them (what it is to be hit is what it is to be photographed). Something
covered by another is still in the photo by this measure, and a model in depth is not listed.

- `box: TBox`: The box it is, or is in.
- `picture: TSprite | TText | TNineSlice`: What it draws that came out: the sprite, text or panel (as `createSprite` gave it).
- `name: string`: Its box's name.
- `seen: number`: How much of it is inside the photo, from `0` to `1`: `1` all of it, `0.5` half.
- `x: number`: The middle of what of it is inside, in the photo's own pixels.
- `y: number`

#### TPhotoBoxRef (type, from `nacatamalon`)

```ts
type TPhotoBoxRef: TBox | TSprite | TText | TNineSlice | string
```

Something named in a photo's options: a box, what it draws (a sprite, a text or a panel, as
`createSprite` gave it), or a box's name, which stands for every box of the running scenes called
that (`'Hud'`), the way a behaviour finds the others.

#### TPhotoOptions (type, from `nacatamalon`)

```ts
type TPhotoOptions
```

What a photo is of (`useCapture`). Nothing at all is the screen as the player sees it.

- `region?: TPixelRegion | TPhotoBoxRef`: The part of the screen it is of: a rectangle in the game's pixels (`{ x, y, width, height }`
- `hide?: typeOperator`: Boxes left out of the photo, with everything in them: the HUD, the touch pad, the viewfinder's
- `only?: typeOperator`: The only boxes in the photo, with everything in them, on a transparent background unless
- `width?: number`: How big the photo comes out, in pixels. Made smaller or bigger by whole pixels, never smoothed,
- `height?: number`
- `effects?: boolean`: Whether the screen effects (a CRT, a palette, a dither) are in it, as the player sees them.
- `background?: { r, g, b, a }`: What is behind everything. Default the game's background, or transparent with `only`.

### Render

What the renderer is told and what it answers: backends, capabilities and draw items.

#### TCaptureResult (type, from `nacatamalon`)

```ts
type TCaptureResult
```

Pixels brought back from the graphics card, as `RGBA8`, row by row from the top-left corner.

Top-left because that is where every other picture in this engine starts. WebGL2 reads from the
bottom, so its backend turns the rows around before handing them over: a picture that comes back
upside down depending on which backend ran is exactly the kind of difference the interface exists
to hide.

- `width: number`
- `height: number`
- `data: Uint8Array`: `width * height * 4` bytes.

#### TRendererBackend (type, from `nacatamalon`)

```ts
type TRendererBackend: 'WEBGPU' | 'WEBGL2'
```

A backend that can actually draw.

#### TRendererType (type, from `nacatamalon`)

```ts
type TRendererType: 'AUTO' | TRendererBackend
```

What a caller may ask `createRenderer` for.

- `'AUTO'`: tries WebGPU, and on any failure falls back to WebGL2.
- `'WEBGPU'` / `'WEBGL2'`: that backend or nothing. A named backend never falls back:
asking for one explicitly and silently getting the other hides the thing you were testing.

### Constants

What a game reads and never changes, on the root in groups: the renderers, input names, limits, presets and what the engine carries by kind.

#### BUILTIN (variable, from `nacatamalon`)

```ts
BUILTIN: { FONTS, MUSIC, SOUNDS, LOOKS, PALETTES }
```

What the engine carries, asked for by name, by kind.

- `FONTS`: the fonts a text can name, `'nacatamalon:arcade'` first, the one it uses when it
names none.
- `MUSIC.SONGS`: the songs, `useLoadSong({ src: 'nacatamalon:' + key })`. `MUSIC.INSTRUMENTS`
and `MUSIC.SAMPLES`: what a song's notes play with `@name`, and what its sample voices play.
- `SOUNDS`: the kinds of sound effect `renderSfx` makes.
- `LOOKS`: the looks a material can take.
- `PALETTES`: the colours of the machines that had a fixed list of them, `nes`, `gameBoy` and `c64`,
as `#rrggbb`: for `getColor`, `snapToPalette` or a palette of rows. For `paletteMatch`,
`useLoadPalette({ src: 'nacatamalon:nes' })`.

- `FONTS: typeOperator`
- `MUSIC: { SONGS, INSTRUMENTS, SAMPLES }`
- `SOUNDS: typeOperator`
- `LOOKS: typeOperator`
- `PALETTES: Readonly<Record<TConsolePaletteName, typeOperator>>`

#### DOCUMENTS (variable, from `nacatamalon/files`)

```ts
DOCUMENTS: { SCENE, MOTION, STORE, TILEMAP, PALETTE, ATLAS, PARTICLES, SONG, SHADER_GRAPH }
```

What the engine's documents say they are, field by field: `FORMAT` is what a file's `format`
holds and `VERSION` its `version`, for a tool or a game that writes one by hand. The engine's own
`serialize…` and the editor write them already.

Some documents name themselves and count their versions apart (a scene is
`format: 'nacatamalon-scene'`, `version: 1`); the rest have only `format`, and it is the version.
A version goes up when a document written today would be misread by an older engine. A scene
claiming a higher one than this engine's is loaded anyway and warned about once.

- `SCENE: { FORMAT, VERSION }`
- `MOTION: { FORMAT, VERSION }`
- `STORE: { FORMAT, VERSION }`
- `TILEMAP: { FORMAT }`
- `PALETTE: { FORMAT }`
- `ATLAS: { FORMAT }`
- `PARTICLES: { FORMAT }`
- `SONG: { FORMAT }`
- `SHADER_GRAPH: { FORMAT }`

#### INPUT (variable, from `nacatamalon`)

```ts
INPUT: { GAMEPAD_BUTTONS, GAMEPAD_AXES, POINTER_BUTTONS, DEADZONE, ACTION_PRESS }
```

The names input is read by, and the numbers it starts from.

- `GAMEPAD_BUTTONS` and `GAMEPAD_AXES`: what a standard pad's buttons and sticks are called, in
order, and `GAMEPAD_BUTTON_LABELS` / `GAMEPAD_AXIS_LABELS` how a controls screen shows them.
- `POINTER_BUTTONS`: `main`, `middle`, `right`, `back` and `forward`.
- `DEADZONE`: how far a stick or a trigger moves before it counts, the pad's and an action's,
when nothing says. `ACTION_PRESS`: how far it moves before an action counts as pressed.
- `ACTION_PRESETS`: input maps ready to use, so a game does not write its sixteen bindings by
hand. By console, `nes`, `genesis`, `snes` and `arcade`, whose actions are its buttons (`a`, `b`,
`start`; `button1` to `button6` and `coin` on an arcade), and by kind of game, `platformer`
(`jump`, `action`, `pause`). Every one has `up`, `down`, `left` and `right` on the arrows, WASD, the
d-pad and the left stick; a console's buttons sit on the pad where they sat on its own.

- `GAMEPAD_BUTTONS: typeOperator`
- `GAMEPAD_AXES: typeOperator`
- `POINTER_BUTTONS: typeOperator`
- `DEADZONE: number`
- `ACTION_PRESS: number`

#### LIMITS (variable, from `nacatamalon`)

```ts
LIMITS: { LIGHTS, SCENE_LIGHTS, PALETTE_COLORS }
```

How far the engine goes, fixed by what it draws and simulates with: a game cannot raise them.

- `LIGHTS`: how many lights shine on one thing drawn, eight, as the consoles of the era had:
each model is lit by the eight that reach it most. An ambient light does not count.
- `SCENE_LIGHTS`: how many a scene holds at a time, thirty-two, which each model takes its eight
from. Past them they are dropped with a warning.
- `PHYSICS_LAYERS`: how many collision layers a scene has, sixteen, the most both physics
backends can tell apart.
- `PALETTE_COLORS`: as many colours as a palette may hold, 256.

- `LIGHTS: number`
- `SCENE_LIGHTS: number`
- `PALETTE_COLORS: number`

#### PHYSICS (variable, from `nacatamalon`)

```ts
PHYSICS: { … }
```

Values physics is written with. `ALL_LAYERS` is every layer at once, what a collider hits unless
it says otherwise.

#### POST_EFFECTS (variable, from `nacatamalon/effects`)

```ts
POST_EFFECTS: { NAMES }
```

The post effects: the ones the engine carries, and starting points for them.

- `NAMES`: the post effects a project names in its `post` list.
- `CRT_PRESETS`: four tubes for `crt()`. `consumer` is the television in the living room, `pvm` a
studio monitor, `arcade` a cabinet, `composite` a cheap cable.
- `COLOR_LEVELS`: how many steps a channel had on each machine, for `dither()` and `posterize()`.

- `NAMES: typeOperator`

#### RENDER (variable, from `nacatamalon`)

```ts
RENDER: { TYPE, PIXEL_SNAP }
```

How the engine draws: the renderers `createGame` can be asked for, which pixels it draws on, and
how many numbers one corner of a line takes.

`TYPE.WEBGL2` is the same as writing `'WEBGL2'` in `createGame({ renderer })`. `PIXEL_SNAP.SCREEN`,
`GAME` and `OFF` are what `createGame({ pixelSnap })` and `useGame().setPixelSnap` take.
`LINE_VERTEX_FLOATS` is seven, where a corner is (`x`, `y`, `z`) and its colour (`r`, `g`, `b`,
`a`), for a game that writes the corners of its lines itself.

- `TYPE: { AUTO, WEBGPU, WEBGL2 }`
- `PIXEL_SNAP: { SCREEN, GAME, OFF }`

#### SCREEN (variable, from `nacatamalon`)

```ts
SCREEN: { CONSOLES, SCALING, KEEP, ALIGN }
```

The game's screen: what shape it is, and how it sits in its page.

- `CONSOLES`: each console's screen, its size, the shape of its pixels (`pixelAspect`) and its
pictures a second, to spread into `createGame`: `nes`, `snes`, `genesis`, `gameBoy`, `gba`,
`ps1` and `n64`.
- `SCALING`: how big it is painted in its page, what `createGame({ scaling })`,
`fullscreenScaling` and `useGame().setScaling` take: `NONE`, `INTEGER`, `CONTAIN` or `FILL`.
- `KEEP`: which sides keep the game's size when the page is another shape,
`createGame({ keep })`: `BOTH`, `WIDTH`, `HEIGHT` or `NONE`.
- `ALIGN`: where `createGame({ align })` puts it in its container: `CENTER`, a side (`TOP`,
`BOTTOM`, `LEFT`, `RIGHT`) or a corner (`TOP_LEFT`, `TOP_RIGHT`, `BOTTOM_LEFT`, `BOTTOM_RIGHT`).

- `CONSOLES: Readonly<Record<TConsoleScreenName, Required<Pick<TGameOptions, 'height' | 'width' | 'pixelAspect' | 'step'>>>>`
- `SCALING: { NONE, INTEGER, CONTAIN, FILL }`
- `KEEP: { BOTH, WIDTH, HEIGHT, NONE }`
- `ALIGN: { CENTER, TOP, BOTTOM, LEFT, RIGHT, TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT }`

### Math

Angles, interpolation, quaternions and seeded randomness.

#### clamp (function, from `nacatamalon/math`)

```ts
clamp(value: number, min: number, max: number) => number
```

Keeps `value` between `min` and `max`: below `min` it gives `min`, above `max` it gives `max`,
and in between the value itself. It is what keeps a player inside the screen or a volume
between 0 and 1, and it replaces `Math.min(max, Math.max(min, value))`, which is easy to write
with the two the wrong way round.

- `value: number`: The number to keep in range.
- `min: number`: The lowest it may be.
- `max: number`: The highest it may be.

#### degToRad (function, from `nacatamalon/math`)

```ts
degToRad(deg: number) => number
```

Converts `deg` (degrees) to radians: the unit every rotation in the engine uses
(sprite `transform.rotation`, a camera's `rotation`, a 3D object's `rotationX` and `rotationY`). Lets you author
angles the way you think about them: `transform.rotation = degToRad(45)`.

- `deg: number`: The angle in degrees.

#### lerp (function, from `nacatamalon/math`)

```ts
lerp(a: number, b: number, t: number) => number
```

Linear interpolation between numbers `a` and `b`: `t = 0` returns `a`, `t = 1`
returns `b`. Not clamped. The scalar building block under fades, manual tweens, and
`lerpColor` / `vec2Lerp`. GLSL calls this `mix`.

- `a: number`: Where it starts.
- `b: number`: Where it ends.
- `t: number`: How far along, 0 to 1.

#### lerpAngle (function, from `nacatamalon/math`)

```ts
lerpAngle(a: number, b: number, t: number) => number
```

Interpolates from angle `a` toward `b` (both radians) along the shortest arc,
so going from 350° to 10° turns 20° forward instead of 340° back. Ideal for
smoothly steering a sprite toward a target heading. `t=0` returns `a`, `t=1`
returns an angle equivalent to `b`. Not clamped.

- `a: number`: The angle it starts at, in radians.
- `b: number`: The angle it turns towards, in radians.
- `t: number`: How far along, 0 to 1.

#### quatFromAxisAngle (function, from `nacatamalon/math`)

```ts
quatFromAxisAngle(ax: number, ay: number, az: number, angle: number) => TQuat
```

Builds a quaternion that rotates by `angle` radians about the axis `(ax, ay, az)`.
The natural way to author "spin this much around this direction": a barrel roll is
a rotation about the ship's forward axis, a turn is one about its up axis.

- `ax: number`: The axis to turn about. Any length: it is made a unit first, and an axis of no length
- `ay: number`: The axis to turn about.
- `az: number`: The axis to turn about.
- `angle: number`: Rotation about the axis, in radians.

#### quatFromEuler (function, from `nacatamalon/math`)

```ts
quatFromEuler(x: number, y: number, z: number) => TQuat
```

Builds a quaternion from yaw/pitch/roll Euler angles (radians), using the exact
same axis order the renderer applies to Euler transforms: yaw (Y), then pitch
(X), then roll (Z). That equivalence is the point: a mesh authored with
`rotation`/`rotationX`/`rotationY` can be switched to a `quaternion` built here and
face the identical direction, so you only pay the Euler→quat conversion once, up
front, and evolve the quaternion from then on.

- `x: number`: Pitch, rotation about X, in radians.
- `y: number`: Yaw, rotation about Y, in radians.
- `z: number`: Roll, rotation about Z, in radians.

#### quatMultiply (function, from `nacatamalon/math`)

```ts
quatMultiply(a: TQuat, b: TQuat) => TQuat
```

Composes two rotations: the result applies `b` first, then `a` (same convention as
matrix multiplication). This is how you accumulate orientation incrementally:
`orientation = multiply(deltaSpin, orientation)` each frame rolls a ship a little
further without ever touching Euler angles or risking gimbal lock.

- `a: TQuat`: The second turn.
- `b: TQuat`: The first turn.

#### quatNormalize (function, from `nacatamalon/math`)

```ts
quatNormalize(q: TQuat) => TQuat
```

Re-normalizes a quaternion back onto the unit sphere. Repeated multiplications
accumulate floating-point error that slowly scales the rotation; call this
periodically on a long-lived accumulated orientation to keep it a pure rotation.

- `q: TQuat`: A quaternion.

#### quatRotateVec3 (function, from `nacatamalon/math`)

```ts
quatRotateVec3(q: TQuat, v: { x, y, z }) => { x, y, z }
```

Rotates a vector by a quaternion: `q · v · q⁻¹`, in its expanded (branch-free) form.

The scene graph's composition is expressed in position/rotation/scale rather than in
matrices (see `composeWorldTransforms`), and this is the one operation that needs: a child's
local offset, turned by its parent's orientation, is where the child actually sits.

- `q: TQuat`: The turn.
- `v: { x, y, z }`: The vector to turn.

#### quatSlerp (function, from `nacatamalon/math`)

```ts
quatSlerp(a: TQuat, b: TQuat, t: number) => TQuat
```

Spherically interpolates from `a` to `b` by `t` in `[0, 1]`: the correct way to
blend two orientations (a component-wise lerp warps angular speed and drifts off
the unit sphere). Use it to ease a camera or ship toward a target facing.

- `a: TQuat`
- `b: TQuat`
- `t: number`

#### radToDeg (function, from `nacatamalon/math`)

```ts
radToDeg(rad: number) => number
```

Converts `rad` (radians) to degrees: handy for showing an engine angle in a debug
overlay or editor field, where degrees read more naturally than radians.

- `rad: number`: The angle in radians.

#### TAU (variable, from `nacatamalon/math`)

```ts
TAU: number
```

A full turn in radians (2π): the period of the trig functions. Useful for
expressing whole rotations and for wrapping angles back into range.

#### useRandom (function, from `nacatamalon/math`)

```ts
useRandom() => TRandomHandle
```

The random numbers of this game.

Use it instead of `Math.random()` whenever a result should be repeatable: give `createGame` a
`seed` and every run of the game draws exactly the same numbers in the same order, which is what
makes a level layout, a replay or a bug report reproducible. Without a seed each run is different.

`seed(n)` starts the sequence again from `n` at any moment.

#### vec2Add (function, from `nacatamalon/math`)

```ts
vec2Add(a: TVec2, b: TVec2) => TVec2
```

`a + b`, component by component: a position moved by an offset.

- `a: TVec2`: The first vector.
- `b: TVec2`: The vector added to it.

#### vec2Angle (function, from `nacatamalon/math`)

```ts
vec2Angle(a: TVec2) => number
```

The angle a direction points at, in radians: 0 is right and it turns towards +y, which is
down on screen. It is what a sprite's `transform.rotation` wants, so
`ship.transform.rotation = vec2Angle(velocity)` turns a ship to face where it is going.

- `a: TVec2`: The direction.

#### vec2Distance (function, from `nacatamalon/math`)

```ts
vec2Distance(a: TVec2, b: TVec2) => number
```

How far apart two points are. With transforms, `vec2Distance(player.transform, coin.transform) < 12`
is a pickup check.

- `a: TVec2`: One point.
- `b: TVec2`: The other.

#### vec2Dot (function, from `nacatamalon/math`)

```ts
vec2Dot(a: TVec2, b: TVec2) => number
```

Returns the dot product. Result is 0 if vectors are perpendicular, positive if same direction, negative if opposite.

- `a: TVec2`: The first vector.
- `b: TVec2`: The second vector.

#### vec2Equals (function, from `nacatamalon/math`)

```ts
vec2Equals(a: TVec2, b: TVec2) => boolean
```

Whether every component is exactly equal. Exact, so two results of arithmetic that should agree
can differ in the last decimal: compare a distance with a small number for those.

- `a: TVec2`: The first vector.
- `b: TVec2`: The second vector.

#### vec2Length (function, from `nacatamalon/math`)

```ts
vec2Length(a: TVec2) => number
```

How long the vector is: the speed of a velocity, the reach of an offset.

- `a: TVec2`: The vector.

#### vec2Lerp (function, from `nacatamalon/math`)

```ts
vec2Lerp(a: TVec2, b: TVec2, t: number) => TVec2
```

Linearly interpolates between `a` and `b`. `t=0` returns `a`, `t=1` returns `b`. Not clamped.

- `a: TVec2`: Where it starts.
- `b: TVec2`: Where it ends.
- `t: number`: How far along, 0 to 1.

#### vec2Negate (function, from `nacatamalon/math`)

```ts
vec2Negate(a: TVec2) => TVec2
```

The same vector pointing the opposite way.

- `a: TVec2`: The vector.

#### vec2Normalize (function, from `nacatamalon/math`)

```ts
vec2Normalize(a: TVec2) => TVec2
```

Returns a unit vector in the same direction. Returns `(0, 0)` if the input is a zero vector.

- `a: TVec2`: The vector.

#### vec2Scale (function, from `nacatamalon/math`)

```ts
vec2Scale(a: TVec2, s: number) => TVec2
```

Every component multiplied by `s`: the same direction, `s` times as long.

- `a: TVec2`: The vector.
- `s: number`: The factor.

#### vec2Sub (function, from `nacatamalon/math`)

```ts
vec2Sub(a: TVec2, b: TVec2) => TVec2
```

`a - b`, component by component. `sub(target, from)` is the arrow from `from` to `target`.

- `a: TVec2`: The vector subtracted from.
- `b: TVec2`: The vector taken away.

#### vec3Add (function, from `nacatamalon/math`)

```ts
vec3Add(a: TVec3, b: TVec3) => TVec3
```

`a + b`, component by component: a position moved by an offset.

- `a: TVec3`: The first vector.
- `b: TVec3`: The vector added to it.

#### vec3Cross (function, from `nacatamalon/math`)

```ts
vec3Cross(a: TVec3, b: TVec3) => TVec3
```

Returns a vector perpendicular to both `a` and `b`. The result magnitude equals the area of the parallelogram they form.

- `a: TVec3`: The first vector.
- `b: TVec3`: The second vector.

#### vec3Distance (function, from `nacatamalon/math`)

```ts
vec3Distance(a: TVec3, b: TVec3) => number
```

How far apart two points are. With transforms, `vec3Distance(player.transform, coin.transform) < 12`
is a pickup check.

- `a: TVec3`: One point.
- `b: TVec3`: The other.

#### vec3Dot (function, from `nacatamalon/math`)

```ts
vec3Dot(a: TVec3, b: TVec3) => number
```

Returns the dot product. Result is 0 if vectors are perpendicular, positive if same direction, negative if opposite.

- `a: TVec3`: The first vector.
- `b: TVec3`: The second vector.

#### vec3Equals (function, from `nacatamalon/math`)

```ts
vec3Equals(a: TVec3, b: TVec3) => boolean
```

Whether every component is exactly equal. Exact, so two results of arithmetic that should agree
can differ in the last decimal: compare a distance with a small number for those.

- `a: TVec3`: The first vector.
- `b: TVec3`: The second vector.

#### vec3Length (function, from `nacatamalon/math`)

```ts
vec3Length(a: TVec3) => number
```

How long the vector is: the speed of a velocity, the reach of an offset.

- `a: TVec3`: The vector.

#### vec3Lerp (function, from `nacatamalon/math`)

```ts
vec3Lerp(a: TVec3, b: TVec3, t: number) => TVec3
```

Linearly interpolates between `a` and `b`. `t=0` returns `a`, `t=1` returns `b`. Not clamped.

- `a: TVec3`: Where it starts.
- `b: TVec3`: Where it ends.
- `t: number`: How far along, 0 to 1.

#### vec3Negate (function, from `nacatamalon/math`)

```ts
vec3Negate(a: TVec3) => TVec3
```

The same vector pointing the opposite way.

- `a: TVec3`: The vector.

#### vec3Normalize (function, from `nacatamalon/math`)

```ts
vec3Normalize(a: TVec3) => TVec3
```

Returns a unit vector in the same direction. Returns `(0, 0, 0)` if the input is a zero vector.

- `a: TVec3`: The vector.

#### vec3Scale (function, from `nacatamalon/math`)

```ts
vec3Scale(a: TVec3, s: number) => TVec3
```

Every component multiplied by `s`: the same direction, `s` times as long.

- `a: TVec3`: The vector.
- `s: number`: The factor.

#### vec3Sub (function, from `nacatamalon/math`)

```ts
vec3Sub(a: TVec3, b: TVec3) => TVec3
```

`a - b`, component by component. `sub(target, from)` is the arrow from `from` to `target`.

- `a: TVec3`: The vector subtracted from.
- `b: TVec3`: The vector taken away.

#### wrapAngle (function, from `nacatamalon/math`)

```ts
wrapAngle(rad: number) => number
```

Wraps `rad` into the range [-π, π). Rotations accumulated frame by frame
(`transform.rotation += spin * dt`) drift far outside that range over time;
wrapping keeps angle comparisons and interpolation well-behaved: call it before
`lerpAngle`, or whenever you need a canonical heading.

- `rad: number`: The angle in radians, however many turns it has added up.

#### TQuat (type, from `nacatamalon/math`)

```ts
type TQuat: [number, number, number, number]
```

A unit quaternion `[x, y, z, w]`: the engine's storage form for a full 3D
orientation. Chosen over Euler angles for anything that rotates freely in 3D (a
flying ship, a physics body): it never gimbal-locks, composes with a single
multiply, and interpolates smoothly with `slerp`. It is a plain number tuple on
purpose: four JSON-serializable numbers, so a `TransformRecord.quaternion` round-
trips through the editor/save with no special casing (a `Float32Array` would
stringify as `{"0":...}`, not an array). The matrix is only ever the derived
render form (`toMat4`); the quaternion is the state you keep and evolve.

#### TRandomHandle (type, from `nacatamalon/math`)

```ts
type TRandomHandle
```

A per-game random handle, returned by `useRandom`. Every method draws from this
game's own seeded generator: two games on the same page never share a stream, and
`seed` only affects the game it belongs to. Seeding makes a sequence reproducible
(same seed → same numbers), which is what the editor/metadata model needs to rebuild
a procedural scene from a single saved seed.

- `rand: `
- `randInt: `
- `chance: `
- `choose: `
- `shuffle: `
- `seed: `

#### TVec2 (type, from `nacatamalon/math`)

```ts
type TVec2
```

A point or a direction in 2D: `x`, `y`.

Every `vec2*` function takes anything with those fields, so a transform works as it is:
`vec2Distance(player.transform, coin.transform)` needs no vector built first. They never change
what they are given: a vector that comes back is always a new object.

- `x: number`
- `y: number`

#### TVec3 (type, from `nacatamalon/math`)

```ts
type TVec3
```

A point or a direction in 3D: `x`, `y`, `z`.

Every `vec3*` function takes anything with those fields, so a 3D transform works as it is:
`vec3Distance(player.transform, coin.transform)` needs no vector built first. They never change
what they are given: a vector that comes back is always a new object.

- `x: number`
- `y: number`
- `z: number`

### Easing

The easing curves, kept apart from Math so they do not swamp it.

#### easeInBack (function, from `nacatamalon/math`)

```ts
easeInBack(t: number) => number
```

Back ease-in: anticipates by pulling back before moving (overshoots below 0).

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInBounce (function, from `nacatamalon/math`)

```ts
easeInBounce(t: number) => number
```

Bounce ease-in: the mirror of `easeOutBounce`.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInCirc (function, from `nacatamalon/math`)

```ts
easeInCirc(t: number) => number
```

Circular ease-in: based on a circle arc, slow then steep.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInCubic (function, from `nacatamalon/math`)

```ts
easeInCubic(t: number) => number
```

Cubic ease-in (`t³`): stronger slow start than quad.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInElastic (function, from `nacatamalon/math`)

```ts
easeInElastic(t: number) => number
```

Elastic ease-in: springy oscillation building up from rest.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInExpo (function, from `nacatamalon/math`)

```ts
easeInExpo(t: number) => number
```

Exponential ease-in: very slow start, sharp finish.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInOutBack (function, from `nacatamalon/math`)

```ts
easeInOutBack(t: number) => number
```

Back ease-in-out: anticipate on the way out, overshoot on the way in.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInOutBounce (function, from `nacatamalon/math`)

```ts
easeInOutBounce(t: number) => number
```

Bounce ease-in-out.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInOutCirc (function, from `nacatamalon/math`)

```ts
easeInOutCirc(t: number) => number
```

Circular ease-in-out.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInOutCubic (function, from `nacatamalon/math`)

```ts
easeInOutCubic(t: number) => number
```

Cubic ease-in-out: the workhorse smooth-step for UI and camera moves.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInOutElastic (function, from `nacatamalon/math`)

```ts
easeInOutElastic(t: number) => number
```

Elastic ease-in-out.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInOutExpo (function, from `nacatamalon/math`)

```ts
easeInOutExpo(t: number) => number
```

Exponential ease-in-out.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInOutQuad (function, from `nacatamalon/math`)

```ts
easeInOutQuad(t: number) => number
```

Quadratic ease-in-out: accelerate then decelerate, symmetric.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInOutQuart (function, from `nacatamalon/math`)

```ts
easeInOutQuart(t: number) => number
```

Quartic ease-in-out.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInOutQuint (function, from `nacatamalon/math`)

```ts
easeInOutQuint(t: number) => number
```

Quintic ease-in-out.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInOutSine (function, from `nacatamalon/math`)

```ts
easeInOutSine(t: number) => number
```

Sine ease-in-out: the softest symmetric curve.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInQuad (function, from `nacatamalon/math`)

```ts
easeInQuad(t: number) => number
```

Quadratic ease-in (`t²`): slow start, accelerating.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInQuart (function, from `nacatamalon/math`)

```ts
easeInQuart(t: number) => number
```

Quartic ease-in (`t⁴`).

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInQuint (function, from `nacatamalon/math`)

```ts
easeInQuint(t: number) => number
```

Quintic ease-in (`t⁵`).

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeInSine (function, from `nacatamalon/math`)

```ts
easeInSine(t: number) => number
```

Sine ease-in: gentle, based on a quarter cosine.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeOutBack (function, from `nacatamalon/math`)

```ts
easeOutBack(t: number) => number
```

Back ease-out: overshoots past the target then settles.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeOutBounce (function, from `nacatamalon/math`)

```ts
easeOutBounce(t: number) => number
```

Bounce ease-out: lands and bounces like a dropped ball.

The other two bounce curves are written in terms of this one, so it is the only place
the four arcs are spelled out.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeOutCirc (function, from `nacatamalon/math`)

```ts
easeOutCirc(t: number) => number
```

Circular ease-out.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeOutCubic (function, from `nacatamalon/math`)

```ts
easeOutCubic(t: number) => number
```

Cubic ease-out: decelerating to a stop, stronger than quad.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeOutElastic (function, from `nacatamalon/math`)

```ts
easeOutElastic(t: number) => number
```

Elastic ease-out: overshoot then springy settle. Great for pops and bounces.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeOutExpo (function, from `nacatamalon/math`)

```ts
easeOutExpo(t: number) => number
```

Exponential ease-out: sharp start, long tail.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeOutQuad (function, from `nacatamalon/math`)

```ts
easeOutQuad(t: number) => number
```

Quadratic ease-out: fast start, decelerating to a stop.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeOutQuart (function, from `nacatamalon/math`)

```ts
easeOutQuart(t: number) => number
```

Quartic ease-out.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeOutQuint (function, from `nacatamalon/math`)

```ts
easeOutQuint(t: number) => number
```

Quintic ease-out.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### easeOutSine (function, from `nacatamalon/math`)

```ts
easeOutSine(t: number) => number
```

Sine ease-out: gentle deceleration.

- `t: number`: How far along, from `0` at the start to `1` at the end.

#### linear (variable, from `nacatamalon/math`)

```ts
linear: (t: number) => number
```

No easing: constant rate. `t` maps to itself.

#### TEaseFn (type, from `nacatamalon/math`)

```ts
type TEaseFn: (t: number) => number
```

An easing function: maps normalized time `t` in `[0, 1]` to an eased progress value
(also normally `[0, 1]`, though `back`/`elastic` overshoot outside it). Drives how a
tween accelerates: pass one as the `ease` of a tween, or sample it directly.

### Utils

Id generation and asset path resolution.

#### nanoId (function, from `nacatamalon`)

```ts
nanoId(size: number) => string
```

Generates a short unique ID using a URL-safe alphabet.
Default size of 21 characters gives the same collision probability as UUID v4.

- `size: number`: Number of characters. Defaults to 21.

### Debug

Lines, grids and axes drawn over the scene, to see what the game is doing.

#### useHelperAxis (function, from `nacatamalon`)

```ts
useHelperAxis(options: TUseHelperAxisOptions) => TLines
```

Draws the three axes as coloured lines from where it is placed: X red, Y green, Z blue, the colours
every 3D tool uses.

Made inside something, it shows which way that thing is turned, because the lines are measured from
it and turn with it. Made in the scene, it marks the world's origin.

- `options: TUseHelperAxisOptions`: How long the arms are, their colours, and where the gizmo goes.

#### useHelperGrid (function, from `nacatamalon`)

```ts
useHelperGrid(options: TUseHelperGridOptions) => TLines
```

Draws a floor grid on the ground plane, the one every 3D editor shows, with its middle lines in the
colours of the X and Z axes.

It is drawn in the scene like anything else, so a model standing on it hides the lines behind it
instead of the grid being painted over everything. Raise it, turn it or stand it up with
`transform`.

- `options: TUseHelperGridOptions`: How big it is, how close its lines are, and their colours.

#### useLines (function, from `nacatamalon`)

```ts
useLines(options: TUseLinesOptions) => TLinesHandle
```

Draws any lines you like, and hands back the function that replaces them at any time.

The one to reach for when what has to be seen changes: an outline that follows what the player
picked, the path an enemy is about to take, the shape of a collider being tuned. The axis gizmo and
the floor grid are this with the corners worked out for you.

Each corner is seven numbers, `x, y, z, r, g, b, a`, and every two corners make a line. They are
measured from where the lines are placed, so lines made inside a moving thing move with it.
`setVertices` copies what it is given, so the same array can be reused every frame.

Lines are drawn in the scene's 3D view: models in front hide them, and they hide what is behind
them. They are one pixel wide.

- `options: TUseLinesOptions`: The first lines, and where they are drawn from.

#### THelperAxisColors (type, from `nacatamalon`)

```ts
type THelperAxisColors
```

The colour of each arm of the gizmo.

- `x?: TColorOption`
- `y?: TColorOption`
- `z?: TColorOption`

#### TLinesHandle (type, from `nacatamalon`)

```ts
type TLinesHandle: [lines, setVertices]
```

What `useLines` gives back: the lines, and the function that replaces them.

#### TNewLinesOptions (type, from `nacatamalon`)

```ts
type TNewLinesOptions
```

What every way of making lines shares: where they go, and whether they are drawn.

- `name?: string`: What to call it when you look at it. The engine never looks it up: to find it, name its box.
- `transform?: Partial<TTransform3d>`: Where the lines are drawn from. Left out, the place of whatever they are made in.
- `zIndex?: number`: Draw order within the scene, as for any other drawing.
- `visible?: boolean`: Whether they start drawn. Default `true`.

#### TUseHelperAxisOptions (type, from `nacatamalon`)

```ts
type TUseHelperAxisOptions: TNewLinesOptions & { size, negative, colors }
```

What `useHelperAxis` is asked for.

- `size?: number`: How long each arm is, in world units. Default `1`.
- `negative?: boolean`: Also draw the other half of each axis, dimmed, so all six directions show. Default `false`.
- `colors?: THelperAxisColors`: The colour of any arm. Left out, the usual ones: X red, Y green, Z blue.
- `name?: string`: What to call it when you look at it. The engine never looks it up: to find it, name its box.
- `transform?: Partial<TTransform3d>`: Where the lines are drawn from. Left out, the place of whatever they are made in.
- `zIndex?: number`: Draw order within the scene, as for any other drawing.
- `visible?: boolean`: Whether they start drawn. Default `true`.

#### TUseHelperGridOptions (type, from `nacatamalon`)

```ts
type TUseHelperGridOptions: TNewLinesOptions & { half, step, color, xColor, zColor }
```

What `useHelperGrid` is asked for.

- `half?: number`: How far the grid reaches from its middle, in world units, on both sides. Default `10`.
- `step?: number`: How far apart its lines are. Default `1`.
- `color?: TColorOption`: The colour of the ordinary lines. Default a dim grey.
- `xColor?: TColorOption | null`: The colour of the middle line along X. Default red. `null` draws it like the rest.
- `zColor?: TColorOption | null`: The colour of the middle line along Z. Default blue. `null` draws it like the rest.
- `name?: string`: What to call it when you look at it. The engine never looks it up: to find it, name its box.
- `transform?: Partial<TTransform3d>`: Where the lines are drawn from. Left out, the place of whatever they are made in.
- `zIndex?: number`: Draw order within the scene, as for any other drawing.
- `visible?: boolean`: Whether they start drawn. Default `true`.

#### TUseLinesOptions (type, from `nacatamalon`)

```ts
type TUseLinesOptions: TNewLinesOptions & { vertices }
```

What `useLines` is asked for.

- `vertices?: Float32Array`: The first lines to draw. Left out, it starts empty, which is normal for lines rewritten every frame.
- `name?: string`: What to call it when you look at it. The engine never looks it up: to find it, name its box.
- `transform?: Partial<TTransform3d>`: Where the lines are drawn from. Left out, the place of whatever they are made in.
- `zIndex?: number`: Draw order within the scene, as for any other drawing.
- `visible?: boolean`: Whether they start drawn. Default `true`.

### Headless

The game run from Node or Bun with no browser, a frame at a time and the same every time: for tests, scripts and servers that check what a game does.

#### createHeadlessGame (function, from `nacatamalon/headless`)

```ts
createHeadlessGame(options: THeadlessGameOptions) => THeadlessGame
```

Makes a game that runs with no browser and no screen, in Node or Bun: the same scenes, hooks and
updates as in a page, moved a frame at a time by whoever holds it. Everything the game would show
is worked out, so a test can ask where things are and what is shown, and `capture()` draws the
screen without a graphics card when a picture of it is wanted.

It is the same every time it runs. Every frame is one step of the game (`step`, 60 a second
unless it says), the random numbers start from `seed` (`1` unless it says), and keys are pressed
and let go between frames, exactly when they are asked to be.

What a game loads is read from the assets folder (`assets`, default `'public'`): a picture's size
and pixels when it is a PNG, its other files as they are. Physics runs as in a page, once the game
installs it (`installPhysics2d()`, `installPhysics3d()`); `loaded()` waits for its world too. Sound
is silent and plays on, so a sound waited on still ends. Without a browser there is no pointer and
no gamepad.

- `options: THeadlessGameOptions`: The game's options, its scenes, and where its files are.

#### THeadlessCaptureOptions (type, from `nacatamalon/headless`)

```ts
type THeadlessCaptureOptions
```

What `capture` takes.

- `region?: { x, y, width, height }`: Part of the screen, in game pixels from its top-left corner, each edge taken to the whole pixel
- `scale?: number`: Every pixel as a square this many across, a whole number. `1` by default.

#### THeadlessGame (type, from `nacatamalon/headless`)

```ts
type THeadlessGame
```

A game running with no browser and no screen: the same scenes, hooks and updates as in a page,
moved a frame at a time by whoever holds it, the same way every time. For checking a game from a
test or a script: press keys, run frames, and look at where things are and what is shown.

- `frame: number`: Frames run so far.
- `time: number`: Seconds of game run so far: `frame` steps of `1 / step` each.
- `scenes: typeOperator`: The scenes running now, by name, in the order they draw.
- `run: `
- `press: `
- `release: `
- `tap: `
- `find: `
- `findAll: `
- `drawn: `
- `loaded: `
- `capture: `
- `destroy: `

#### THeadlessGameOptions (type, from `nacatamalon/headless`)

```ts
type THeadlessGameOptions: Omit<TGameOptions, 'renderer' | 'msaa' | 'scaling' | 'keep' | 'pixelRatio' | 'fullscreenScaling' | 'align' | 'pauseOnBlur' | 'banner' | 'splash'> & { scenes, initialScene, assets }
```

How a game with no browser is made: the game's own options, its scenes, and where its files are.
What only a page has (the canvas's scaling, its pixel ratio, pausing when the tab is hidden, the
splash, the startup line) is left out.

- `scenes: Readonly<Record<string, TSceneFn>>`: The scenes, by name, as `createGame` takes them.
- `initialScene?: string`: The scene it starts on. Left out, the first one.
- `assets?: string`: The folder a path the game loads is read from: `/assets/hero.png` is `<assets>/assets/hero.png`.

#### THeadlessPicture (type, from `nacatamalon/headless`)

```ts
type THeadlessPicture
```

Something the game shows, as it would be on the screen: a sprite, a text or a panel, where it
lands in the game's pixels and how much of it is inside the screen.

- `name: string`: Its box's name.
- `kind: 'sprite' | 'text' | 'panel'`
- `picture: TSprite | TText | TNineSlice`: What it draws, as `createSprite`, `createText` or `createNineSlice` gave it.
- `text?: string`: What a text says.
- `x: number`: The middle of what of it is on the screen, in the game's pixels.
- `y: number`
- `left: number`: The rectangle around all of it, in whole game pixels, turned as it is drawn.
- `top: number`
- `width: number`
- `height: number`
- `seen: number`: How much of it is on the screen, from `0` to `1`.
