CLI Reference
pngine is one binary. It compiles a .sjon document (SJON, the S-expression
format in which PNGine describes WebGPU work) to PNGB, PNGine’s compact
bytecode; wraps that bytecode in a PNG or a standalone HTML file; renders
frames on a native GPU; and replays a compiled payload to report what it does.
Installation
Section titled “Installation”npm install pngine@^3The flags and messages on this page are pngine 3.0.0’s. Or build from source:
git clone https://github.com/HugoDaniel/pngine.gitcd pnginezig buildSynopsis
Section titled “Synopsis”pngine <command> <input> [options]With no command, pngine <input.sjon> runs render.
Commands
Section titled “Commands”| Command | Purpose |
|---|---|
render |
Create a PNG with embedded bytecode (the default) |
compile |
Compile source to raw .pngb bytecode |
validate |
Check source: syntax, semantics, WGSL |
inspect |
Replay bytecode against a mock GPU (a call recorder), or under a real WASM runtime for deep analysis |
embed |
Embed bytecode into an existing PNG |
extract |
Extract bytecode from a PNG or ZIP |
bundle |
Package a shader and assets into a ZIP |
list |
List the contents of a ZIP or PNG |
diff |
Pixel-compare two PNGs under a tolerance model |
help, version |
Show help / version |
render (default)
Section titled “render (default)”Compile source and write a PNG carrying the bytecode in its pNGb chunk (an
ancillary PNG chunk that image tools carry along untouched). By default the
image is a 1×1 transparent pixel and the payload is self-contained: the
bytecode plus the executor, the small WASM interpreter that plays it, in the
smallest variant that covers the document’s features.
pngine <input.sjon> [-o output.png] [options]pngine render <input.sjon> [-o output.png] [options]| Option | Description |
|---|---|
-o, --output <path> |
Output PNG path (default: <input>.png) |
-f, --frame |
Render an actual frame via GPU instead of a 1×1 pixel (needs a GPU-capable build, see below) |
-s, --size <WxH> |
Dimensions when using --frame (default: 512x512) |
-t, --time <seconds> |
Time value for animation (default: 0.0) |
-n, --scene <name> |
Render one named frame (default: all frames) |
-e, --embed / --no-embed |
Embed bytecode in the PNG (default: on) |
--no-executor |
Omit the embedded executor; the page must supply pngine.wasm |
-m, --minify |
Minify WGSL shaders (~30% smaller shader text after compression; no effect on mesh data) |
--validate / --no-validate |
WGSL validation at compile time (default: on) |
--html |
Emit a self-contained HTML file instead (implies --flat; give it -o name.html, since the default output path still ends in .png) |
--fullscreen / --no-fullscreen, --fixed |
HTML canvas sizing (default: fullscreen) |
--unpack |
Emit unpacked HTML (no deflate; larger but debuggable) |
--flat |
Write a pNGf chunk (flat, pre-decoded command buffers for the small main-thread player, pngine/mini) instead of bytecode; no executor |
-a, --audio <path> |
Embed an audio WASM (e.g. a song compiled with the Sointu tracker synthesizer) |
--types |
Also write TypeScript definitions (.d.ts) for the uniforms |
--frame is the one option that needs a GPU. It renders through the native
backend, wgpu-native, which a CLI carries only when it was built against that
vendored library (the default when you zig build on a macOS host, after
scripts/download-wgpu-native.sh). The binaries npm ships are GPU-less by
design and stop with “needs a native GPU backend that this build lacks”, exit
code 7. Everything else, the default 1×1 output included, is pure compilation
and runs everywhere.
compile
Section titled “compile”Compile source to raw bytecode.
pngine compile <input.sjon> [-o output.pngb]| Option | Description |
|---|---|
-o, --output <path> |
Output .pngb path (default: <input>.pngb; stdout when the input is -) |
-m, --minify |
Minify WGSL shaders (~30% smaller shader text after compression) |
--embed-executor |
Embed the executor WASM in the payload |
--executors-dir <dir> |
Where to load executor variants from (default: zig-out/executors) |
--validate / --no-validate |
WGSL validation (default: on) |
Note the asymmetry with render: compile produces a bare payload and takes
--embed-executor to opt in, while render embeds the executor by default
and takes --no-executor to opt out.
validate
Section titled “validate”Source-level validation: the full compiler pipeline with WGSL checking, but no output artifact.
pngine validate <input.sjon> [--json] [--strict] [--verbose]| Option | Description |
|---|---|
--json |
Structured JSON report on stdout |
--strict |
Exit 1 on warnings (for CI) |
-v, --verbose |
Show each validation phase |
It catches syntax errors, semantic errors (unresolved references, schema violations, cycles) and WGSL errors (type errors, undefined symbols, uniformity).
It also reports advisory WGSL lint findings. Advisory means advisory by
default: they print as warnings, ride the --json diagnostics array, and
leave the exit code at 0. The one worth acting on is an unused binding:
:layout auto strips bindings no entry point uses, which desyncs any
(bind-group …) that binds them and otherwise surfaces only as an opaque abort
at render time.
--strict turns every warning into exit 1, for CI. It changes the exit code
and nothing else: --json still reports "status": "ok" for a valid document,
with the findings in diagnostics, so the shape of the report never depends on
the flag. The report also carries a dropped count, because the diagnostic
sink holds 64 entries; a non-zero dropped means diagnostics is a prefix and
“no warnings visible” cannot be read as “no warnings”.
pngine validate shader.sjonpngine validate shader.sjon --json | jqpngine validate shader.sjon --strict # fail CI on advisory findingsinspect
Section titled “inspect”Bytecode-level inspection. The default mode replays the payload against a mock
GPU; --deep runs it under a real WASM runtime for phase-by-phase analysis.
pngine inspect <input> [options]Accepts .sjon, .pngb, .png and .zip.
| Option | Description |
|---|---|
-v, --verbose |
Full GPU call trace |
--json |
Structured JSON on stdout (auto-enables --deep) |
--strict |
Exit 1 on warnings (for CI) |
-q, --quiet |
Only output errors |
--deep |
Enable deep runtime analysis (auto-enabled by the options below) |
--phase <init|frame|both> |
Restrict analysis to one phase |
--frames <list> |
Analyse several frames, e.g. 0,30,60 |
--symptom <name> |
Focus diagnosis: black, colors, blend, flicker, geometry |
-t, --time <seconds> |
Base time for frame 0 |
--time-step <seconds> |
Time between frames (default: 1/60) |
-s, --size <WxH> |
Canvas size (default: 512x512) |
--extract-wgsl |
Include WGSL source in the output |
pngine inspect shader.sjon # quick summarypngine inspect shader.sjon --verbose # full GPU call tracepngine inspect shader.png --symptom black # diagnose a black canvaspngine inspect shader.sjon --frames 0,30,60 --jsonpngine embed <image.png> <bytecode.pngb> [-o output.png]Default output is <image>.embedded.png. Either input may be -, but not both.
extract
Section titled “extract”pngine extract <file.png|file.zip> [-o output.pngb] [--list]-l, --list reports every PNGine ancillary chunk in the file instead of
extracting.
bundle
Section titled “bundle”pngine bundle <input.sjon> [-o output.zip] [--assets <dir>] [--no-runtime]pngine list <file.zip|file.png>Pixel-compare two PNGs under a shared tolerance model.
pngine diff <a.png> <b.png> [--preset <name>] [--precision <f>] [--max-diff <n>] [--json]| Preset | Precision | Max per-channel delta |
|---|---|---|
default |
0.985 | 5 |
high-precision |
0.99 | 2 |
compute-tolerant |
0.95 | 10 |
diff has its own exit codes: 0 match, 1 differ, 2 read error, 4
not-a-PNG or size mismatch.
Piping
Section titled “Piping”Every command takes - as an input path (read stdin) and as -o (write
stdout), so pngine composes:
pngine extract art.png | pngine inspect - # inspect what a PNG carriespngine compile - -o - < shader.sjon > out.pngb # explicit both wayspngine compile - < shader.sjon > out.pngb # -o defaults to stdout for `-`curl -s "$url" | pngine validate - --json | jqpngine - --frame -s 512x512 -o - < shader.sjon | open -f -a PreviewThe rules:
- stdin is sniffed, not named. The leading bytes decide: a PNG signature,
PNGB,PK\x03\x04, else SJON text. Bytes matching none of those are rejected as unrecognized rather than guessed at. - A real path still dispatches on its extension. A
.pngwhose contents are not a PNG reports a PNG error rather than being silently reinterpreted. -as input defaults the output to stdout, so-o -is optional.- Binary output is refused on a terminal (“pipe or redirect it”); text reports print to a terminal freely.
- stdout carries only the artifact or the
--jsonreport. Every status line, warning and diagnostic goes to stderr, so| jqand> filestay clean. - Two-input commands accept
-for at most one side;diff - -is an error, not a comparison of stdin with itself. - Piped source resolves relative references (a
(data … :file "mesh.bin")path, a(wasm-data … :file …)module) against the current directory, since it has no directory of its own. - Input is capped at 16 MiB, the same guard that applies to file reads.
Examples
Section titled “Examples”Compilation
Section titled “Compilation”# Self-contained PNG with embedded bytecode + executorpngine shader.sjon
# Custom output pathpngine shader.sjon -o build/shader.png
# Raw bytecode onlypngine compile shader.sjon -o shader.pngb
# Smaller payload: minified WGSLpngine compile shader.sjon -o shader.pngb --minifyGPU Rendering
Section titled “GPU Rendering”pngine shader.sjon --frame # 512x512 previewpngine shader.sjon --frame -s 1920x1080 # 1080ppngine shader.sjon --frame -t 2.5 # frame at t=2.5spngine shader.sjon --frame --no-embed # image only, no payloadChecking
Section titled “Checking”pngine validate shader.sjon # source errors + advisory lintpngine inspect shader.png # what the payload doespngine inspect shader.sjon --verbose # full GPU call tracePNG Manipulation
Section titled “PNG Manipulation”pngine embed artwork.png shader.pngb -o final.pngpngine extract final.png -o extracted.pngbInput Formats
Section titled “Input Formats”| Extension | Description |
|---|---|
.sjon |
SJON source (schema-driven S-expressions) |
.pngb |
Compiled bytecode |
.png |
PNG with embedded bytecode |
.zip |
Bundle produced by pngine bundle |
Exit Codes
Section titled “Exit Codes”| Code | Description |
|---|---|
| 0 | Success |
| 1 | Invalid arguments; for validate, a document with errors (or warnings under --strict) |
| 2 | File I/O error |
| 3 | Compilation error: compile, render and inspect exit 3 on a document that fails validation |
| 4 | Format error (PNG/PNGB) |
| 5 | Execution error |
| 6 | -n, --scene named a frame the payload does not define |
| 7 | --frame on a build without the native GPU backend |
| 8 | A GPU validation error during --frame |
diff overrides these with its own scheme (see above).
Environment
Section titled “Environment”The CLI reads no environment variables. All configuration is via command-line arguments.
NPM Integration
Section titled “NPM Integration”When installed via npm, use npx:
npx pngine shader.sjon -o shader.pngnpx pngine inspect shader.png --verboseOr add to package.json scripts:
{ "scripts": { "build:shader": "pngine src/shader.sjon -o dist/shader.png", "build:preview": "pngine src/shader.sjon --frame -o preview.png" }}Related
Section titled “Related”- Getting Started - First program tutorial
- SJON Syntax - Language overview
- Shipping a Player - What
--flat,--htmland--no-executorare for - Bytecode Format - PNGB specification
- Limits & Roadmap - The caps
validateenforces