# ThicketScript documentation
Source: https://thicketscript.com/docs/
---
URL: https://thicketscript.com/docs/start/
# Getting started
ThicketScript is a language built from the ground up to make games. It reads like TypeScript, has its own type system, and runs on its own virtual machine. One program, `thicket`, runs your code, checks it, formats it, debugs it and builds it into a standalone program.
This section takes you from nothing to a program you can share:
1. **Install** `thicket` (this page).
2. [Your first script](./first-script): write a `.tks` file and run it.
3. [Your first window](./first-window): open a window and put a sprite in it.
4. [Checking your code](./check): find type errors before you run anything.
5. [Building a program](./build-executable): turn a project into a folder you can ship.
## Install
::: info Downloads are coming soon
The [download page](/download) is where `thicket` for Windows, macOS and Linux, plus the IDE plugin, will land.
:::
Installing is one program, `thicket`, on your `PATH`, with nothing else to install: the compiler, the virtual machine, the standard library, the 2D module and the language server all live inside it.
Once it is installed, this prints the version:
```sh
thicket version
```
And this lists every command:
```sh
thicket help
```
## The commands you will use most
| Command | What it does |
|---|---|
| `thicket new ` | Make a new project folder from a template. |
| `thicket run` | Run a script or a project. |
| `thicket check` | Type-check without running anything. |
| `thicket watch` | Run, and run again whenever a file changes. |
| `thicket fmt ` | Format a source file. |
| `thicket build` | Package a project as a standalone program. |
| `thicket debug` | Run with a debugger attached. |
`thicket help ` prints the full reference for any one of them.
## Editor support
`thicket lsp` is a built-in language server. The JetBrains plugin starts it for you and gives you autocomplete, type checking, go-to-definition and error highlighting as you type.
Next: [your first script](./first-script).
---
URL: https://thicketscript.com/docs/start/build-executable
# Building a program
```sh
thicket build
```
`thicket build` turns a project into a standalone program. The result runs on a machine with no `thicket`, no source code and no `thicket_packages/` folder.
## Try it
```sh
thicket new hello
cd hello
thicket build
./build/hello
```
```text
hello from thicket
```
## What you get
The output goes to `build/` inside the project, replaced on each build:
```text
build/
hello the program (a prebuilt runtime)
bundle.tbc your compiled code and its frozen config
package-receipt.json a record of every file that was placed
```
The program file is a prebuilt runtime. `bundle.tbc` is your compiled bytecode. Keep them side by side.
- A **console app** gets the console runtime.
- A **window app** (a project with a `window` section) gets the window runtime, in a flat folder you can zip and share.
## Ship your assets
Only what the `assets` field lists ships with the program:
```json5
{
entry: "app.tks",
window: { title: "My Game" },
assets: ["assets/**"],
}
```
Each file keeps the path it has in your project, so a path like `assets/player.png` means the same thing in `thicket run` and in the built program.
## Give your app an id
`thicket build` warns when `thicket.json` has no `id`. Without one, the app's save and cache folders are named after the project folder, so two projects in folders with the same name would share them. Set an id before you ship.
## Useful flags
| Flag | What it does |
|---|---|
| `--out ` | Write somewhere other than `build/`. A folder that is not empty is refused unless you add `--force`. |
| `--release` | Drop the source map and function names. |
| `--strategy ` | Choose how the program is packaged. |
| `--list-strategies` | Show the strategies that fit this project. |
## Ship a fix without rebuilding everything
The runtime is not tied to one bundle. A `bundle.tbc` from a later build of the same project, made with the same `thicket` version, can be dropped over the old one and runs the new code. Changed assets have to be copied too.
Platforms, strategies and asset options are covered in [Building for each platform](/docs/build/).
---
URL: https://thicketscript.com/docs/start/check
# Checking your code
```sh
thicket check
```
`check` type-checks and compile-checks a program. Nothing runs and nothing is written. It prints `ok` when the program is clean. Otherwise it prints each diagnostic with its file, line, column and severity (`error`, `warning` or `hint`). It exits with code 1 when there is an error. Warnings and hints alone do not fail the check.
Like `run`, it takes a file, a project folder, or nothing (the nearest `thicket.json` above the current folder):
```sh
thicket check
thicket check app.tks
thicket check ./mygame
```
## What it catches
Save this as `oops.tks`:
```ts
let score: int = "ten";
```
```sh
thicket check oops.tks
```
```text
oops.tks(1,5): error: Cannot assign "ten" to int (variable 'score')
oops.tks(1,5): hint PF012: variable 'score' is declared but never used
```
The checker also reports:
- missing type annotations on variables and missing return types on functions (warning `PF071`),
- unused variables (a hint),
- calls with the wrong number or type of arguments,
- members that a class or interface does not declare,
- a class that says `implements` but leaves a member out,
- `await` in the wrong place, and other async mistakes,
- a `switch strict` that misses an enum member.
`check` also reports everything `run` would refuse at compile time, so a clean `check` means the program compiles.
## Window apps are checked too
You can check a window app on a machine with no display. `check` never opens a window, so it is a good fit for a build server or a pre-commit hook.
## In your editor
The JetBrains plugin runs the same checker through `thicket lsp` and underlines problems as you type.
## Formatting
```sh
thicket fmt app.tks
thicket fmt --check app.tks
```
`fmt` formats a file in place and keeps your comments. `--check` writes nothing and exits 1 when the file is not formatted.
## Silencing one line
When a warning is wrong for one line, suppress it on the line before. The code in brackets limits it to one diagnostic:
```ts
// @thicket-ignore-next-line [PF078]
const y: int = legacyRect.width;
```
Next: [building a program](./build-executable).
---
URL: https://thicketscript.com/docs/start/first-script
# Your first script
## One file
Save this as `hello.tks`:
```ts
const name: string = "world";
console.log("Hello, " + name + "!");
```
Run it:
```sh
thicket run hello.tks
```
```text
Hello, world!
```
A `.tks` file runs from top to bottom. You do not need a project file for a quick script.
Two things to notice already:
- **Every declaration has a type.** `const name: string`, not `const name`. The checker warns when a type is missing.
- **Every statement ends with a semicolon.**
## A little more
Template strings, typed arrays and loops all look the way you expect:
```ts
const name: string = "ThicketScript";
const version: int = 1;
console.log(`Hello from ${name} v${version}!`);
const items: Array = ["fast startup", "typed numbers", "batteries included"];
for (const item of items) {
console.log(` - ${item}`);
}
```
```text
Hello from ThicketScript v1!
- fast startup
- typed numbers
- batteries included
```
## A project
Real programs live in a project: a folder with a `thicket.json` (or `thicket.json5`) file. Make one with `thicket new`:
```sh
thicket new hello
cd hello
thicket run
```
`thicket new` writes three files:
```text
hello/
.gitignore
app.tks
thicket.json5
```
The project file names the entry file:
```json5
{
"entry": "app.tks"
}
```
And `app.tks` holds a class with a `static main()` method:
```ts
class ConsoleApp {
static main(): void {
console.log("hello from thicket");
}
}
```
When the entry file declares a class with `static main()`, `thicket run` runs the file's top-level code first, then calls that `main`. A plain `function main()` at the top level is ordinary code and is never called for you.
`main` can also take the command-line arguments and return an exit code:
```ts
class App {
static main(args: Array): int {
console.log("started with " + args.length + " arguments");
return 0;
}
}
```
Pass arguments after `--`:
```sh
thicket run -- --verbose --level=3
```
## Templates
`thicket new ` picks a starting point:
| Template | What you get |
|---|---|
| `console` | A console app (the default). |
| `window` | A window app. |
| `willow` | A console app that uses the willow 2D module. |
## Run it again on every save
```sh
thicket watch
```
`watch` runs the program, then runs it again from scratch whenever a `.tks` file or `thicket.json` in the project changes.
Next: [your first window](./first-window).
---
URL: https://thicketscript.com/docs/start/first-window
# Your first window
A project becomes a **window app** when its `thicket.json` has a `window` section. Without one it is a console app.
## Make the project
```sh
thicket new mygame window
cd mygame
```
Open `thicket.json5` and give the window a title and a size. List the `willow` system, which is the 2D module, and turn on the `fs` permission so the game can load images from disk:
```json5
{
entry: "app.tks",
systems: ["willow"],
window: { title: "My Game", width: 800, height: 600 },
permissions: { fs: true },
}
```
`window: {}` also works and uses every default.
## Show a sprite
Put an image at `assets/player.png`, then replace `app.tks` with this:
```ts
import { Sprite } from "willow";
class Game {
static main(): void {
const player: Sprite = new Sprite("assets/player.png");
player.setAnchor(0.5, 0.5);
player.x = 400;
player.y = 300;
engine.root2d.add(player);
const SPEED: float = 200;
App.onFrame((dt: float): void => {
if (Keyboard.isDown(Key.RIGHT)) {
player.x += SPEED * dt;
}
if (Keyboard.isDown(Key.LEFT)) {
player.x -= SPEED * dt;
}
if (Keyboard.isDown(Key.DOWN)) {
player.y += SPEED * dt;
}
if (Keyboard.isDown(Key.UP)) {
player.y -= SPEED * dt;
}
player.rotation += Math.PI * dt;
});
}
}
```
Run it:
```sh
thicket run
```
The window opens with the sprite in the middle, spinning half a turn per second. The arrow keys move it.
## What is going on
- **`new Sprite(path)`** loads the image and makes a display node that draws it.
- **`setAnchor(0.5, 0.5)`** puts the sprite's origin at its center, so `x` and `y` place the middle of the image and rotation turns it around its center.
- **`engine.root2d`** is the root of the 2D display tree. Anything you `add` to it is drawn every frame.
- **`App.onFrame`** runs your function once per drawn frame. `dt` is the time since the last frame, in seconds, so `SPEED * dt` moves the same distance per second at any frame rate.
- **`Keyboard`** and **`Key`** are global. You do not import them. `Keyboard.isDown` is true while a key is held. `Keyboard.isPressed` is true only on the frame the key goes down.
- **`rotation`** is in radians.
::: tip Avoid naming your class `App`
`App` is the built-in lifecycle class. A class of your own called `App` hides it, and `App.onFrame` stops working in that file. Name your entry class after your game instead.
:::
## Game logic on fixed ticks
`App.onFrame` runs once per drawn frame, so it runs more often on a faster screen. For game logic that should run at a steady rate, use `App.onTick`. Ticks run 60 times a second by default (`App.tps`). Draw-only work, like smoothing an animation, stays in `onFrame`.
Learn more about the 2D module in [willow](/docs/modules/willow).
Next: [checking your code](./check).
---
URL: https://thicketscript.com/docs/language/
# Language guide
ThicketScript will feel familiar if you know TypeScript. Classes, interfaces, generics, arrow functions, `async` and `await` all look the way you expect. Underneath it is its own language, with its own type system, a bytecode compiler and a virtual machine. A few things are deliberately different, and this guide points them out as it goes.
```ts
enum Team {
Red,
Blue,
}
class Player {
constructor(public name: string, public team: Team, public hp: int = 100) {}
isAlive(): boolean {
return this.hp > 0;
}
}
const players: Array = [
new Player("Ada", Team.Red),
new Player("Brin", Team.Blue, 0),
];
for (const p of players) {
const status: string = p.isAlive() ? "alive" : "down";
console.log(`${p.name}: ${status}`);
}
```
## The pages
| Page | What it covers |
|---|---|
| [Types](./types) | `int`, `uint`, `float`, `string`, `boolean`, unions, `null`, arrays, maps, `is` and `as`. |
| [Variables](./variables) | `let`, `const`, scope and assignment. |
| [Functions](./functions) | Declarations, arrow functions, default and rest parameters, closures. |
| [Classes](./classes) | Fields, constructors, inheritance, access modifiers, getters, abstract and final. |
| [Interfaces](./interfaces) | Shapes, `implements`, optional members. |
| [Enums](./enums) | Named constants, bitflags, `switch strict`. |
| [Generics](./generics) | Type parameters on functions, classes and interfaces. |
| [Control flow](./control-flow) | `if`, loops, ranges, `switch`, labels. |
| [Async, series and parallel](./async) | Promises, `await`, and the `series` and `parallel` blocks. |
| [Errors](./errors) | `throw`, `try`, typed `catch`, inline `try`, `defer`. |
| [Modules and imports](./modules) | `import`, `export`, system modules and path aliases. |
| [Coming from TypeScript](./coming-from-typescript) | What is left out, what is accepted and ignored, and what works differently. |
## The house style
Every sample on this site follows the same rules, and the checker nudges you toward them too:
- **Explicit types everywhere.** `let count: int = 0;`, not `let count = 0;`. Functions declare their return type, even `void`.
- **Semicolons** at the end of every statement.
- **`int`, `uint` and `float`, never `number`.** There is no `number` type.
- **`boolean`, never `bool`.**
- **`for (const x of items)`** to loop over a collection.
- **Parentheses** around every `if`, `for`, `while` and `switch` condition.
## Feature checklist at a glance
Everything on this list runs on the current `thicket`.
**Language**
- Classes, interfaces, generics, enums (numeric, string and bitflag) and `switch strict`.
- `struct` value types with methods and operator overloads.
- Packed numbers in struct fields: `int8`, `int16`, `int32`, `uint8`, `uint16`, `uint32`, `float32`.
- Integer overflow and out-of-range stores raise a `RangeError`.
- Ranges: `for (const i of 0..<3)` (or `in`) counts 0, 1, 2, and `0..<3` on its own is the array `[0, 1, 2]`.
- `defer` for cleanup on any scope exit, typed `catch`, and inline `try` expressions.
- `dump()` prints values with their variable names.
**Async and threads**
- `async` and `await` with Promises.
- `series` and `parallel`, as function modifiers or blocks.
- `async worker` functions: real threads, each in an isolated VM.
- `async mutex` functions with per-key locks.
**Standard library**
- 41 easing curves through `Ease.apply`, plus `Math.lerp`, `moveTowards`, `snap`, `wrap` and `smoothstep`.
- Seeded `Random`: the same seed gives the same rolls.
- Collection extras: `unique`, `compact`, `sum`, `avg` and `first` on arrays; `union` and `intersection` on `Set`.
- Reactive state: `Ref`, `computed` and `Signal` from the `reactivity` module.
- Databases: SQLite everywhere. Linux release builds add MySQL/MariaDB, Postgres and Redis. Packaged window builds carry SQLite only.
**Safety**
- A sandbox by default: file, network, crypto, database, shell, environment and audio access stay off until `thicket.json` grants them.
- A stack overflow is a catchable `RangeError`, not a crash.
**Tooling, all in `thicket`**
- `run`, `watch`, `check`, `fmt`, `debug`, `compile`, `build`, `new`, `tool`, `lsp` and `mcp`. `mcp` runs a Model Context Protocol server. Use `--project ` when a client starts it outside the project folder.
- Debugging over the Debug Adapter Protocol for your IDE, or Chrome DevTools.
## Coming from TypeScript
The differences that trip people up most. The [Coming from TypeScript](./coming-from-typescript) page has the full list.
| TypeScript | ThicketScript |
|---|---|
| `number` | `int`, `uint` or `float` |
| `7 / 2` is `3.5` | `7 / 2` is `3` when both sides are `int` |
| `==` coerces | `==` and `===` are both strict |
| Structural typing | Nominal: a class must say `implements` to count as the interface |
| Integer overflow is silent | Integer overflow raises a `RangeError` |
| `Promise.all` | Also `parallel { ... }` |
---
URL: https://thicketscript.com/docs/language/async
# Async, series and parallel
Games wait on things all the time: files, network calls, a timer, an animation finishing. ThicketScript has the `async` and `await` you know from TypeScript, plus two blocks of its own, `series` and `parallel`, that say what you mean with less ceremony.
## async and await
```ts
async function loadLevel(name: string): Promise {
await sleep(10);
return "level " + name;
}
const level: string = await loadLevel("1-1");
console.log(level);
```
- An `async` function always returns a `Promise`. Write the return type that way.
- `await` waits for a promise and gives you its value.
- `await` works inside `async` functions and at the top level of the entry file.
- `sleep(ms)` is built in.
A rejected promise throws at the `await`, so `try` and `catch` work as you expect:
```ts
async function risky(): Promise {
throw new Error("network down");
}
try {
const n: int = await risky();
console.log(n);
} catch (err) {
console.log("failed: " + err.message);
}
```
A promise that rejects with nobody listening is an **unhandled rejection**. `thicket run` reports it and exits with code 1.
## Promise helpers
`Promise.all`, `Promise.race`, `Promise.allSettled` and `Promise.any` work as in TypeScript, along with `.then`, `.catch` and `.finally`.
```ts
async function fetchScore(id: int): Promise {
await sleep(5);
return id * 10;
}
const scores: Array = await Promise.all([fetchScore(1), fetchScore(2)]);
console.log(scores.join(", "));
```
## Timers
```ts
const handle: int = setTimeout((): void => {
console.log("too late");
}, 5000);
clearTimeout(handle);
setTimeout((): void => {
console.log("fired");
}, 10);
```
`setInterval` and `clearInterval` repeat. In a window app, the [App lifecycle](/docs/start/first-window#game-logic-on-fixed-ticks) callbacks are usually the better tool for per-frame work.
## series: one after another
Inside a `series` block, every call that returns a promise is awaited for you. No `await` on each line:
```ts
async function step(name: string): Promise {
await sleep(5);
return name + " done";
}
series {
const a: string = step("load map");
const b: string = step("spawn enemies");
console.log(a);
console.log(b);
}
```
Calls that are not async cost nothing extra. A call typed `Promise` gives you an `int` directly.
`series` also works on a function or method, which makes the whole body implicitly awaited:
```ts
async function fade(name: string): Promise {
await sleep(5);
console.log("faded " + name);
}
series function intro(): void {
fade("logo");
fade("title");
console.log("intro finished");
}
await intro();
```
To start something without waiting for it, put `void` in front: `void playMusic("town.ogg");`.
## parallel: all at once
Every statement in a `parallel` block starts at the same time. The block ends when all of them have finished, like `Promise.all`:
```ts
async function loadAsset(name: string, ms: int): Promise {
await sleep(ms);
return name;
}
parallel {
let texture: string = loadAsset("tiles.png", 20);
let music: string = loadAsset("theme.ogg", 10);
}
console.log(texture, music);
```
Variables declared with `let` in a `parallel` block can be read after it.
## Mixing them
Nest `parallel` inside `series` to load some things in order and others together:
```ts
async function load(name: string): Promise {
await sleep(5);
return name;
}
series {
const config: string = load("config");
parallel {
let tiles: string = load("tiles");
let sounds: string = load("sounds");
}
console.log(config, tiles, sounds);
}
```
`series` and `parallel` also work on loops and `if`: `series for (const item of items) { ... }` handles one item at a time, and `parallel for (const item of items) { ... }` starts them all at once.
## The rules
- **No `await` inside `series` or `parallel`.** They do the waiting.
- **They are statements, not expressions.** `let x = parallel { ... }` is an error.
- **They need an async context**: an `async`, `series` or `parallel` function, or the top level of the entry file.
- **No `parallel` directly inside `parallel`.** Use one block.
- **No `return` out of a `parallel` statement.** Each statement runs as its own task.
- If a statement in a `parallel` block throws, the block waits for the others to finish, then throws the first error.
---
URL: https://thicketscript.com/docs/language/classes
# Classes
## A class
```ts
class Enemy {
name: string;
hp: int;
constructor(name: string, hp: int) {
this.name = name;
this.hp = hp;
}
hit(damage: int): void {
this.hp -= damage;
if (this.hp < 0) {
this.hp = 0;
}
}
isDefeated(): boolean {
return this.hp == 0;
}
}
const slime: Enemy = new Enemy("Slime", 10);
slime.hit(4);
console.log(slime.name, slime.hp, slime.isDefeated());
```
Methods do not use the `function` keyword. Writing `function hit()` inside a class is a syntax error.
## Field defaults
When every field has a default and there is no constructor, one is made for you:
```ts
class Settings {
volume: float = 0.8;
fullscreen: boolean = false;
language: string = "en";
}
const settings: Settings = new Settings();
console.log(settings.volume, settings.language);
```
## Constructor parameter properties
Put `public`, `private`, `protected` or `readonly` (alone or after an access modifier) on a constructor parameter to declare the field and assign it in one step:
```ts
class Point {
constructor(public x: int, public y: int) {}
}
const p: Point = new Point(3, 4);
console.log(p.x, p.y);
```
## Access modifiers and readonly
| Modifier | Visible from |
|---|---|
| `public` | Everywhere (the default) |
| `protected` | The class and its subclasses |
| `private` | The class only |
`readonly` fields can only be set in the constructor. The checker enforces all of these.
```ts
class Wallet {
readonly owner: string;
private coins: int = 0;
constructor(owner: string) {
this.owner = owner;
}
add(amount: int): void {
this.coins += amount;
}
get balance(): int {
return this.coins;
}
}
const w: Wallet = new Wallet("Ada");
w.add(25);
console.log(w.owner, w.balance);
```
## Getters and setters
`get` and `set` define properties that run code. They are read and written like fields:
```ts
class Temperature {
private celsius: float = 0;
get fahrenheit(): float {
return this.celsius * 1.8 + 32;
}
set fahrenheit(value: float) {
this.celsius = (value - 32) / 1.8;
}
}
const t: Temperature = new Temperature();
t.fahrenheit = 212;
console.log(t.fahrenheit);
```
## Static members and constants
```ts
class Limits {
static const MAX_PARTY: int = 4;
static created: int = 0;
static track(): void {
Limits.created += 1;
}
}
Limits.track();
console.log(Limits.MAX_PARTY, Limits.created);
```
`static const` is the preferred way to write a class constant. Statics are reached through the class name, not an instance. A subclass inherits its parent's statics.
## Inheritance
A class extends one parent. A subclass constructor must call `super()` before it touches `this`:
```ts
class Animal {
constructor(public name: string) {}
speak(): string {
return this.name + " makes a sound";
}
}
class Dog extends Animal {
tricks: int;
constructor(name: string, tricks: int) {
super(name);
this.tricks = tricks;
}
override speak(): string {
return this.name + " barks";
}
}
const rex: Animal = new Dog("Rex", 3);
console.log(rex.speak());
console.log(rex instanceof Dog);
```
`override` is optional, but the checker then makes sure the parent really has that method. `super.speak()` calls the parent's version.
## Abstract classes
An abstract class cannot be created directly. Subclasses must implement its abstract methods:
```ts
abstract class Shape {
abstract area(): float;
describe(): string {
return "area " + this.area();
}
}
class Circle extends Shape {
constructor(public radius: float) {
super();
}
area(): float {
return Math.PI * this.radius * this.radius;
}
}
const c: Circle = new Circle(2);
console.log(c.describe());
```
## Final
`final class` cannot be extended. A `final` method cannot be overridden.
```ts
final class Token {
value: string = "";
}
class Base {
final id(): string {
return "base";
}
}
console.log(new Token().value.length, new Base().id());
```
## Custom `toString`
A `toString()` method is used whenever the object becomes a string:
```ts
class Rgb {
constructor(public r: int, public g: int, public b: int) {}
toString(): string {
return `rgb(${this.r}, ${this.g}, ${this.b})`;
}
}
console.log(String(new Rgb(255, 128, 0)));
```
## Not supported
- `#private` fields. Use `private`.
- Static initializer blocks. Use static fields with initializers.
- Multiple inheritance. Use [interfaces](./interfaces) and composition.
---
URL: https://thicketscript.com/docs/language/coming-from-typescript
# Coming from TypeScript
ThicketScript shares TypeScript's syntax, but it is its own language, with its own type checker, compiler and VM.
There is no JavaScript underneath. Most of what you write in TypeScript works as it is. This page covers the rest:
what works differently, what is accepted and ignored, and what is left out.
## The differences that matter
### Numbers have real types
There is no `number`. Pick `int` (32-bit signed), `uint` (32-bit unsigned) or `float` (64-bit). Types are always
lowercase: `int`, `uint`, `float`, `string`, `boolean`.
`Int(x)`, `Float(x)` and their siblings are conversion functions, not types. `Int("42")` turns a string into an
`int`, and raises if it can't. Writing `Int` where a type goes is an error that points you to `int`.
```ts
const n: int = Int("42");
const f: float = Float("1.5");
console.log(n + 1, f * 2.0);
```
```text
43 3
```
### `int / int` truncates
Dividing two `int` values gives an `int`, rounded toward zero. A `float` operand, or a `float` target, makes it float
math.
```ts
const a: int = 7;
const b: int = 2;
console.log(a / b, -9 / 4);
const half: float = a / b;
console.log(half);
```
```text
3 -2
3.5
```
### Overflow raises an error
Integers never wrap and never quietly turn into floats. Going past the range raises a `RangeError`, so the bug
shows up on the line that caused it.
```ts
const big: int = 2147483647;
try {
console.log(big + 1);
} catch (e) {
console.log(e);
}
```
```text
RangeError: integer overflow
```
The same goes for packed struct fields: storing a computed 300 in a `uint8` raises instead of keeping 44, and a literal 300 there is a compile error.
### Equality is strict
`==` and `===` both compare without coercion. Comparing a string to a number is a checker error, because it can
never be true.
### Interfaces are nominal
A class counts as an interface only when it says `implements`. Matching the shape is not enough.
### Structs are values
`struct` is new. A struct is copied on assignment, carries methods and operators, and can use packed number fields.
See [Features](/features#structs-with-operators).
### Async has more words
`series` and `parallel` sit beside `async` and `await`. `async worker` runs a function on its own thread, and
`async mutex` runs one call at a time. See [Async, series and parallel](./async).
## Accepted and ignored
These type-level features parse without error. They have no runtime effect, and `thicket fmt` prints them as you
wrote them. You can paste TypeScript that uses them and keep going.
- Intersection types: `A & B`. The type is checked as `any`, so `x.a` and `x.b` work on an `A & B` value.
- Conditional types: `T extends U ? X : Y`. Checked as `any`.
- Mapped types: `{ [K in keyof T]: ... }`. Checked as `any`.
- Template literal types: `` `on${string}` ``. Checked as `string`.
- The `infer` keyword, inside a conditional type.
- `keyof` as a type operator. It is checked as `string`. `typeof x` in a type annotation is supported.
- Type predicates: a `x is string` return type. The function returns `boolean`, and the call does not narrow.
- Indexed access types: `T[K]`, such as the value of a mapped type, `{ [K in keyof T]: T[K] }`. Checked as `any`.
- Index signatures in a type literal: `{ [key: string]: int }`. Checked as `any`.
- Generic constraints: `T extends Something`. Not enforced: any argument is accepted.
- Type-only imports: `import type { X } from "./x"`. The `type` keyword is dropped and the name is imported, but,
as in TypeScript, `X` is then usable only in type positions.
- Static initializer blocks: `static { ... }` inside a class. The block never runs; `thicket fmt` keeps it.
- Type arguments on calls: `first(names)` and `new Box(...)`.
Union types are not on this list. They are fully checked: a union with `null` needs `?.` or a guard before you use
it, and `typeof` and `is` narrow a union inside a branch. `abstract` is not on it either. Abstract classes and
methods are enforced.
### Non-null assertions are checked
ThicketScript supports TypeScript's postfix `!` with the same syntax and typing.
It narrows `T | null | undefined` to `T`:
```ts
function findTitle(id: int): string | null {
return id == 1 ? "Intro" : null;
}
const title: string = findTitle(1)!;
console.log(title); // Intro
```
The deliberate difference is run-time behavior. TypeScript erases `!`, so a
wrong assertion carries the null forward until something else fails.
ThicketScript checks it: a nullish value raises a `TypeError` at the `!` that
names the expression. One more difference: `x! = v` is an error here (assign to
`x`), where TypeScript accepts it. Use `??` or an explicit guard when absence is
normal.
Two TypeScript features look ignorable and are not. Index signatures on an `interface` are discarded, so reading an
undeclared property is an error (`Property 'zzz' does not exist`): declare the properties, or use `Map`.
Declaration merging does not happen: a second `interface A { ... }` is an error (`Duplicate declaration 'A'`), so
write one declaration.
### `Map.get` can be undefined
As in TypeScript, `map.get(key)` is typed `V | undefined`, and `has(key)` does not
narrow it. Use `!` when the key must exist, `??` when a missing key is normal, or
test the result against `undefined`:
```ts
const ages: Map = new Map();
ages.set("ada", 36);
const known: int = ages.get("ada")!;
const other: int = ages.get("bob") ?? 0;
```
The bracket read `ages["ada"]` is typed `int | undefined` too, like `get`; `ages["ada"] = 1` is checked against `int`.
## Left out
These are recognized and rejected with a clear error.
| Feature | What you get | Use instead |
|---|---|---|
| Decorators, `@sealed` | "Decorators are not supported" | Plain functions or base classes |
| BigInt, `10n` | "ThicketScript has no bigint/int64" | `int`, `uint` or `float` (exact to 2^53) |
| Private fields, `#x` | "Private fields are not supported" | The `private` modifier |
| `new Function()` | A runtime error | Function declarations or arrows |
| `eval()` | Not defined | Nothing: there is no runtime code generation |
These have no support at all:
- **Namespaces.** `namespace Foo { }` doesn't parse. `import * as ns from "./mod"` works.
- **Generators.** `function*` and `yield` don't parse.
- **`Symbol`.** Not defined.
- **The wrapping `Proxy` and `Reflect`.** `new Proxy(target, handler)` and `Reflect` are not defined. For wildcard
property hooks, extend the built-in `Proxy` class instead.
## Different on purpose
- **`Date`** is ThicketScript's own struct, not the JavaScript class. There is no `new Date()`. `Date.now()` returns a
`Date`, and `Date.nowMs()` gives epoch milliseconds.
- **No prototype chain, no `this` rebinding, no implicit coercion.** A method's `this` is its object.
## Why leave things out
ThicketScript keeps the practical core you use to write a game and drops what adds weight without helping game code.
The aim is a language a TypeScript developer can read on day one, running on a small, fast VM.
---
URL: https://thicketscript.com/docs/language/control-flow
# Control flow
Conditions always go in parentheses: `if (x > 0)`, never `if x > 0`.
## if and else
```ts
const hp: int = 12;
if (hp <= 0) {
console.log("defeated");
} else if (hp < 20) {
console.log("hurt");
} else {
console.log("healthy");
}
const label: string = hp > 50 ? "strong" : "weak";
console.log(label);
```
## Looping over a collection: `for...of`
```ts
const party: Array = ["Ada", "Brin", "Cass"];
for (const name of party) {
console.log(name);
}
```
`for...of` is the loop to reach for. It works on arrays, maps, sets and strings.
::: tip One way to loop
The language also accepts a `foreach` keyword as an alias. Don't use it. The style guide allows only `for...of`, and every sample on this site follows it.
:::
## Counting loops
```ts
let total: int = 0;
for (let i: int = 1; i <= 10; i++) {
total += i;
}
console.log(total); // 55
```
Each iteration gets a fresh `i`, so closures made inside the loop each see their own value.
## Ranges
`a..b` includes both ends. `a.. 0) {
console.log(n);
n--;
}
let tries: int = 0;
do {
tries++;
} while (tries < 3);
console.log(tries);
```
## switch
Each branch needs `case` and usually a `break`. Without `break`, a case falls through to the next one.
```ts
function weekday(day: int): string {
switch (day) {
case 0:
return "Sunday";
case 6:
return "Saturday";
default:
return "a weekday";
}
}
console.log(weekday(6));
```
For enums, `switch strict` checks that every member is covered, and `switch bitflag` matches flags. See [Enums](./enums).
## break, continue and labels
```ts
for (let i: int = 0; i < 10; i++) {
if (i == 5) {
break;
}
if (i % 2 == 0) {
continue;
}
console.log(i); // 1, 3
}
outer: for (let row: int = 0; row < 3; row++) {
for (let col: int = 0; col < 3; col++) {
if (row + col > 2) {
break outer;
}
}
}
```
Labels can only go on loops.
## Running code on the way out: `defer`
`defer` runs a statement when the current scope ends, however it ends. See [Errors](./errors#defer).
---
URL: https://thicketscript.com/docs/language/enums
# Enums
## A basic enum
Members count up from 0:
```ts
enum State {
Idle,
Walking,
Jumping,
}
const s: State = State.Walking;
console.log(s); // 1
```
The type of a member is the enum, so declare the variable as `State`, not `int`.
## Explicit values
```ts
enum Direction {
Up = "UP",
Down = "DOWN",
}
enum Status {
Unknown,
Active = 10,
Pending,
}
console.log(Direction.Up, Status.Pending); // UP 11
```
After an explicit number, counting carries on from it.
## Bitflags
`enum bitflag` gives each member its own bit (1, 2, 4, 8 and so on), so you can combine them:
```ts
enum bitflag Permission {
Read,
Write,
Execute,
}
const p: int = Permission.Read | Permission.Write;
console.log(p); // 3
```
`switch bitflag` tests each case with a bitwise AND, so more than one case can run:
```ts
enum bitflag Perm {
Read,
Write,
Execute,
}
const granted: int = Perm.Read | Perm.Write;
switch bitflag (granted) {
case Perm.Read:
console.log("can read");
break;
case Perm.Write:
console.log("can write");
break;
case Perm.Execute:
console.log("can execute");
break;
}
```
In a `switch bitflag`, `break` moves on to the next case. `stop` leaves the whole switch.
## Exhaustive switches
`switch strict` makes the checker confirm that every member of the enum has a case. Add a member later and every `switch strict` that misses it becomes an error:
```ts
enum Suit {
Hearts,
Spades,
}
function suitName(suit: Suit): string {
switch strict (suit) {
case Suit.Hearts:
return "hearts";
case Suit.Spades:
return "spades";
}
return "";
}
console.log(suitName(Suit.Spades));
```
## Doc comments
`/** */` comments on members show up in IDE hover and autocomplete:
```ts
enum HttpStatus {
/** The request succeeded. */
Ok = 200,
/** Nothing at that path. */
NotFound = 404,
}
console.log(HttpStatus.NotFound);
```
---
URL: https://thicketscript.com/docs/language/errors
# Errors
## Throwing and catching
```ts
function divide(a: float, b: float): float {
if (b == 0) {
throw new RangeError("division by zero");
}
return a / b;
}
try {
console.log(divide(10, 0));
} catch (err) {
console.log(err.name + ": " + err.message);
} finally {
console.log("done");
}
```
`finally` runs whether the `try` succeeds or throws. You can use `try` with only `finally`.
## The error classes
`Error` is the base class. It has `message`, `name` and `stack`, a trace of where the error was created. The built-in subclasses are `TypeError`, `RangeError`, `ReferenceError`, `SyntaxError` and `AggregateError`.
You can make your own:
```ts
class SaveError extends Error {
constructor(message: string, public slot: int) {
super(message);
}
}
try {
throw new SaveError("disk full", 2);
} catch (err) {
console.log(err.message);
}
```
## Typed catch clauses
Give the catch variable a type to catch only that kind of error. The first matching clause runs. An untyped clause catches everything, so put it last. If nothing matches, the error keeps going.
```ts
function parseLevel(text: string): int {
if (text.length == 0) {
throw new TypeError("empty level number");
}
return Int(text);
}
try {
parseLevel("");
} catch (err: TypeError) {
console.log("type error: " + err.message);
} catch (err: RangeError) {
console.log("range error: " + err.message);
} catch (err) {
console.log("something else");
}
```
## Inline try
`try` in front of an expression gives `null` when it throws. Pair it with `??` for a fallback:
```ts
const good: int | null = try Int("123");
const bad: int | null = try Int("abc");
const level: int = try Int("abc") ?? 1;
console.log(good, bad, level);
```
## defer
`defer` runs a statement when the current scope ends: normally, by `return`, or by an error. It is the tidy way to release something you opened:
```ts
function work(): void {
console.log("open");
defer console.log("close");
console.log("working");
}
work();
```
Several `defer` statements run in reverse order, last one first.
## Errors in async code
When an `async` function throws, its promise rejects. Awaiting it throws the error at the `await`, so the same `try` and `catch` work. See [Async, series and parallel](./async).
---
URL: https://thicketscript.com/docs/language/functions
# Functions
## Declaring a function
Every parameter has a type and every function declares what it returns:
```ts
function add(a: int, b: int): int {
return a + b;
}
function log(message: string): void {
console.log("[game] " + message);
}
log("sum is " + add(2, 3));
```
Functions declared at the top level of a file can be called from anywhere in that file, even above the declaration.
## Arrow functions
```ts
const square: (x: float) => float = (x: float): float => x * x;
const clamp: (v: int, lo: int, hi: int) => int = (v: int, lo: int, hi: int): int => {
if (v < lo) {
return lo;
}
if (v > hi) {
return hi;
}
return v;
};
console.log(square(3), clamp(12, 0, 10));
```
A function type is written `(x: float) => float`. You can also use the plain `Function` type when the exact signature does not matter.
When an arrow function is passed somewhere that already has a function type, its parameter types come from there:
```ts
const nums: Array = [1, 2, 3, 4];
const evens: Array = nums.filter((n: int): boolean => n % 2 == 0);
console.log(evens.join(","));
```
## Default parameters
```ts
function greet(name: string = "world"): string {
return "Hello, " + name;
}
console.log(greet());
console.log(greet("Ada"));
```
## Rest parameters and spread
```ts
function sum(...nums: Array): int {
let total: int = 0;
for (const n of nums) {
total += n;
}
return total;
}
const values: Array = [4, 5, 6];
console.log(sum(1, 2, 3));
console.log(sum(...values));
```
Every argument a rest parameter collects is checked against its element type.
## Closures
A function keeps access to the variables around it, even after the outer function returns:
```ts
function makeCounter(): () => int {
let count: int = 0;
return (): int => {
count += 1;
return count;
};
}
const next: () => int = makeCounter();
console.log(next()); // 1
console.log(next()); // 2
```
## Functions are values
Pass them in, return them, store them:
```ts
function applyTwice(f: (n: int) => int, x: int): int {
return f(f(x));
}
const double: (n: int) => int = (n: int): int => n * 2;
console.log(applyTwice(double, 3)); // 12
```
## Async functions
An `async` function returns a `Promise` and can use `await`. See [Async, series and parallel](./async).
---
URL: https://thicketscript.com/docs/language/generics
# Generics
Type parameters let one function or class work with many types while keeping them checked.
## Generic functions
```ts
function firstOr(items: Array, fallback: T): T {
if (items.length == 0) {
return fallback;
}
return items[0];
}
const n: int = firstOr([3, 4], 0);
const s: string = firstOr([], "none");
console.log(n, s);
```
The checker infers `T` from the arguments, then checks the result. `const s: string = firstOr([3, 4], 0);` would be an error, because `T` is `int` there.
You can also pass the type yourself: `firstOr(scores, 0)`.
## Generic classes
```ts
class Stack {
private items: Array = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | null {
if (this.items.length == 0) {
return null;
}
return this.items.pop();
}
get size(): int {
return this.items.length;
}
}
const moves: Stack = new Stack();
moves.push("left");
moves.push("jump");
console.log(moves.pop(), moves.size);
```
## Write the type arguments in annotations
A type annotation must give every type argument. `let m: Map = ...` is an error. Write `Map`, or `Map` when you really mean any. A `new` expression can leave them out:
```ts
const scores: Map = new Map();
scores.set("ada", 10);
console.log(scores.get("ada")); // get answers int | undefined; use ! or ?? to get an int
```
## Type arguments are checked
`Array` and `Array` are different types, and so are `Promise` and `Promise`. Arguments are covariant, as in TypeScript: an `Array` can be used where an `Array` is expected, but not the other way round.
```ts
const ints: Array = [1, 2];
const floats: Array = ints;
console.log(floats.length);
```
## At run time
Generics are erased. Every `Stack` shares the same compiled code, and there is no type information for `T` at run time. All the checking happens before the program runs.
---
URL: https://thicketscript.com/docs/language/interfaces
# Interfaces
An interface describes the members something must have. It exists only for the checker and produces no code.
```ts
interface Damageable {
hp: int;
takeDamage(amount: int): void;
}
class Crate implements Damageable {
hp: int = 3;
takeDamage(amount: int): void {
this.hp -= amount;
}
}
function smash(target: Damageable): void {
target.takeDamage(1);
}
const crate: Crate = new Crate();
smash(crate);
console.log(crate.hp);
```
## `implements` is checked
When a class says `implements`, the checker verifies that it really provides every member, with compatible types. A missing method is an error on the class name:
```ts
class Ghost implements Damageable {
hp: int = 1;
}
// error: Class 'Ghost' incorrectly implements interface 'Damageable': missing member 'takeDamage'
```
Members inherited from a parent class count.
## Interfaces are nominal
This is the biggest difference from TypeScript. A class only counts as an interface when it says so. Having the same members is not enough:
```ts
class Barrel {
hp: int = 5;
takeDamage(amount: int): void {}
}
const d: Damageable = new Barrel(); // error: Cannot assign Barrel to Damageable
```
Add `implements Damageable` to `Barrel` and the assignment is fine.
## Optional and readonly members
```ts
interface ItemInfo {
readonly id: string;
label: string;
description?: string;
}
class Potion implements ItemInfo {
readonly id: string = "potion";
label: string = "Potion";
}
function describe(item: ItemInfo): string {
return item.label + ": " + (item.description ?? "no description");
}
console.log(describe(new Potion()));
```
- `?` means a class may leave the member out. Reading it gives `T | null`, so handle the `null` with `??`, `?.` or a check.
- `readonly` cannot be assigned through the interface.
## Extending an interface
An interface can extend one other interface:
```ts
interface Named {
name: string;
}
interface Pet extends Named {
owner: string;
}
class Cat implements Pet {
name: string = "Mochi";
owner: string = "Ada";
}
const n: Named = new Cat();
console.log(n.name);
```
A class can extend a parent and implement several interfaces: `class GuideDog extends Dog implements Pet, Trained`.
## Generic interfaces
```ts
interface Container {
get(): T;
set(value: T): void;
}
class Slot implements Container {
private value: string = "";
get(): string {
return this.value;
}
set(value: string): void {
this.value = value;
}
}
const s: Slot = new Slot();
s.set("sword");
console.log(s.get());
```
## Methods cannot be swapped
A method declared as `go(): void;` is a contract, not a field you can reassign. If you need a replaceable callback, declare a property with a function type instead: `go: () => void;`.
---
URL: https://thicketscript.com/docs/language/modules
# Modules and imports
Every `.tks` file is a module. Share code between files with `export` and `import`, using the same syntax as TypeScript.
## Exporting
```ts
// src/combat/damage.tks
export const CRIT_MULTIPLIER: int = 2;
export function damage(base: int, crit: boolean): int {
return crit ? base * CRIT_MULTIPLIER : base;
}
export default class Dice {
static label(sides: int): string {
return "d" + sides;
}
}
```
## Importing
```ts
// src/main.tks
import Dice, { damage } from "./combat/damage";
console.log(damage(5, true));
console.log(Dice.label(20));
```
Relative paths start with `./` or `../`. The `.tks` extension is optional. A path that names a folder loads that folder's `index.tks`.
All the usual forms work:
```ts
import { damage, CRIT_MULTIPLIER } from "./combat/damage"; // named
import { damage as hit } from "./combat/damage"; // renamed
import Dice from "./combat/damage"; // default
import * as combat from "./combat/damage"; // namespace
import type { Enemy } from "./enemy"; // types only
import "./setup"; // run for side effects
```
And on the export side, `export { a, b as c }`, `export { x } from "./other"` and `export * from "./other"` re-export from another module.
Each module runs once, the first time it is imported. Circular imports are allowed: classes and functions can refer to each other across the cycle.
## System modules
Some modules are built into `thicket`. You import them by name:
```ts
import { Files } from "fs";
import { Http } from "http";
import { Sprite } from "willow";
```
Each one exports a class you call directly, like `Files.readText(path)` or `Http.get(url)`.
| Kind | Names |
|---|---|
| Always available | `fs`, `env`, `events`, `reactivity` |
| Listed in `systems` | `audio`, `bitmap`, `compress`, `crypto`, `database`, `http`, `net`, `ws`, `shell` |
| Host modules | `willow` |
`fs` also carries the path helpers, such as `Files.join` and `Files.dirname`. There is no separate `path` module.
The everyday names, like `console`, `Math`, `JSON`, `print`, `sleep` and the error classes, need no import at all. The same goes for `System` (the platform, arguments, `System.exit`), and in a window app `engine`, `App`, `Keyboard`, `Mouse` and `Gamepad`.
## systems and permissions
A project's `thicket.json` can list the systems it uses:
```json5
{
entry: "src/main.tks",
systems: ["http", "willow"],
permissions: { net: true, fs: true },
}
```
- **No `systems` key** means every system the host has.
- **A list is strict.** Importing a listable system that is not on the list is an error, with a hint to add it.
- **Permissions are separate.** A system says "include this code". A permission says "allow it at run time". The first call that needs a permission you have not granted throws a `PermissionError` naming the fix. The permissions are `fs`, `net`, `crypto`, `database`, `shell`, `env` and `audio`, all off by default. `async worker` functions need no permission: a worker is pure computation and gets no I/O modules. The checker rejects an imported I/O binding used in a worker.
## Path aliases
Long relative paths get old fast. `compiler.paths` in `thicket.json` defines shortcuts:
```json5
{
entry: "src/main.tks",
compiler: { paths: { "@combat/*": "src/combat/*" } },
}
```
```ts
import { CRIT_MULTIPLIER } from "@combat/damage";
console.log(CRIT_MULTIPLIER);
```
## Packages
Code from another folder, a git repository or a zip is a package. You import it by name, just like a system. See [Packages](/docs/packages/).
---
URL: https://thicketscript.com/docs/language/types
# Types
ThicketScript checks types before your program runs. The checker catches mistakes early, and the IDE uses the same information for autocomplete.
## The basic types
| Type | What it holds | Example |
|---|---|---|
| `int` | 32-bit signed integer | `let lives: int = 3;` |
| `uint` | 32-bit unsigned integer | `let color: uint = 0xFF8800FF;` |
| `float` | 64-bit floating point | `let speed: float = 2.5;` |
| `string` | Text | `let name: string = "Ada";` |
| `boolean` | `true` or `false` | `let ready: boolean = false;` |
| `void` | No value, for function returns | `function log(): void {}` |
| `null`, `undefined` | No value | `let target: Enemy \| null = null;` |
| `any` | Turns checking off for one value | Use sparingly. |
There is no `number` type and no `bool`. Pick `int`, `uint` or `float`, and write `boolean`.
## Integers are integers
Arithmetic on two `int` values stays an `int`. That includes division, which truncates toward zero:
```ts
const a: int = 7;
const b: int = 2;
console.log(a / b); // 3
console.log(-9 / 4); // -2
const half: float = a / b;
console.log(half); // 3.5
```
A `float` target, like `half` above, makes the whole expression float math. A `float` operand does the same.
Integers never wrap silently. Going past the range raises a `RangeError`:
```ts
const big: int = 2147483647;
console.log(big + 1); // RangeError: integer overflow
```
Values above `0x7FFFFFFF`, like most RGBA colors, need `uint`.
## Number literals
```ts
const hex: int = 0xFF;
const binary: int = 0b1010;
const octal: int = 0o77;
const ratio: float = 0.75;
console.log(hex, binary, octal, ratio);
```
## Strings
Strings use double quotes, single quotes or backticks. Backticks allow `${}` interpolation:
```ts
const name: string = "Ada";
const level: int = 4;
console.log(`${name} reached level ${level}`);
console.log(name + " has " + level + " gems");
console.log(name.toUpperCase(), name.length);
```
`"a" + 1` is `"a1"`, as in TypeScript.
## Unions and null
A union lets a value be one of several types. Adding `null` makes a value optional:
```ts
function greet(name: string | null): string {
if (name != null) {
return "Hello, " + name;
}
return "Hello, stranger";
}
console.log(greet("Ada"));
console.log(greet(null));
```
Inside the `if`, the checker knows `name` is a `string`. That is called narrowing. It works with `!= null`, truthiness checks, `is`, `instanceof` and early returns.
`x != null` catches both `null` and `undefined`, and narrows both away from `string | null | undefined`; `x == null` matches both. Strict `x !== null` removes only `null`, and `x !== undefined` only `undefined`. The `?.` and `??` operators work as in TypeScript:
```ts
class Weapon {
damage: int = 5;
}
class Hero {
weapon: Weapon | null = null;
}
const hero: Hero = new Hero();
const damage: int = hero.weapon?.damage ?? 1;
console.log(damage); // 1
```
Use postfix `!` when a value must exist but a guard is impractical. It narrows
the expression from `T | null | undefined` to `T`:
```ts
function findTitle(id: int): string | null {
return id == 1 ? "Intro" : null;
}
const title: string = findTitle(1)!;
console.log(title); // Intro
```
This differs from TypeScript: ThicketScript checks `!` at run time. A null or
undefined value raises a `TypeError` at the assertion, and the message names the
expression (`non-null assertion failed for 'findTitle()': value is null`).
## Equality is strict
`==` and `===` mean the same thing. Neither converts types. `0 == ""` is `false`. The one exception is `null == undefined`, which is `true`, so `x != null` is the usual null check.
## Truthiness
`null`, `undefined`, `false`, `0` and `""` are falsy. Everything else is truthy, including empty arrays and empty objects.
## Type aliases and literal types
```ts
type Direction = "up" | "down" | "left" | "right";
type EntityId = int;
let facing: Direction = "up";
const id: EntityId = 42;
console.log(facing, id);
```
Aliases exist only for the checker. They produce no code.
## Arrays, tuples, maps and sets
```ts
const scores: Array = [10, 20, 30];
scores.push(40);
console.log(scores.length, scores[0]);
const doubled: Array = scores.map((s: int): int => s * 2);
console.log(doubled.join(", "));
const pair: [string, int] = ["level", 3];
console.log(pair[0], pair[1]);
const hp: Map = new Map();
hp.set("slime", 12);
hp.set("bat", 6);
console.log(hp.get("slime"), hp.has("bat"), hp.size);
// get answers `int | undefined`: a missing key is undefined.
const slime: int = hp.get("slime")!; // ! : the key must exist
const dragon: int = hp.get("dragon") ?? 0; // ?? : a missing key is normal
console.log(slime, dragon);
for (const [name, value] of hp) {
console.log(name, value);
}
const seen: Set = new Set();
seen.add("cave");
console.log(seen.has("cave"));
```
An array's element type is enforced. Pushing a `string` into an `Array` is an error.
Arrays come with extras beyond the JavaScript set, such as `first()`, `last()`, `isEmpty()`, `unique()`, `shuffle()`, `sum()` and `avg()`.
## Checking a type at run time: `is`
`is` tests a value and narrows it:
```ts
function describe(value: int | string): string {
if (value is int) {
return "a number: " + value;
}
return "some text: " + value;
}
console.log(describe(5));
console.log(describe("hi"));
```
`is` works with the basic types, the built-in object types (`Array`, `Map`, `Set` and others), your classes and structs.
## Converting and casting: `as`
Between basic types, `as` **converts**:
```ts
const text: string = 42 as string; // "42"
const whole: int = 3.9 as int; // 3, truncates toward zero
const parsed: int = "120" as int; // 120
console.log(text, whole, parsed);
```
A string that is not a number converts to `NaN`. Check it with `isNaN()`. When you would rather get an error, use `Int(text)`, which throws, or `tryInt(text)`, which returns `null`.
Between classes, `as` **asserts**. If the value is not that class, it throws a `TypeError` instead of carrying on with the wrong object. Use `is` first when you are not sure.
## Structs
A `struct` is a value type with a fixed memory layout. Struct fields can use compact storage types such as `uint8`, `int16` and `float32`:
```ts
struct Pixel {
r: uint8;
g: uint8;
b: uint8;
a: uint8;
}
const p: Pixel = Pixel{ r: 255, g: 128, b: 0, a: 255 };
const red: uint = p.r;
console.log(red);
```
Reading a packed field gives you an ordinary `uint`, `int` or `float`. Writing checks the range. Storing a wider value without `as` is a warning (PF072), but when every input already fits the field, as in `Pixel{ r: (a.r + b.r) / 2, ... }` or `Vec{ x: a.x + b.x }` with `float32` fields, there is no warning. A literal that cannot fit, such as `r: 300`, is a compile error. The compact types are only for struct fields and similar storage: variables, parameters and class fields use `int`, `uint` and `float`.
When `dump()` renders a struct, a `float32` field uses the shortest decimal
that round-trips at single precision. A field read on its own is an ordinary
`float`, so format that value explicitly when it needs a fixed display width.
---
URL: https://thicketscript.com/docs/language/variables
# Variables
## `let` and `const`
```ts
let score: int = 0;
const MAX_SCORE: int = 9999;
score = score + 10;
score += 5;
console.log(score, MAX_SCORE);
```
- **`let`** declares a variable you can reassign.
- **`const`** declares one you cannot. It must have a value.
Both are block-scoped: a variable declared inside `{ }` is not visible outside it.
```ts
let x: int = 10;
{
let inner: int = 20;
console.log(x + inner); // 30
}
console.log(x); // 10
```
`var` still parses, but it is deprecated and the checker warns. Use `let` or `const`.
## Always write the type
```ts
let name: string = "Ada";
let speed: float = 1.5;
let alive: boolean = true;
let inventory: Array = [];
console.log(name, speed, alive, inventory.length);
```
Leaving the type off (`let name = "Ada";`) earns warning `PF071`. Inference exists, but explicit types are the house style and keep the intent clear.
## Several at once
```ts
let a: int = 1, b: int = 2, c: int = 3;
console.log(a + b + c);
```
## Compound assignment
```ts
let n: int = 10;
n += 5; // 15
n -= 3; // 12
n *= 2; // 24
n /= 4; // 6, integer division
n %= 4; // 2
n **= 3; // 8
console.log(n);
```
Only a variable, a property (`a.b`, `a[i]`) or, for plain `=`, a destructuring pattern can be assigned to. `f() = 3` or `(n + 1) = 2` is a compile error.
Bitwise forms work too: `<<=`, `>>=`, `&=`, `|=` and `^=`.
## Destructuring
Pull values out of an array or a tuple in one line:
```ts
const [first, second]: Array = [1, 2];
console.log(first, second);
```
## Shadowing
Declaring a variable with the same name as one in an outer scope is allowed, but the checker warns. Names that start with `_` are exempt.
## Constants are inlined
A `const` set to a literal number, string or boolean is replaced by its value wherever it is used, so it costs nothing at run time. The same goes for `static const` on a class.
---
URL: https://thicketscript.com/docs/modules/
# Modules
ThicketScript comes with the pieces a game needs. They sit in layers, and you pick the ones you want:
| Module | What it is | How you get it |
|---|---|---|
| [willow](./willow) | The 2D display tree: sprites, text, tweens, cameras, particles, tilemaps. | Built into `thicket`. List `willow` in `systems`. |
| [willowui](./willowui) | UI widgets built on willow: buttons, lists, text input, windows, themes. | A package, written in ThicketScript. |
| [gamekit](./gamekit) | Game structure: entities, components, scenes, saves. | A package, written in ThicketScript. |
Under all of them is the engine core: windows, input, audio and rendering. You never import it directly.
## Systems and packages
The difference matters only in `thicket.json`. In your code, every import looks the same.
- A **system** is built into the `thicket` program. You list it in `systems`.
- A **package** is ThicketScript source from a folder, a git repository or a zip. You list it in `packages`. See [Packages](/docs/packages/).
```json5
{
entry: "app.tks",
systems: ["willow", "bitmap"],
packages: { willowui: "../willowui-tks" },
window: { title: "My Game" },
}
```
Leave `systems` out and the project can use every system the host has.
---
URL: https://thicketscript.com/docs/modules/gamekit
# gamekit
gamekit gives a game its structure: entities and components, a world that runs your systems in phases, scenes, input actions, assets, saves and test helpers. It is a **package** written entirely in ThicketScript, with no native code. It needs willow, and pairs with [willowui](./willowui) for window games.
::: tip The gamekit portal
gamekit has its own documentation: a getting-started walkthrough, one guide per concept, recipes and a full reference.
**[Open the gamekit docs](/gamekit/)**
:::
## Add it to a project
```json5
{
entry: "app.tks",
systems: ["willow", "bitmap"],
packages: { gamekit: "../gamekit-tks" },
}
```
You list the `willow` and `bitmap` systems yourself, because a package's systems are never inherited. A console project works too: willow runs without a window.
## The families
| Family | What it holds |
|---|---|
| `core` | Entities, component keys, events, attributes, state machines, effects, conditions and actions, definitions and prefabs, assets, 2D helpers. |
| `ecs` | A world, component stores, queries, phase schedules and a world ticker. |
| `app` | Scenes, the scene stack and coordinator, focus, and input actions. |
| `save` | Codecs, versioned schemas, save slots and settings. |
| `audio` | A music director over the core audio system. |
| `willow` | Presentation bindings and a pointer bridge on willow. |
| `ui` | Scene windows and screen windows through willowui. |
| `testing` | Deterministic clocks, debug snapshots, scenarios and traces. |
Start with [Getting started](/gamekit/getting-started.html).
---
URL: https://thicketscript.com/docs/modules/willow
# willow
willow is the 2D module, built into `thicket`. It is a **display tree**, in the spirit of Flash and Starling: you build a tree of nodes (sprites, text, shapes, tilemaps, particles), set their position, scale, rotation and colour, and willow draws the whole tree every frame. List it under `systems` in `thicket.json` and import from `"willow"`.
::: tip The willow docs
willow has its own documentation section: a getting-started walkthrough, a guide per topic, recipes, and a reference page for every class.
**[Open the willow docs](/willow/)**
:::
- [Getting started](/willow/getting-started.html): a window, a sprite, moving it with the keyboard.
- [The scene graph](/willow/guide/scene-graph.html): the tree, the world and screen roots, layers.
- [Reference](/willow/reference/): every willow class.
- [willowui](./willowui) adds widgets on top of willow.
---
URL: https://thicketscript.com/docs/modules/willowui
# willowui
willowui is the UI toolkit: buttons, labels, text input, lists, trees, tables, windows, menus, themes, reactive data binding and an XML template system. It is written entirely in ThicketScript on top of [willow](./willow.html), and it comes as a **package**, not a built-in system.
**[Open the willowui documentation](/willowui/)**: getting started, layout, the XML template system in depth, and a reference for every widget.
## Add it to a project
```json5
{
entry: "app.tks",
systems: ["willow", "bitmap"],
packages: { willowui: "../willowui-tks" },
window: { title: "My UI", width: 800, height: 600 },
}
```
The key, `willowui`, is the name you import from: `import { Button, Screen, DEFAULT_STAGE } from "willowui";`.
## Authoring tools
willowui offers `themec` (compile a theme) and `xmluic` (compile an XML template to its binary form). A project that lists willowui runs them with `thicket tool themec` and `thicket tool xmluic`.
---
URL: https://thicketscript.com/docs/packages/
# Using packages
A package is ThicketScript source from somewhere else: another folder, a git repository or a zip. You list it under `packages` in `thicket.json` and import it by name. Packages are **source only**: nothing runs when one is fetched, and no package contains native code.
```json5
{
entry: "app.tks",
packages: {
willowui: "../willowui-tks", // a folder next to this project
shared: "https://github.com/example/shared-tks#v1.2.0", // a git repo at a tag
icons: "https://example.com/icons.zip", // a zip
mathx: "mathx", // thicket_packages/mathx/
oldlib: false, // switched off
},
}
```
```ts
import { Button } from "willowui/src/core/button";
import { lerp } from "mathx/easing";
```
The key is the import name. `"name"` loads the package's entry file. `"name/sub/path"` loads another file inside it.
## One string per package
The shape of the string says where the package comes from:
| You write | It is |
|---|---|
| `./`, `../`, `/`, `~/` or a drive letter | A folder, used in place. |
| `https://`, `http://`, `ssh://` or `git@host:` | A git repository. Add `#tag`, `#branch` or `#commit` to pin it. |
| Anything ending in `.zip` | A zip archive, local or downloaded. |
| A plain name | A folder called `thicket_packages//`. |
| `false`, `null`, `""`, `"no"`, `"false"` or `"none"` | Switched off. |
## Where a plain name is found
The first folder that exists wins:
1. `thicket_packages//` next to your `thicket.json`.
2. `thicket_packages//` in each parent folder, up to the drive root. Handy for a workspace of projects that share packages.
3. Your per-user folder: `~/.local/share/thicket/thicket_packages/` on Linux, `~/Library/Application Support/thicket/thicket_packages/` on macOS, `%APPDATA%\thicket\thicket_packages\` on Windows.
There is no install command yet. To use a plain name, copy or link the folder into `thicket_packages/` yourself.
## One flat folder
Every package, including the packages your packages use, is resolved once, by name, for the whole program.
- Git and zip packages are fetched into your project's `thicket_packages/`, side by side, never nested.
- Two different sources for the same name anywhere in the program is an error that names both.
- Packages that list each other are fine.
## Git and zip packages
- Fetched once and reused. After the first fetch, runs and checks work offline.
- Fetched again only when the string changes, or when you delete the package's entry in `thicket.lock`.
- Git uses the `git` on your `PATH` with your own credentials.
- Zips are checked for unsafe entries before anything is written.
- `thicket check` fetches what is missing. The editor never fetches.
## thicket.lock
`thicket.lock` sits next to `thicket.json` and records what each string resolved to: the git commit, the zip's hash, or a folder and its content hash. **Commit it**, and gitignore `thicket_packages/`. The `.gitignore` that `thicket new` writes already does.
When something no longer matches the lock, the error names the package and the fix: delete that entry in `thicket.lock` to accept the change.
## You import only what you list
If a package you use depends on `dep`, you cannot import `dep` yourself until you add it to your own `packages`.
## You grant what packages need
A package's `permissions` and `systems` say what it needs. Only the project you run grants anything. If willowui needs `fs`, your project turns on `fs`. If it lists `willow`, your project lists `willow` too, or leaves `systems` out entirely. Every unmet need is reported at once:
```text
willowui needs fs; add it to permissions
```
## Names you cannot use
Package keys cannot be the name of a system or core module (`willow`, `fs`, `http` and so on), or `system`, `engine`, `thicket`, `thicketscript`, `tks` or `heartwood`. They cannot be empty, start with `.`, or contain `/`, `\`, `:` or spaces.
Next: [writing a package](./writing).
---
URL: https://thicketscript.com/docs/packages/writing
# Writing a package
A package is just a project whose `entry` gets imported instead of run. There is no special format and no `name` field: the project that uses it picks the import name.
## The layout
```text
mathx/
thicket.json
src/
index.tks
easing.tks
```
```json5
// mathx/thicket.json
{
entry: "src/index.tks",
}
```
```ts
// mathx/src/index.tks
export function lerp(a: float, b: float, t: float): float {
return a + (b - a) * t;
}
export function clamp01(t: float): float {
if (t < 0) {
return 0;
}
if (t > 1) {
return 1;
}
return t;
}
```
Importing the package loads the entry's exports. It never calls `main`. A consumer reaches other files by path, relative to the package root: `import { easeIn } from "mathx/src/easing";`.
A package cannot import anything outside its own folder.
## Using it
```ts
// game/app.tks, with packages: { mathx: "../mathx" }
import { clamp01, lerp } from "mathx";
console.log(lerp(0, 10, clamp01(0.25)));
```
## Tests are a sub-project
Put the tests in a folder with its own `thicket.json` that lists the package by a relative path. They import it exactly as a real consumer would:
```json5
// mathx/tests/thicket.json
{ entry: "all.tks", packages: { mathx: ".." } }
```
There is no `thicket test` command yet. Run the tests with `thicket run`.
## Declare what you need
List what the package uses. These are requirements, not grants:
```json5
{
entry: "src/index.tks",
systems: ["willow"],
permissions: { fs: true },
}
```
A project that uses your package has to list the same systems and turn on the same permissions. It gets a clear error naming the fix if it does not.
A package whose code uses `engine` makes its consumer a window app. Code that only needs the scene can read `Scene.main` instead, which is `null` in a console program and imposes no window.
## Reach your own files with `moduleDir`
A relative path given to `fs` resolves against the working directory, which is the game's folder, not yours. To load your own fonts, images or data, build the path from `moduleDir`, the folder the current source file lives in:
```ts
import { Files } from "fs";
export async function loadTheme(): Promise {
return await Files.readText(Files.join(moduleDir, "theme.json"));
}
```
`moduleDir` needs no import. It is right for a local folder, a package in `thicket_packages/` and a built program alike. Reading through it is an ordinary file read, so the package declares `fs` and the consuming project grants it.
In a built program, a consumer ships your files by listing them as `":"` in its `assets`.
## Offer tools
A package can offer script tools to the projects that use it. Map a tool name to a script in `tools`:
```json5
{
entry: "src/index.tks",
tools: { themec: "tools/themec.tks" },
}
```
A project that lists the package runs it with `thicket tool themec`, and `thicket tool` lists every tool available.
## Share it
Publish the folder as a git repository and tag a version. Users add it with a URL pinned to that tag, like `"https://github.com/you/mathx-tks#v1.0.0"`. There is no package registry.
---
URL: https://thicketscript.com/docs/build/
# Building for each platform
`thicket build` packages a project as a standalone program: a prebuilt runtime, your compiled bytecode (`bundle.tbc`) and the assets you list. New to building? Start with [Building a program](/docs/start/build-executable).
```sh
thicket build [] [--out ] [--strategy ] [--force] [--release]
```
## Console apps and window apps
What you get depends on the project:
- A project **without** a `window` section is a **console app**. It gets the smallest console runtime that has every system it lists.
- A project **with** a `window` section is a **window app**. It gets the window runtime: willow, the frame loop, input, audio and rendering, linked into one executable.
A console app can still list `willow`. It gets the console runtime plus willow, which runs the node tree, tweens and hit tests with no window, display or GPU. That is useful for servers and tools.
## Strategies
A strategy decides the shape of the output. With no `--strategy`, a console app uses `headless` (or `headless-willow` when it lists willow) and a window app uses `linux-portable` on Linux.
| Strategy | Platform | What it makes |
|---|---|---|
| `headless` | host | A console app: the console runtime beside `bundle.tbc`. |
| `headless-willow` | Linux | A console app that lists willow. |
| `linux-portable` | Linux | A window app as one flat folder with `assets/` beside it. |
| `linux-appdir` | Linux | A window app as an `.AppDir` with `AppRun`, a desktop entry and an icon. Needs `build.icon`. |
| `linux-appimage` | Linux | An AppDir wrapped as an `.AppImage`. Needs `appimagetool` on your `PATH`. |
| `windows-portable` | Windows | A Windows folder made from the `windows-x86_64` runtime. |
| `macos-app` | macOS | A macOS `.app` made from the `macos-arm64` runtime. Any `build.icon` must be an `.icns` file. |
`thicket build --list-strategies` shows the strategies that fit your project and whether each one is ready.
A strategy refuses a project it does not fit, before anything is written, and names the one that does.
## Building for another platform
`thicket` looks for each platform's runtime in a `runtimes/` folder beside itself: `linux-x86_64/`, `windows-x86_64/` and `macos-arm64/`. If a runtime folder is missing, build on that platform, or copy its runtime folder next to `thicket`. A build never relabels one platform's runtime as another's.
## What a built window app needs
The folder is self-contained except for the operating system. On Linux the program needs only the C library and the window-system and graphics libraries the desktop already has. Nothing is installed beside the folder. The player needs a desktop session and a working GPU driver, plus an audio device for sound.
## What each runtime includes
| | Console runtime | Window runtime |
|---|---|---|
| The language and standard library | yes | yes |
| `audio` | | yes |
| `database` | SQLite and server drivers | SQLite only |
| `http`, `net`, `ws` | yes | no |
| `shell`, `terminal`, package fetching | no | no |
| willow | with `headless-willow` | yes |
In a built window app, an uncaught error goes to `crash.log` beside the program and a native dialog.
## Assets
Only what `assets` in `thicket.json` lists ships. Entries are paths or globs (`*`, `**`, `?`, `{a,b}`) relative to the project, or `:` for a file inside a package. A glob that matches nothing is a build error.
```json5
assets: [
"assets/**",
"willowui:assets/fonts/go-{regular,bold}.ttf",
]
```
A plain list **embeds** the files inside `bundle.tbc`. The object form mixes three kinds:
| Kind | What happens |
|---|---|
| `embed` | Stored inside `bundle.tbc` and read only when opened. Read-only. |
| `pack` | Stored in one encrypted `app.pak` beside the program. A deterrent against casual browsing, not real secrecy: the key ships with the app. |
| `copy` | Copied beside the program as plain files. |
```json5
assets: {
embed: ["assets/**"],
copy: ["data/save.db"],
}
```
Your code reads every file the same way. A read looks in the embedded files first, then the pack, then the disk, at the same project-relative path. SQLite databases must be listed under `copy`, because the app writes to them.
## Version and icon
The `build` section of `thicket.json` holds `version`, `icon` and the per-platform settings under `linux`, `windows` and `macos`. On Windows, `build.windows.console: true` makes a console-subsystem program instead of a GUI one.
## Bytecode only
`thicket compile` is the smaller step. It writes a `.tbc` bundle and nothing else, which `thicket run app.tbc` can run.
```sh
thicket compile app.tks -o app.tbc
```
## Coming next
These are on the way. Plans can change.
- **Single-file programs**: the bundle appended to the runtime, so a game is one file.
- **Web builds**: a WebAssembly runtime that runs a bundle in the browser.
---
URL: https://thicketscript.com/docs/ai/
# Using ThicketScript with AI agents
AI coding agents write better ThicketScript when they can check their work against the real compiler. ThicketScript gives them two ways in:
- **`thicket mcp`**, a [Model Context Protocol](https://modelcontextprotocol.io) server built into the `thicket` program. It answers diagnostics, module APIs and symbol lookups for the exact version you have installed.
- **[`/llms.txt`](/llms.txt)** and **[`/llms-full.txt`](/llms-full.txt)**, plain-text copies of these docs for agents that read the web.
::: info Downloads are coming soon
Once you have `thicket`, everything on this page works with no extra install. There is no hosted server and no account.
:::
## Start the server
`thicket mcp` talks MCP over stdio: JSON-RPC 2.0, one JSON object per line. Stdout carries only protocol messages; warnings go to stderr. Your agent starts it for you, so you rarely run it by hand.
**Start it in your project directory.** On startup it loads the project's own types from `thicket.json` in the current directory, and workspace symbol search scans the `.tks` files under it. It skips `.git`, `build`, `node_modules` and `.tks-cache`, stops after 5,000 files, and never writes a cache into your source tree.
## Connect your agent
Any MCP client that can launch a stdio server needs only this:
```json
{ "command": "thicket", "args": ["mcp"] }
```
### Claude Code
Run this in your project directory:
```sh
claude mcp add thicket -- thicket mcp
```
### Cursor and other editors
Editors that read an `mcpServers` block (Cursor's `.cursor/mcp.json`, for example) take:
```json
{
"mcpServers": {
"thicket": {
"command": "thicket",
"args": ["mcp"]
}
}
}
```
If `thicket` is not on your `PATH`, use its full path as `command`.
## Tools
Paths can be absolute or relative to the directory the server started in. Line and column numbers are **1-based**, in both arguments and results, which print locations as `path:line:column`.
- **`get_diagnostics`** (optional `path`): Runs `thicket check` on the file, or on the project when `path` is left out, and returns its output and exit status.
- **`list_modules`** (no arguments): The built-in and host declaration modules, the same list as `thicket types list`.
- **`get_module_api`** (`module`): A module's full declaration file with its doc comments, the same as `thicket types print `.
- **`get_symbol`** (`path`, `line`, `column`): Hover text for the symbol at that position, plus where it is defined.
- **`get_references`** (`path`, `line`, `column`): Every reference to the symbol at that position, including its declaration.
- **`get_document_symbols`** (`path`): The outline of one file: its classes, structs, functions, methods and so on, with positions.
- **`find_symbols`** (`query`): Symbols across the project whose names match `query`.
The answers come from the same language server that powers the IDE plugin, so an agent sees the same hovers, definitions and errors you do.
## A good loop for agents
1. Call `list_modules`, then `get_module_api` for the modules the task touches, before writing code against them.
2. Write the code.
3. Call `get_diagnostics` and fix what it reports. Repeat until it is clean.
4. Use `get_symbol` and `get_references` before renaming or changing a signature.
You can give your agent that loop in its instructions file (for example `CLAUDE.md` or `AGENTS.md`) along with a link to this page.
## llms.txt
[`https://thicketscript.com/llms.txt`](/llms.txt) follows the [llms.txt](https://llmstxt.org) format: a short summary of ThicketScript and a link to every docs page. [`https://thicketscript.com/llms-full.txt`](/llms-full.txt) is the full text of the docs in one file, for agents that want it all in context at once.
The [API reference](/docs/api/) is generated from the same declaration files `get_module_api` prints. For the version you have installed, prefer the MCP server; it is never out of date.
---
URL: https://thicketscript.com/docs/api/
# API reference
Every built-in class, function and module, generated from the declaration files that ship with ThicketScript. The same files power hover, completion and go-to-definition in the IDE plugin and in `thicket mcp`.
## Always available
In scope in every file. No import needed.
- [Global functions](./globals): Output, timers, parsers, type checks and the global constants.
- [console](./console): Leveled logging to stdout and stderr.
- [String](./string): The string type and its methods.
- [Number conversions](./numerics): Conversion functions and constants for numbers: `Int(x)`, `Float(x)`, `Int.MAX`, `Number.isInteger` and friends. The types themselves are lowercase: `int`, `uint`, `float`, and `int8` to `float32` for packed storage.
- [Number methods](./primitives): Methods you can call on a number value, such as `n.toFixed(2)`. The class names on this page are internal; you never write them in code.
- [Math](./math): Math functions and constants.
- [Random](./random): Seedable random number generators.
- [Array, Map and Set](./collections): The collection types: `Array`, `FixedArray`, `Map`, `Set` and the weak references.
- [Buffers and typed arrays](./buffer): `ByteArray`, `Buffer`, the typed arrays and their encodings.
- [Promise](./async): Async results for `async` / `await`.
- [Errors](./errors): The built-in error classes.
- [JSON](./json): Parse and stringify JSON.
- [Object](./object): `Object` helpers and `Proxy`.
- [RegExp](./regexp): Regular expressions.
- [URL](./url): Parse and build URLs and query strings.
- [App and System](./system): The app lifecycle (`App`), process and platform info (`System`), and autotest hooks.
- [Input constants](./input): `Key`, `GamepadButton`, `GamepadAxis` and `MouseCursor`.
- [InputMap](./inputmap): Named actions bound to keys, buttons and axes.
- [Color](./color): The `Color` value type: construct, convert and adjust colors.
- [Geometry](./geometry): The `Vec2`, `Point`, `Vec3`, `Size` and `Rect` value types.
- [Date and Duration](./date): Dates, times, time zones and durations.
- [Ease](./ease): Easing curves for animation and tweens.
- [Ticker](./ticker): Timers and sequences that advance with the app tick.
- [BitmapRect](./bitmaprect): The rectangle `Bitmap.contentBounds` returns.
## Modules
Import what you use, for example `import { Files } from "fs";`. Some modules also need a permission in `thicket.json`; each page says so where it applies.
- [audio](./audio): Sound playback, mixing, music, procedural sound effects and 3D spatial sound.
- [bitmap](./bitmap): Bitmaps: load, draw, composite and save images.
- [compress](./compress): zlib and gzip deflate and inflate, with streaming helpers.
- [crypto](./crypto): Hashing, HMAC, AES, PBKDF2, random bytes and UUIDs.
- [database](./database): Database connections: SQLite, MySQL, PostgreSQL, Redis and MongoDB.
- [env](./env): Environment variables.
- [events](./events): String-keyed event channels with `EventEmitter`.
- [fs](./fs): Files and directories.
- [http](./http): Asynchronous HTTP client and an HTTP server.
- [net](./net): TCP sockets.
- [reactivity](./reactivity): Signals, refs, computed values and reactive collections.
- [shell](./shell): Run child processes.
- [terminal](./terminal): Terminal input, output, cursor control and ANSI styling.
- [udp](./udp): UDP datagram sockets.
- [ws](./ws): WebSocket client and server.
---
URL: https://thicketscript.com/docs/api/async
# Promise
Async results for `async` / `await`.
::: tip Always available
No import needed. These names are in scope in every file.
:::
## Promise
```ts
class Promise
```
*Final: it cannot be subclassed.*
Async result of a deferred computation. State is one of pending,
fulfilled, or rejected; transitions are one-way. Handler chains run
via the microtask queue.
### Promise.then
```ts
then(onFulfilled?: Function, onRejected?: Function): Promise
```
Attach fulfillment and rejection handlers. Returns a new promise
that resolves to the handler's return value (or rejects with a
thrown value). Either handler may be omitted to pass the
settlement through unchanged.
### Promise.catch
```ts
catch(onRejected?: Function): Promise
```
Shorthand for `then(undefined, onRejected)`.
### Promise.finally
```ts
finally(onFinally?: Function): Promise
```
Run `onFinally` after the promise settles, regardless of
outcome. The returned promise mirrors the receiver's state;
`onFinally` cannot observe or alter the resolution value.
### Promise.all
```ts
static all(iterable: Array): Promise>
```
Resolve when every input settles fulfilled, or reject on the
first rejection. Inputs may be promises or plain values; plain
values are wrapped via `Promise.resolve`. Resolves with an array
of fulfilled values in input order.
### Promise.allSettled
```ts
static allSettled(iterable: Array): Promise>
```
Resolve when every input settles, regardless of outcome.
Resolves with an array of `SettledResult` records in input
order.
### Promise.any
```ts
static any(iterable: Array): Promise
```
Resolve on the first input to fulfil, or reject when every
input has rejected. The aggregate rejection is reported as an
AggregateError-shaped string today; the typed
AggregateError class is registered separately.
### Promise.race
```ts
static race(iterable: Array): Promise
```
Resolve or reject as soon as any input settles, mirroring its
outcome. Subsequent settlements are ignored.
### Promise.reject
```ts
static reject(reason?: any): Promise
```
Return a promise rejected with `reason`. Already-rejected
promises pass through unchanged.
### Promise.resolve
```ts
static resolve(value: Promise): Promise
static resolve(value?: T): Promise
```
Return a promise fulfilled with `value`. Already-fulfilled
promises pass through unchanged.
Two signatures, because the result's type argument is the
ARGUMENT's type and a promise argument is flattened rather than
nested. The promise overload is declared first so it wins for a
promise argument; `T` binds from anything else.
**Example**
```ts
const value: string = await Promise.resolve("ready");
console.log(value); // ready
```
## SettledResult
```ts
class SettledResult
```
*Type only: the runtime hands you instances; you never construct it yourself.*
Per-entry record produced by `Promise.allSettled`. `status` is
`"fulfilled"` or `"rejected"`. Fulfilled records carry `value`;
rejected records carry `reason`. The other field is absent.
```ts
readonly status: string
readonly value?: any
readonly reason?: any
```
---
*Generated from `lang/types/builtin_async.d.tks` in the ThicketScript repository.*
---
URL: https://thicketscript.com/docs/api/audio
# audio
Sound playback, mixing, music, procedural sound effects and 3D spatial sound.
::: tip Import
```ts
import { SfxPreset, Wave, Sfx, SoundGroup, ... } from "audio";
```
Needs the `"audio"` permission in `thicket.json`.
:::
## SoundPlayOptions
```ts
class SoundPlayOptions
```
*Type only: the runtime hands you instances; you never construct it yourself.*
Playback options for a SoundChannel.
### SoundPlayOptions.volume
```ts
volume?: float64
```
Volume; 0 is silent, 1 is normal.
### SoundPlayOptions.pitch
```ts
pitch?: float64
```
Speed multiplier; 1.0 is normal speed.
### SoundPlayOptions.pan
```ts
pan?: float64
```
Stereo pan; -1 is left, 0 is center, 1 is right.
### SoundPlayOptions.loop
```ts
loop?: boolean
```
Whether playback repeats when it reaches the end.
### SoundPlayOptions.group
```ts
group?: int
```
Sound group identifier; see `SoundGroup` constants.
## SoundChannel
```ts
class SoundChannel
```
*Type only: the runtime hands you instances; you never construct it yourself.*
### SoundChannel.volume
```ts
volume: float64
```
Playback volume; 0 is silent, 1 is normal.
### SoundChannel.pitch
```ts
pitch: float64
```
Playback speed multiplier; 1.0 is normal speed.
### SoundChannel.pan
```ts
pan: float64
```
Stereo pan; -1 is left, 0 is center, 1 is right.
### SoundChannel.spatialized
```ts
spatialized: boolean
```
Whether this sound uses 3D spatial positioning.
### SoundChannel.position
```ts
position: Vec3 | float64
```
3D position in world space or 2D distance fallback.
### SoundChannel.velocity
```ts
velocity: Vec3
```
3D velocity in world space for Doppler effect.
### SoundChannel.minDistance
```ts
minDistance: float64
```
Closest distance for spatial attenuation falloff in world units.
### SoundChannel.maxDistance
```ts
maxDistance: float64
```
Farthest distance for spatial attenuation falloff in world units.
### SoundChannel.rolloff
```ts
rolloff: float64
```
Exponent for distance attenuation curve; typically 0.0–2.0.
### SoundChannel.attenuation
```ts
attenuation: int
```
Spatial attenuation model; see `Attenuation` constants.
### SoundChannel.position2D
```ts
position2D: float64
```
2D stereo pan when spatialized is false; -1 to 1.
### SoundChannel.isPlaying
```ts
readonly isPlaying: boolean
```
True if the sound is currently playing. Read-only.
### SoundChannel.playbackPosition
```ts
readonly playbackPosition: float64
```
The voice's live decoder cursor in seconds. Zero after it ends or is stolen.
### SoundChannel.stop
```ts
stop(): void
```
Stops playback immediately.
### SoundChannel.pause
```ts
pause(): void
```
Pauses playback; call `resume()` to continue.
### SoundChannel.resume
```ts
resume(): void
```
Resumes playback after `pause()`.
### SoundChannel.onComplete
```ts
onComplete(callback: () => void): void
```
Runs once on the main thread in the frame after natural playback completion.
## Sound
```ts
class Sound
```
*Type only: the runtime hands you instances; you never construct it yourself.*
### Sound.duration
```ts
readonly duration: float64
```
The decoded source length in seconds.
### Sound.play
```ts
play(options?: SoundPlayOptions): SoundChannel
```
Begins playback and returns a SoundChannel for real-time control.
### Sound.unload
```ts
unload(): void
```
Frees decoded audio and invalidates all SoundChannels from this sound.
## MusicTrack
```ts
class MusicTrack
```
*Type only: the runtime hands you instances; you never construct it yourself.*
A streamed music asset returned by Audio.loadMusic(path).
### MusicTrack.unload
```ts
unload(): void
```
Stops playback and frees resources.
## SfxPreset
```ts
static class SfxPreset
```
Named procedural sound-effect recipes.
```ts
static readonly COIN: int
static readonly LASER: int
static readonly EXPLOSION: int
static readonly POWERUP: int
static readonly HIT: int
static readonly JUMP: int
static readonly BLIP: int
static readonly RANDOM: int
```
## Wave
```ts
static class Wave
```
Oscillator wave shapes for Sfx.tone.
```ts
static readonly SINE: int
static readonly SQUARE: int
static readonly TRIANGLE: int
static readonly SAWTOOTH: int
static readonly NOISE: int
```
## Sfx
```ts
static class Sfx
```
Static procedural sound-effect synthesizer.
### Sfx.generate
```ts
static generate(preset: int, seed?: int): Sound
```
Generate a reusable Sound asset from a named preset and optional deterministic seed.
**Parameters**
- `preset`: A SfxPreset constant.
- `seed`: Optional deterministic seed; omitted or 0 for random variation.
**Returns:** A new Sound asset that can be played multiple times.
### Sfx.play
```ts
static play(preset: int, seed?: int): void
```
Play a preset immediately as a one-shot effect.
**Parameters**
- `preset`: A SfxPreset constant.
- `seed`: Optional deterministic seed; omitted or 0 for random variation.
### Sfx.tone
```ts
static tone(frequency: float64, durationMs: int, wave?: int): void
```
Play an oscillator tone for the given duration.
**Parameters**
- `frequency`: Frequency in Hz.
- `durationMs`: Duration in milliseconds.
- `wave`: Optional Wave constant; defaults to SINE.
## AudioListener
```ts
class AudioListener
```
*Type only: the runtime hands you instances; you never construct it yourself.*
```ts
position: Vec3
direction: Vec3
```
## SoundGroup
```ts
static class SoundGroup
```
Top-level sound mix-group constants.
### SoundGroup.Music
```ts
static readonly Music: int
```
::: warning Deprecated
Use `MUSIC`.
:::
### SoundGroup.Voice
```ts
static readonly Voice: int
```
::: warning Deprecated
Use `VOICE`.
:::
### SoundGroup.Ambient
```ts
static readonly Ambient: int
```
::: warning Deprecated
Use `AMBIENT`.
:::
### Other members of SoundGroup
```ts
static readonly SFX: int
static readonly MUSIC: int
static readonly VOICE: int
static readonly AMBIENT: int
static readonly UI: int
```
## Attenuation
```ts
static class Attenuation
```
Top-level spatial attenuation constants.
### Attenuation.None
```ts
static readonly None: int
```
::: warning Deprecated
Use `NONE`.
:::
### Attenuation.InverseDistance
```ts
static readonly InverseDistance: int
```
::: warning Deprecated
Use `INVERSE`.
:::
### Attenuation.LinearDistance
```ts
static readonly LinearDistance: int
```
::: warning Deprecated
Use `LINEAR`.
:::
### Attenuation.ExponentialDistance
```ts
static readonly ExponentialDistance: int
```
::: warning Deprecated
Use `EXPONENTIAL`.
:::
### Other members of Attenuation
```ts
static readonly NONE: int
static readonly INVERSE: int
static readonly LINEAR: int
static readonly EXPONENTIAL: int
```
## Audio
```ts
static class Audio
```
### Audio.SoundGroup
```ts
static readonly SoundGroup: any
```
::: warning Deprecated
Use top-level `SoundGroup`.
:::
### Audio.AttenuationModel
```ts
static readonly AttenuationModel: any
```
::: warning Deprecated
Use top-level `Attenuation`.
:::
### Audio.loadSound
```ts
static loadSound(path: string, group?: int): Promise
static loadSound(buffer: Buffer, group?: int): Promise
```
Loads a sound asset from a project or bundled path.
**Parameters**
- `path`: Project or bundled asset path.
- `group`: Optional sound group; see `SoundGroup` constants.
**Returns:** Promise that resolves to a loaded Sound.
**Example**
```ts
import { Audio, Sound } from "audio";
const click: Sound = await Audio.loadSound("assets/click.wav");
Audio.play(click);
```
### Audio.play
```ts
static play(sound: Sound | int, options?: SoundPlayOptions | float64, pitch?: float64, loop?: boolean): SoundChannel | void
```
Begins playback of a sound or sound channel.
**Parameters**
- `sound`: Sound object or numeric handle.
- `options`: Optional SoundPlayOptions or volume (float64).
- `pitch`: Optional speed multiplier.
- `loop`: Optional boolean for looping playback.
**Returns:** SoundChannel if sound is a Sound object, void if a handle.
### Audio.unloadSound
```ts
static unloadSound(sound: Sound | int): void
```
Stops playback and frees resources for the given sound.
**Parameters**
- `sound`: Sound object or numeric handle.
### Audio.stop
```ts
static stop(sound: Sound | int): void
```
Stops playback of a sound or sound channel.
**Parameters**
- `sound`: Sound object or numeric handle.
### Audio.setSoundVolume
```ts
static setSoundVolume(sound: Sound | int, volume: float64): void
```
Sets the volume of a sound or sound channel.
**Parameters**
- `sound`: Sound object or numeric handle.
- `volume`: Volume; 0 is silent, 1 is normal.
### Audio.soundVolume
```ts
static soundVolume(sound: Sound | int): float64
```
Reads the current volume of a sound or sound channel.
**Parameters**
- `sound`: Sound object or numeric handle.
**Returns:** Current volume in range 0..1.
### Audio.setSoundPitch
```ts
static setSoundPitch(sound: Sound | int, pitch: float64): void
```
Sets the pitch (speed) of a sound or sound channel.
**Parameters**
- `sound`: Sound object or numeric handle.
- `pitch`: Speed multiplier; 1.0 is normal speed.
### Audio.soundPitch
```ts
static soundPitch(sound: Sound | int): float64
```
Reads the current pitch of a sound or sound channel.
**Parameters**
- `sound`: Sound object or numeric handle.
**Returns:** Current pitch multiplier.
### Audio.setSoundPan
```ts
static setSoundPan(sound: Sound | int, pan: float64): void
```
Sets the pan (left/right balance) of a sound or sound channel.
**Parameters**
- `sound`: Sound object or numeric handle.
- `pan`: Stereo pan; -1 is left, 0 is center, 1 is right.
### Audio.soundPan
```ts
static soundPan(sound: Sound | int): float64
```
Reads the current pan of a sound or sound channel.
**Parameters**
- `sound`: Sound object or numeric handle.
**Returns:** Current pan in range -1..1.
### Audio.playPanned
```ts
static playPanned(sound: Sound | int, pan: float64, volume?: float64, pitch?: float64, loop?: boolean): void
```
Plays a sound with a specific pan and volume.
**Parameters**
- `sound`: Sound object or numeric handle.
- `pan`: Stereo pan; -1 is left, 0 is center, 1 is right.
- `volume`: Optional volume; 0 is silent, 1 is normal.
- `pitch`: Optional speed multiplier.
- `loop`: Optional boolean for looping playback.
### Audio.loadMusic
```ts
static loadMusic(path: string): Promise
```
Streams a music asset from a project or bundled path.
**Parameters**
- `path`: Project or bundled asset path.
**Returns:** Promise that resolves to a loaded MusicTrack.
### Audio.unloadMusic
```ts
static unloadMusic(track: MusicTrack | int): void
```
Stops playback and frees resources for the given music track.
**Parameters**
- `track`: MusicTrack object or numeric handle.
### Audio.playMusicHandle
```ts
static playMusicHandle(track: MusicTrack, volume?: float64, loop?: boolean, startSeconds?: float64): void
static playMusicHandle(id: int, volume?: float64, loop?: boolean, startSeconds?: float64): void
```
Begins playback of a music track.
**Parameters**
- `track`: MusicTrack object to play.
- `volume`: Optional volume; 0 is silent, 1 is normal.
- `loop`: Optional boolean for looping playback.
- `startSeconds`: Optional start point in seconds (default 0), e.g. a saved `musicPosition()`.
**Example**
```ts
import { Audio, MusicTrack } from "audio";
const theme: MusicTrack = await Audio.loadMusic("assets/theme.ogg");
Audio.playMusicHandle(theme, 0.8, true);
```
### Audio.crossfadeTo
```ts
static crossfadeTo(track: MusicTrack, durationMs: int, volume?: float64, loop?: boolean): void
static crossfadeTo(id: int, durationMs: int, volume?: float64, loop?: boolean): void
```
Crossfades to a new music track over a specified duration.
**Parameters**
- `track`: MusicTrack object to crossfade to.
- `durationMs`: Crossfade duration in milliseconds.
- `volume`: Optional target volume; 0 is silent, 1 is normal.
- `loop`: Optional boolean for looping playback.
### Audio.stopMusic
```ts
static stopMusic(fadeMs?: int): void
```
Stops music playback with optional fade-out.
**Parameters**
- `fadeMs`: Optional fade-out duration in milliseconds.
### Audio.pauseMusic
```ts
static pauseMusic(): void
```
Pauses music playback.
### Audio.resumeMusic
```ts
static resumeMusic(): void
```
Resumes music playback after `pauseMusic()`.
### Audio.musicPlaying
```ts
static musicPlaying(): boolean
```
Returns true if music is currently playing.
### Audio.activeMusic
```ts
static activeMusic(): int
```
Returns the numeric handle of the currently playing track, or 0 if none.
### Audio.musicPosition
```ts
static musicPosition(): float64
```
Playback position of the active music track in seconds (playing or paused), or 0 if none.
### Audio.masterVolume
```ts
static masterVolume: float64
```
Master mixer gain, clamped to 0..1.
### Audio.sfxVolume
```ts
static sfxVolume: float64
```
Sound-effects mix-group gain, clamped to 0..1.
### Audio.musicVolume
```ts
static musicVolume: float64
```
Music mix-group gain, clamped to 0..1.
### Audio.listener
```ts
static readonly listener: AudioListener
```
The 3D listener's position and orientation for spatial audio. Read-only.
### Audio.setMasterVolume
```ts
static setMasterVolume(volume: float64): void
```
::: warning Deprecated
Assign masterVolume.
:::
### Audio.setSfxVolume
```ts
static setSfxVolume(volume: float64): void
```
::: warning Deprecated
Assign sfxVolume.
:::
### Audio.setMusicVolume
```ts
static setMusicVolume(volume: float64): void
```
::: warning Deprecated
Assign musicVolume.
:::
### Audio.setGroupVolume
```ts
static setGroupVolume(group: int, volume: float64): void
```
Sets the volume for a sound group.
**Parameters**
- `group`: A SoundGroup constant.
- `volume`: Volume; 0 is silent, 1 is normal.
### Audio.groupVolume
```ts
static groupVolume(group: int): float64
```
Reads the volume for a sound group.
**Parameters**
- `group`: A SoundGroup constant.
**Returns:** Current volume in range 0..1.
### Audio.setListenerPosition
```ts
static setListenerPosition(x: float64, y: float64, z: float64): void
```
Sets the 3D listener position for spatial audio.
**Parameters**
- `x`: World x coordinate.
- `y`: World y coordinate.
- `z`: World z coordinate.
### Audio.setListenerDirection
```ts
static setListenerDirection(fx: float64, fy: float64, fz: float64, ux?: float64, uy?: float64, uz?: float64): void
```
Sets the 3D listener orientation for spatial audio.
**Parameters**
- `fx`: Forward vector x component.
- `fy`: Forward vector y component.
- `fz`: Forward vector z component.
- `ux`: Optional up vector x; defaults to 0.
- `uy`: Optional up vector y; defaults to 1.
- `uz`: Optional up vector z; defaults to 0.
### Audio.listenerPosition
```ts
static listenerPosition(): Vec3
```
Reads the 3D listener position.
**Returns:** Current listener position as Vec3.
### Audio.setSoundPosition
```ts
static setSoundPosition(sound: Sound | int, x: float64, y: float64, z: float64): void
```
Sets the 3D world position of a sound.
**Parameters**
- `sound`: Sound object or numeric handle.
- `x`: World x coordinate.
- `y`: World y coordinate.
- `z`: World z coordinate.
### Audio.setSoundSpatialized
```ts
static setSoundSpatialized(sound: Sound | int, enabled: boolean): void
```
Enables or disables 3D spatial positioning for a sound.
**Parameters**
- `sound`: Sound object or numeric handle.
- `enabled`: True to use 3D positioning, false for 2D panning only.
### Audio.setSoundDistanceRange
```ts
static setSoundDistanceRange(sound: Sound | int, minDist: float64, maxDist: float64): void
```
Sets the distance range for spatial attenuation.
**Parameters**
- `sound`: Sound object or numeric handle.
- `minDist`: Minimum distance in world units; no attenuation closer than this.
- `maxDist`: Maximum distance in world units; silent beyond this.
### Audio.setSoundRolloff
```ts
static setSoundRolloff(sound: Sound | int, rolloff: float64): void
```
Sets the rolloff factor for distance attenuation.
**Parameters**
- `sound`: Sound object or numeric handle.
- `rolloff`: Rolloff exponent; typical range 0.0–2.0.
### Audio.soundRolloff
```ts
static soundRolloff(sound: Sound | int): float64
```
Reads the rolloff factor of a sound.
**Parameters**
- `sound`: Sound object or numeric handle.
**Returns:** Current rolloff exponent.
### Audio.setSoundDopplerFactor
```ts
static setSoundDopplerFactor(sound: Sound | int, factor: float64): void
```
Sets the Doppler effect factor for a sound.
**Parameters**
- `sound`: Sound object or numeric handle.
- `factor`: Doppler factor; 1.0 is realistic, 0.0 disables the effect.
### Audio.soundDopplerFactor
```ts
static soundDopplerFactor(sound: Sound | int): float64
```
Reads the Doppler effect factor of a sound.
**Parameters**
- `sound`: Sound object or numeric handle.
**Returns:** Current Doppler factor.
### Audio.setSoundVelocity
```ts
static setSoundVelocity(sound: Sound | int, x: float64, y: float64, z: float64): void
```
Sets the 3D velocity of a sound for Doppler effects.
**Parameters**
- `sound`: Sound object or numeric handle.
- `x`: Velocity x component.
- `y`: Velocity y component.
- `z`: Velocity z component.
### Audio.setSoundAttenuationModel
```ts
static setSoundAttenuationModel(sound: Sound | int, model: int): void
```
Sets the spatial attenuation model for a sound.
**Parameters**
- `sound`: Sound object or numeric handle.
- `model`: An Attenuation constant.
### Audio.soundAttenuationModel
```ts
static soundAttenuationModel(sound: Sound | int): int
```
Reads the spatial attenuation model of a sound.
**Parameters**
- `sound`: Sound object or numeric handle.
**Returns:** Current Attenuation constant.
### Audio.playSpatial
```ts
static playSpatial(sound: Sound | int, x: float64, y: float64, z: float64, minDist?: float64, maxDist?: float64, volume?: float64, pitch?: float64, loop?: boolean): void
```
Plays a sound at a 3D position with optional attenuation.
**Parameters**
- `sound`: Sound object or numeric handle.
- `x`: World x coordinate.
- `y`: World y coordinate.
- `z`: World z coordinate.
- `minDist`: Optional minimum distance for attenuation.
- `maxDist`: Optional maximum distance for attenuation.
- `volume`: Optional volume; 0 is silent, 1 is normal.
- `pitch`: Optional speed multiplier.
- `loop`: Optional boolean for looping playback.
## Music
```ts
static class Music
```
Global background-music playback. Audio's music methods remain aliases.
### Music.volume
```ts
static volume: float64
```
Music volume; 0 is silent, 1 is normal.
### Music.isPlaying
```ts
static readonly isPlaying: boolean
```
True if music is currently playing. Read-only.
### Music.current
```ts
static readonly current: int
```
Numeric handle of the currently playing track, or 0 if none. Read-only.
### Music.play
```ts
static play(track: int | MusicTrack): void
```
Begins playback of a music track.
**Parameters**
- `track`: MusicTrack object or numeric handle.
### Music.crossfade
```ts
static crossfade(track: int | MusicTrack, ms: int): void
```
Crossfades to a new music track over a specified duration.
**Parameters**
- `track`: MusicTrack object or numeric handle to crossfade to.
- `ms`: Crossfade duration in milliseconds.
### Music.pause
```ts
static pause(): void
```
Pauses music playback.
### Music.resume
```ts
static resume(): void
```
Resumes music playback after `pause()`.
### Music.stop
```ts
static stop(fadeMs?: int): void
```
Stops music playback with optional fade-out.
**Parameters**
- `fadeMs`: Optional fade-out duration in milliseconds.
## audio
```ts
const audio: any
```
::: warning Deprecated
Use `Audio`.
:::
---
*Generated from `lang/types/audio.d.tks` in the ThicketScript repository.*
---
URL: https://thicketscript.com/docs/api/bitmap
# bitmap
Bitmaps: load, draw, composite and save images.
::: tip Import
```ts
import { BitmapFormat, BlendMode, ScaleFilter, LineCap, ... } from "bitmap";
```
:::
## BitmapFormat
```ts
enum BitmapFormat
```
Pixel format identifier for `Bitmap` allocation and conversion.
- `BitmapFormat.RGB8`: 24-bit RGB, no alpha. 3 bytes per pixel.
- `BitmapFormat.RGBA8`: 32-bit RGBA. 4 bytes per pixel. The default.
- `BitmapFormat.RGB16`: 48-bit RGB, no alpha. 6 bytes per pixel.
- `BitmapFormat.RGBA16`: 64-bit RGBA. 8 bytes per pixel.
## BlendMode
```ts
enum BlendMode
```
Compositing operator for `Bitmap.composite`, `DrawContext.setBlendMode`, and `Bitmap.drawScaled`.
- `BlendMode.REPLACE`: Direct overwrite. Source replaces destination unconditionally.
- `BlendMode.ALPHA`: Standard alpha compositing. Source alpha blends over destination.
- `BlendMode.ADDITIVE`: Add channels: dest + src. Saturates at 255.
- `BlendMode.MULTIPLY`: Multiply channels: dest * src / 255.
- `BlendMode.SCREEN`: Inverse multiply: 255 - ((255-dest) * (255-src) / 255).
- `BlendMode.OVERLAY`: Combination of multiply and screen depending on dest brightness.
- `BlendMode.DARKEN`: Per-channel `min(dest, src)`.
- `BlendMode.LIGHTEN`: Per-channel `max(dest, src)`.
- `BlendMode.COLOR_DODGE`: Brighten dest by src; high src values produce extreme highlights.
- `BlendMode.COLOR_BURN`: Darken dest by src; high src values produce deep shadows.
- `BlendMode.DIFFERENCE`: Absolute difference between channels.
- `BlendMode.EXCLUSION`: Like difference but lower contrast.
## ScaleFilter
```ts
enum ScaleFilter
```
Sampling filter for `Bitmap.resize` and `Bitmap.drawScaled`.
- `ScaleFilter.NEAREST`: Nearest-neighbour. Fastest, blocky output, preserves hard edges.
- `ScaleFilter.BILINEAR`: Bilinear interpolation. Smooth but soft. The default.
- `ScaleFilter.BICUBIC`: Bicubic interpolation. Sharpest of the smooth filters, slowest.
## LineCap
```ts
enum LineCap
```
Line-end style for `DrawContext.setLineCap`.
- `LineCap.BUTT`: Flat end at the line endpoint. The default.
- `LineCap.ROUND`: Semicircular cap extending past the endpoint by half the line width.
- `LineCap.SQUARE`: Square cap extending past the endpoint by half the line width.
## LineJoin
```ts
enum LineJoin
```
Line-join style for `DrawContext.setLineJoin`.
- `LineJoin.MITER`: Sharp mitered corner. The default.
- `LineJoin.ROUND`: Rounded corner with radius equal to half the line width.
- `LineJoin.BEVEL`: Beveled corner: a straight edge between the two segments.
## TextAlign
```ts
enum TextAlign
```
Horizontal text alignment for `DrawContext.setTextAlign`.
- `TextAlign.LEFT`: Anchor on the left edge of the text bounding box. The default.
- `TextAlign.CENTER`: Anchor on the horizontal center.
- `TextAlign.RIGHT`: Anchor on the right edge.
## TextBaseline
```ts
enum TextBaseline
```
Vertical text alignment for `DrawContext.setTextBaseline`.
- `TextBaseline.ALPHABETIC`: Anchor on the alphabetic baseline. The default.
- `TextBaseline.TOP`: Anchor on the top of the text bounding box.
- `TextBaseline.MIDDLE`: Anchor on the vertical center of the text bounding box.
- `TextBaseline.BOTTOM`: Anchor on the bottom of the text bounding box.
## ImageFormat
```ts
enum ImageFormat
```
File format for `Bitmap.encode`.
- `ImageFormat.PNG`: Portable Network Graphics. Lossless.
- `ImageFormat.JPEG`: JPEG. Lossy, no alpha channel.
- `ImageFormat.BMP`: Windows BMP. Lossless, uncompressed.
- `ImageFormat.TGA`: Truevision TGA. Lossless.
- `ImageFormat.WEBP`: WebP. Lossy or lossless, smaller than PNG/JPEG.
## TextMetrics
```ts
class TextMetrics
```
Glyph metrics for a string under the active font, returned by `DrawContext.measureText`.
### TextMetrics.width
```ts
readonly width: float
```
Advance width: total horizontal space the text occupies.
### TextMetrics.height
```ts
readonly height: float
```
Total vertical extent (ascent + descent).
### TextMetrics.ascent
```ts
readonly ascent: float
```
Distance from the baseline to the highest glyph point.
### TextMetrics.descent
```ts
readonly descent: float
```
Distance from the baseline to the lowest glyph point.
### TextMetrics.leftBearing
```ts
readonly leftBearing: float
```
Horizontal padding to the left of the first glyph's ink.
### TextMetrics.rightBearing
```ts
readonly rightBearing: float
```
Horizontal padding to the right of the last glyph's ink.
## Bitmap
```ts
class Bitmap
```
2D pixel buffer. Owns its memory; pixel data is freed when the
Bitmap is garbage-collected, or immediately when `dispose()` is
called.
Pixels are stored row-major, top-left origin. Colors are 32-bit
`Color` values (one packed `0xRRGGBBAA` uint32) — use the `Color` helpers to build
them.
**Example**
```ts
import { Bitmap } from "bitmap";
const bmp: Bitmap = new Bitmap(320, 240);
bmp.fill(Color.rgb(0, 0, 0));
bmp.fillRect(10, 10, 50, 50, Color.rgb(255, 0, 0));
```
### Bitmap.width
```ts
readonly width: int
```
Width in pixels. Read-only — set at construction.
### Bitmap.height
```ts
readonly height: int
```
Height in pixels. Read-only — set at construction.
### Bitmap.format
```ts
readonly format: string
```
Pixel format identifier. One of `BitmapFormat`'s string values.
### Bitmap.channels
```ts
readonly channels: int
```
Number of color channels in the active format (3 for RGB, 4 for RGBA).
### Bitmap.byteSize
```ts
readonly byteSize: int
```
Total size of the pixel buffer in bytes.
### new Bitmap()
```ts
constructor(width: int, height: int)
constructor(width: int, height: int, format: BitmapFormat)
```
Allocate a new bitmap of `width` x `height` pixels in `RGBA8`
format. Pixel contents are uninitialized — call `fill()` before
reading.
### Bitmap.dispose
```ts
dispose(): void
```
Free the pixel buffer immediately. Any further operation on
this bitmap signals an error. Idempotent — safe to call more
than once.
### Bitmap.getPixel
```ts
getPixel(x: int, y: int): Color
```
Read the pixel at `(x, y)` as a packed `0xRRGGBBAA` integer.
Out-of-bounds coordinates return zero rather than erroring.
### Bitmap.setPixel
```ts
setPixel(x: int, y: int, color: Color): void
```
Write `color` (packed `0xRRGGBBAA`) to the pixel at `(x, y)`.
Out-of-bounds coordinates are silently ignored.
### Bitmap.getPixelPacked
```ts
getPixelPacked(x: int, y: int): int
```
Read the pixel at `(x, y)` in the bitmap's native packed layout.
The exact byte order depends on `format`; intended for fast paths
that round-trip through `setPixelPacked` rather than mixing with
`Color` helpers.
### Bitmap.setPixelPacked
```ts
setPixelPacked(x: int, y: int, packed: int): void
```
Write `packed` (native layout) to the pixel at `(x, y)`. Pair
with `getPixelPacked` for format-preserving copies.
### Bitmap.fill
```ts
fill(color: Color): void
```
Fill every pixel with `color` (packed `0xRRGGBBAA`).
### Bitmap.fillRect
```ts
fillRect(x: int, y: int, w: int, h: int, color: Color): void
```
Fill the axis-aligned rectangle `(x, y, w, h)` with `color`.
### Bitmap.drawLine
```ts
drawLine(x0: int, y0: int, x1: int, y1: int, color: Color): void
```
Draw a 1-pixel line from `(x0, y0)` to `(x1, y1)` in `color`.
### Bitmap.drawRect
```ts
drawRect(x: int, y: int, w: int, h: int, color: Color): void
```
Draw the outline of the rectangle `(x, y, w, h)` in `color`.
### Bitmap.drawCircle
```ts
drawCircle(cx: int, cy: int, r: int, color: Color): void
```
Draw the outline of a circle centered at `(cx, cy)` with radius `r`.
### Bitmap.fillCircle
```ts
fillCircle(cx: int, cy: int, r: int, color: Color): void
```
Fill the circle centered at `(cx, cy)` with radius `r`.
### Bitmap.composite
```ts
composite(src: Bitmap, destX: int, destY: int, blendMode?: BlendMode): void
```
Composite `src` onto this bitmap with the top-left corner of
`src` at `(destX, destY)`. Defaults to `BlendMode.ALPHA`.
### Bitmap.blit
```ts
blit(src: Bitmap, destX: int, destY: int): void
```
Direct copy of `src` onto this bitmap with the top-left corner
at `(destX, destY)`. Faster than `composite` because it skips
blending — pixels are overwritten.
### Bitmap.drawScaled
```ts
drawScaled(src: Bitmap, destX: int, destY: int, destW: int, destH: int, filter: ScaleFilter, blendMode?: BlendMode): void
```
Scale `src` to `(destW, destH)` and composite at `(destX, destY)`.
`blendMode` defaults to `BlendMode.ALPHA`.
### Bitmap.blur
```ts
blur(radius?: int): void
```
Box blur with the given pixel `radius`. Defaults to `1`.
### Bitmap.gaussianBlur
```ts
gaussianBlur(sigma?: float): void
```
Gaussian blur with the given `sigma`. Defaults to `1.0`.
### Bitmap.sharpen
```ts
sharpen(amount?: float): void
```
Unsharp-mask sharpen by `amount` (1.0 = neutral). Defaults to `1.0`.
### Bitmap.grayscale
```ts
grayscale(): void
```
Convert all pixels to luma-weighted grayscale, in place.
### Bitmap.invert
```ts
invert(): void
```
Invert R, G, B channels, in place. Alpha is preserved.
### Bitmap.sepia
```ts
sepia(intensity?: float): void
```
Sepia tone, in place. `intensity` blends between original and
full sepia (0.0 = none, 1.0 = full). Defaults to `1.0`.
### Bitmap.adjustBrightness
```ts
adjustBrightness(delta: float): void
```
Add `delta` (-1.0 to 1.0) to every channel.
### Bitmap.adjustContrast
```ts
adjustContrast(factor: float): void
```
Multiply contrast around mid-gray by `factor` (1.0 = unchanged).
### Bitmap.adjustHue
```ts
adjustHue(degrees: float): void
```
Rotate hue by `degrees` (-360 to 360).
### Bitmap.adjustSaturation
```ts
adjustSaturation(factor: float): void
```
Multiply saturation by `factor` (0.0 = grayscale, 1.0 = unchanged).
### Bitmap.adjustOpacity
```ts
adjustOpacity(factor: float): void
```
Multiply alpha by `factor` (0.0 = transparent, 1.0 = unchanged).
### Bitmap.pixelate
```ts
pixelate(size?: int): void
```
Quantize into `size`-pixel blocks for a mosaic effect. Defaults to `4`.
### Bitmap.tint
```ts
tint(color: Color): void
```
Multiply every pixel by `color`. Preserves alpha.
### Bitmap.replaceColor
```ts
replaceColor(from: Color, to: Color, tolerance?: int): void
```
Replace every pixel within `tolerance` of `from` with `to`.
`tolerance` is in 0-255 distance per channel; `0` means exact
match.
### Bitmap.posterize
```ts
posterize(levels?: int): void
```
Posterize each channel to `levels` discrete values. Defaults to `4`.
### Bitmap.threshold
```ts
threshold(level?: float): void
```
Binarize: pixels with luma >= `level` (0.0 to 1.0) become white,
others black. Defaults to `0.5`.
### Bitmap.applyMask
```ts
applyMask(mask: Bitmap): void
```
Multiply this bitmap's alpha channel by `mask`'s luma, pixel by
pixel. `mask` must match this bitmap's dimensions.
### Bitmap.crop
```ts
crop(x: int, y: int, w: int, h: int): Bitmap
```
Return a new bitmap containing the rectangle `(x, y, w, h)`.
### Bitmap.resize
```ts
resize(w: int, h: int, filter?: ScaleFilter): Bitmap
```
Return a new bitmap resized to `(w, h)`. `filter` defaults to
`ScaleFilter.BILINEAR`.
### Bitmap.flipH
```ts
flipH(): Bitmap
```
Return a new bitmap mirrored horizontally.
### Bitmap.flipV
```ts
flipV(): Bitmap
```
Return a new bitmap mirrored vertically.
### Bitmap.rotate90
```ts
rotate90(): Bitmap
```
Return a new bitmap rotated 90° clockwise.
### Bitmap.rotate180
```ts
rotate180(): Bitmap
```
Return a new bitmap rotated 180°.
### Bitmap.rotate270
```ts
rotate270(): Bitmap
```
Return a new bitmap rotated 270° clockwise (90° counter-clockwise).
### Bitmap.rotate
```ts
rotate(degrees: float, bgColor?: Color): Bitmap
```
Return a new bitmap rotated by an arbitrary angle. The output is
sized to fit the rotated source; new corners are filled with
`bgColor` (default fully transparent).
### Bitmap.trim
```ts
trim(bg?: Color): Bitmap
```
Return a new bitmap with edges of color `bg` (default fully
transparent) trimmed away.
### Bitmap.pad
```ts
pad(top: int, right: int, bottom: int, left: int, fill?: Color): Bitmap
```
Return a new bitmap padded by the given margins, filling new
pixels with `fill` (default fully transparent).
### Bitmap.convert
```ts
convert(format: BitmapFormat): Bitmap
```
Return a new bitmap converted to `format`.
### Bitmap.equals
```ts
equals(other: Bitmap): boolean
```
True when `other` has identical dimensions, format, and pixels.
### Bitmap.diff
```ts
diff(other: Bitmap): Bitmap
```
Return a new bitmap whose pixels are the absolute difference
between this and `other`, channel-by-channel.
### Bitmap.clone
```ts
clone(): Bitmap
```
Return an independent copy of this bitmap.
### Bitmap.encode
```ts
encode(format: ImageFormat): Buffer
```
Encode the bitmap to a Buffer in the given file `format`.
Pair with `Files.writeFile` to save to disk.
### Bitmap.context
```ts
context(): DrawContext
```
Open a Canvas2D-style drawing context against this bitmap. The
returned `DrawContext` keeps this bitmap alive for as long as it
is referenced.
### Bitmap.scan
```ts
scan(fn?: Function): Array<[int, int, Color]>
```
Return every pixel as `[x, y, color]` tuples.
Bitmaps larger than 4 megapixels are rejected — use
`getRegion()` to slice subsets first.
**Parameters**
- `fn`: (NYI) When provided, will be called for each pixel as `fn(x: int, y: int, color: Color): void` and the array return becomes empty. Until the C++ wires the callback path, `fn` is ignored and the method behaves identically to the no-arg form. Future-proofed as optional so the callback shape can land without a breaking change.
### Bitmap.pixels
```ts
pixels(): Array
```
Return every pixel color as a flat `Array`, row-major.
Subject to the same 4-megapixel cap as `scan()`.
### Bitmap.region
```ts
region(x: int, y: int, w: int, h: int): Array
```
Return the colors of the pixels inside `(x, y, w, h)` as a flat
`Array`, row-major. Out-of-bounds portions are silently
dropped.
### Bitmap.edgeDetect
```ts
edgeDetect(): Bitmap
```
Return a new bitmap containing only edge pixels (Sobel-style).
### Bitmap.emboss
```ts
emboss(angle?: float, strength?: float): void
```
In-place emboss effect.
`angle` is the light direction in degrees (default `135`).
`strength` scales the effect (default `1.0`).
### Bitmap.getRegion
```ts
getRegion(x: int, y: int, w: int, h: int): Bitmap
```
Return a new bitmap containing the rectangle `(x, y, w, h)`.
### Bitmap.putRegion
```ts
putRegion(src: Bitmap, x: int, y: int): void
```
Paste `src` into this bitmap with its top-left at `(x, y)`. Direct copy, no blending.
### Bitmap.contentBounds
```ts
contentBounds(threshold?: int): BitmapRect
```
Return the smallest `BitmapRect` enclosing pixels above
`threshold` alpha (default `0`).
### Bitmap.fromBuffer
```ts
static fromBuffer(buffer: Buffer): Bitmap
```
Decode an image Buffer into a new Bitmap. Format is detected
from the buffer header (PNG, JPEG, BMP, TGA, WebP).
**Example**
```ts
import { Bitmap } from "bitmap";
import { Files } from "fs";
const png: any = await Files.readFile("sprite.png");
const image: Bitmap = Bitmap.fromBuffer(png);
console.log(image.width, image.height);
```
## DrawContext
```ts
class DrawContext
```
Canvas2D-style drawing context bound to a Bitmap. Obtained via
`bmp.context()`. State is layered: `save()` pushes the current
style/clip/transform; `restore()` pops it.
Floating-point coordinates are accepted and rasterized with
antialiasing when enabled (`setAntialias(true)`, the default).
### DrawContext.save
```ts
save(): void
```
Push the current drawing state (style, clip, transform) onto the stack.
### DrawContext.restore
```ts
restore(): void
```
Pop the most recently saved drawing state.
### DrawContext.reset
```ts
reset(): void
```
Reset all drawing state to defaults and clear the state stack.
### DrawContext.setFillColor
```ts
setFillColor(color: Color): void
```
Set the fill color used by `fill*` methods. Packed `0xRRGGBBAA`.
### DrawContext.setStrokeColor
```ts
setStrokeColor(color: Color): void
```
Set the stroke color used by `stroke*` methods. Packed `0xRRGGBBAA`.
### DrawContext.setLineWidth
```ts
setLineWidth(width: float): void
```
Set the stroke line width in pixels.
### DrawContext.setLineCap
```ts
setLineCap(cap: LineCap): void
```
Set the line end style.
### DrawContext.setLineJoin
```ts
setLineJoin(join: LineJoin): void
```
Set the line join style.
### DrawContext.setGlobalAlpha
```ts
setGlobalAlpha(alpha: float): void
```
Multiply all subsequent draws by `alpha` (0.0 to 1.0).
### DrawContext.setBlendMode
```ts
setBlendMode(mode: BlendMode): void
```
Set the blend mode for subsequent draws.
### DrawContext.setAntialias
```ts
setAntialias(enabled: boolean): void
```
Toggle antialiasing for subsequent draws. Defaults to enabled.
### DrawContext.clearRect
```ts
clearRect(x: float, y: float, w: float, h: float): void
```
Clear `(x, y, w, h)` to fully transparent.
### DrawContext.fillRect
```ts
fillRect(x: float, y: float, w: float, h: float): void
```
Fill the rectangle `(x, y, w, h)` with the current fill color.
### DrawContext.strokeRect
```ts
strokeRect(x: float, y: float, w: float, h: float): void
```
Stroke the outline of the rectangle `(x, y, w, h)`.
### DrawContext.fillCircle
```ts
fillCircle(cx: float, cy: float, r: float): void
```
Fill the circle centered at `(cx, cy)` with radius `r`.
### DrawContext.strokeCircle
```ts
strokeCircle(cx: float, cy: float, r: float): void
```
Stroke the outline of the circle centered at `(cx, cy)` with radius `r`.
### DrawContext.drawLine
```ts
drawLine(x0: float, y0: float, x1: float, y1: float): void
```
Stroke a line from `(x0, y0)` to `(x1, y1)`.
### DrawContext.fillRoundRect
```ts
fillRoundRect(x: float, y: float, w: float, h: float, radius: float): void
```
Fill the rounded rectangle `(x, y, w, h)` with corner `radius`.
### DrawContext.strokeRoundRect
```ts
strokeRoundRect(x: float, y: float, w: float, h: float, radius: float): void
```
Stroke the rounded rectangle `(x, y, w, h)` with corner `radius`.
### DrawContext.fillEllipse
```ts
fillEllipse(cx: float, cy: float, rx: float, ry: float): void
```
Fill the ellipse centered at `(cx, cy)` with radii `(rx, ry)`.
### DrawContext.strokeEllipse
```ts
strokeEllipse(cx: float, cy: float, rx: float, ry: float): void
```
Stroke the outline of the ellipse centered at `(cx, cy)` with radii `(rx, ry)`.
### DrawContext.setFontFromSystem
```ts
setFontFromSystem(name: string, size: float): void
```
Load a font by system family name (e.g. `"Helvetica"`,
`"Arial"`) at the given pixel `size` and use it for subsequent
`fillText` / `strokeText` / `measureText`.
The font is cached per-context by name.
### DrawContext.setFontFromPath
```ts
setFontFromPath(path: string, size: float): void
```
Load a font from a `.ttf` / `.otf` file at `path`, at the given
pixel `size`. Cached per-context by path.
### DrawContext.setFontFromBuffer
```ts
setFontFromBuffer(buffer: Buffer, size: float): void
```
Load a font from an in-memory Buffer at the given pixel `size`.
The Buffer must remain valid for the lifetime of the context.
### DrawContext.setTextAlign
```ts
setTextAlign(align: TextAlign): void
```
Set horizontal alignment for `fillText` / `strokeText`.
### DrawContext.setTextBaseline
```ts
setTextBaseline(baseline: TextBaseline): void
```
Set vertical alignment.
### DrawContext.fillText
```ts
fillText(text: string, x: float, y: float): void
```
Fill `text` at `(x, y)` using the current font and fill color.
### DrawContext.strokeText
```ts
strokeText(text: string, x: float, y: float): void
```
Stroke the outline of `text` at `(x, y)` using the current font and stroke color.
### DrawContext.measureText
```ts
measureText(text: string): TextMetrics
```
Measure `text` with the current font, returning a `TextMetrics`.
### DrawContext.beginPath
```ts
beginPath(): void
```
Start a new path, discarding any pending sub-paths.
### DrawContext.closePath
```ts
closePath(): void
```
Close the current sub-path with a straight line to its starting point.
### DrawContext.moveTo
```ts
moveTo(x: float, y: float): void
```
Move the path cursor to `(x, y)` without drawing.
### DrawContext.lineTo
```ts
lineTo(x: float, y: float): void
```
Add a straight line from the current cursor to `(x, y)`.
### DrawContext.arc
```ts
arc(cx: float, cy: float, r: float, startAngle: float, endAngle: float, ccw?: boolean): void
```
Add a circular arc centered at `(cx, cy)` of radius `r`, from
`startAngle` to `endAngle` (radians). When `ccw` is `true` the
arc is drawn counter-clockwise.
### DrawContext.arcTo
```ts
arcTo(x1: float, y1: float, x2: float, y2: float, radius: float): void
```
Add an arc tangent to the line from the cursor to `(x1, y1)` and
the line from `(x1, y1)` to `(x2, y2)`, with the given `radius`.
### DrawContext.bezierTo
```ts
bezierTo(cp1x: float, cp1y: float, cp2x: float, cp2y: float, x: float, y: float): void
```
Add a cubic Bézier curve to `(x, y)` with control points `(cp1x, cp1y)` and `(cp2x, cp2y)`.
### DrawContext.quadraticTo
```ts
quadraticTo(cpx: float, cpy: float, x: float, y: float): void
```
Add a quadratic Bézier curve to `(x, y)` with control point `(cpx, cpy)`.
### DrawContext.rect
```ts
rect(x: float, y: float, w: float, h: float): void
```
Add a closed rectangle sub-path.
### DrawContext.fill
```ts
fill(): void
```
Fill the current path with the current fill color.
### DrawContext.stroke
```ts
stroke(): void
```
Stroke the current path with the current stroke color and line settings.
### DrawContext.clip
```ts
clip(): void
```
Restrict subsequent drawing to the current path.
### DrawContext.resetClip
```ts
resetClip(): void
```
Remove any active clip region.
### DrawContext.floodFill
```ts
floodFill(x: int, y: int, tolerance?: float): void
```
Fill connected pixels reachable from `(x, y)` whose color
matches the seed within `tolerance` (0.0 to 1.0, default `0`).
### DrawContext.isPointInPath
```ts
isPointInPath(x: float, y: float): boolean
```
True when `(x, y)` is inside the current path's filled region.
### DrawContext.isPointInStroke
```ts
isPointInStroke(x: float, y: float): boolean
```
True when `(x, y)` is inside the current path's stroked outline.
---
*Generated from `lang/types/bitmap.d.tks` in the ThicketScript repository.*
---
URL: https://thicketscript.com/docs/api/bitmaprect
# BitmapRect
The rectangle `Bitmap.contentBounds` returns.
::: tip Always available
No import needed. These names are in scope in every file.
:::
## BitmapRect
```ts
struct BitmapRect
```
Axis-aligned bounding rectangle in whole pixels, returned by `Bitmap.contentBounds`.
### BitmapRect.x
```ts
x: int
```
Left edge in pixels.
### BitmapRect.y
```ts
y: int
```
Top edge in pixels.
### BitmapRect.w
```ts
w: int
```
Width in pixels.
### BitmapRect.h
```ts
h: int
```
Height in pixels.
### Other members of BitmapRect
```ts
operator eq(other: BitmapRect): boolean
toString(): string
```
---
*Generated from `lang/bitmaprect/index.tks` in the ThicketScript repository.*
---
URL: https://thicketscript.com/docs/api/buffer
# Buffers and typed arrays
`ByteArray`, `Buffer`, the typed arrays and their encodings.
::: tip Always available
No import needed. These names are in scope in every file.
:::
## TextEncoding
```ts
enum TextEncoding
```
Text encoding accepted by `ByteArray.from(string, ...)`,
`toString`, `readString` and `writeString`. The runtime rejects
unknown encodings and non-string arguments at the call site.
- `TextEncoding.UTF8`: UTF-8. The default for both decode and encode.
- `TextEncoding.ASCII`: 7-bit ASCII; bytes above 127 decode to `?`.
- `TextEncoding.LATIN1`: ISO-8859-1; one byte per code point, no transformation.
- `TextEncoding.HEX`: Lowercase hexadecimal; two characters per byte.
- `TextEncoding.BASE64`: Standard base64.
## BufferEncoding
```ts
enum BufferEncoding
```
`Buffer`'s old name for `TextEncoding`; the same enum.
- `BufferEncoding.UTF8`: UTF-8. The default for both decode and encode.
- `BufferEncoding.ASCII`: 7-bit ASCII; bytes above 127 decode to `?`.
- `BufferEncoding.LATIN1`: ISO-8859-1; one byte per code point, no transformation.
- `BufferEncoding.HEX`: Lowercase hexadecimal; two characters per byte.
- `BufferEncoding.BASE64`: Standard base64.
## Endian
```ts
enum Endian
```
Byte order of a `ByteArray`'s multi-byte reads and writes.
- `Endian.LITTLE`: Least significant byte first. The default; matches the machines and the GPU.
- `Endian.BIG`: Most significant byte first.
## ByteArray
```ts
class ByteArray
```
A growable sequence of bytes, held by reference: everything holding it
sees the same bytes. Streamed `read*`/`write*` move `position`;
`get*`/`set*` take a byte offset and leave it alone. A read past the end
and an offset out of range are `RangeError`s; a write past the end grows
the array.
### new ByteArray()
```ts
constructor(size?: int)
```
`size` zero bytes (default 0).
### ByteArray.length
```ts
length: int
```
Length in bytes. Setting it grows (zero-filled) or truncates.
### ByteArray.byteLength
```ts
readonly byteLength: int
```
`Buffer`'s spelling of `length`. Read-only.
### ByteArray.position
```ts
position: int
```
The cursor for the streamed `read*`/`write*` methods.
### ByteArray.bytesAvailable
```ts
readonly bytesAvailable: int
```
`length - position`.
### ByteArray.endian
```ts
endian: Endian
```
Byte order for multi-byte values. `Endian.LITTLE` by default.
### ByteArray.readInt8
```ts
readInt8(): int
```
Read a signed 8-bit integer at `position`; advances `position`. @returns The value.
### ByteArray.readUInt8
```ts
readUInt8(): uint
```
Read an unsigned 8-bit integer at `position`; advances `position`. @returns The value.
### ByteArray.readInt16
```ts
readInt16(): int
```
Read a signed 16-bit integer at `position` in the set byte order; advances `position`. @returns The value.
### ByteArray.readUInt16
```ts
readUInt16(): uint
```
Read an unsigned 16-bit integer at `position` in the set byte order; advances `position`. @returns The value.
### ByteArray.readInt32
```ts
readInt32(): int
```
Read a signed 32-bit integer at `position` in the set byte order; advances `position`. @returns The value.
### ByteArray.readUInt32
```ts
readUInt32(): uint
```
Read an unsigned 32-bit integer at `position` in the set byte order; advances `position`. @returns The value.
### ByteArray.readFloat32
```ts
readFloat32(): float
```
Read a 32-bit float at `position` in the set byte order; advances `position`. @returns The value.
### ByteArray.readFloat64
```ts
readFloat64(): float64
```
Read a 64-bit float at `position` in the set byte order; advances `position`. @returns The value.
### ByteArray.readBool
```ts
readBool(): boolean
```
Read a boolean (as a byte) at `position`; advances `position`. @returns True if the byte is nonzero.
### ByteArray.writeInt8
```ts
writeInt8(value: int): void
```
Write a signed 8-bit integer at `position`; advances `position`. @param value The value to write.
### ByteArray.writeUInt8
```ts
writeUInt8(value: uint): void
```
Write an unsigned 8-bit integer at `position`; advances `position`. @param value The value to write.
### ByteArray.writeInt16
```ts
writeInt16(value: int): void
```
Write a signed 16-bit integer at `position` in the set byte order; advances `position`. @param value The value to write.
### ByteArray.writeUInt16
```ts
writeUInt16(value: uint): void
```
Write an unsigned 16-bit integer at `position` in the set byte order; advances `position`. @param value The value to write.
### ByteArray.writeInt32
```ts
writeInt32(value: int): void
```
Write a signed 32-bit integer at `position` in the set byte order; advances `position`. @param value The value to write.
### ByteArray.writeUInt32
```ts
writeUInt32(value: float64): void
```
Write an unsigned 32-bit integer at `position` in the set byte order; advances `position`. @param value The value to write.
### ByteArray.writeFloat32
```ts
writeFloat32(value: float64): void
```
Write a 32-bit float at `position` in the set byte order; advances `position`. @param value The value to write.
### ByteArray.writeFloat64
```ts
writeFloat64(value: float64): void
```
Write a 64-bit float at `position` in the set byte order; advances `position`. @param value The value to write.
### ByteArray.writeBool
```ts
writeBool(value: boolean): void
```
Write a boolean at `position` as a single byte; advances `position`. @param value Nonzero values write as 1.
### ByteArray.getInt8
```ts
getInt8(offset: int): int
```
Read a signed 8-bit integer at `offset`; does not change `position`. @param offset Byte offset in the array. @returns The value.
### ByteArray.getUInt8
```ts
getUInt8(offset: int): uint
```
Read an unsigned 8-bit integer at `offset`; does not change `position`. @param offset Byte offset in the array. @returns The value.
### ByteArray.getInt16
```ts
getInt16(offset: int): int
```
Read a signed 16-bit integer at `offset` in the set byte order; does not change `position`. @param offset Byte offset in the array. @returns The value.
### ByteArray.getUInt16
```ts
getUInt16(offset: int): uint
```
Read an unsigned 16-bit integer at `offset` in the set byte order; does not change `position`. @param offset Byte offset in the array. @returns The value.
### ByteArray.getInt32
```ts
getInt32(offset: int): int
```
Read a signed 32-bit integer at `offset` in the set byte order; does not change `position`. @param offset Byte offset in the array. @returns The value.
### ByteArray.getUInt32
```ts
getUInt32(offset: int): uint
```
Read an unsigned 32-bit integer at `offset` in the set byte order; does not change `position`. @param offset Byte offset in the array. @returns The value.
### ByteArray.getFloat32
```ts
getFloat32(offset: int): float
```
Read a 32-bit float at `offset` in the set byte order; does not change `position`. @param offset Byte offset in the array. @returns The value.
### ByteArray.getFloat64
```ts
getFloat64(offset: int): float64
```
Read a 64-bit float at `offset` in the set byte order; does not change `position`. @param offset Byte offset in the array. @returns The value.
### ByteArray.getBool
```ts
getBool(offset: int): boolean
```
Read a boolean at `offset` as a single byte; does not change `position`. @param offset Byte offset in the array. @returns True if the byte is nonzero.
### ByteArray.setInt8
```ts
setInt8(offset: int, value: int): void
```
Write a signed 8-bit integer at `offset`; does not change `position`. @param offset Byte offset in the array. @param value The value to write.
### ByteArray.setUInt8
```ts
setUInt8(offset: int, value: uint): void
```
Write an unsigned 8-bit integer at `offset`; does not change `position`. @param offset Byte offset in the array. @param value The value to write.
### ByteArray.setInt16
```ts
setInt16(offset: int, value: int): void
```
Write a signed 16-bit integer at `offset` in the set byte order; does not change `position`. @param offset Byte offset in the array. @param value The value to write.
### ByteArray.setUInt16
```ts
setUInt16(offset: int, value: uint): void
```
Write an unsigned 16-bit integer at `offset` in the set byte order; does not change `position`. @param offset Byte offset in the array. @param value The value to write.
### ByteArray.setInt32
```ts
setInt32(offset: int, value: int): void
```
Write a signed 32-bit integer at `offset` in the set byte order; does not change `position`. @param offset Byte offset in the array. @param value The value to write.
### ByteArray.setUInt32
```ts
setUInt32(offset: int, value: float64): void
```
Write an unsigned 32-bit integer at `offset` in the set byte order; does not change `position`. @param offset Byte offset in the array. @param value The value to write.
### ByteArray.setFloat32
```ts
setFloat32(offset: int, value: float64): void
```
Write a 32-bit float at `offset` in the set byte order; does not change `position`. @param offset Byte offset in the array. @param value The value to write.
### ByteArray.setFloat64
```ts
setFloat64(offset: int, value: float64): void
```
Write a 64-bit float at `offset` in the set byte order; does not change `position`. @param offset Byte offset in the array. @param value The value to write.
### ByteArray.setBool
```ts
setBool(offset: int, value: boolean): void
```
Write a boolean at `offset` as a single byte; does not change `position`. @param offset Byte offset in the array. @param value Nonzero values write as 1.
### ByteArray.readBytes
```ts
readBytes(dest: ByteArray, offset?: int, length?: int): void
```
Copy `length` bytes from `position` into `dest` at `offset`
(default 0), growing `dest`; `length` 0 or omitted means all the
bytes available. Advances `position`.
### ByteArray.writeBytes
```ts
writeBytes(src: ByteArray, offset?: int, length?: int): void
```
Copy `length` bytes of `src` starting at `offset` (default 0) in at
`position`, growing this array; `length` 0 or omitted means the rest
of `src`. Advances `position`.
### ByteArray.readUTFBytes
```ts
readUTFBytes(length: int): string
```
Read `length` bytes as UTF-8 text.
### ByteArray.writeUTFBytes
```ts
writeUTFBytes(text: string): void
```
Write `text` as UTF-8 bytes.
### ByteArray.readString
```ts
readString(length: int, encoding?: TextEncoding): string
```
Read `length` bytes as text in `encoding` (default `TextEncoding.UTF8`).
### ByteArray.writeString
```ts
writeString(text: string, encoding?: TextEncoding): void
```
Write `text` encoded in `encoding` (default `TextEncoding.UTF8`).
### ByteArray.clear
```ts
clear(): void
```
Length 0 and position 0.
### ByteArray.slice
```ts
slice(start?: int, end?: int): ByteArray
```
Return a fresh array copying bytes in the half-open range
`[start, end)`. `end` defaults to `length`. Out-of-range
indices are clamped; an empty range yields an empty array.
### ByteArray.toString
```ts
toString(encoding?: TextEncoding): string
```
Decode the whole array to a string. `encoding` defaults to
`TextEncoding.UTF8`. Throws on unknown encodings or
non-string arguments.
### ByteArray.from
```ts
static from(data: any, encoding?: TextEncoding): ByteArray
```
Build an array from `data`. An `Array` is treated as a byte
sequence (each element masked to 8 bits). A `ByteArray` is copied.
A typed array is copied element by element, each masked to 8 bits.
A numeric `FixedArray` copies its bytes, packed at native width, in
little-endian order.
A string is encoded using `encoding` (default `TextEncoding.UTF8`).
Throws if `encoding` is provided but is not a string. Other input
types yield an empty array.
**Example**
```ts
const bytes: ByteArray = ByteArray.from("hi");
const buf: Buffer = Buffer.from("hi");
console.log(bytes.length, buf.length); // 2 2
```
## Buffer
```ts
class Buffer extends ByteArray
```
The old name of `ByteArray`: an alias of the same type, kept so
existing code compiles unchanged. `Buffer.alloc(n)`, `Buffer.from(...)`,
`byteLength` and `BufferEncoding` all work on it. Prefer `ByteArray`.
### Buffer.alloc
```ts
static alloc(size: int): ByteArray
```
`new ByteArray(size)`: a zero-filled array of `size` bytes.
## TypedArray
```ts
class TypedArray
```
*Type only: the runtime hands you instances; you never construct it yourself.*
The type name for "any typed array".
All eight spellings use the fixed-array runtime representation.
`t is TypedArray` is true for every one. `t is Uint8Array` is true
exactly when the element type is `uint8`, and likewise for the other
seven spellings.
### TypedArray.length
```ts
readonly length: int
```
Number of elements in the view.
### TypedArray.byteLength
```ts
readonly byteLength: int
```
Total byte size of the view.
### TypedArray.set
```ts
set(source: any, offset?: int): void
```
Copy `source` (an array or another typed array) in starting at
`offset` (default 0), converting each element to this element type.
Throws RangeError when `source.length + offset` exceeds `length` or
`offset` is negative.
**Parameters**
- `source`: Array or typed array whose elements to copy.
- `offset`: Optional starting index in this array; defaults to 0.
## Int8Array
```ts
class Int8Array extends TypedArray
```
*Type only: the runtime hands you instances; you never construct it yourself.*
Old-grammar declaration for the `FixedArray` alias. `new Int8Array(x)` is compiler-lowered to `__fa_construct`; no runtime global exists.
### new Int8Array()
```ts
constructor()
constructor(length: int)
constructor(source: ByteArray)
constructor(source: TypedArray)
constructor(values: Array)
```
Create an empty typed array.
### Int8Array.slice
```ts
slice(begin?: int, end?: int): Int8Array
```
A COPY of elements [begin, end) in new storage. Negative indices
count from the end; both clamp to the length.
**Parameters**
- `begin`: Optional starting index; defaults to 0.
- `end`: Optional ending index (exclusive); defaults to length.
**Returns:** A new Int8Array containing the copied elements.
## Uint8Array
```ts
class Uint8Array extends TypedArray
```
*Type only: the runtime hands you instances; you never construct it yourself.*
Old-grammar declaration for the `FixedArray` alias. `new Uint8Array(x)` is compiler-lowered to `__fa_construct`; no runtime global exists.
### new Uint8Array()
```ts
constructor()
constructor(length: int)
constructor(source: ByteArray)
constructor(source: TypedArray)
constructor(values: Array)
```
Create an empty typed array.
### Uint8Array.slice
```ts
slice(begin?: int, end?: int): Uint8Array
```
A COPY of elements [begin, end) in new storage. Negative indices
count from the end; both clamp to the length.
**Parameters**
- `begin`: Optional starting index; defaults to 0.
- `end`: Optional ending index (exclusive); defaults to length.
**Returns:** A new Uint8Array containing the copied elements.
## Int16Array
```ts
class Int16Array extends TypedArray
```
*Type only: the runtime hands you instances; you never construct it yourself.*
Old-grammar declaration for the `FixedArray` alias. `new Int16Array(x)` is compiler-lowered to `__fa_construct`; no runtime global exists.
### new Int16Array()
```ts
constructor()
constructor(length: int)
constructor(source: ByteArray)
constructor(source: TypedArray)
constructor(values: Array)
```
Create an empty typed array.
### Int16Array.slice
```ts
slice(begin?: int, end?: int): Int16Array
```
A COPY of elements [begin, end) in new storage. Negative indices
count from the end; both clamp to the length.
**Parameters**
- `begin`: Optional starting index; defaults to 0.
- `end`: Optional ending index (exclusive); defaults to length.
**Returns:** A new Int16Array containing the copied elements.
## Uint16Array
```ts
class Uint16Array extends TypedArray
```
*Type only: the runtime hands you instances; you never construct it yourself.*
Old-grammar declaration for the `FixedArray` alias. `new Uint16Array(x)` is compiler-lowered to `__fa_construct`; no runtime global exists.
### new Uint16Array()
```ts
constructor()
constructor(length: int)
constructor(source: ByteArray)
constructor(source: TypedArray)
constructor(values: Array)
```
Create an empty typed array.
### Uint16Array.slice
```ts
slice(begin?: int, end?: int): Uint16Array
```
A COPY of elements [begin, end) in new storage. Negative indices
count from the end; both clamp to the length.
**Parameters**
- `begin`: Optional starting index; defaults to 0.
- `end`: Optional ending index (exclusive); defaults to length.
**Returns:** A new Uint16Array containing the copied elements.
## Int32Array
```ts
class Int32Array extends TypedArray
```
*Type only: the runtime hands you instances; you never construct it yourself.*
Old-grammar declaration for the `FixedArray` alias. `new Int32Array(x)` is compiler-lowered to `__fa_construct`; no runtime global exists.
### new Int32Array()
```ts
constructor()
constructor(length: int)
constructor(source: ByteArray)
constructor(source: TypedArray)
constructor(values: Array)
```
Create an empty typed array.
### Int32Array.slice
```ts
slice(begin?: int, end?: int): Int32Array
```
A COPY of elements [begin, end) in new storage. Negative indices
count from the end; both clamp to the length.
**Parameters**
- `begin`: Optional starting index; defaults to 0.
- `end`: Optional ending index (exclusive); defaults to length.
**Returns:** A new Int32Array containing the copied elements.
## Uint32Array
```ts
class Uint32Array extends TypedArray
```
*Type only: the runtime hands you instances; you never construct it yourself.*
Old-grammar declaration for the `FixedArray` alias. `new Uint32Array(x)` is compiler-lowered to `__fa_construct`; no runtime global exists.
### new Uint32Array()
```ts
constructor()
constructor(length: int)
constructor(source: ByteArray)
constructor(source: TypedArray)
constructor(values: Array)
```
Create an empty typed array.
### Uint32Array.slice
```ts
slice(begin?: int, end?: int): Uint32Array
```
A COPY of elements [begin, end) in new storage. Negative indices
count from the end; both clamp to the length.
**Parameters**
- `begin`: Optional starting index; defaults to 0.
- `end`: Optional ending index (exclusive); defaults to length.
**Returns:** A new Uint32Array containing the copied elements.
## Float32Array
```ts
class Float32Array extends TypedArray
```
*Type only: the runtime hands you instances; you never construct it yourself.*
Old-grammar declaration for the `FixedArray` alias. `new Float32Array(x)` is compiler-lowered to `__fa_construct`; no runtime global exists.
### new Float32Array()
```ts
constructor()
constructor(length: int)
constructor(source: ByteArray)
constructor(source: TypedArray)
constructor(values: Array)
```
Create an empty typed array.
### Float32Array.slice
```ts
slice(begin?: int, end?: int): Float32Array
```
A COPY of elements [begin, end) in new storage. Negative indices
count from the end; both clamp to the length.
**Parameters**
- `begin`: Optional starting index; defaults to 0.
- `end`: Optional ending index (exclusive); defaults to length.
**Returns:** A new Float32Array containing the copied elements.
## Float64Array
```ts
class Float64Array extends TypedArray
```
*Type only: the runtime hands you instances; you never construct it yourself.*
Old-grammar declaration for the `FixedArray` alias. `new Float64Array(x)` is compiler-lowered to `__fa_construct`; no runtime global exists.
### new Float64Array()
```ts
constructor()
constructor(length: int)
constructor(source: ByteArray)
constructor(source: TypedArray)
constructor(values: Array)
```
Create an empty typed array.
### Float64Array.slice
```ts
slice(begin?: int, end?: int): Float64Array
```
A COPY of elements [begin, end) in new storage. Negative indices
count from the end; both clamp to the length.
**Parameters**
- `begin`: Optional starting index; defaults to 0.
- `end`: Optional ending index (exclusive); defaults to length.
**Returns:** A new Float64Array containing the copied elements.
---
*Generated from `lang/types/builtin_buffer.d.tks` in the ThicketScript repository.*
---
URL: https://thicketscript.com/docs/api/collections
# Array, Map and Set
The collection types: `Array`, `FixedArray`, `Map`, `Set` and the weak references.
::: tip Always available
No import needed. These names are in scope in every file.
:::
## Array
```ts
class Array
```
Indexed, mutable, ordered. The element type `T` is enforced: a
member call binds `T` to the receiver's element type, so
`a.push("nope")` on an `Array` is a diagnostic and
`a.slice(0, 1)` answers `Array`. Element reads answer `T`,
including indexed reads and the single-element methods below. Like an
out-of-range indexed read, an empty or out-of-range method result is
deliberately typed as `T` even though the runtime returns `undefined`
from `at` / `pop` / `shift` and `null` from `first` / `last` / `sample`.
### Array.length
```ts
readonly length: int
```
Number of elements.
### Array.size
```ts
readonly size: int
```
Alias of `length`. Present so `Array`, `Map`, and `Set` share the collection contract.
### new Array()
```ts
constructor()
constructor(length: int)
constructor(...items: Array)
```
Empty array.
### Array.at
```ts
at(index: int): T
```
Element at `index`. Negative indices count from the end. Runtime result is `undefined` when out of range.
### Array.includes
```ts
includes(searchElement: T): boolean
```
True when `searchElement` is present (uses `===` semantics).
### Array.indexOf
```ts
indexOf(searchElement: T, fromIndex?: int): int
```
Index of the first match, or `-1`. `fromIndex` defaults to `0`.
### Array.lastIndexOf
```ts
lastIndexOf(searchElement: T): int
```
Index of the last match, or `-1`.
### Array.find
```ts
find(callback: Function): any
```
First element matching `callback`, or `undefined`. Callback shape: `(value: T, index: int, array: Array): boolean`.
### Array.findIndex
```ts
findIndex(callback: Function): int
```
Index of the first match, or `-1`. Same callback shape as `find`.
### Array.some
```ts
some(callback: Function): boolean
```
True when at least one element passes `callback`.
### Array.every
```ts
every(callback: Function): boolean
```
True when every element passes `callback`.
### Array.push
```ts
push(...items: Array): int
```
Append items, returns the new length.
**Example**
```ts
const items: int[] = [1, 2];
const length: int = items.push(3, 4);
console.log(length); // 4
```
### Array.pop
```ts
pop(): T
```
Remove and return the last element. Runtime result is `undefined` when empty.
### Array.shift
```ts
shift(): T
```
Remove and return the first element, shifting the rest down. Runtime result is `undefined` when empty.
### Array.unshift
```ts
unshift(...items: Array): int
```
Prepend items, returns the new length.
### Array.fill
```ts
fill(value: T, start?: int, end?: int): Array
```
Fill `[start, end)` with `value` in place. Defaults: `start=0`, `end=length`.
### Array.reverse
```ts
reverse(): Array
```
Reverse in place and return the receiver.
### Array.sort
```ts
sort(compareFn?: Function): Array
```
Sort in place. `compareFn(a, b)` returns negative / zero / positive; default is lexicographic.
### Array.splice
```ts
splice(start: int, deleteCount?: int, ...items: Array): Array
```
Remove `deleteCount` elements at `start`, insert `items` in their place, return the removed slice.
### Array.concat
```ts
concat(...items: Array>): Array
```
Shallow copy with `items` and arrays-of-items appended. This matches
JavaScript and TypeScript: when `T` is an array, an array argument
is flattened one level at runtime even though the return is `Array`.
### Array.join
```ts
join(separator?: string): string
```
Join elements with `separator` (default `","`).
### Array.slice
```ts
slice(start?: int, end?: int): Array
```
Shallow copy of `[start, end)`. Negative indices count from the end.
### Array.flat
```ts
flat(): Array
```
One level of nesting flattened. `[[1,2],[3]]` becomes `[1,2,3]`.
### Array.toString
```ts
toString(): string
```
Comma-joined string form.
### Array.forEach
```ts
forEach(callback: Function): void
```
Invoke `callback` for each element. Shape: `(value: T, index: int, array: Array): void`.
### Array.map
```ts
map(callback: Function): Array
```
New array of the results of `callback(value, index, array)`.
### Array.filter
```ts
filter(callback: Function): Array
```
New array of the elements `callback` returned truthy for.
### Array.reduce
```ts
reduce(callback: Function, initialValue?: any): any
```
Fold with `callback(acc, value, index, array)`. `initialValue` seeds the accumulator.
### Array.entries
```ts
entries(): Array<[int, T]>
```
New array of `[index, value]` pairs.
### Array.keys
```ts
keys(): Array
```
New array of indices `[0..length)`.
### Array.values
```ts
values(): Array
```
Shallow copy of the values.
### Array.toArray
```ts
toArray(): Array
```
Self — present so `Array`, `Map`, and `Set` share the collection contract.
### Array.compact
```ts
compact(): Array
```
New array with `null` and `undefined` removed.
### Array.unique
```ts
unique(): Array
```
New array with duplicates removed (uses `===` equality).
### Array.shuffle
```ts
shuffle(): Array
```
New array with the elements in random order. Uses `std::rand()`.
### Array.first
```ts
first(): T
```
First element. Runtime result is `null` when empty.
### Array.last
```ts
last(): T
```
Last element. Runtime result is `null` when empty.
### Array.sample
```ts
sample(): T
```
Random element. Runtime result is `null` when empty. Uses `std::rand()`.
### Array.isEmpty
```ts
isEmpty(): boolean
```
True when `length === 0`.
### Array.sum
```ts
sum(): float
```
Sum of numeric elements. Non-numeric elements are skipped.
### Array.avg
```ts
avg(): float
```
Mean of numeric elements. `NaN` when empty.
## FixedArray
```ts
class FixedArray
```
`FixedArray`: an array of a number, boolean, char or struct type whose
length is chosen when it is made and never changes. The length is not part
of the type: `new FixedArray(n)` makes `n` zero elements, and a struct
field states its own, `name: FixedArray(24)`.
Checks as an array of `T`; assignment to and from `Array` converts
automatically (a copy), and an out-of-range index or element value raises
`RangeError`. `Uint8Array` ... `Float64Array` are aliases of `FixedArray`
... `FixedArray` (one type, one runtime kind).
Construction and storage rules are in `docs/fixed-array.md`.
### new FixedArray()
```ts
constructor(length: int)
```
`n` zero elements; `RangeError` for a negative or non-integer `n`.
### FixedArray.length
```ts
readonly length: int
```
Number of elements: fixed when the array was made.
### FixedArray.byteLength
```ts
readonly byteLength: int
```
Total size of the elements in bytes.
### FixedArray.fill
```ts
fill(value: T): void
```
Set every element to `value`.
### FixedArray.slice
```ts
slice(begin?: int, end?: int): FixedArray
```
A COPY of elements [begin, end) in new storage. Negative indices count
from the end; both clamp to the length.
### FixedArray.set
```ts
set(source: any, offset?: int): void
```
Copy `source` (an `Array` or another FixedArray) in starting at `offset`
(default 0). `RangeError` when it does not fit, or an element is out of range.
### FixedArray.indexOf
```ts
indexOf(value: T): int
```
Index of the first element equal to `value`, or -1.
### FixedArray.includes
```ts
includes(value: T): boolean
```
True when an element equals `value`.
### FixedArray.toArray
```ts
toArray(): Array
```
A plain `Array` copy of the elements. Packed element storage widens to its value type on read.
### FixedArray.toSet
```ts
toSet(): Set
```
A `Set` of the elements (duplicates collapse). Packed element storage widens to its value type on read.
### FixedArray.textLength
```ts
readonly textLength: int
```
Text rules, `FixedArray` only: the text is the elements up to
the first zero. `length` stays the made length; `textLength` is the text's length.
### FixedArray.left
```ts
left(n: int): Array
```
The first `n` characters, as a char array of the same type.
### FixedArray.right
```ts
right(n: int): Array
```
The last `n` characters, as a char array of the same type.
### FixedArray.mid
```ts
mid(start: int, count: int): Array
```
`count` characters from `start`, as a char array of the same type.
### FixedArray.startsWith
```ts
startsWith(prefix: any): boolean
```
True when the text begins with `prefix` (a string or char array).
### FixedArray.equalsIgnoreCase
```ts
equalsIgnoreCase(other: any): boolean
```
Equality ignoring ASCII case, against a string or char array.
### FixedArray.from
```ts
static from(source: any): Array
```
The explicit spelling of the automatic `Array` to `FixedArray`
conversion. It builds the FixedArray the receiving slot declares, so
it is only valid where one does: `const a: FixedArray = FixedArray.from(arr)`.
`source` may also be a `ByteArray`: its bytes are copied at the element's native
width, little-endian (a whole number of elements, else `RangeError`).
## Map
```ts
class Map
```
Ordered key-value store. Member calls enforce the receiver's `K` and
`V`. `length` is exposed as an alias of `size` for parity with the
rest of the collection family.
### Map.size
```ts
readonly size: int
```
Number of live entries.
### Map.length
```ts
readonly length: int
```
Alias of `size`.
### new Map()
```ts
constructor(entries?: any)
```
Empty map, or one filled from an iterable of `[key, value]` pairs (an `Array` of pairs or another `Map`).
### Map.clear
```ts
clear(): void
```
Remove every entry.
### Map.delete
```ts
delete(key: K): boolean
```
Remove `key`. Returns `true` when an entry was removed.
### Map.remove
```ts
remove(key: K): boolean
```
Alias of `delete`.
### Map.get
```ts
get(key: K): V | undefined
```
Value for `key`, or `undefined`.
**Example**
```ts
const ages: Map = new Map();
ages.set("ada", 36);
console.log(ages.get("ada")); // 36
```
### Map.has
```ts
has(key: K): boolean
```
True when `key` is present.
### Map.set
```ts
set(key: K, value: V): Map
```
Set `key` to `value`. Returns the receiver.
**Example**
```ts
const ages: Map = new Map();
ages.set("ada", 36).set("alan", 41);
console.log(ages.size); // 2
```
### Map.add
```ts
add(key: K, value: V): Map
```
Alias of `set`.
### Map.keys
```ts
keys(): Array
```
Insertion-ordered keys.
### Map.values
```ts
values(): Array
```
Insertion-ordered values.
### Map.entries
```ts
entries(): Array<[K, V]>
```
Insertion-ordered `[key, value]` pairs.
### Map.forEach
```ts
forEach(callback: Function): void
```
Invoke `callback` for each entry. Shape: `(value: V, key: K, map: Map): void`.
### Map.toArray
```ts
toArray(): Array
```
Insertion-ordered values. Mirrors the collection contract.
### Map.compact
```ts
compact(): Map
```
New map with entries whose value is `null` or `undefined` removed.
### Map.first
```ts
first(): any
```
First inserted value, or `null` when empty.
### Map.last
```ts
last(): any
```
Last inserted value, or `null` when empty.
### Map.sample
```ts
sample(): any
```
Random value, or `null` when empty. Uses `std::rand()`.
### Map.isEmpty
```ts
isEmpty(): boolean
```
True when `size === 0`.
### Map.sum
```ts
sum(): float
```
Sum of numeric values. Non-numeric values are skipped.
### Map.avg
```ts
avg(): float
```
Mean of numeric values. `NaN` when empty.
## Set
```ts
class Set
```
Insertion-ordered, deduplicated value collection. Includes the
TKS-specific set algebra extensions (`union`, `intersection`,
`difference`, `isSubsetOf`) registered alongside the standard JS
surface.
### Set.size
```ts
readonly size: int
```
Number of live entries.
### Set.length
```ts
readonly length: int
```
Alias of `size`.
### new Set()
```ts
constructor(values?: any)
```
Empty set, or one filled from an iterable of values (an `Array` or another `Set`).
### Set.clear
```ts
clear(): void
```
Remove every value.
### Set.add
```ts
add(value: T): Set
```
Insert `value`. Returns the receiver.
**Example**
```ts
const seen: Set = new Set();
seen.add("a").add("b").add("a");
console.log(seen.size); // 2
```
### Set.set
```ts
set(value: T): Set
```
Alias of `add`.
### Set.delete
```ts
delete(value: T): boolean
```
Remove `value`. Returns `true` when a value was removed.
### Set.remove
```ts
remove(value: T): boolean
```
Alias of `delete`.
### Set.has
```ts
has(value: T): boolean
```
True when `value` is present.
### Set.forEach
```ts
forEach(callback: Function): void
```
Invoke `callback` for each value. Shape: `(value: T, value2: T, set: Set): void`.
### Set.values
```ts
values(): Array
```
Insertion-ordered values.
### Set.toArray
```ts
toArray(): Array
```
Insertion-ordered values. Mirrors the collection contract.
### Set.compact
```ts
compact(): Set
```
New set with `null` and `undefined` removed.
### Set.union
```ts
union(other: Set): Set
```
Values present in either set.
### Set.intersection
```ts
intersection(other: Set): Set
```
Values present in both sets.
### Set.difference
```ts
difference(other: Set): Set
```
Values present in this set but not in `other`.
### Set.isSubsetOf
```ts
isSubsetOf(other: Set): boolean
```
True when every value in this set is also in `other`.
### Set.first
```ts
first(): any
```
First inserted value, or `null` when empty.
### Set.last
```ts
last(): any
```
Last inserted value, or `null` when empty.
### Set.sample
```ts
sample(): any
```
Random value, or `null` when empty. Uses `std::rand()`.
### Set.isEmpty
```ts
isEmpty(): boolean
```
True when `size === 0`.
### Set.sum
```ts
sum(): float
```
Sum of numeric values. Non-numeric values are skipped.
### Set.avg
```ts
avg(): float
```
Mean of numeric values. `NaN` when empty.
## WeakRef
```ts
class WeakRef
```
JavaScript-compatible weak reference to an object. Constructing the
reference and successfully dereferencing it keep the target alive until
the current job and its Promise microtasks finish.
### WeakRef.deref
```ts
deref(): T | undefined
```
Target while it remains alive, otherwise `undefined`.
### Other members of WeakRef
```ts
constructor(target: T)
```
## WeakMap
```ts
class WeakMap
```
### WeakMap.get
```ts
get(key: K): V | undefined
```
Value for `key`, or `undefined` when absent or already collected.
### WeakMap.set
```ts
set(key: K, value: V): WeakMap
```
Set `key` to `value`. Returns the receiver.
### WeakMap.has
```ts
has(key: K): boolean
```
True when `key` is present.
### WeakMap.delete
```ts
delete(key: K): boolean
```
Remove `key`. Returns `true` when an entry was removed.
### Other members of WeakMap
```ts
constructor()
```
## WeakSet
```ts
class WeakSet
```
Weakly-referencing set. Members are held weakly; entries disappear
when collected. Not iterable and has no `size`, for the same reason
`WeakMap` has neither.
### WeakSet.add
```ts
add(value: T): WeakSet
```
Add `value`. Returns the receiver.
### WeakSet.has
```ts
has(value: T): boolean
```
True when `value` is present.
### WeakSet.delete
```ts
delete(value: T): boolean
```
Remove `value`. Returns `true` when it was removed.
### Other members of WeakSet
```ts
constructor()
```
---
*Generated from `lang/types/builtin_collections.d.tks` in the ThicketScript repository.*
---
URL: https://thicketscript.com/docs/api/color
# Color
The `Color` value type: construct, convert and adjust colors.
::: tip Always available
No import needed. These names are in scope in every file.
:::
## Color
```ts
struct Color
```
### Color.fromUint
```ts
static fromUint(n: uint): Color
```
A packed 0xRRGGBBAA number.
### Color.fromArgb
```ts
static fromArgb(n: uint): Color
```
A packed 0xAARRGGBB number.
### Color.rgb
```ts
static rgb(r: int, g: int, b: int): Color
```
Red, green and blue bytes (0-255), fully opaque.
**Example**
```ts
const orange: Color = Color.rgb(255, 128, 0);
console.log(orange.r(), orange.g(), orange.b()); // 255 128 0
```
### Color.rgba
```ts
static rgba(r: int, g: int, b: int, a: int): Color
```
Red, green, blue and alpha bytes (0-255).
**Example**
```ts
const glass: Color = Color.rgba(255, 255, 255, 128);
console.log(glass.a()); // 128
```
### Color.rgbaf
```ts
static rgbaf(r: float, g: float, b: float, a: float = 1.0): Color
```
Red, green, blue and alpha as 0-1 floats.
### Color.hex
```ts
static hex(s: string): Color
```
A hex string such as "#ff8800"; throws when it is not one.
**Example**
```ts
const orange: Color = Color.hex("#ff8800");
console.log(orange.r(), orange.g(), orange.b()); // 255 136 0
```
### Color.hsl
```ts
static hsl(h: float, s: float, l: float): Color
```
Hue (degrees), saturation and lightness (0-1), fully opaque.
### Color.hsla
```ts
static hsla(h: float, s: float, l: float, a: float): Color
```
Hue, saturation, lightness and alpha (0-1).
### Color.hsv
```ts
static hsv(h: float, s: float, v: float, a: float = 1.0): Color
```
Hue (degrees), saturation and value (0-1).
### Other members of Color
```ts
value: uint32
static readonly WHITE: Color
static readonly BLACK: Color
static readonly TRANSPARENT: Color
static readonly RED: Color
static readonly GREEN: Color
static readonly BLUE: Color
static readonly YELLOW: Color
static readonly CYAN: Color
static readonly MAGENTA: Color
static readonly GRAY: Color
static readonly ORANGE: Color
static readonly PURPLE: Color
static tryHex(s: string): Color | null
r(): int
g(): int
b(): int
a(): int
rf(): float
gf(): float
bf(): float
af(): float
hue(): float
lightness(): float
saturation(): float
brightness(): float
luminance(): float
contrastRatio(other: Color): float
isDark(): boolean
isLight(): boolean
withR(v: int): Color
withG(v: int): Color
withB(v: int): Color
withA(v: int): Color
withAlpha(v: int): Color
fade(f: float): Color
hueShift(deg: float): Color
complement(): Color
saturate(amt: float): Color
desaturate(amt: float): Color
lighten(amt: float): Color
darken(amt: float): Color
brighten(f: float): Color
mix(other: Color, t: float): Color
lerp(other: Color, t: float): Color
tint(amt: float): Color
shade(amt: float): Color
invert(): Color
grayscale(): Color
premultiplied(): Color
toHex(): string
toHexRGB(): string
toCss(): string
toString(): string
toArgb(): uint
operator eq(other: Color): boolean
```
---
*Generated from `lang/color/index.tks` in the ThicketScript repository.*
---
URL: https://thicketscript.com/docs/api/compress
# compress
zlib and gzip deflate and inflate, with streaming helpers.
::: tip Import
```ts
import { Deflater, Inflater, Compress } from "compress";
```
:::
## Deflater
```ts
class Deflater
```
Streaming deflate. Construct via `Compress.createDeflater(level?)`.
Feed input chunks with `feed`, drain compressed bytes with
`takeOutput`, and call `finish` once when the input is
exhausted. After `finish`, `takeOutput` may still be called
one or more times to drain the tail of the compressed stream.
### Deflater.feed
```ts
feed(data: Buffer | string): void
```
Append `data` to the input stream. Throws if the stream
was already finished. Strings are treated as raw bytes.
### Deflater.finish
```ts
finish(): void
```
Mark the input stream complete and flush any pending
compressed bytes into the output buffer. Throws if
already finished.
### Deflater.isFinished
```ts
isFinished(): boolean
```
True after `finish` has been called and every byte has
been drained via `takeOutput`. Until then, the deflater
is still in mid-stream.
### Deflater.takeOutput
```ts
takeOutput(): Buffer
```
Drain and return the compressed bytes accumulated so far.
The internal buffer is cleared. Returns an empty Buffer
when no output is currently available.
## Inflater
```ts
class Inflater
```
Streaming inflate. Construct via `Compress.createInflater()`.
Feed compressed chunks with `feed`; drain decompressed bytes
with `takeOutput`. The stream finishes implicitly when the
compressed payload's terminator is reached — there is no
explicit `finish` call, mirroring how zlib's inflate state
machine works.
### Inflater.feed
```ts
feed(data: Buffer | string): void
```
Append `data` to the compressed input stream. Throws an
Error if the stream has already reached its end. Throws a
RangeError if the data is corrupt or the output limit is
exceeded. Strings are treated as raw bytes.
### Inflater.isFinished
```ts
isFinished(): boolean
```
True once the inflater has consumed the compressed
stream's terminator. Further `feed` calls will throw.
### Inflater.takeOutput
```ts
takeOutput(): Buffer
```
Drain and return the decompressed bytes accumulated so
far. The internal buffer is cleared. Returns an empty
Buffer when no output is currently available.
## Compress
```ts
static class Compress
```
One-shot zlib / gzip helpers plus factories for the streaming
variants. The class is `static`-only — there are no instances.
The level argument on the compressing helpers is clamped to
the zlib range `0..9` and defaults to `6`. The `maxOutput`
argument on the decompressing helpers caps the output size in
bytes; pass `0` or a negative value (or omit it) for
the default cap of 256 MiB.
### Compress.compress
```ts
static compress(data: Buffer | string, level?: int): Buffer
```
Deflate `data` using zlib framing. Returns the compressed
payload as a Buffer. `level` is clamped to `0..9` and
defaults to `6`.
**Example**
```ts
import { Compress } from "compress";
const packed: Buffer = Compress.compress("aaaaaaaaaaaaaaaaaaaaaaaa");
console.log(packed.length < 24); // true
```
### Compress.createDeflater
```ts
static createDeflater(level?: int): Deflater
```
Construct a streaming `Deflater`. `level` is clamped to
`0..9` and defaults to `6`.
### Compress.createInflater
```ts
static createInflater(maxOutput?: int): Inflater
```
Construct a streaming `Inflater`. `maxOutput` caps the
total decompressed output in bytes; omit, pass `0`, or
pass a negative value for the default cap of 256 MiB.
`feed` throws a RangeError when the cap is exceeded or
when the compressed data is corrupt.
### Compress.decompress
```ts
static decompress(data: Buffer | string, maxOutput?: int): Buffer
```
Inflate zlib-framed `data`. Returns the decompressed
payload as a Buffer. `maxOutput` caps the output in
bytes; omit, pass `0`, or pass a negative value for
the default cap of 256 MiB.
**Example**
```ts
import { Compress } from "compress";
const packed: Buffer = Compress.compress("hello hello hello");
console.log(Compress.decompress(packed).toString()); // hello hello hello
```
### Compress.gzipCompress
```ts
static gzipCompress(data: Buffer | string, level?: int): Buffer
```
Deflate `data` using gzip framing. Returns the compressed
payload as a Buffer. `level` is clamped to `0..9` and
defaults to `6`.
### Compress.gzipDecompress
```ts
static gzipDecompress(data: Buffer | string, maxOutput?: int): Buffer
```
Inflate gzip-framed `data`. Returns the decompressed
payload as a Buffer. `maxOutput` caps the output in
bytes; omit, pass `0`, or pass a negative value for
the default cap of 256 MiB.
---
*Generated from `lang/types/compress.d.tks` in the ThicketScript repository.*
---
URL: https://thicketscript.com/docs/api/console
# console
Leveled logging to stdout and stderr.
::: tip Always available
No import needed. These names are in scope in every file.
:::
## console
```ts
class console
```
### console.assert
```ts
static assert(condition: any, ...messages: any[]): void
```
Throw-style assertion. When `condition` is falsy, prints
`Assertion failed:` followed by the space-joined `messages`.
When truthy, the call is a no-op. Calling with zero arguments
is treated as a failed assertion with no message.
### console.error
```ts
static error(...args: any[]): void
```
Print one space-joined line prefixed with `ERROR:` to stdout
and capture it as an error-tagged output event.
### console.info
```ts
static info(...args: any[]): void
```
Print one space-joined line to stdout. Behaviourally identical
to `log` today; reserved for informational output that hosts
may route to a separate channel later.
### console.log
```ts
static log(...args: any[]): void
```
Print one space-joined line to stdout and capture it as an
output event. The standard diagnostic entry point.
### console.table
```ts
static table(...args: any[]): void
```
Print a tab-separated tabular dump of the argument. Arrays of
objects render as `(index)` plus one column per field of the
first element; maps render as `Key`/`Value` pairs; instances
render as `Property`/`Value`. Other inputs print their string
form. Calling with zero arguments prints `(empty)`.
### console.time
```ts
static time(label?: string): void
```
Start a named timer. Defaults to the label `"default"` when
omitted. Pair with `timeEnd` to print the elapsed milliseconds.
### console.timeEnd
```ts
static timeEnd(label?: string): void
```
Stop the named timer started by `time` and print
`