---
title: "postprocess"
description: "The postprocess namespace — the engine's Luau API reference for postprocess."
section: "API Reference"
slug: "api-postprocess"
canonical: "https://origozero.ai/docs/api-postprocess"
updated: "2026-09-05T23:13:47.007793017+00:00"
tags: ["api", "reference"]
---

# postprocess

The `postprocess` namespace — 28 functions.

## globals/postprocess/add {#globals-postprocess-add}

```lua
postprocess.add(name: string, shader: string | AssetRef, opts: PostprocessOpts?) -> boolean
```

Register a fullscreen post-process effect. This call is what
puts a pass into the frame — a `post-process` `.shader` asset defines an
effect, and renders only once registered here. The chain applies the
registration on the caller's own stack and the returned boolean is its
answer, so a `setProperty` or `setTexture` naming the effect in the same
call finds it. `shader` is a `.shader` asset reference whose `shader.wgsl`
provides `fn fragment(in: PostInput) -> vec4<f32>` and whose
`properties.yaml` declares the effect's properties; the engine generates the
group(0) framework + schema-driven group(1) from that schema. Editing that
shader afterwards recompiles this effect in place, keeping its enabled
state, priority, layer and tuned property values. WGSL text is also
accepted, and then `opts.properties` is the whole schema. Effects run in
priority order (lower first, default 100).
A registered effect runs over the live viewport's frame AND over every
offscreen one — a capture from a world-space station, one orbiting an
entity, one of a named camera, a render-to-texture camera. In each of those
the effect's `engine.view_proj` / `engine.prev_view_proj` /
`engine.inv_view_proj` are the camera THAT render was drawn from and
`engine.resolution` is that target's own size, so a pass reconstructing
world space from `zero_scene_depth(uv)` reconstructs against the station
and lens the capture asked for. An offscreen capture is therefore an oracle
for an authored grade: it photographs a chosen station without taking the
on-screen camera from whoever else is driving the scene, and a capture's
`postProcessing = false` is the one control that takes the chain off the
frame it returns. An offscreen render keeps no view history of its own, so
`engine.prev_view_proj` there holds that same matrix rather than the frame
before it, and a pass taking camera motion from the two reads none.

**Parameters**

- `name` `string` — Unique effect name.
- `shader` `string | AssetRef` — A resolved `shader` asset reference, or author WGSL
(`fn fragment(in: PostInput)` only).
- `opts` `PostprocessOpts` _(optional)_ — `{ priority = 100, enabled = true, layer = "all"|"scene", properties = {{name, type, default?, min?, max?, textureDefault?}} }`
— with a shader asset, `properties` layers over the asset's own schema.
`layer` picks which composited image the effect grades: `"scene"` runs it
before the UI is drawn, so it grades the rendered picture and leaves every
widget on screen as authored, and `"all"` (the default) runs it after the UI
has landed, so the interface is graded along with the picture — an effect
that belongs to the world's look wants `"scene"`, since a screen another
author drew is otherwise graded by it too.
`textureDefault` is what a `type = "texture"` property samples while
nothing is bound to it: `"white"` (1,1,1,1 — the default), `"black"`
(0,0,0,1), `"normal"` (0.5,0.5,1,1) or `"transparent"` (0,0,0,0). An
effect that lays its texture over the scene wants `"transparent"`, so the
frame is untouched until `setTexture` binds a texture that exists.

**Returns** `boolean` — True when the chain registered the effect; false when it refused it. A shader that does not compile draws nothing at any property value, so it is not registered and `postprocess.list()` never names it — the compiler's message is in the engine log. A call made from inside `queue()` or `batch()`, where the engine has not run the registration by the time the call returns, answers true for the queued request.

```lua
postprocess.add("vignette", asset.resolve("@builtin::shaders.post.vignette", "shader"))
postprocess.add("vignette", VIGNETTE_WGSL, { properties = {{ name = "intensity", type = "float", default = 0.5 }} })
```

## globals/postprocess/describe {#globals-postprocess-describe}

```lua
postprocess.describe(name: string) -> PostprocessDescription?
```

One effect by name, read in full: the chain state
`postprocess.status()` lists for it, and on top of that `properties` — the
schema the effect declared, each entry `{ name, type, default?, min?, max?,
textureDefault? }` in the shape `add` takes — and `values`, what each of
those properties currently holds. A property's value is the one the last
`setProperty` wrote, or the schema's own default where nothing has written
one, and it comes back as a number for a scalar and as the array for a
wider value, which is what `setProperty` takes, so a property read here is
written straight back.

