rxx API reference

The complete rxx scripting API. Plugins are Rune scripts; the rx object passed to every hook is the host interface. New here? Start with the scripting guide. Every constructor that can fail returns an Option and posts the error to the message line on None.

rxx is in early development — expect the script API to change. Report breakage on the issues page.

Click a function to show its parameter types, units, return value, and failure cases. Click again to collapse it. Method receivers (rx, encoder, texture, and pass) are not repeated in parameter lists. () means no return value; Result<T, String> is Ok(value) or Err(message).

plugins

Plugins are flat <name>.rune files or packages at <name>/<name>.rune with adjacent assets. Search order: repeated --plugin-dir <path> arguments, the PATH-style RXX_PLUGIN_PATH list, local plugins/, then the OS config directory's plugins/. First match per name wins; plugins load in lexicographic name order. --isolated-plugins restricts discovery to explicitly supplied directories.

pub fn init(rx) runs at load and returns the plugin's state value, which is passed back as the first argument of every hook, command handler, and export. Script errors and GPU validation errors disable the plugin and post one message; other plugins are unaffected.

pub fn init(rx) {
    rx.register_command("greet", ["str"], "Say hello", greet);
    #{ count: 0 }
}

pub fn greet(state, rx, args) {
    state.count += 1;
    rx.message(`hello ${args[0]} (${state.count})`);
}

hooks

Defined as pub fn at the top level; all optional except init.

# init(rx) -> Value once at load; the return value is the plugin state
rx Ctx
Host interface for this hook call; do not retain it in plugin state.
returns Value
The initial plugin state, passed to later hooks, command handlers, and exported functions.
# unload(state, rx) before the plugin is dropped (unload / hot reload)
state Value
The plugin state returned by init; mutate this value to keep state between calls.
rx Ctx
Host interface for this hook call; do not retain it in plugin state.
returns Value
Ignored by the host; normally return unit, ().
# update(state, rx) every frame, before rendering
state Value
The plugin state returned by init; mutate this value to keep state between calls.
rx Ctx
Host interface for this hook call; do not retain it in plugin state.
returns Value
Ignored by the host; normally return unit, ().
# switch_mode(state, rx) on mode edges (query rx.mode()); fires once for the initial mode, and on command-mode entries
state Value
The plugin state returned by init; mutate this value to keep state between calls.
rx Ctx
Host interface for this hook call; do not retain it in plugin state.
returns Value
Ignored by the host; normally return unit, ().
# cursor_moved(state, rx, x, y) mouse motion, window logical coords (rx.session_coords(x, y) converts)
state Value
The plugin state returned by init; mutate this value to keep state between calls.
rx Ctx
Host interface for this hook call; do not retain it in plugin state.
x f64
Horizontal mouse position in window logical coordinates.
y f64
Vertical mouse position in window logical coordinates; increases downward.
returns Value
Ignored by the host; this hook does not consume the mouse event.
# mouse_input(state, rx, button, input) button is "left"/"right"/"middle", input is "pressed"/"released"
state Value
The plugin state returned by init; mutate this value to keep state between calls.
rx Ctx
Host interface for this hook call; do not retain it in plugin state.
button String
Mouse button: "left", "right", or "middle".
input String
Button transition: "pressed" or "released".
returns Value
Ignored by the host; this hook does not consume the button event.
# capture_mouse(state, rx, button, input) -> bool button is "left"/"right"/"middle", input is "pressed"/"released"
state Value
The plugin state returned by init; mutate this value to keep state between calls.
rx Ctx
Host interface for this hook call; do not retain it in plugin state.
button String
Mouse button: "left", "right", or "middle".
input String
Button transition: "pressed" or "released".
returns bool
True consumes the event; false passes it to the next handler.

Returns bool: true consumes the button event before ordinary mouse hooks and builtin handling. Hooks run in reverse plugin load order. Capture both press and release for a gesture started on the panel, including releases outside it; let canvas-started gestures finish. This does not intercept cursor motion, wheel events, or keyboard input. An invalid return value or error disables the plugin.

# view_added(state, rx, id) view opened
state Value
The plugin state returned by init; mutate this value to keep state between calls.
rx Ctx
Host interface for this hook call; do not retain it in plugin state.
id i64
ID of the view that was opened.
returns Value
Ignored by the host; normally return unit, ().
# view_removed(state, rx, id) view closed
state Value
The plugin state returned by init; mutate this value to keep state between calls.
rx Ctx
Host interface for this hook call; do not retain it in plugin state.
id i64
ID of the view that was closed; it may no longer be available to query.
returns Value
Ignored by the host; normally return unit, ().
# shade(state, rx, encoder) per frame: record render/compute passes on the frame encoder (before screen composition)
state Value
The plugin state returned by init; mutate this value to keep state between calls.
rx Ctx
Host interface for this hook call; do not retain it in plugin state.
encoder ScriptEncoder
The current frame encoder. Begin render or compute passes here; it expires when the hook returns.
returns Value
Ignored by the host. Recorded GPU commands are the output of this hook.
# render(state, rx, pass) per frame: the live screen pass — everything drawn, present ahead — for screen-space pipeline drawing
state Value
The plugin state returned by init; mutate this value to keep state between calls.
rx Ctx
Host interface for this hook call; do not retain it in plugin state.
pass ScriptPass
The live screen render pass for this hook call; do not keep it for another frame.
returns Value
Ignored by the host. Drawing recorded on pass is the output of this hook.
# overlay(state, rx, pass) per frame: draw above all ordinary render hooks, editor UI, and plugin previews; overlays run in plugin load order
state Value
The plugin state returned by init; mutate this value to keep state between calls.
rx Ctx
Host interface for this hook call; do not retain it in plugin state.
pass ScriptPass
The live screen render pass for this hook call; do not keep it for another frame.
returns Value
Ignored by the host. Drawing recorded on pass is the output of this hook.
# draw(state, rx) per frame: UI-tier text/line/rectangle drawing (rx.draw_text / rx.draw_line are only live here)
state Value
The plugin state returned by init; mutate this value to keep state between calls.
rx Ctx
Host interface for this hook call; do not retain it in plugin state.
returns Value
Ignored by the host; normally return unit, ().

