SDK CLI reference
The bunx hypercolor authoring CLI: build, validate, install, add. Flags, exit codes, and env vars for the effect workspace toolchain.
The authoring CLI ships inside hypercolor and runs from a scaffolded effect workspace. It compiles your TypeScript and GLSL into self-contained HTML artifacts, validates them, and installs them so the daemon can pick them up. This page is the exhaustive reference for every command, flag, exit code, and environment variable.
This is the authoring CLI: hypercolor resolved from your effect workspace, used to build and ship effects. It is a different binary from the system CLI (hypercolor, installed alongside the daemon) that talks to the running daemon to list devices, apply effects, and manage scenes. The system CLI lives in its own reference. When this page says bunx hypercolor build, it means the workspace tool, never the daemon client.
#Invocation
Inside a scaffolded workspace the CLI is wired three ways, all equivalent:
# Through the package script (the scaffold defines these)
bun run build
# Through bunx, resolving the hypercolor bin
bunx hypercolor build
# Directly against the source entrypoint (what build:effects uses in-repo)
bun run packages/core/src/cli.ts buildThe bin name is hypercolor and resolves through the workspace’s hypercolor dependency. The companion scaffolder is a separate package, create-hypercolor, invoked as create-hypercolor-effect and covered in its own section below.
Run with no command, --help, or help to print usage:
bunx hypercolor --helphypercolor <command>
Commands:
build Build effect entrypoints into HTML artifacts
validate Validate built HTML artifacts
install Install HTML artifacts into the user effects directory
add Scaffold a new effect inside the workspace#Command map
graph TD
ADD["add: scaffold an effect"] --> BUILD["build: compile to HTML"]
BUILD --> VALIDATE["validate: check artifacts"]
VALIDATE --> INSTALL["install: ship to the daemon or user dir"]
The everyday loop is add to create an effect, build to compile it, validate to confirm the artifact is well-formed, then install to deliver it. Those four are the whole surface; there is no preview-server command.
#build
Compile effect entrypoints into self-contained HTML artifacts. Each effects/<id>/main.ts (or faces/<id>/main.ts) becomes one HTML file in the output directory, with the JS bundle, inlined shader source, palette tables, and <meta> control metadata all baked in. Display-face font controls can load selected Google Fonts at runtime unless capture mode disables remote fonts.
# Build everything the workspace discovers
bunx hypercolor build --all
# Build one effect by entrypoint path
bunx hypercolor build effects/my-effect/main.tsWith no positional path and no --all, the CLI builds all discovered entrypoints anyway: an empty positional list implies --all.
#Flags
| Flag | Argument | Default | Behavior |
|---|---|---|---|
--all | — | implied when no path given | Discover and build every entrypoint under each entry root. |
--out | <dir> | dist | Output directory for HTML artifacts, resolved against the cwd. |
--entry-root | <dir> | effects | Root to scan for <id>/main.ts. Repeatable; pass once per root. |
--workspace-root | <dir> | . | Root that --all discovery walks from. |
--sdk-alias-path | <file> | (none) | Alias the hypercolor import to a source file. Used in-repo to point at packages/core/src/index.ts. |
--minify | — | off | Minify the bundled JS. |
--prune | — | off | Delete artifacts in --out with no matching entrypoint. Requires --all; the build errors otherwise. |
--watch | — | off | Rebuild on .ts / .glsl changes. Ctrl-C (SIGINT) stops the watchers. |
--entry-root is repeatable, which is how the in-repo build:effects script builds effects and faces in one pass:
bun run hypercolor build --all \
--workspace-root . \
--entry-root src/effects \
--entry-root src/faces \
--out ../effects/hypercolor \
--sdk-alias-path packages/core/src/index.ts \
--prune#Output
On success each artifact prints a line: a ✓ for canvas and shader effects, a 💎 for display faces, followed by the effect id, the output path, and the artifact size in KB.
✓ my-effect → dist/my-effect.html (42.3 KB)
💎 now-playing → dist/now-playing.html (58.1 KB)The build enforces correctness: these fail the build, they do not warn. If your source reads audio (audio(, ctx.audio, getAudioData(, or engine.audio) but the effect didn’t set audio: true in its options, the build throws an audio-validation error. Reading input (getInputData(, engine.keyboard, or engine.mouse) without input: true throws the matching input-validation error. Every shader control except asset must have a matching uniform i<Key> in the GLSL, or the build reports missing control uniforms. And a module that never calls canvas(), effect(), or face() fails metadata extraction with “no effect definitions were registered.” Treat a clean build as a real gate, not a formality.
#validate
Check one or more built HTML artifacts for the required render surface, title, and script. Validation runs automatically before every install, but you can run it standalone, for example in CI against dist/*.html.
bunx hypercolor validate dist/*.html
bunx hypercolor validate dist/my-effect.html --strict
bunx hypercolor validate dist/my-effect.html --jsonAt least one file is required. With no files the CLI prints the usage line and exits non-zero.
#Flags
| Flag | Behavior |
|---|---|
--strict | Treat warnings as failures. Exit non-zero if any artifact has a warning, even when no hard errors are present. |
--json | Emit the raw validation result as JSON instead of the human-readable PASS/FAIL report. A single file emits one object; multiple files emit an array. |
#Human output
Each file reports a PASS/FAIL header, then one WARN line per warning and one FAIL line per error, then a final result with the warning count:
dist/my-effect.html
PASS Render surface + title + script
Result: PASSA failing artifact lists its errors:
dist/broken.html
FAIL Validation errors present
FAIL Missing required render surface
Result: FAIL#install
Validate and deliver built artifacts. By default the CLI copies artifacts into your local user effects directory; with --daemon it uploads them to a running daemon over HTTP. Both paths validate first and reject invalid artifacts.
# Local install into the user effects directory
bunx hypercolor install dist/*.html
# Upload to a running daemon
bunx hypercolor install dist/*.html --daemonWith no positional patterns, install defaults to dist/*.html. Patterns may be globs or plain paths; non-matching plain paths are silently skipped.
#Flags
| Flag | Argument | Default | Behavior |
|---|---|---|---|
--daemon | — | off | Upload to the daemon’s install endpoint instead of copying locally. |
--daemon-url | <url> | $HYPERCOLOR_DAEMON_URL or http://127.0.0.1:9420 | Base URL of the daemon for --daemon installs. |
#Local install path
Local artifacts land in the user effects directory, derived from XDG:
$XDG_DATA_HOME/hypercolor/effects/user/
# falls back to ~/.local/share/hypercolor/effects/user/ when XDG_DATA_HOME is unsetOn a name collision the installer appends a numeric suffix (my-effect.html, my-effect-2.html) rather than overwriting. The daemon picks up new local artifacts on startup or when you ask it to rescan from the system CLI:
hypercolor effects rescan#Daemon install path
The --daemon path POSTs each validated artifact as a multipart form (field file) to /api/v1/effects/install on the daemon base URL:
/api/v1/effects/installUpload a built HTML effect to the running daemon. Multipart form, field file, content type text/html. Returns the standard { data, meta } envelope where data carries { id, name, path, controls, presets }: the installed effect id (the value you pass to apply it afterwards), its name, its on-disk path, and the count of controls and presets the daemon extracted.
On a successful daemon install the CLI prints the installed name and control count:
✓ dist/my-effect.html → /home/you/.local/share/hypercolor/effects/user/my-effect.html (my-effect, 4 controls)If the daemon rejects the artifact, the CLI surfaces the daemon’s error details when present, otherwise the HTTP status.
The scaffold defines ship and ship:daemon package scripts so you rarely type the install flags by hand. bun run ship is the local install, bun run ship:daemon is the daemon upload. Use the daemon path while iterating against a live app, and the local path when you want the effect to survive a daemon restart.
#add
Scaffold a new effect inside an existing workspace. Runs interactively when name or template is missing, prompting for the parts you didn’t pass.
bunx hypercolor add aurora --template canvas
bunx hypercolor add aurora --template shader --audio
bunx hypercolor add # fully interactive#Flags
| Flag | Argument | Behavior |
|---|---|---|
--template | canvas | shader | face | html | Starter template for the new effect. |
--audio | — | Seed audio-reactive boilerplate and set audio: true. |
When both a name and a valid --template are supplied, add runs non-interactively. Otherwise it prompts for the missing pieces. On success it prints the entry path of the new effect:
Entry: effects/aurora/main.tsIf $VISUAL or $EDITOR is set, add opens the new entrypoint in that editor.
#There is no dev command
build, validate, install, and add are the complete command set. bunx hypercolor dev takes the unknown-command path: it prints Unknown command "dev". on stderr, follows it with the usage banner, and exits 1.
The real iteration loop is build, ship, then preview inside the actual daemon or desktop app. Any guide that mentions a standalone effect preview server is stale.
#Exit codes
Every command returns a process exit code you can rely on in scripts and CI.
| Command | 0 (success) | Non-zero (1) |
|---|---|---|
build | At least one artifact built. | No entrypoints found, or a build error (missing audio: true, shader uniform mismatch, no registered effect). |
validate | All artifacts valid; under --strict, also no warnings. | Any artifact invalid, any warning under --strict, or no files given. |
install | At least one artifact installed and none failed. | Any artifact failed, or zero artifacts installed. |
add | Effect scaffolded. | Missing name or template after prompting. |
unknown command (including dev) | — | 1, after printing the error and the help banner. |
#Environment variables
| Variable | Used by | Default | Effect |
|---|---|---|---|
HYPERCOLOR_DAEMON_URL | install --daemon | http://127.0.0.1:9420 | Daemon base URL for daemon installs. --daemon-url overrides it. |
HYPERCOLOR_SDK_PACKAGE_SPEC | create-hypercolor-effect | published caret range | Overrides the hypercolor dependency spec for new workspaces (e.g. a file: path to a local checkout). --sdk-spec overrides it. |
VISUAL / EDITOR | add | (none) | Editor opened on the new entrypoint. VISUAL wins when both are set. |
#The scaffolder: create-hypercolor-effect
Bootstrapping a brand-new workspace is a separate package, create-hypercolor, exposed as the create-hypercolor-effect bin. The authoring hypercolor add command reuses it internally to add effects to an existing workspace.
bun create hypercolor my-effects --template canvascreate-hypercolor-effect [name] [options]
Options:
--template <type> Starter template: canvas, shader, face, html
--first <effect-name> Name of the first effect (default: my-effect)
--audio Include audio-reactive starter boilerplate
--no-git Skip git init
--no-install Skip bun install
--sdk-spec <spec> Override the hypercolor dependency spec.
Defaults to the published caret range.New workspaces depend on the published hypercolor package by default. To author against a local engine checkout, pass --sdk-spec file:../hypercolor/sdk/packages/core or set the HYPERCOLOR_SDK_PACKAGE_SPEC environment variable. Bun’s link: is not a drop-in for a relative path here; use file:.
The scaffolder runs interactively when the workspace name or template is missing, otherwise it builds the workspace directly. It initializes git and runs bun install by default; --no-git and --no-install opt out. When finished it prints the next command: bun run build for code templates, bun run validate for the raw html template.
#Scaffolded package scripts
A fresh workspace defines the everyday commands as package scripts, so the typical loop is short bun run invocations rather than long flag strings:
| Script | Runs | Purpose |
|---|---|---|
bun run build | hypercolor build --all | Build every effect to dist/. |
bun run build:one | hypercolor build | Build a single effect by entry path. |
bun run validate | hypercolor validate dist/*.html | Validate built artifacts. |
bun run ship | hypercolor install dist/*.html | Local install into the user effects directory. |
bun run ship:daemon | hypercolor install dist/*.html --daemon | Upload to the running daemon. |
bun run add | hypercolor add | Scaffold another effect interactively. |
#Where to go next
The dev workflow page walks the full build, validate, ship loop with screenshots and the live-preview story. The setup page covers installing Bun and scaffolding a workspace from scratch, including the file: spec for local engine checkouts. For the runtime API the CLI compiles against, see the SDK API reference page in this section. And for the daemon-facing client this CLI is deliberately not, see the system CLI reference.