This is the read-back for a property write. `setProperty` answers whether
the uniform took the value; this answers what the effect holds now, which
is the reading a pass that writes its properties every frame needs and the
one that tells a mistyped property name from an effect that is not
grading. The schema and the values are the engine's own record of the
effect — the schema it was registered with and every write the chain
accepted into its uniform, the same record `/runtime/fx/<name>/meta.json`
is serialized from. A write the chain refused is not in it, and neither is
one made against a property the schema does not declare.

Before an effect is registered its schema lives on the `.shader` asset it
will render: `asset.resolve("@builtin::shaders.post.bloom",
"shader"):getProperties()` names what that shader declares.

**Parameters**

- `name` `string` — Effect name.

**Returns** `PostprocessDescription?` — The effect's state, schema and live values, or nil when nothing is registered under the name. An effect the renderer registers itself declares no properties of its own, and its `properties` and `values` are empty.

```lua
local e = postprocess.describe("vignette")
print(if e then e.values.intensity else "not registered")
for _, p in ipairs(postprocess.describe("bloom").properties) do
print(p.name, p.type, p.min, p.max)
end
```

## globals/postprocess/list {#globals-postprocess-list}

```lua
postprocess.list() -> { string }
```

List all registered post-process effect names in renderer
priority order (lower priority runs first).

**Returns** `{ string }` — Array of effect names.

```lua
for _, n in ipairs(postprocess.list()) do print(n) end
```

## globals/postprocess/remove {#globals-postprocess-remove}

```lua
postprocess.remove(name: string) -> boolean
```

Queue removal of a post-process effect. Takes effect on the
next frame. Removing a name that isn't registered is a silent
no-op.

**Parameters**

- `name` `string` — Effect name to remove.

**Returns** `boolean` — True — the mutation was queued.

```lua
postprocess.remove("vignette")
```

## globals/postprocess/setEnabled {#globals-postprocess-setenabled}

```lua
postprocess.setEnabled(name: string, enabled: boolean) -> boolean
```

Queue an enable/disable toggle on a registered post-process
effect. Targeting an unknown name is a silent no-op.

**Parameters**

- `name` `string` — Effect name.
- `enabled` `boolean` — True to enable, false to disable.

**Returns** `boolean` — True — the mutation was queued.

```lua
postprocess.setEnabled("bloom", false)
```

## globals/postprocess/setProperty {#globals-postprocess-setproperty}

```lua
postprocess.setProperty(name: string, prop: string, value: (number | { number })) -> boolean
```

Set a named material property on a registered post-process
effect. The property must be declared in the effect's `properties`
schema; read in WGSL as `material.<prop>`. `value` is a number or a
number array (vec/color).

**Parameters**

- `name` `string` — Effect name.
- `prop` `string` — Declared property name.
- `value` `(number | { number })` — Number or array of numbers.

**Returns** `boolean` — True when the effect's uniform took the value; false when it did not — an effect that is not registered, or one that declares no property by that name, is named in a WARN in the engine log. A call made from inside `queue()` or `batch()`, where the engine has not run the write by the time the call returns, answers true for the queued request.

```lua
postprocess.setProperty("vignette", "intensity", 0.6)
```

## globals/postprocess/setSampler {#globals-postprocess-setsampler}

```lua
postprocess.setSampler(name: string, opts: { [string]: any }) -> boolean
```

Configure the per-effect user sampler shared by the effect's
declared texture properties. opts.filter = "linear" (default) or
"nearest". opts.wrap (alias .address) = "clamp" (default), "repeat",
or "mirror" — applied to all axes.

**Parameters**

- `name` `string` — Effect name.
- `opts` `{ [string]: any }` — `{ filter = "linear"|"nearest", wrap = "clamp"|"repeat"|"mirror" }`.

**Returns** `boolean` — True — the mutation was queued.

```lua
postprocess.setSampler("blur", { filter = "linear", wrap = "clamp" })
```

## globals/postprocess/setTexture {#globals-postprocess-settexture}

```lua
postprocess.setTexture(name: string, prop: string, path: string) -> boolean
```

