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.
This means you never list files by hand, and leftovers can't leak into a release by accident.
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:mathanddisko: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.
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:
| Construct | Problem code |
|---|---|
import(...) | import_dynamic |
import.meta | import_meta |
Top-level await | top_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
.tsfiles with their.tsextension; the build rewrites it to.js. You can mix in.jsfiles too.import typedisappears in the build, and a module imported only for its types isn't packaged.Types for the globals live in the
Diskonamespace, for exampleDisko.Player,Disko.DiscandDisko.UiNode.Not supported:
.tsx,.mts,.ctsand local.d.tsfiles,import x = require(),export =,/// <reference>directives anddeclare moduleaugmentation.
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.