Targ Apps Docs
Chroma

CLI

Every chroma command, flag, environment variable, and exit behavior for scaffolding, serving, validating, and building apps.

The chroma command (chroma.sh on macOS and Linux, chroma.ps1 on Windows) scaffolds, serves, validates, and builds Chroma apps. For a first run from scratch, follow the Quick start. This page is the reference.

chroma build always produces the same dist/ layout for a given chroma.json; it does not emit separate "web" and "desktop" binaries. You choose Portable (Web) or Executable (Chroma Desktop) when you run the bundle (static host, browser tab, or chroma-desktop). The plugin list in chroma.json is copied into every build; hooks and public APIs are the same, but storage, origins, and some plugin internals follow the runtime mode.

CommandUse it to
chroma installProvision ~/.chroma (once per machine, or after updating Chroma)
chroma new <name>Create an app from a template
chroma validateCheck chroma.json and the project layout
chroma devServe the app locally while you edit
chroma buildProduce dist/ (and optionally a .zip)
chroma fs init, chroma fs linkSet up Chroma-Fs storage
chroma addVendor a legacy library into the app
chroma help, chroma --versionPrint usage or the CLI version

Install

Install the CLI from the installer package that Targ Apps provides to licensed customers (see the Quick start). Extract the package and run ./chroma.sh install (macOS/Linux) or .\chroma.ps1 install (Windows).

chroma install [--no-rebase-root]

install copies the chroma command, the dev-server binary (engine), the templates, the compiled engine (pkg/), and the first-party plugins into $CHROMA_HOME, then adds $CHROMA_HOME/bin to your PATH. It also installs chroma-desktop when a binary or Chroma-Desktop source is available to it.

FlagEffect
--no-rebase-rootUpdate in place. Without it, every run wipes $CHROMA_HOME and reinstalls from scratch, which is the safe way to repair a broken install.

CHROMA_HOME defaults to ~/.chroma; export it before running install to change it.

~/.chroma/
├── bin/            # chroma, engine, chroma-desktop
├── engine-src/     # compiled engine (plus Rust source when installed from a full checkout)
├── plugins/        # first-party plugins
└── templates/      # static, dynamic, standard

The installer package installs the prebuilt engine; it does not install Node.js, Rust, or wasm-pack.

Commands

chroma new

chroma new <name> [--template static|dynamic|standard]

Creates ./<name> from the chosen template, with a fresh pkg/, the plugins listed in the template's chroma.json vendored under plugins/, and the name fields rewritten to <name>. The default template is static. init is an alias for new.

It fails if <name> already exists, if the template is not one of the three names, or if chroma install has not run. Template differences are in Templates.

chroma validate

chroma validate [--strict] [--no-schema]

Run it before dev or build. On macOS and Linux it runs two steps:

  • JSON Schema: chroma.json, and chroma-fs.json when present, against the schema catalog (needs Node.js and npm; it installs ajv into a temporary folder on every run). The catalog is read from $CHROMA_SCHEMAS (a directory containing chroma/chroma.schema.json), the schemas/ folder installed under $CHROMA_HOME, or the docs catalog when run inside the TargApps workspace. If none is found, the step is skipped with a warning. A schema failure exits immediately, before the structural step.
  • Structure: name exists and mode is static or dynamic; entry and pkg/chroma.js (or chroma.d.ts) exist; a dynamic app has an existing build.jsEntry; a static app's build.scripts files exist; each plugins[] id has a plugins/<id>/ folder. Legacy .js files under src/ produce a warning, as do a plugin without chroma.plugin.json and a static app with no build.scripts.

On Windows, chroma.ps1 validate takes no flags and runs only the structural checks (no schema step, no plugin-manifest warning, no build.scripts check).

FlagEffect
--no-schemaSkip the JSON Schema step; run only the structural checks.
--strictParsed and listed in --help (which says it treats warnings as errors), but it has no effect: nothing reads the flag after it is parsed, and warnings never change the exit code.

On macOS and Linux any other option is an error. See chroma.json for the schema.

chroma dev

chroma dev [--port <n>] [--no-desktop] [--no-reload] [--reload]

Serves the current directory with the installed dev server (engine) and requires a chroma.json. Static and dynamic apps are served the same way: files are loaded as native ES modules, with .chr served as JavaScript, so nothing is bundled in development.

FlagEffect
--port <n>Port to use. Default: dev.port from chroma.json, else 4173.
--no-desktopServe in the browser only, even if dev.desktop is true.
--no-reload, --no-hot-reloadDisable live reload for this session.
--reload, --hot-reloadEnable live reload for this session.

Unknown flags are ignored.

Desktop develop mode. For a dynamic app with dev.desktop: true (the dynamic and standard template default), chroma dev starts the dev server in the background and then runs chroma-desktop --develop --url http://127.0.0.1:<port>/<entry> --title <name>. The Desktop binary must be installed in $CHROMA_HOME/bin; if it is missing, the command stops with an error that tells you to run chroma install from a full workspace checkout or pass --no-desktop. Live reload is always off in this mode.

Ports. chroma dev uses the port above (the templates set 5173 for dynamic and 4173 for static). Chroma Desktop serves imported apps from a different, fixed loopback port; see Server.

Live reload switches (dev.hotReload, CHROMA_DEV_RELOAD, debounce) are documented once in Live reload.

dev.desktop only changes which host opens during development (browser vs Desktop develop window). It does not change chroma build output or which plugins are vendored. For a matrix of host-specific behavior, see Runtime modes.

chroma build

chroma build