session & modes

# rx.mode() -> String current mode ("normal", "visual", "command", … or a script mode)
parameters
None.
returns String
The current mode name, such as "normal", "visual", "command", or a script-mode name.
# rx.prev_mode() -> Option<String> previous mode
parameters
None.
returns Option<String>
Some(name) for the previous mode; None if there is no previous mode.
# rx.switch_mode(name) -> bool builtin names switch builtin modes; any other name enters a script mode — builtin input handling is inert there, escape exits
name String
A builtin mode name or a nonempty script-mode name.
returns bool
true when the name is accepted and the switch is requested; false for an invalid name.
# rx.message(msg) -> () post to the message line
msg String
Text to display in the editor message line.
returns ()
No return value (unit).
# rx.fg() -> Rgba8 the color pair
parameters
None.
returns Rgba8
The current foreground color.
# rx.bg() -> Rgba8 the color pair
parameters
None.
returns Rgba8
The current background color.
# rx.set_fg(color) -> () picker semantics: old fg becomes bg; transparent ignored
color Rgba8
RGBA color, for example rx::rgba(255, 128, 0, 255).
returns ()
No return value (unit).

coordinates & screen

# rx.offset() -> (f64, f64) workspace pan offset
parameters
None.
returns (f64, f64)
Workspace pan offset as (x, y), in session pixels.
# rx.screen_size() -> (i64, i64) the render stage target size; build screen orthos against it
parameters
None.
returns (i64, i64)
The render target dimensions (width, height), in session pixels.
# rx.cursor() -> (f64, f64) cursor in session coordinates
parameters
None.
returns (f64, f64)
Cursor position (x, y) in session coordinates.
# rx.session_coords(x, y) -> (f64, f64) window logical → session
x f64
Horizontal position in window logical coordinates.
y f64
Vertical position in window logical coordinates.
returns (f64, f64)
The converted (x, y) in session coordinates, floored to integer pixels.
# rx.active_view_coords(x, y) -> (f64, f64) session → active-view (floored)
x f64
Horizontal position in session coordinates.
y f64
Vertical position in session coordinates.
returns (f64, f64)
The converted (x, y) in active-view coordinates, floored to integer pixels.

Session, view, UI drawing coordinates, and pixel rows all increase downward from a top-left origin. Mouse events use window logical coordinates; use rx.session_coords(x, y) to apply the interface scale.

views

# rx.active_view_id() -> i64
parameters
None.
returns i64
The active view ID; this is an identifier, not a position in rx.views().
# rx.views() -> Vec<ViewInfo> snapshots, in view order
parameters
None.
returns Vec<ViewInfo>
Read-only snapshots of all views, in view order. Empty when there are no views.
# rx.view_pixels(id, rect) -> Option<Bytes> rgba8, row-major, from the recorded snapshot (see pitfalls); rect clamped
id i64
View ID from rx.active_view_id() or a ViewInfo snapshot.
rect Rect
Requested rectangle in view coordinates (y-down); normalized and clamped to the layer bounds.
returns Option<Bytes>
Some(bytes) from the recorded bottom-layer snapshot, RGBA8 row-major with the top row first; None for a missing view, an empty intersection, or a failed read.
# rx.view_layer_pixels(id, layer, rect) -> Option<Bytes> Read a recorded layer strip, with 0 as the bottom layer. The rectangle uses display-space y in 0..frame_height; x can span the frame strip.
id i64
View ID from rx.active_view_id() or a ViewInfo snapshot.
layer i64
Zero-based layer index; 0 is the bottom layer, regardless of one-based UI labels.
rect Rect
Requested rectangle in view coordinates (y-down); normalized and clamped to the layer bounds.
returns Option<Bytes>
Some(bytes) from the requested recorded layer, RGBA8 row-major with the top row first; None for a missing view/layer, an empty intersection, or a failed read.
# rx.layer_visibility(id) -> Vec<bool> per-layer visible, bottom strip first; empty if the view doesn't exist
id i64
View ID from rx.active_view_id() or a ViewInfo snapshot.
returns Vec<bool>
One visibility flag per layer, bottom first (index 0); empty for a missing view.
# rx.layer_opacity(id) -> Vec<f64> per-layer opacity (0.0–1.0), bottom strip first; empty if the view doesn't exist
id i64
View ID from rx.active_view_id() or a ViewInfo snapshot.
returns Vec<f64>
One opacity per layer, bottom first (index 0); empty for a missing view.
# rx.set_animation_frame(id, frame) -> bool set the view's current animation frame; wraps by its frame count; false if the view doesn't exist
id i64
View ID from rx.active_view_id() or a ViewInfo snapshot.
frame i64
Zero-based frame index; positive and negative indices wrap by the frame count.
returns bool
true when the view exists and its current frame is set; false for a missing view.
# rx.set_animation_sequence(id, frames) -> bool set a custom playback sequence of zero-based frame indices; empty restores natural order; false for a missing view or an out-of-range index
id i64
View ID from rx.active_view_id() or a ViewInfo snapshot.
frames Vec<i64>
Playback order as zero-based frame indices. Repeated indices are allowed; an empty list restores natural order.
returns bool
true when accepted; false if the view is missing or any index is outside its frame range.
# rx.animation_sequence(id) -> Vec<i64> the custom playback sequence; empty means natural order (and is also returned for a missing view)
id i64
View ID from rx.active_view_id() or a ViewInfo snapshot.
returns Vec<i64>
The custom zero-based playback sequence; empty for natural order or a missing view.
# rx.clear_animation_sequence(id) -> bool restore natural frame-order playback without changing the visible frame; false if the view doesn't exist
id i64
View ID from rx.active_view_id() or a ViewInfo snapshot.
returns bool
true when natural playback order is restored; false for a missing view. The visible frame is preserved.
# rx.set_animation_preview_visible(id, visible) -> bool show or hide the built-in animation preview for the view; false if the view doesn't exist
id i64
View ID from rx.active_view_id() or a ViewInfo snapshot.
visible bool
true to show the built-in preview; false to suppress it.
returns bool
true when the view exists and its preview flag is set; false for a missing view.
# rx.touch_view(id) -> () mark modified → contents re-recorded (do this after painting a view via a pass)
id i64
View ID from rx.active_view_id() or a ViewInfo snapshot.
returns ()
No return value (unit).
# rx.clear_view_rect(rect) -> () clear a rect of the active view to transparent — a recorded paint
rect Rect
Rectangle of the active view to erase, in y-down view coordinates; corners are normalized.
returns ()
No return value (unit).
# rx.damage_view(id) -> () re-render from the snapshot, discarding unrecorded GPU-side paint (kill a preview)
id i64
View ID from rx.active_view_id() or a ViewInfo snapshot.
returns ()
No return value (unit).