Bind a texture to one of an effect's declared `texture` properties.
Declare it in `properties` (`{ name = "noise", type = "texture" }`) and
sample in WGSL as `textureSample(noise, noise_sampler, in.uv)`. `path`
is any TextureCache-resolvable spec (`@builtin::textures.foo`,
`color:1,0,0`, `default:white`, a render-target name, ...). A path whose
texture has not reached the GPU yet — one this same script created — is
held and bound as soon as it does; `postprocess.status()` reports it under
`pendingTextures` until then.

**Parameters**

- `name` `string` — Effect name.
- `prop` `string` — Declared texture-property name.
- `path` `string` — Texture path / spec.

**Returns** `boolean` — True when the slot took the binding, including one held until its texture reaches the GPU; false when it did not — an effect that is not registered, or one that declares no texture property by that name, is named in a WARN in the engine log. A call made from inside `queue()` or `batch()`, where the engine has not run the binding by the time the call returns, answers true for the queued request.

```lua
postprocess.setTexture("__vsky_composite", "cloud", "cloud_target")
```

## globals/postprocess/status {#globals-postprocess-status}

```lua
postprocess.status() -> { PostprocessStatus }
```

Every registered effect in chain order with the state that decides
whether it reaches the frame — enabled flag, priority, layer, the
shader's compile error when it has one, the `.shader` asset it renders
when it was registered from one, the texture each declared slot is bound
to (`textures`) and the bindings still waiting for their texture
(`pendingTextures`). This is what the renderer draws with, so a survey of
the chain answers "is this one affecting the picture right now?" without
capturing a frame and reading pixels.

An effect this script has just registered is listed with `pending =
true` until the renderer publishes it, since a registration is queued
for the next frame.

**Returns** `{ PostprocessStatus }` — Array of per-effect state, in the order the chain runs them.

```lua
for _, e in ipairs(postprocess.status()) do
print(e.name, e.enabled, e.layer, e.error)
end
```

## modules/postprocess/README {#modules-postprocess-readme}

```lua
require("@builtin/modules/api/engine/postprocess") -- postprocess (also available as global 'postprocess')
```

Fullscreen post-process effects — register / remove / toggle / named-property updates / list the chain / read one effect's declared property schema and the value each property holds. Public Luau surface over the `__postprocess` Internal FFI namespace.

Usage: local postprocess = require("@builtin/modules/api/engine/postprocess")
Also available as global: postprocess

## modules/postprocess/add {#modules-postprocess-add}

```lua
add(name: string, shader: string | AssetRef, opts: PostprocessOpts?): boolean
```

Register a fullscreen post-process effect. This call is what
puts a pass into the frame — a `post-process` `.shader` asset defines an
effect, and renders only once registered here. The chain applies the
registration on the caller's own stack and the returned boolean is its
answer, so a `setProperty` or `setTexture` naming the effect in the same
call finds it. `shader` is a `.shader` asset reference whose `shader.wgsl`
provides `fn fragment(in: PostInput) -> vec4<f32>` and whose
`properties.yaml` declares the effect's properties; the engine generates the
group(0) framework + schema-driven group(1) from that schema. Editing that
shader afterwards recompiles this effect in place, keeping its enabled
state, priority, layer and tuned property values. WGSL text is also
accepted, and then `opts.properties` is the whole schema. Effects run in
priority order (lower first, default 100).
A registered effect runs over the live viewport's frame AND over every
offscreen one — a capture from a world-space station, one orbiting an
entity, one of a named camera, a render-to-texture camera. In each of those
the effect's `engine.view_proj` / `engine.prev_view_proj` /
`engine.inv_view_proj` are the camera THAT render was drawn from and
`engine.resolution` is that target's own size, so a pass reconstructing
world space from `zero_scene_depth(uv)` reconstructs against the station
and lens the capture asked for. An offscreen capture is therefore an oracle
for an authored grade: it photographs a chosen station without taking the
on-screen camera from whoever else is driving the scene, and a capture's
`postProcessing = false` is the one control that takes the chain off the
frame it returns. An offscreen render keeps no view history of its own, so
`engine.prev_view_proj` there holds that same matrix rather than the frame
before it, and a pass taking camera motion from the two reads none.

**Parameters**

