Skip to content
Docs
Games

Project structure & imports

How a game project is laid out, which imports are allowed, and how TypeScript is built.

A game project is an ordinary folder of JavaScript or TypeScript files, but two rules make it different from a typical Node or web project. Knowing them up front saves you from most build errors.

What goes into a release

A project has a game.json at its root. Its entry field names the first module, where your game starts.

When disko packages your game, it starts at the entry and follows every import. Whatever it reaches goes into the release: modules and imported assets alike. Anything it doesn't reach is left out, even if it sits in the same folder.

game.json names src/main.ts as the entry. main.ts imports scoring.ts and art/puck.png, so all three are packaged. notes.txt and old.ts are not imported by anything, so they are left out.

This means you never list files by hand, and leftovers can't leak into a release by accident.

text

Games are self-contained

A game runs inside the room, in a sandbox, not in Node or a browser. It can't use npm packages, Node modules or the web, so it can only import two kinds of things:

  • Your own files, by relative path.

  • The disko standard library: disko:math and disko:geometry.

The globals game, world and ui are available in every module without importing them. They're how your code talks to disko.

Writing imports

A relative import starts with ./ or ../ and names the exact file, extension and capitalization included: ./scoring.ts, ../art/puck.png. There's no guessing: ./scoring (no extension) and ./utils (a folder) are both errors. An import also can't reach outside the project folder.

src/main.ts

Anything else is rejected: npm package names, node: modules, URLs and absolute paths.

Modules don't need to export anything; the entry's exports are ignored. Circular imports work the way they do in any ES module code.

Code that runs at load time

Top-level code runs once when the room loads your game, and it must finish right away: it sets up the game and builds the first Menu within a short startup deadline. So a few language features that wait or load code later aren't allowed:

ConstructProblem code
import(...)import_dynamic
import.metaimport_meta
Top-level awaittop_level_await
import … with { … }import_attributes

Modules are ES2023 modules in strict mode.

TypeScript

If entry is a .ts file, the project is TypeScript.

The build always uses its own copy of TypeScript with fixed settings: strict checking, an ES2023 target, no browser (DOM) types, and disko's own types for the globals, the disko: modules and asset imports. Your tsconfig.json only configures your editor, so what builds on your machine builds everywhere.

A type error stops the build (ts_type_error), the same way a syntax error would.

  • Import .ts files with their .ts extension; the build rewrites it to .js. You can mix in .js files too.

  • import type disappears in the build, and a module imported only for its types isn't packaged.

  • Types for the globals live in the Disko namespace, for example Disko.Player, Disko.Disc and Disko.UiNode.

  • Not supported: .tsx, .mts, .cts and local .d.ts files, import x = require(), export =, /// <reference> directives and declare module augmentation.

JavaScript projects are packaged exactly as written, without minification.

Limits

A release is at most 10 MiB compressed, 2 MiB per module and 16 MiB per asset. See game.json for every limit.