ViewInfo fields (read-only): id, width (full sheet: frame_width * frames), height, offset_x, offset_y, zoom, frames, frame_width, frame_height, animation_frame, animation_preview_visible (bool), nlayers (1 for a flat view), active_layer (0 is the bottom strip).

selection

# rx.selection() -> Option<(i64, i64, i64, i64)> normalized (x1 <= x2, y1 <= y2) regardless of drag direction
parameters
None.
returns Option<(i64, i64, i64, i64)>
Some((x1, y1, x2, y2)) with normalized bounds, or None when no selection exists.
# rx.set_selection(x1, y1, x2, y2) -> ()
x1 i64
Horizontal coordinate of the first corner, in view pixels.
y1 i64
Vertical coordinate of the first corner, in y-down view pixels.
x2 i64
Horizontal coordinate of the second corner, in view pixels.
y2 i64
Vertical coordinate of the second corner, in y-down view pixels.
returns ()
No return value (unit).
# rx.clear_selection() -> ()
parameters
None.
returns ()
No return value (unit).

The selection is cleared when the session returns to normal mode.

settings

# rx.setting(name) -> Value bool / int / float / string / tuple; unit if absent
name String
Setting name, including its namespace when applicable, e.g. "layer-status/show".
returns Value
The setting as bool, i64, f64, String, or a pair of numbers. Color settings are returned as strings. An unknown setting returns unit, ().
# rx.set_setting(name, value) -> bool Set an existing scalar setting; the value must match its current type.
name String
Setting name, including its namespace when applicable, e.g. "layer-status/show".
value Value
A bool, i64, f64, or String matching the existing setting type. Tuple and color settings are not supported by this setter.
returns bool
true when the update is applied; false for an unknown setting, a type mismatch, an unsupported type, or a rejected value.
# rx.declare_setting(name, default) -> bool plugin-owned, :set-able like builtins; re-declaring is a no-op
name String
Setting name, including its namespace when applicable, e.g. "layer-status/show".
default Value
Default bool, i64, f64, or String. Integer settings use unsigned 32-bit storage; use values in 0..=4294967295.
returns bool
true for a supported default, including when the setting already exists (its value is preserved); false for an unsupported type.

commands & bindings

# rx.register_command(name, sig, help, handler) -> bool sig is typed params: "int", "float", "str", "color", "bool", suffix ? for optional; handler is handler(state, rx, args)
name String
Command name without a leading colon; must not shadow a builtin or an existing script command.
sig Vec<String>
Parameter types in order: "int", "float", "str", "color", or "bool". Add ? for an optional parameter; optional parameters must come last.
help String
Short help text shown for the command.
handler Function
Called as handler(state, rx, args), where args is a Vec<Value> of parsed parameters. The handler return value is ignored.
returns bool
true when registered; false for an unavailable registration context, invalid signature, or conflicting name. Failure posts a message.
# rx.register_command_repeating(name, sig, help, handler) -> bool same, repeats while the bound key is held
name String
Command name without a leading colon; must not shadow a builtin or an existing script command.
sig Vec<String>
Parameter types in order: "int", "float", "str", "color", or "bool". Add ? for an optional parameter; optional parameters must come last.
help String
Short help text shown for the command.
handler Function
Called as handler(state, rx, args), where args is a Vec<Value> of parsed parameters. The handler return value is ignored.
returns bool
true when registered; false for an unavailable registration context, invalid signature, or conflicting name. Failure posts a message.
# rx.bind(mode, mapping) -> bool script-tier binding in a script mode; :map syntax, e.g. "<tab> :v/prev", "'r' :rotate 90 {:rotate 0}"; wins over general bindings while the mode is active, may fire with the mouse held
mode String
Nonempty script-mode name prefix for this binding.
mapping String
A complete mapping in :map syntax, such as "<tab> :v/prev"; omit the :map prefix.
returns bool
true when the mapping parses and is installed; false for an invalid mode name or mapping, including trailing input.
# rx.run_builtin(invocation) -> bool run a builtin command (script commands aren't resolvable through it)
invocation String
Builtin command and arguments without a leading colon, e.g. "v/center".
returns bool
true when parsed and dispatched. false for parse failure or a script command; true does not guarantee that the builtin operation itself succeeded.