- `name` `string` — Unique effect name.
- `shader` `string | AssetRef` — A resolved `shader` asset reference, or author WGSL
(`fn fragment(in: PostInput)` only).
- `opts` `PostprocessOpts?` _(optional)_ — `{ priority = 100, enabled = true, layer = "all"|"scene", properties = {{name, type, default?, min?, max?, textureDefault?}} }`
— with a shader asset, `properties` layers over the asset's own schema.
`layer` picks which composited image the effect grades: `"scene"` runs it
before the UI is drawn, so it grades the rendered picture and leaves every
widget on screen as authored, and `"all"` (the default) runs it after the UI
has landed, so the interface is graded along with the picture — an effect
that belongs to the world's look wants `"scene"`, since a screen another
author drew is otherwise graded by it too.
`textureDefault` is what a `type = "texture"` property samples while
nothing is bound to it: `"white"` (1,1,1,1 — the default), `"black"`
(0,0,0,1), `"normal"` (0.5,0.5,1,1) or `"transparent"` (0,0,0,0). An
effect that lays its texture over the scene wants `"transparent"`, so the
frame is untouched until `setTexture` binds a texture that exists.

```lua
postprocess.add("vignette", asset.resolve("@builtin::shaders.post.vignette", "shader"))
postprocess.add("vignette", VIGNETTE_WGSL, { properties = {{ name = "intensity", type = "float", default = 0.5 }} })
```

## modules/postprocess/describe {#modules-postprocess-describe}

```lua
describe(name: string): PostprocessDescription?
```

One effect by name, read in full: the chain state
`postprocess.status()` lists for it, and on top of that `properties` — the
schema the effect declared, each entry `{ name, type, default?, min?, max?,
textureDefault? }` in the shape `add` takes — and `values`, what each of
those properties currently holds. A property's value is the one the last
`setProperty` wrote, or the schema's own default where nothing has written
one, and it comes back as a number for a scalar and as the array for a
wider value, which is what `setProperty` takes, so a property read here is
written straight back.

This is the read-back for a property write. `setProperty` answers whether
the uniform took the value; this answers what the effect holds now, which
is the reading a pass that writes its properties every frame needs and the
one that tells a mistyped property name from an effect that is not
grading. The schema and the values are the engine's own record of the
effect — the schema it was registered with and every write the chain
accepted into its uniform, the same record `/runtime/fx/<name>/meta.json`
is serialized from. A write the chain refused is not in it, and neither is
one made against a property the schema does not declare.

Before an effect is registered its schema lives on the `.shader` asset it
will render: `asset.resolve("@builtin::shaders.post.bloom",
"shader"):getProperties()` names what that shader declares.

**Parameters**

- `name` `string` — Effect name.

```lua
local e = postprocess.describe("vignette")
print(if e then e.values.intensity else "not registered")
for _, p in ipairs(postprocess.describe("bloom").properties) do
print(p.name, p.type, p.min, p.max)
end
```

## modules/postprocess/list {#modules-postprocess-list}

```lua
list(): { string }
```

List all registered post-process effect names in renderer
priority order (lower priority runs first).

```lua
for _, n in ipairs(postprocess.list()) do print(n) end
```

## modules/postprocess/remove {#modules-postprocess-remove}

```lua
remove(name: string): boolean
```

Queue removal of a post-process effect. Takes effect on the
next frame. Removing a name that isn't registered is a silent
no-op.

**Parameters**

- `name` `string` — Effect name to remove.

```lua
postprocess.remove("vignette")
```

## modules/postprocess/setEnabled {#modules-postprocess-setenabled}

```lua
setEnabled(name: string, enabled: boolean): boolean
```

Queue an enable/disable toggle on a registered post-process
effect. Targeting an unknown name is a silent no-op.

**Parameters**

- `name` `string` — Effect name.
- `enabled` `boolean` — True to enable, false to disable.

```lua
postprocess.setEnabled("bloom", false)
```

## modules/postprocess/setProperty {#modules-postprocess-setproperty}

```lua
setProperty(name: string, prop: string, value: (number | { number })): boolean
```

Set a named material property on a registered post-process
effect. The property must be declared in the effect's `properties`
schema; read in WGSL as `material.<prop>`. `value` is a number or a
number array (vec/color).

**Parameters**

- `name` `string` — Effect name.
- `prop` `string` — Declared property name.
- `value` `(number | { number })` — Number or array of numbers.

```lua
postprocess.setProperty("vignette", "intensity", 0.6)
```

## modules/postprocess/setSampler {#modules-postprocess-setsampler}

