Project Model #
Budo projects are intentionally plain. A project directory contains an entrypoint, optional metadata, and any assets your app needs. There is no required package manager, manifest format, bundler, or generated source tree.
Entrypoints #
At startup, Budo scans the project directory in this order:
- main.ts
- main.js
- main.lua
- main.wat
- main.wasm
The first file found selects the runtime. main.ts and main.js run in QuickJS. main.lua runs in Lua 5.4. main.wat and main.wasm run through Wasmtime on desktop.
Android supports QuickJS apps and Lua apps in the native runtime. Web export supports QuickJS and Lua. Wasmtime is a desktop feature.
Project shape #
When growing and getting organized, a Budo project can often looks like this:
my-app/
app.json
main.ts
draw.ts
input.ts
state.ts
shader.vert
shader.frag
assets/
logo.svg
Inter-Regular.ttf
data/
seed.db
models/
classifier.onnxThe app.json file #
The optional app.json file is used to source application metadata and security policies.
{
"name": "My Budo App",
"author": "N. Developer",
"version": "1.0.0",
"date": "2026-05-01",
"orientation": "portrait",
"network": ["api.example.com"],
"filesystem": false,
"neural": true
}If it's not present, Budo falls back to the directory name, Unknown author, version 1.0, and unspecified orientation.
Network access is disabled by default. Set network to "*", a comma-separated domain string, or an array of domains to allow HTTP fetches.
On Android, external filesystem access is disabled by default. Bundled project files remain readable. Set filesystem to true only when the app genuinely needs shared storage access.
ONNX Runtime inference is opt-in at the app level. Set neural to true before calling sys.neural APIs.
Android packaging reads additional fields from app.json for identity, icons, SDK versions, permissions, signing, shrinking, store listing metadata, and output details. See Packaging and deployment for the release-oriented shape.
Paths #
Most APIs that load files resolve paths relative to the project directory: shaders, fonts, SVGs, textures, modules, ONNX models, and app.json policy references.
Paths must be relative. Absolute paths and traversal through .. are rejected by APIs that load project assets. This rule keeps desktop, Android packaging, and web export aligned: if a path works locally, it can usually be bundled safely.
sys.files exposes a virtual root. assets/ resolves from the application bundle. sys.assets is a read-only convenience wrapper that prepends this mount; its paths are bundle-relative, it has no writes, and it shares the sys.files error state. files/ resolves from host storage: on desktop it defaults to the project directory unless --file-root is passed; on Android it defaults to app-specific external files or uses broad external storage when enabled and granted by policy.
TypeScript support #
TypeScript support is deliberately lightweight. The Budo runtime strips type annotations, interfaces, access modifiers, generic type syntax, as casts, and related TypeScript-only constructs while preserving line and column positions as much as possible. It then runs the stripped JavaScript as a normal js content in QuickJS.
This gives a smooth editor experience with budo.d.ts and no build step. But it does not run the TypeScript compiler, emit decorator transforms, bundle modules, or type-check before execution.
Capabilities #
The sys.capabilities object lets code adapt to the runtime it is actually running on.
// Check for neural inference before loading a model.
if (sys.capabilities.neural.available) {
// Use the feature only on builds that expose it.
console.log('Neural inference is available');
}
// Check for sensor support before offering tilt controls.
if (!sys.capabilities.sensors.available) {
// Pick a fallback that still works on desktop.
console.log('Using keyboard fallback for tilt controls');
}The main capability entries are neural, midi, udp, http and sensors.
Runtime lifecycle #
When a Budo application executes, the main code is evaluated once, then the runtime enters a frame loop. Each frame follows the same sequence: poll platform events, update input and timing, poll asynchronous subsystems such as MIDI, UDP, and network completion, clear or prepare the canvas, invoke your animation callback, run pending jobs when needed, and present the frame. It's usual to run at 120 fps.
The important part for application authors and developpers is that your frame callback should finish quickly and request the next frame when it wants continuous animation. Long blocking work will block rendering and input.