Character bindings ('r') fire on char/received, not raw key input. The command line is not reachable from script modes — drive script modes entirely through bindings.

meta plugins

# rx.export(name, handler) -> bool offer handler(state, rx, args) to other plugins, run with the exporting plugin's state
name String
Export name, unique within this plugin.
handler Function
Called as handler(state, rx, args) with the exporting plugin state and a Vec<Value>. Its return value is passed back to the caller.
returns bool
true when exported; false for an unavailable registration context or a duplicate export name.
# rx.call_plugin(plugin, name, args) -> Result<Value, String> call another plugin's export
plugin String
Name of the plugin that owns the export.
name String
The exported function name.
args Vec<Value>
Arguments passed as the handler’s args vector, in the order that export expects.
returns Result<Value, String>
Ok(value) with the handler return value; Err(message) if unavailable, missing, not initialized, or the call fails.

UI drawing (the draw hook only)

# rx.draw_text(text, x, y, color) -> () UI coordinates; no-op outside draw
text String
Text to draw, left-aligned.
x f64
Horizontal text position in UI drawing coordinates.
y f64
Vertical text position in UI drawing coordinates (y-down).
color Rgba8
RGBA color, for example rx::rgba(255, 128, 0, 255).
returns ()
No return value (unit).
# rx.draw_line(p1, p2, options) -> Result<(), String> Screen-space line with explicit width and end caps; draw hook only.
p1, p2
(f64, f64) endpoints in screen pixels, y-down.
options.color
Required Rgba8 color.
options.width
Float, default 1.0; finite, between 0 and 1,000,000.
options.cap
"butt" (default), "square", or "round". Square and round extend half a width beyond endpoints. Zero-length lines produce nothing, a square, or a disk respectively.
returns Result<(), String>
Use .unwrap() or handle errors. Unknown fields, invalid values, and calls outside draw return errors. Coordinates must be finite and within ±1,000,000,000. Options can be reused.
rx.draw_line((10.0, 10.0), (80.0, 30.0), #{
    color: rx::rgb(255, 51, 102), width: 3.0, cap: "round",
}).unwrap();

Breaking change: replace the old third color argument with #{ color } and handle the result.

# rx.draw_rect(rect, options) -> Result<(), String> Rectangle with optional fill and a joined stroke; draw hook only.
rect
rx::rect(x1, y1, x2, y2) in screen pixels, with positive width and height. Coordinates must be finite and within ±1,000,000,000.
options.fill
Optional Rgba8; omitted or () means no fill.
options.stroke
Optional object; omitted or () means no stroke. Requires color. Width defaults to 1.0 and must be a finite float between 0 and 1,000,000.
stroke.align
"inside", "center" (default), or "outside", relative to the supplied geometric boundary.
stroke.join
"miter" (default), "bevel", or "round". Bevel and round shape the outward portion of corners. Inside strokes retain the outer rectangle. Round curves are tessellated, not antialiased.
returns Result<(), String>
Invalid options and calls outside draw return errors. Descriptors are reusable. Fill occupies the remaining inner area; joined strokes avoid double blending at corners. Zero width draws no stroke.
rx.draw_rect(rx::rect(10.0, 10.0, 80.0, 60.0), #{
    stroke: #{ color: rx::rgb(255, 51, 102), width: 2.0, align: "outside" },
}).unwrap();

files & output

# rx.read_file(path) -> Option<String> relative to the plugin's directory (shaders etc.)
path String
File path; relative paths resolve from the plugin directory, absolute paths are used as given.
returns Option<String>
Some(text) for a UTF-8 file; None with an error message if reading or decoding fails.
# rx.read_png(path) -> Option<(i64, i64, Bytes)> decode an 8-bit RGBA PNG relative to the plugin's directory (cursor/icon assets) → (w, h, pixels), row-major rgba8 — the layout upload/write_png use
path String
File path; relative paths resolve from the plugin directory, absolute paths are used as given.
returns Option<(i64, i64, Bytes)>
Some((width, height, pixels)) for an 8-bit RGBA PNG. pixels contains top-first RGBA8 rows; None if reading or decoding fails.
# rx.write_png(path, w, h, data) -> bool rgba8 row-major → PNG; path resolves like :export (cwd-relative, not plugin-relative); loud on both outcomes
path String
Output path, relative to the working directory unless absolute; existing output may be overwritten.
w i64
Width in pixels; must be positive.
h i64
Height in pixels; must be positive.
data Bytes
Exactly w * h * 4 RGBA8 bytes, top row first. The Bytes value is moved into the call.
returns bool
true when the PNG is written; false for invalid dimensions, a byte-count mismatch, or an output error. Both outcomes post a message.

gpu: resources

All constructors return Option and post the error (shader compile errors included) to the message line on None.