```lua
setSampler(name: string, opts: { [string]: any }): boolean
```

Configure the per-effect user sampler shared by the effect's
declared texture properties. opts.filter = "linear" (default) or
"nearest". opts.wrap (alias .address) = "clamp" (default), "repeat",
or "mirror" — applied to all axes.

**Parameters**

- `name` `string` — Effect name.
- `opts` `{ [string]: any }` — `{ filter = "linear"|"nearest", wrap = "clamp"|"repeat"|"mirror" }`.

```lua
postprocess.setSampler("blur", { filter = "linear", wrap = "clamp" })
```

## modules/postprocess/setTexture {#modules-postprocess-settexture}

```lua
setTexture(name: string, prop: string, path: string): boolean
```

Bind a texture to one of an effect's declared `texture` properties.
Declare it in `properties` (`{ name = "noise", type = "texture" }`) and
sample in WGSL as `textureSample(noise, noise_sampler, in.uv)`. `path`
is any TextureCache-resolvable spec (`@builtin::textures.foo`,
`color:1,0,0`, `default:white`, a render-target name, ...). A path whose
texture has not reached the GPU yet — one this same script created — is
held and bound as soon as it does; `postprocess.status()` reports it under
`pendingTextures` until then.

**Parameters**

- `name` `string` — Effect name.
- `prop` `string` — Declared texture-property name.
- `path` `string` — Texture path / spec.

```lua
postprocess.setTexture("__vsky_composite", "cloud", "cloud_target")
```

## modules/postprocess/status {#modules-postprocess-status}

```lua
status(): { PostprocessStatus }
```

Every registered effect in chain order with the state that decides
whether it reaches the frame — enabled flag, priority, layer, the
shader's compile error when it has one, the `.shader` asset it renders
when it was registered from one, the texture each declared slot is bound
to (`textures`) and the bindings still waiting for their texture
(`pendingTextures`). This is what the renderer draws with, so a survey of
the chain answers "is this one affecting the picture right now?" without
capturing a frame and reading pixels.

An effect this script has just registered is listed with `pending =
true` until the renderer publishes it, since a registration is queued
for the next frame.

```lua
for _, e in ipairs(postprocess.status()) do
print(e.name, e.enabled, e.layer, e.error)
end
```

## typed/builtin//modules/api/engine/postprocess/postprocess/add {#typed-builtin-modules-api-engine-postprocess-postprocess-add}

```lua
postprocess.add(name: string, shader: string | AssetRef, opts: PostprocessOpts?) -> boolean
```

Register a fullscreen post-process effect. This call is what
puts a pass into the frame — a `post-process` `.shader` asset defines an
effect, and renders only once registered here. The chain applies the
registration on the caller's own stack and the returned boolean is its
answer, so a `setProperty` or `setTexture` naming the effect in the same
call finds it. `shader` is a `.shader` asset reference whose `shader.wgsl`
provides `fn fragment(in: PostInput) -> vec4<f32>` and whose
`properties.yaml` declares the effect's properties; the engine generates the
group(0) framework + schema-driven group(1) from that schema. Editing that
shader afterwards recompiles this effect in place, keeping its enabled
state, priority, layer and tuned property values. WGSL text is also
accepted, and then `opts.properties` is the whole schema. Effects run in
priority order (lower first, default 100).
A registered effect runs over the live viewport's frame AND over every
offscreen one — a capture from a world-space station, one orbiting an
entity, one of a named camera, a render-to-texture camera. In each of those
the effect's `engine.view_proj` / `engine.prev_view_proj` /
`engine.inv_view_proj` are the camera THAT render was drawn from and
`engine.resolution` is that target's own size, so a pass reconstructing
world space from `zero_scene_depth(uv)` reconstructs against the station
and lens the capture asked for. An offscreen capture is therefore an oracle
for an authored grade: it photographs a chosen station without taking the
on-screen camera from whoever else is driving the scene, and a capture's
`postProcessing = false` is the one control that takes the chain off the
frame it returns. An offscreen render keeps no view history of its own, so
`engine.prev_view_proj` there holds that same matrix rather than the frame
before it, and a pass taking camera motion from the two reads none.

**Parameters**

