Skip to content

SJON Reference

PNGine source is SJON: S-expression forms validated against the WebGPU schema in schema/pngine.sjon. Each form maps to a WebGPU resource or operation.

The words in those forms are WebGPU’s, re-spelled: a form is a descriptor, a key is one of its members, a value is one of the member’s legal values, and every page below ends with a table tracing each key and value to its definition in the specification. How that tracing is made, and what keeps it from rotting, is Where the Words Come From.

PNGine is a host of the SJON language: SJON owns the syntax, validation, cross-reference resolution, expressions and sugar lowering, while PNGine owns the schema above, the pngine/* lowering hooks (the compiler steps that expand sugar into ordinary forms), and bytecode emission. For the language itself (reader syntax, value kinds, how a schema is written), see the SJON documentation.

Every named form also requires its :name; the column below lists what else the form cannot compile without.

Form Purpose Required
(shader-module …) Named WGSL module :code
(buffer …) GPU buffer :usage, and a size from :size, :data, :index-of or :file
(texture …) GPU texture :size, :format, :usage
(texture-view …) Explicit view over a texture :texture
(sampler …) Texture sampler -
(query-set …) Occlusion / timestamp queries :type, :count
(bind-group-layout …) Explicit bind group layout - (an (entry …) per binding is usual; an empty layout is legal WebGPU)
(pipeline-layout …) Explicit pipeline layout :bind-group-layouts
(bind-group …) Resource binding :layout
(render-pipeline …) Render pipeline :layout, (vertex …)
(compute-pipeline …) Compute pipeline :layout, (compute …)
(render-pass …) Render pass an attachment (a pass with attachments alone is a clear-only pass)
(compute-pass …) Compute pass :pipeline, one (dispatch …)
(render-bundle …) Pre-recorded draw sequence :color-formats, one (draw …)
(frame …) Frame execution :perform
(data …) Embedded data / shapes :float32, a shape sub-form, or :file
(queue …) Queue operations - (an empty queue is legal; several actions are usual)
(image-bitmap …) Decoded image → texture :data
(wasm-call …) WASM function call :file, :func, :returns
(init …) One-shot compute init (sugar) :buffer, :module, :workgroups
(pass-graph …) Shader-art (pass …) sugar one (pass …)
(define …) Compile-time constant :value
(limits …) Raised device limits (singleton) -
(canvas …) Canvas alpha mode (singleton) -
  • (data …) - Embedded float arrays, shape generators, image files, or WASM-generated bytes
  • (queue …) - Buffer writes, copies, and query resolves
  • (wasm-call …) - Call WASM module functions each frame
  • References - Bare identifier resolution and special values
  • Expressions - Compile-time arithmetic and constants

A few capabilities are deliberately absent today:

  • Multi-file documents. There is no include or import form; keep every shader, resource, pipeline, pass and frame in a single .sjon file.
  • Shader fragment composition. (shader-module …) holds one self-contained WGSL module as opaque text; there is no fragment/imports mechanism. Repeat shared helper code in each module that needs it.
  • Timeline / scene scheduling. SJON models a frame loop; drive scene changes from the JavaScript API instead.
(form-name :key value :flag true
(sub-form positional :key value)
[array of items])
; comments start with a semicolon
Type Example Description
String "WGSL code" / """multi-line""" Quoted / triple-quoted text
Number 123, 0.5 Decimal or float
Boolean true, false Boolean values
Symbol myName, triangle-list Bare identifier (enum or cross-ref)
Array [a b c] Space-separated list
Sub-form (vertex :module code :entry vs) Positional nested form
Expression (* NUM 16) Compile-time arithmetic

Resources are referenced by name. SJON’s validator resolves identifiers document-wide against the declared forms:

:pipeline mainPipeline ; resolves to a (render-pipeline …) or (compute-pipeline …)
:module shader ; resolves to a (shader-module …)
:buffer vertices ; resolves to a (buffer …)

Names live in one namespace across every form kind, so a buffer and a texture cannot share one, and the built-in spellings (canvas, context-current-texture, preferred-canvas-format, pngine-inputs) cannot be declared at all.

See References for complete resolution rules.