# rx.create_texture(w, h) -> Option<ScriptTexture> rgba8 (sRGB), max dimension 8192; owned by the plugin, dropped with its state
w i64
Width in pixels, 1..=8192.
h i64
Height in pixels, 1..=8192.
returns Option<ScriptTexture>
Some(texture) for a new RGBA8 texture; None with a message for invalid dimensions or an unavailable GPU.
# rx.texture_pixels(tex) -> Option<Bytes> readback, mirror of upload; forces a GPU sync — command-handler tool, not per-frame (see pitfalls)
tex ScriptTexture
Texture to read back; pending commands must have been submitted to see their changes.
returns Option<Bytes>
Some(bytes) with RGBA8 pixels in top-first row-major order; None if the GPU is unavailable or bytes cannot be allocated. The read waits for the GPU.
# rx.create_shader(wgsl) -> Option<ScriptShader>
wgsl String
Complete WGSL shader source, not a file path.
returns Option<ScriptShader>
Some(shader) when compilation succeeds; None with a message for shader validation failure or an unavailable GPU.
# rx.create_render_pipeline(shader, options) -> Option<ScriptPipeline> Create a pipeline with explicit blending and independent topology and vertex layout.
shader ScriptShader
Compiled WGSL module from rx.create_shader. Both entry points must belong to this module.
options Object
Pipeline descriptor with the fields below. Required fields must be present; unknown fields and invalid values are errors. Descriptors can be reused.
options.vertex String
Required vertex entry-point name.
options.fragment String
Required fragment entry-point name.
options.blend String | Object
Required. "alpha" composites straight-alpha source colors; "premultiplied_alpha" composites already-premultiplied colors; "replace" overwrites exact RGBA, including zero alpha. An object instead supplies separate color and alpha equations, documented below.
options.textures i64
Optional, default 0. Number of texture + nearest-sampler groups, 0–3. Group 0 always uses the transform/params layout; texture groups occupy slots 1 through this count.
options.topology String
Optional, default "triangle_list". Accepted: "triangle_list", "triangle_strip", "point_list", "line_list", "line_strip". Independent of the vertex layout.
options.vertex_layout String
Optional, default "sprite": position (Float32x3), UV (Float32x2), color (Unorm8x4), opacity (Float32), at locations 0–3, with a 28-byte vertex stride. "none" declares no vertex buffers; the shader can generate geometry from @builtin(vertex_index).
options.format String
Optional, default "rgba8unorm-srgb". Also accepts "rgba8unorm". Must match the render attachment: use the default for editor targets and texture.view(), or linear format for texture.raw_view().
options.blend.color Object
Required when blend is an object. RGB equation, with src, dst, and op fields as below.
options.blend.alpha Object
Required when blend is an object. Alpha equation, with the same three fields. Each equation is evaluated independently.
src / dst String
Required in each custom equation. Source/destination multipliers: "zero", "one", "src", "one_minus_src", "src_alpha", "one_minus_src_alpha", "dst", "one_minus_dst", "dst_alpha", "one_minus_dst_alpha", "src_alpha_saturated". Source is the fragment output; destination is the stored pixel. wgpu validates supported combinations.
op String
Required in each custom equation. "add", "subtract", "reverse_subtract", "min", "max". Add/subtract combine the factored source and destination; min/max choose the minimum/maximum of the source and destination components, ignoring factors.
returns Option<ScriptPipeline>
Some(pipeline) on success; None with an error message for invalid options, an unavailable GPU, or pipeline validation failure.

The pipeline has one color attachment, all RGBA channels writable, no depth attachment, and no multisampling. Arbitrary vertex and binding layouts are not exposed.

let pipeline = rx.create_render_pipeline(shader, #{
    vertex: "vs_main", fragment: "fs_main", textures: 1,
    blend: "replace",
}).unwrap();

A vertex-index-driven point pipeline uses topology: "point_list", vertex_layout: "none" in this constructor.

# rx.create_compute_pipeline(shader, entry, inputs) -> Option<ScriptComputePipeline> inputs = 1–4 input textures at bindings 0..n, write-only rgba8 storage output at binding n (one group)
shader ScriptShader
Compiled WGSL shader from rx.create_shader().
entry String
Name of the WGSL compute entry point.
inputs i64
Number of input textures, 1..=4. Inputs occupy bindings 0 through inputs - 1; storage output uses binding inputs.
returns Option<ScriptComputePipeline>
Some(pipeline) when valid; None with a message for an unavailable GPU, invalid input count, or validation failure.

texture methods

texture is a ScriptTexture returned by rx.create_texture.

# texture.view() -> ScriptTextureView Create a persistent rgba8unorm-srgb view over a script-owned texture.
parameters
None.
returns ScriptTextureView
A view of the same storage; no copy or CPU readback. It can be retained independently of the texture handle and used as a render attachment or sampled binding. RGB is decoded when sampled and encoded when rendered; alpha is unchanged by sRGB conversion. Render pipelines targeting it must use format: "rgba8unorm-srgb".
# texture.raw_view() -> ScriptTextureView Create a persistent rgba8unorm view over a script-owned texture.
parameters
None.
returns ScriptTextureView
A view of the same storage; no copy or CPU readback. It can be retained independently of the texture handle and used as a render attachment or sampled binding. Sampling and rendering use linear unorm values without sRGB conversion. The compute API uses this same raw representation. Render pipelines targeting it must use format: "rgba8unorm".
# texture.width() -> i64 Read the texture width.
parameters
None.
returns i64
Texture width in pixels.
# texture.height() -> i64 Read the texture height.
parameters
None.
returns i64
Texture height in pixels.
# texture.upload(bytes) -> bool Replace the entire texture with pixel data.
bytes Bytes
Exactly texture.width() * texture.height() * 4 RGBA8 bytes in top-first row-major order. The Bytes value is moved.
returns bool
true when the byte count matches and the upload is queued; false for a size mismatch, leaving the texture unchanged.
# texture.fill(color) -> () Fill the entire texture with one color.
color Rgba8
RGBA color, for example rx::rgba(255, 128, 0, 255).
returns ()
No return value (unit).