- `name` `string` — Unique effect name.
- `shader` `string | AssetRef` — A resolved `shader` asset reference, or author WGSL
(`fn fragment(in: PostInput)` only).
- `opts` `PostprocessOpts` _(optional)_ — `{ priority = 100, enabled = true, layer = "all"|"scene", properties = {{name, type, default?, min?, max?, textureDefault?}} }`
— with a shader asset, `properties` layers over the asset's own schema.
`layer` picks which composited image the effect grades: `"scene"` runs it
before the UI is drawn, so it grades the rendered picture and leaves every
widget on screen as authored, and `"all"` (the default) runs it after the UI
has landed, so the interface is graded along with the picture — an effect
that belongs to the world's look wants `"scene"`, since a screen another
author drew is otherwise graded by it too.
`textureDefault` is what a `type = "texture"` property samples while
nothing is bound to it: `"white"` (1,1,1,1 — the default), `"black"`
(0,0,0,1), `"normal"` (0.5,0.5,1,1) or `"transparent"` (0,0,0,0). An
effect that lays its texture over the scene wants `"transparent"`, so the
frame is untouched until `setTexture` binds a texture that exists.

**Returns** `boolean` — True when the chain registered the effect; false when it refused it. A shader that does not compile draws nothing at any property value, so it is not registered and `postprocess.list()` never names it — the compiler's message is in the engine log. A call made from inside `queue()` or `batch()`, where the engine has not run the registration by the time the call returns, answers true for the queued request.

```lua
postprocess.add("vignette", asset.resolve("@builtin::shaders.post.vignette", "shader"))
postprocess.add("vignette", VIGNETTE_WGSL, { properties = {{ name = "intensity", type = "float", default = 0.5 }} })
```

## typed/builtin//modules/api/engine/postprocess/postprocess/describe {#typed-builtin-modules-api-engine-postprocess-postprocess-describe}

```lua
postprocess.describe(name: string) -> PostprocessDescription?
```

One effect by name, read in full: the chain state
`postprocess.status()` lists for it, and on top of that `properties` — the
schema the effect declared, each entry `{ name, type, default?, min?, max?,
textureDefault? }` in the shape `add` takes — and `values`, what each of
those properties currently holds. A property's value is the one the last
`setProperty` wrote, or the schema's own default where nothing has written
one, and it comes back as a number for a scalar and as the array for a
wider value, which is what `setProperty` takes, so a property read here is
written straight back.

This is the read-back for a property write. `setProperty` answers whether
the uniform took the value; this answers what the effect holds now, which
is the reading a pass that writes its properties every frame needs and the
one that tells a mistyped property name from an effect that is not
grading. The schema and the values are the engine's own record of the
effect — the schema it was registered with and every write the chain
accepted into its uniform, the same record `/runtime/fx/<name>/meta.json`
is serialized from. A write the chain refused is not in it, and neither is
one made against a property the schema does not declare.

Before an effect is registered its schema lives on the `.shader` asset it
will render: `asset.resolve("@builtin::shaders.post.bloom",
"shader"):getProperties()` names what that shader declares.

**Parameters**

- `name` `string` — Effect name.

**Returns** `PostprocessDescription?` — The effect's state, schema and live values, or nil when nothing is registered under the name. An effect the renderer registers itself declares no properties of its own, and its `properties` and `values` are empty.

```lua
local e = postprocess.describe("vignette")
print(if e then e.values.intensity else "not registered")
for _, p in ipairs(postprocess.describe("bloom").properties) do
print(p.name, p.type, p.min, p.max)
end
```

## typed/builtin//modules/api/engine/postprocess/postprocess/list {#typed-builtin-modules-api-engine-postprocess-postprocess-list}

```lua
postprocess.list() -> { string }
```

List all registered post-process effect names in renderer
priority order (lower priority runs first).

## typed/builtin//modules/api/engine/postprocess/postprocess/remove {#typed-builtin-modules-api-engine-postprocess-postprocess-remove}

```lua
postprocess.remove(name: string) -> boolean
```

Queue removal of a post-process effect. Takes effect on the
next frame. Removing a name that isn't registered is a silent
no-op.

**Parameters**

- `name` `string` — Effect name to remove.

**Returns** `boolean` — True — the mutation was queued.

```lua
postprocess.remove("vignette")
```

## typed/builtin//modules/api/engine/postprocess/postprocess/setEnabled {#typed-builtin-modules-api-engine-postprocess-postprocess-setenabled}

```lua
postprocess.setEnabled(name: string, enabled: boolean) -> boolean
```

