TIC-80 is a tiny fantasy computer for small games. Code, sprites, maps, sound and music are all edited inside it.
Website: https://tic80.com Tools repository: https://git.teletypegames.org/build/tic80-tools
A cartridge is one .lua file. To keep the code modular, we split it into many files and concatenate them at build time.
project-root/
├── inc/
│ ├── core.lua
│ ├── player.lua
│ └── world.lua
├── impostor.inc # build order, one file name per line
├── Makefile
└── README.md
impostor.lua is generated. Order matters: a file must come after the globals it uses.
make build # concatenate inc/ files into <project>.lua
make minify # shrink the code, backup to <project>.original.lua
make export VERSION=1.0.0 # build + minify, then save .tic and .html.zip
make export_assets # PALETTE/TILES/SPRITES/MAP/SFX/WAVES -> inc/meta/meta.assets.lua
make import_assets # import assets/<type>/*.png into the cartridge
make watch # rebuild on change in inc/, the .inc file and assets/
make lint # luacheck, with line numbers mapped back to source files
make docs # ldoc HTML into docs/
make install_precommit_hook # .git/hooks/pre-commit runs make lint
make clean # remove generated files
export also copies the versioned files to unversioned names (impostor.tic, impostor.html.zip). export_assets keeps the graphics in version control as source code.
Copy example-tasks.json from the tools repo to .vscode/tasks.json and replace impostor.lua with your cart name. Tasks: Run TIC80 (default, Cmd+Shift+B), Build & Run TIC80, Export assets, Make build.
| File | Description |
|---|---|
<project>.lua |
Merged (and minified) source |
<project>.original.lua |
Pre-minification backup |
<project>.tic |
Cartridge |
<project>.html.zip |
HTML build |
<project>-*-docs.zip |
Documentation |
<project>-*-win-x64.zip |
Windows player, cart baked in |
<project>-*-linux-x64.zip |
Linux player, cart baked in |
<project>-*-mac-x64.zip |
macOS (Intel) player, cart baked in |
Local only: make, tic80 in PATH, lua, luacheck, ldoc, curl, and fswatch for watch mode. CI installs its own tools.
The pipeline is not in the game repo. Woodpecker asks the update server for it, and WarpEngine serves the whole config from /build/config (template lib/warp_engine/ci/woodpecker/platforms/tic80/pipeline.yaml.erb).
The repo needs three things:
# .woodpecker.yaml
platform: tic80
name: <software> — only when the software name differs from the repo nameapplication_token Woodpecker secret: a WarpEngine ApplicationToken with update + upload scopes, created in the adminTIC-80 projects have no metadata.json. The version and the catalog metadata come from the comment header in inc/meta/meta.header.lua.
See the exact commands:
curl "https://teletypegames.org/build/config?platform=tic80"
| Step | What it does |
|---|---|
| 1. version | the engine's shared step (since WarpEngine 1.7): the version from the -- version: line of inc/meta/meta.header.lua; on a tag a version tag has to match it; outside the repo's default branch and on a dev-<branch> tag dev-<version>-<branch>, with no version dev-<commit>; write .version |
| 2. lint | luacheck in the tools image (ci-tools on teletypegames.org, luacheck and ldoc preinstalled; without one, alpine + luarocks install on every run); merge inc/ in .inc order with a line map; fail on any issue |
| 3. minify | concatenate and minify with lua-minify-tic80; keep the original for docs |
| 4. docs | ldoc on the pre-minification source, zipped as <name>-<version>-docs.zip |
| 5. export | in the tic80pro image: save the .tic cart and export .html.zip |
| 6. binaries | export win / linux / mac with the cart baked in, one zip per slug; templates are downloaded from tic80.com, so this step needs network |
| 7. upload | on the tag only: POST /build/upload per file, X-Update-Secret header from application_token |
| 8. publish | on the tag only: POST /build/publish?platform=tic80 |
| 9. tag | the engine's shared step (since WarpEngine 1.9), after a push only: a new version in the source gets its v<version> tag, whose pipeline runs the steps marked on the tag only; a version tagged already releases nothing; a dev build moves the branch's dev-<branch> tag onto the commit, so the newest dev build is published. See Versions and releases on the WarpEngine page |
Raising the version is the release: a push builds and tags, and the tag's pipeline uploads and publishes (WarpEngine 1.9). A push that leaves the version as it is only builds.
The builder image (tic80pro) and the tools image come from the update server's ci_platforms config, not from the repo. With ci-tools the lint step went from 10–15 s to 3 s and the docs step from 12–14 s to 1 s.
Linted, minified, versioned build; docs and native players attached as ReleaseAssets; the public game index updates itself. All build logic lives server-side — the repo carries only the marker and its token.
TIC-80 Pro is a commercial product, sold on itch.io. The source is open, so with a license you can build your own Pro binary in a clean image.
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y \
build-essential cmake git \
libsdl2-dev libpipewire-0.3-dev libwayland-dev \
ruby-dev lua5.4 libcurl4-openssl-dev \
&& rm -rf /var/lib/apt/lists/*
RUN git clone --recursive https://github.com/nesbox/TIC-80 /opt/TIC-80
RUN cd /opt/TIC-80/build \
&& cmake -DBUILD_PRO=On -DBUILD_SDLGPU=On -DBUILD_WITH_ALL=On -DBUILD_STATIC=On .. \
&& cmake --build . --parallel
ENV PATH="/opt/TIC-80/build/bin:${PATH}"
WORKDIR /workspace
CMD ["/bin/bash"]
| Flag | Why |
|---|---|
BUILD_PRO=On |
enables the Pro features |
BUILD_SDLGPU=On, BUILD_WITH_ALL=On |
GPU support and every optional feature |
BUILD_STATIC=On |
one portable binary, no shared library surprises |