bind groups & vertices

Render/point pipeline layout: group 0 = transform + params, groups 1..=textures = one texture + nearest sampler each. Texture bindings are visible to both vertex and fragment stages.

# rx.create_transform_bind_group(w, h, transform) -> Option<ScriptBindGroup> group 0: ortho for a w×h target composed with transform; params slot zeroed (WGSL that doesn't declare binding 1 is unaffected)
w i64
Width in pixels; must be positive.
h i64
Height in pixels; must be positive.
transform Mat4
Model transform composed with the target-size orthographic projection, e.g. rx::mat4_identity().
returns Option<ScriptBindGroup>
Some(group) for group 0 with transform uniforms and a zeroed vec4 parameter slot; None for an unavailable GPU or nonpositive target dimensions.
# rx.create_transform_params_bind_group(w, h, transform, params) -> Option<ScriptBindGroup> adds user params (1–64 floats) at group 0 binding 1: var<uniform> params: array<vec4<f32>, N> where N = ceil(len/4) — the buffer is sized to the floats passed, not a fixed 64, so declare N to match or it's a validation error; packed in order, zero-padded to the vec4; an f32 holds 24 exact integer bits — pass 32-bit masks as two 16-bit halves
w i64
Width in pixels; must be positive.
h i64
Height in pixels; must be positive.
transform Mat4
Model transform composed with the target-size orthographic projection, e.g. rx::mat4_identity().
params Vec<f64>
1–64 values, converted to f32 and zero-padded to a multiple of four. WGSL binding 1 must use array<vec4<f32>, ceil(len / 4)>.
returns Option<ScriptBindGroup>
Some(group) for group 0; None for an unavailable GPU, nonpositive dimensions, or an invalid parameter count.
# rx.create_texture_bind_group(view) -> Option<ScriptBindGroup> Bind a texture view and nearest sampler using the shared texture-group layout.
view ScriptTextureView
Sampled texture view from texture.view(), texture.raw_view(), encoder.view_layer(id), or encoder.view_staging(id). sRGB views decode RGB to linear values; raw views do not. Bind at a texture group slot declared by the pipeline.
returns Option<ScriptBindGroup>
Some(group) on success; None with an error message if the GPU is unavailable or the view has expired. Editor-derived bindings expire when the next frame's shade stage begins; script-owned bindings can be retained. Sampling an attachment being written by the same pass is a GPU validation error.
# encoder.view_bind_group(view_id) -> Result<ScriptBindGroup, String> shade-stage: the view's live layer texture as input — unrecorded paints included, unlike view_pixels; built per call, so resizes are tracked next frame; binding a view as input to a pass targeting it is a validation error (disables the plugin)
view_id i64
ID of the view whose current live GPU layer texture will be sampled.
returns Result<ScriptBindGroup, String>
Ok(group) for the current frame; Err(message) for a missing view or unavailable GPU.
# rx.create_compute_bind_group(inputs, output) -> Option<ScriptBindGroup> 1–4 input textures + storage output; must match the pipeline's declared count; compute reads raw (no sRGB decode)
inputs Vec<ScriptTexture>
1–4 input textures, in binding order, matching the pipeline input count. Reads use raw RGBA8 values.
output ScriptTexture
Storage texture written by the compute shader at the binding after the inputs.
returns Option<ScriptBindGroup>
Some(group) when valid; None with a message for an unavailable GPU, invalid input count/type, or bind-group validation failure.
# rx.create_sprite_vertices(texture, options) -> Option<ScriptBuffer> convenience helper for the existing render-pass API: creates a six-vertex textured quad; bind the buffer with pass.set_vertex_buffer and draw with pass.draw
texture ScriptTexture
Texture whose dimensions normalize source coordinates into UVs. This helper does not bind the texture or issue a draw.
options Object
A descriptor such as #{ dst, src, color, opacity }. Only dst is required. Unknown fields and incorrectly typed values are rejected. The descriptor can be reused across calls.
options.dst Rect
Required destination rectangle in target pixels (y-down).
options.src Rect
Optional source rectangle in texture pixels (y-down). Defaults to the whole texture.
options.color Rgba8
Optional vertex tint. Defaults to opaque white.
options.opacity f64
Optional opacity multiplier, normally 0.0–1.0. Defaults to 1.0. Can be combined with a source rectangle and tint.
returns Option<ScriptBuffer>
Some(buffer) containing six vertices in the existing sprite pipeline layout; None with an error message for invalid options or an unavailable GPU. Each call allocates a new GPU vertex buffer.

buffer methods

buffer is a ScriptBuffer returned by rx.create_sprite_vertices.

# buffer.count() -> i64 Read the number of vertices in a buffer.
parameters
None.
returns i64
Number of vertices in this buffer; pass it to pass.draw(vertices, instances).

passes (the shade encoder)

shade(state, rx, encoder) records passes on the frame's command encoder. One pass is open at a time: beginning a new pass auto-ends its predecessors, and any pass left open is ended when the hook returns. load is "load" (keep) or "clear" (to transparent).