Queue an enable/disable toggle on a registered post-process
effect. Targeting an unknown name is a silent no-op.

**Parameters**

- `name` `string` — Effect name.
- `enabled` `boolean` — True to enable, false to disable.

**Returns** `boolean` — True — the mutation was queued.

```lua
postprocess.setEnabled("bloom", false)
```

## typed/builtin//modules/api/engine/postprocess/postprocess/setProperty {#typed-builtin-modules-api-engine-postprocess-postprocess-setproperty}

```lua
postprocess.setProperty(name: string, prop: string, value: (number | { number })) -> boolean
```

Set a named material property on a registered post-process
effect. The property must be declared in the effect's `properties`
schema; read in WGSL as `material.<prop>`. `value` is a number or a
number array (vec/color).

**Parameters**

- `name` `string` — Effect name.
- `prop` `string` — Declared property name.
- `value` `(number | { number })` — Number or array of numbers.

**Returns** `boolean` — True when the effect's uniform took the value; false when it did not — an effect that is not registered, or one that declares no property by that name, is named in a WARN in the engine log. A call made from inside `queue()` or `batch()`, where the engine has not run the write by the time the call returns, answers true for the queued request.

```lua
postprocess.setProperty("vignette", "intensity", 0.6)
```

## typed/builtin//modules/api/engine/postprocess/postprocess/setSampler {#typed-builtin-modules-api-engine-postprocess-postprocess-setsampler}

```lua
postprocess.setSampler(name: string, opts: { [string]: any }) -> boolean
```

Configure the per-effect user sampler shared by the effect's
declared texture properties. opts.filter = "linear" (default) or
"nearest". opts.wrap (alias .address) = "clamp" (default), "repeat",
or "mirror" — applied to all axes.

**Parameters**

- `name` `string` — Effect name.
- `opts` `{ [string]: any }` — `{ filter = "linear"|"nearest", wrap = "clamp"|"repeat"|"mirror" }`.

**Returns** `boolean` — True — the mutation was queued.

```lua
postprocess.setSampler("blur", { filter = "linear", wrap = "clamp" })
```

## typed/builtin//modules/api/engine/postprocess/postprocess/setTexture {#typed-builtin-modules-api-engine-postprocess-postprocess-settexture}

```lua
postprocess.setTexture(name: string, prop: string, path: string) -> boolean
```

Bind a texture to one of an effect's declared `texture` properties.
Declare it in `properties` (`{ name = "noise", type = "texture" }`) and
sample in WGSL as `textureSample(noise, noise_sampler, in.uv)`. `path`
is any TextureCache-resolvable spec (`@builtin::textures.foo`,
`color:1,0,0`, `default:white`, a render-target name, ...). A path whose
texture has not reached the GPU yet — one this same script created — is
held and bound as soon as it does; `postprocess.status()` reports it under
`pendingTextures` until then.

**Parameters**

- `name` `string` — Effect name.
- `prop` `string` — Declared texture-property name.
- `path` `string` — Texture path / spec.

**Returns** `boolean` — True when the slot took the binding, including one held until its texture reaches the GPU; false when it did not — an effect that is not registered, or one that declares no texture property by that name, is named in a WARN in the engine log. A call made from inside `queue()` or `batch()`, where the engine has not run the binding by the time the call returns, answers true for the queued request.

```lua
postprocess.setTexture("__vsky_composite", "cloud", "cloud_target")
```

## typed/builtin//modules/api/engine/postprocess/postprocess/status {#typed-builtin-modules-api-engine-postprocess-postprocess-status}

```lua
postprocess.status() -> { PostprocessStatus }
```

Every registered effect in chain order with the state that decides
whether it reaches the frame — enabled flag, priority, layer, the
shader's compile error when it has one, the `.shader` asset it renders
when it was registered from one, the texture each declared slot is bound
to (`textures`) and the bindings still waiting for their texture
(`pendingTextures`). This is what the renderer draws with, so a survey of
the chain answers "is this one affecting the picture right now?" without
capturing a frame and reading pixels.

An effect this script has just registered is listed with `pending =
true` until the renderer publishes it, since a registration is queued
for the next frame.

**Returns** `{ PostprocessStatus }` — Array of per-effect state, in the order the chain runs them.

```lua
for _, e in ipairs(postprocess.status()) do
print(e.name, e.enabled, e.layer, e.error)
end
```