Packages the app into outDir (default dist/), which is wiped on every run. What happens depends on mode:

staticdynamic
SourceFlat HTML and JS filesES modules under src/**/*.chr from one build.jsEntry
Scripts and stylesEach file in build.scripts and build.styles is minified as written (esbuild)The module graph is bundled into one outfile (default bundle.js) with esbuild (--bundle --format=esm --minify, .chr loaded as JS)
WASM engineInlined as base64 into a minified chroma.js; no .wasm file shipschroma_bg.wasm is copied next to the bundle; it must stay beside it
Engine rebuildNeverIf engine.rebuildWasm is true, the engine is recompiled from $CHROMA_HOME/engine-src first (needs a source-checkout install)
package.zipNot usedIf true, dist/ is archived next to outDir, named after name

The archive is <name>.zip (or .tar.gz when zip is not installed on macOS/Linux; always .zip on Windows). It is the bundle Chroma Desktop imports; see Desktop. Plugins, chroma.json, and chroma-fs.json are copied into the output.

Typical dynamic output:

dist/
├── index.html
├── bundle.js
├── chroma_bg.wasm
├── plugins/
└── chroma.json

To deploy dist/ on a static host, see Server. To import the zip into Chroma Desktop instead, see Desktop. Both paths use the same artifact; only the host changes (Runtime modes).

Compile pipeline

Three layers are involved when you ship a dynamic app:

LayerWhat compiles itOutputRuntime mode
EngineRust → wasm-pack build --target web (via Chroma/build.sh or engine.rebuildWasm during chroma build)pkg/chroma.js + chroma_bg.wasm (or WASM inlined for static)Same WASM in Portable and Executable hosts
Appchroma build → esbuild (dynamic) or per-file minify (static)dist/bundle.js (or minified scripts), index.html, copied plugins/Loaded by browser or Desktop iframe
HostNot part of chroma buildBrowser tab, static CDN, or chroma-desktop + optional .zip importChooses origins, reload, and Chroma-Fs bridges

There is no separate chroma build --desktop flag today. Test Portable by serving dist/ (Server) and Executable by importing the zip (Desktop). Optional plugins such as Chroma-Style and Chroma-t2t ship in the same plugins/ folder regardless of host; per-plugin and per-tool matrices live in Extensions & plugins by mode and Runtime modes.

chroma fs init writes chroma-fs.json in the current app and fails if it already exists. The default backend is filesystem for dynamic apps and indexeddb for static apps.

chroma fs link builds Chroma-Fs from a workspace checkout and links the @chroma/fs package into the project (npm install file:...). It requires Node.js. The standard template exposes both as npm run fs:init and npm run fs:link. Storage options are in the Chroma-Fs docs.

chroma add

chroma add <lib-id> [--list] [--force] [--dry-run] [--registry <path>]

Vendors a UMD or global-script library from the legacy-library registry into the current app (requires Node.js). First-party plugins such as chroma-fs and chroma-style come with chroma install and chroma new, not chroma add. Details: chroma add.

chroma help and chroma --version

chroma help (also --help, -h, or no arguments) prints usage. chroma --version (or -v) prints the CLI version.

Environment variables

VariableDefaultEffect
CHROMA_HOME~/.chromaInstall root.
CHROMA_SCHEMASunsetDirectory with the JSON Schema catalog used by validate.
CHROMA_DEV_RELOADon for browser devSee Live reload.
CHROMA_LOG_LEVELlogVerbosity of the CLI and the dev server.

Log levels

Command output looks like [Chroma] <pid> - <timestamp> LOG [Dev] Starting dev server on port 4173..., with a level and a context (Install, New, Dev, Build, Fs, Desktop, Validate, Add, Cli). Set CHROMA_LOG_LEVEL to one of these values (case-insensitive); each level also shows everything above it:

ValueShowsStream
errorERRORstderr
warnplus WARNstderr
log (default)plus LOGstdout
debugplus DEBUGstdout
verboseplus VERBOSEstdout

An unknown value falls back to log. Colors apply only on a terminal.

Requirements

  • Node.js (with npx): chroma build bundles and minifies with esbuild. chroma add, chroma fs link, and the schema step of chroma validate also use it.
  • wasm-pack: the dynamic build checks for it on PATH and stops if it is missing, even when engine.rebuildWasm is false. chroma install installs it only from a source checkout.
  • Rust: needed only to compile the engine (engine.rebuildWasm: true), which requires an install from a source checkout.

The per-command table, including what the installer does and does not provision, is in the Quick start.

Troubleshooting

Every error below exits with status 1.

MessageCause and fix
chroma.json not found in the current directory.Run the command inside an app folder (one created by chroma new).
'<name>' already exists in the current directory.Pick another name for chroma new.
Invalid template: '<x>' (use 'static', 'dynamic', or 'standard')Use one of the three names.
Chroma environment not found at $CHROMA_HOME. Run 'chroma install' firstRun chroma install.
Compiled engine not found at .../pkg. Run 'chroma install' first.Partial install; run chroma install again (it wipes and rebuilds by default).
engine binary not found at .../bin/engine. Run 'chroma install'.Run chroma install again.
Node.js is required for this step (bundling/minifying).Install Node.js from nodejs.org, then retry chroma build.
wasm-pack not found. Run 'chroma install'.Dynamic build without wasm-pack on PATH. Install it, or reinstall from a source checkout.
chroma-desktop not found. ...Desktop develop mode without the binary. Run chroma dev --no-desktop, or install Chroma Desktop.
Unknown mode in chroma.json: '<x>'mode must be static or dynamic.

Next

On this page