The pack
post
function post(): Pack<{ post: PostApi }>
The post-process runtime pack. Adds the post pass (after lighting, before screen) and exposes
game.post. Owns no effects: compose post-looks and the other post-* packs for those. Supports WebGPU and WebGL2; with no effects added
the pass is skipped and costs nothing.
Helpers
checkPostEffect
function checkPostEffect(def: PostEffectDef, opts: { maxCode?: number } = {}): string | null
Check a post effect definition the way the runtime takes it, with no GPU: the name, each param's default (a finite
number, a CSS colour the kernel parses, or 2 to 4 finite numbers; a param's name is only its key in add and set,
since the snippet reads it by slot, p0..p15) and the sixteen slots they share; the code (it must set color, use no while or loop, since an unbounded loop can hang
the GPU, call no texture function but tex, and stay under maxCode characters, default 8000); and its WebGL2
translation, which names an unknown identifier (a param read by its name rather than its p slot), an unknown
function or an unsupported construct. Returns the problem in one sentence, or null.
registerPostEffect
function registerPostEffect(k: { registry: { add(kind: string, def: PostEffectDef): void } }, def: PostEffectDef): void
Register an effect under the registry kind 'post' so game.post.add(def.name) finds it: the one-liner a post-*
pack's attach is, and a game's own effect as registerPostEffect(game.kernel, def). The definition is checked first
(checkPostEffect), so a bad name, param or snippet throws here, saying what is wrong, rather than when it is
drawn. Registering a name again replaces the effect (the last definition wins), and the copies in the chain take it
on the next frame, keeping their values by param name; an identical definition changes nothing, so a scene's enter
can register its effect every time it runs. Two packs registering one name as they compose throws.
Functions
postScope
function postScope(): SnippetScope
What a snippet can see, by GLSL type, as the WebGL2 translator is told it. tex(p) is a shared helper.
Constants
POST_PARAM_SLOTS
const POST_PARAM_SLOTS: 16
The number of f32 param slots an effect has, p0..p15.
Types
PostApi
interface PostApi
game.post: the effect chain. Effects run in add order, each reading the previous result; the HUD draws above.
add(name: string, params?: Record<string, PostParam>): PostHandle— Append an effect by name. Params override the definition's defaults. Scene-scoped. Throws if no composed pack registeredname(the message names thepost-<name>pack to add), if a param key is unknown or its value the wrong kind, or if the definition needs more than sixteen slots.clear(): void— Empty the chain. Existing handles become inert.readonly chain: ReadonlyArray<string>— Effect names in run order; a fresh array each read.readonly available: ReadonlyArray<string>— Every effect a composed pack registered.
PostEffectDef
interface PostEffectDef
What a post-* pack registers: one full-screen effect as data. code is the body of a WGSL fragment
function with uv, time, texel, tex(p) and p0..p15 in scope; it leaves its result in color.
See engine/packs/post/post/docs/post.md, "Writing an effect pack".
name: string— The namegame.post.add()is called with, e.g.'bloom'. Unique per composed game.description?: string— One line for the planner's menu: what it looks like.code: string— WGSL: the fragment body. Starts withcolor = tex(uv); must leave the result incolor(vec4, straight alpha).params: Record<string, PostParam>— The params and their defaults, laid intop0..p15in key order: a number takes one slot, a CSS colour three (r, g, b), an array its length. At most sixteen slots;add()throws past that.
PostHandle
interface PostHandle
One added effect in the chain, returned by PostApi.add().
set(param: string, value: PostParam): void— Set one param live: a number, a CSS colour for a colour param, or an array (or one number for every component) for a vector param. Throws ifparamis not one the effect declares, if the value is the wrong kind, or if a colour does not parse.remove(): void— Remove this instance from the chain. Idempotent; also run by the scene's teardown.readonly name: string— The effect name this handle was added with.
PostParam
type PostParam = number | string | readonly number[]
One param value. A number fills one slot; a CSS colour string fills three (red, green, blue, 0..1; alpha is
ignored); an array of 2 to 4 numbers fills that many. The DEFAULT in PostEffectDef.params fixes the kind:
a vector param also takes one number for every component, and a colour param takes only a CSS string.


