# ct.js

Publish ct.js projects to the web — ct.js exports a PixiJS-based static bundle that Wavedash hosts directly.

Source: https://docs.wavedash.com/engines/ctjs

<GithubLink href="https://github.com/wvdsh/examples/tree/main/example-ctjs" />
<PlaytestLink href="https://wavedash.com/playtest/ct-js-example/1689cd89-8e78-49d0-adab-74644cf50c24" />

[ct.js](https://ctjs.rocks) is a free, open-source 2D game engine with a visual IDE. The "Export to web" build produces a flat directory of static files — `index.html`, the ct.js runtime, PixiJS, and your game code — which Wavedash hosts as-is. No SDK package to install, no bundler step.

## Export your game

In the ct.js IDE: **Deploy → Export to web**. It writes a zip to `~/ct.js/Builds/<ProjectName>.zip` containing:

- `index.html`
- `ct.<hash>.js` (the ct.js runtime + your compiled game)
- `ct.<hash>.css`
- `pixi.js`, `sscd.min.js`
- favicon and touch-icon images
- `fonts/`, `img/`, `snd/` (your project's assets)

Unzip into `build/` and point `wavedash.toml` at it.

## SDK integration

`window.Wavedash` is injected by the Wavedash runtime before your game loads, so you can call it directly from any ct.js event. The two calls you need are `updateLoadProgressZeroToOne` while loading and `init` once the player can interact.

The easiest place to put them is on your starting room's **OnRoomStart** event:

```typescript
// rm_main → On Room Start
if (typeof (window as any).Wavedash !== 'undefined') {
  // ct.js has already decoded textures and sounds by the time
  // OnRoomStart fires — report the distinct phases here.
  (window as any).Wavedash.updateLoadProgressZeroToOne(0.4); // room loaded
  // Place any remaining async setup (e.g. fetching remote data) here.
  (window as any).Wavedash.updateLoadProgressZeroToOne(0.8); // content ready
  (window as any).Wavedash.updateLoadProgressZeroToOne(1);   // fully ready
  (window as any).Wavedash.init({ debug: true });
}
```

<Urgent>
**Calling `Wavedash.init()` is required.** Your game stays hidden behind the Wavedash loading screen until you do.
</Urgent>

If your game has its own loading phase (loading textures, fetching state), call `updateLoadProgressZeroToOne(progress)` periodically and only call `init()` once gameplay can start.

## Input and the time delta

ct.js doesn't expose `keyboard.down[code]` for "is the key currently held" — keyboard state lives in `inputs.registry`. The standard approach is to define **Actions** in the project (Project → Actions → Edit), then read `actions.MoveUp.down`, but you can also poll the raw registry directly:

```typescript
const up = (inputs as any).registry['keyboard.KeyW'] || (inputs as any).registry['keyboard.ArrowUp'];
const down = (inputs as any).registry['keyboard.KeyS'] || (inputs as any).registry['keyboard.ArrowDown'];
```

The frame time delta is `u.time` (in seconds, scaled by the room's time-scale).

## wavedash.toml

```toml
game_id = "YOUR_GAME_ID_HERE"
upload_dir = "./build"
entrypoint = "index.html"
```

## Known issues

**No bundler step.** ct.js's exporter writes plain static files — there's nothing to npm-install and nothing to configure. If you find yourself trying to integrate Webpack or Vite, you're working against the engine.

**Asset paths are relative.** The exported `index.html` references `pixi.js`, `ct.<hash>.js`, etc. with relative paths. Don't move files around inside `build/` — keep the structure ct.js produced.

**`PIXI` and `inputs` are globals.** ct.js does not module-scope its runtime. `PIXI`, `inputs`, `u`, `keyboard`, `rooms`, `templates` are all available on `window` from inside event code.