# encoder.view_layer(view_id) -> Result<ScriptTextureView, String> Access the live artwork texture view.
view_id i64
ID of an existing editor view, obtainable from rx.active_view_id() or rx.views(). Negative, out-of-range, and missing IDs are rejected. The raw artwork sheet: x spans the frame strip and y spans stacked layer strips. This does not select or crop the active layer.
returns Result<ScriptTextureView, String>
Ok(view) on success; Err(message) for an invalid/missing ID, expired encoder, or unavailable GPU. The sRGB view and bindings derived from it remain valid through the current frame's render and overlay hooks, then expire when the next shade stage begins. Obtain fresh handles each frame, including after a resize.
# encoder.view_staging(view_id) -> Result<ScriptTextureView, String> Access the live preview-overlay texture view.
view_id i64
ID of an existing editor view, obtainable from rx.active_view_id() or rx.views(). Negative, out-of-range, and missing IDs are rejected. The per-frame staging overlay, composited above the artwork and cleared each frame. Painting it does not modify recorded artwork.
returns Result<ScriptTextureView, String>
Ok(view) on success; Err(message) for an invalid/missing ID, expired encoder, or unavailable GPU. The sRGB view and bindings derived from it remain valid through the current frame's render and overlay hooks, then expire when the next shade stage begins. Obtain fresh handles each frame, including after a resize.
# encoder.begin_render_pass(target, options) -> Result<ScriptPass, String> Render into any texture view; beginning a pass ends the previous open pass.
target ScriptTextureView
Color attachment from a script texture or live editor target. Match the pipeline format to this view. Editor views must belong to the current frame.
options Object
Pass descriptor with the fields below. Unknown fields and invalid values return Err; descriptors can be reused.
options.load String
Required. "load" preserves existing pixels; "clear" clears the entire attachment before drawing. Output is always stored.
options.label String
Optional debug label; default "script_pass".
options.clear Vec<f64>
Optional with load: "clear" only. Exactly four finite linear RGBA floats in 0–1. Default [0.0, 0.0, 0.0, 0.0] (transparent black). Supplying this with load: "load" is an error.
returns Result<ScriptPass, String>
Ok(pass) on success; Err(message) for invalid options, an expired target, or an expired encoder. End the pass explicitly or let the next pass/hook completion end it. For undoable artwork edits, also call rx.touch_view(id).
# encoder.begin_view_pass(label, view_id, load) -> Result<ScriptPass, String> a view's layer — follow with rx.touch_view(id) so the edit is recorded (and undoable)
label String
Debug label for the GPU pass.
view_id i64
ID of the view to draw into.
load String
"load" preserves existing pixels; "clear" clears the target to transparent.
returns Result<ScriptPass, String>
Ok(pass) for the target; Err(message) for a missing view, unknown load operation, or expired encoder.
# encoder.begin_staging_pass(label, view_id, load) -> Result<ScriptPass, String> the view's staging overlay: composited above the view, cleared every frame — for uncommitted previews
label String
Debug label for the GPU pass.
view_id i64
ID of the view to draw into.
load String
"load" preserves existing pixels; "clear" clears the target to transparent.
returns Result<ScriptPass, String>
Ok(pass) for the target; Err(message) for a missing view, unknown load operation, or expired encoder.
# encoder.begin_compute_pass(label) -> Result<ScriptComputePass, String>
label String
Debug label for the GPU pass.
returns Result<ScriptComputePass, String>
Ok(pass), ending any earlier open pass; Err(message) if the encoder has expired.

render-pass methods

pass is a ScriptPass returned by encoder.begin_render_pass, encoder.begin_view_pass, or encoder.begin_staging_pass, or received by the render or overlay hook. Use it during the hook that created or received it. Beginning another encoder pass or returning from the hook ends it.

# pass.draw_sprite(source, options) -> Result<(), String> draw a textured quad with the built-in shader, pipeline, bindings, and geometry
source ScriptTexture | ScriptTextureView
A script texture or texture view, including a current-frame editor view. A texture uses its sRGB view.
options.dst Rect
Required destination rectangle in target pixels, top-left origin.
options.src Rect
Source rectangle in texture pixels; defaults to the whole texture.
options.transform Mat4
Applied to destination geometry before projection; defaults to identity.
options.color Rgba8
Multiplicative sRGB tint; defaults to opaque white. Tint RGB is converted to linear before multiplication.
options.opacity f64
0.0–1.0, default 1.0. Output alpha is sampled alpha × tint alpha × opacity; opacity does not darken RGB.
options.blend String
"alpha" (default) or "replace". Replacement writes transparent pixels too.
returns Result<(), String>
Err for invalid fields, non-finite geometry or transforms, invalid opacity/blend, an ended pass, or an expired editor view. Descriptors can be reused.

The pass supplies target dimensions and format. Sampling is nearest-neighbor, clamp-to-edge; no CPU readback occurs. Raw source/target views bypass the corresponding sRGB conversion. Artwork sources contain all frame and layer strips; crop and compose them explicitly. Do not sample the target's own texture subresource.

This changes the active pipeline, bind groups 0–1, and vertex-buffer slot 0. Rebind these before custom drawing in the same pass. Viewport and scissor state are preserved. For examples, see the scripting guide.

pass.draw_sprite(state.icon, #{
    dst: rx::rect(10.0, 10.0, 26.0, 26.0),
    opacity: 0.9,
}).unwrap();
# pass.set_pipeline(p) -> Result<(), String> Select the pipeline.
p ScriptPipeline
Pipeline to use for subsequent drawing or dispatch.
returns Result<(), String>
Ok(()) when recorded; Err(message) if the pass has ended. Invalid GPU state can also cause a validation error at hook completion.
# pass.set_bind_group(i, g) -> Result<(), String> Bind resources for the pipeline.
i i64
Nonnegative bind-group index matching the pipeline layout; group 0 is transforms, groups 1–3 are textures.
g ScriptBindGroup
Bind group with a layout compatible with the pipeline at index i.
returns Result<(), String>
Ok(()) when recorded; Err(message) if the pass has ended. Invalid GPU state can also cause a validation error at hook completion.
# pass.end() -> () End the pass; calling end again is harmless.
parameters
None.
returns ()
No return value (unit).
# pass.set_vertex_buffer(slot, b) -> Result<(), String> Bind a vertex buffer.
slot i64
Nonnegative vertex-buffer slot; sprite pipelines use slot 0.
b ScriptBuffer
Vertex buffer to bind in full.
returns Result<(), String>
Ok(()) when recorded; Err(message) if the pass has ended. The buffer must match the pipeline vertex layout.
# pass.draw(vertices, instances) -> Result<(), String> Record a draw call.
vertices i64
Nonnegative number of vertices, starting at 0; use buffer.count() for a whole sprite buffer.
instances i64
Nonnegative number of instances, starting at 0; normally 1.
returns Result<(), String>
Ok(()) when the draw is recorded; Err(message) if the pass has ended. Pipeline, bind groups, and any required vertex buffers must already be set.

compute-pass methods

compute_pass is a ScriptComputePass returned by encoder.begin_compute_pass. Use it during the hook that created it. Beginning another encoder pass or returning from the hook ends it.

# compute_pass.set_pipeline(p) -> Result<(), String> Select the pipeline.
p ScriptComputePipeline
Pipeline to use for subsequent drawing or dispatch.
returns Result<(), String>
Ok(()) when recorded; Err(message) if the pass has ended. Invalid GPU state can also cause a validation error at hook completion.
# compute_pass.set_bind_group(i, g) -> Result<(), String> Bind resources for the pipeline.
i i64
Nonnegative bind-group index matching the pipeline layout; compute uses group 0.
g ScriptBindGroup
Bind group with a layout compatible with the pipeline at index i.
returns Result<(), String>
Ok(()) when recorded; Err(message) if the pass has ended. Invalid GPU state can also cause a validation error at hook completion.
# compute_pass.end() -> () End the pass; calling end again is harmless.
parameters
None.
returns ()
No return value (unit).
# compute_pass.dispatch(x, y, z) -> Result<(), String> Record a compute dispatch.
x i64
Nonnegative number of workgroups along x, not the number of invocations; each group uses the shader workgroup size.
y i64
Nonnegative number of workgroups along y, not the number of invocations; each group uses the shader workgroup size.
z i64
Nonnegative number of workgroups along z, not the number of invocations; each group uses the shader workgroup size.
returns Result<(), String>
Ok(()) when the dispatch is recorded; Err(message) if the pass has ended. Pipeline and bind groups must already be set.

The render(state, rx, pass) hook receives a ScriptPass over the screen: same methods, screen-sized target (rx.screen_size()), re-begun per hook so errors attribute to the plugin that recorded them.

types & constructors (free functions, rx::)

# rx::rgb(r, g, b) -> Rgba8 alpha 255; fields .r .g .b .a readable
r i64
Red component, 0..=255. Values are cast to 8 bits, not clamped.
g i64
Green component, 0..=255. Values are cast to 8 bits, not clamped.
b i64
Blue component, 0..=255. Values are cast to 8 bits, not clamped.
returns Rgba8
A color with the supplied components and alpha 255.
# rx::rgba(r, g, b, a) -> Rgba8 explicit alpha; blends in draw_line/draw_text (e.g. translucent connection wires)
r i64
Red component, 0..=255. Values are cast to 8 bits, not clamped.
g i64
Green component, 0..=255. Values are cast to 8 bits, not clamped.
b i64
Blue component, 0..=255. Values are cast to 8 bits, not clamped.
a i64
Alpha component, 0..=255. Values are cast to 8 bits, not clamped.
returns Rgba8
A color with the supplied components. Alpha 0 is transparent and 255 is opaque.
# rx::rect(x1, y1, x2, y2) -> Rect fields .x1 .y1 .x2 .y2
x1 f64
First corner x coordinate.
y1 f64
First corner y coordinate.
x2 f64
Second corner x coordinate.
y2 f64
Second corner y coordinate.
returns Rect
A rectangle with these exact corners; construction does not normalize or clamp them. Use the coordinate space required by the receiving function.
# rx::mat4_identity() -> Mat4
parameters
None.
returns Mat4
The identity transform; points are unchanged.
# rx::mat4_translation(x, y) -> Mat4
x f64
Translation along x.
y f64
Translation along y.
returns Mat4
A translation matrix. Values are stored as f32.
# rx::mat4_scale(sx, sy) -> Mat4
sx f64
Scale factor along x; 1.0 preserves size, negative values reflect.
sy f64
Scale factor along y; 1.0 preserves size, negative values reflect.
returns Mat4
A scale matrix. Values are stored as f32.
# rx::mat4_rotation_z(theta) -> Mat4
theta f64
Rotation angle in radians around the Z axis.
returns Mat4
A rotation matrix. With y-down coordinates, positive angles rotate clockwise.
# rx::mat4_mul(a, b) -> Mat4
a Mat4
Left-hand matrix.
b Mat4
Right-hand matrix.
returns Mat4
The product a * b; when applied to a point, b acts first, then a.
# rx::mat4_transform_point(m, x, y) -> (f64, f64)
m Mat4
Matrix applied to the point.
x f64
Point x coordinate.
y f64
Point y coordinate.
returns (f64, f64)
The transformed (x, y). Calculations use the matrix’s f32 precision.
# rx::atan2(y, x) -> f64 radians
y f64
Vertical component of the vector.
x f64
Horizontal component of the vector.
returns f64
The vector angle in radians, in the range -π to π, with the quadrant determined by both components.

conventions & pitfalls