Scripting API

Lua scripts drive the same engine the editor runs on — every tool, layer, frame, tile and selection a person can work with, readable and writable from code, with or without a screen. This reference is generated from the engine itself, so it always matches what the app does: 251 functions (35 reads, 158 changes, 58 helpers for images, random numbers, color math and script dialogs).

All functions live on the global app table. Calls are synchronous from the script's point of view. print(...) writes to the script console, and app.config holds the values of the script's config form (always defined, empty when there is no form). Colors are "#rgb", "#rrggbb" / "#rrggbbaa" strings or { r, g, b, a } tables. Coordinates are integer sprite pixels with (0, 0) at the top-left. Layer, frame, tag and slice ids are strings. Lua tables are 1-based, and a returned list of 0/1 or RGBA numbers is row-major.

  • Start with app.describe(). One call returns the whole document's structure: layers, frames, cels, tags, slices, selection and editing context.
  • Creating returns an id. app.addLayer, app.addFrame, app.addTag and friends return what they made, valid across undo and redo.
  • Selection and symmetry are opt-in. Leave out a color or brush and the editing context fills it in. Selection and symmetry are opt-in: pass respectSelection = true and symmetry = app.getContext().symmetry.
  • One run, one undo. Everything a script changes is a single undo step. Locked layers are refused.
  • No time limit. Any run can be paused — even mid-loop — resumed, stopped, or stopped and reverted.
  • Use app.random, not math.random. app.random.new(seed) is a seeded generator: the same seed gives the same result on every run (and redo), so scripts should take a seed from app.config and prefer it to math.random. app.random.hash(x, y, seed) gives per-pixel noise. pairs() visits a table's keys in the same order on every run too, so iterating a table of colours doesn't change the art.
  • Pixel loops go through an Image. app.getImage, then img:getPixel / img:setPixel as often as you like, then one app.putImage. Each app.* call is a trip to the editor; an Image's methods aren't.
  • Use app.color, not hand-rolled hex parsing and mixing. app.color.mix, adjust, ramp, shades, nearest and contrast work in a perceptual space (OKLab) and clamp to sRGB; parsing "#rrggbb" with string.sub and averaging channels gives muddy results.
  • Sandboxed. No network, files or code loading; a script can only touch the open sprite. A run has a memory budget (its Images, its Lua data, what it adds to the document and to the undo history): past it the script is stopped with an error and what it did so far stays as one undo step. app.apiVersion tells a script which version of this API it runs on.

Sections

  • Option groups: The painting and brush options shared by the drawing functions, defined once.
  • Document: The sprite as a whole: start here with app.describe(), then size, crop, rotate and color mode. (15 functions)
  • Drawing: The editor's tools as commands — the GUI tools dispatch exactly these. Color, brush and tool options you leave out come from the editing context. Painting ignores the selection and symmetry unless you pass respectSelection = true and symmetry = app.getContext().symmetry. (24 functions)
  • Adjustments: The colour adjustments and colour FX of the Filter menu, each one undo step over a layer's cels in the frames you list. They ignore the selection unless you pass respectSelection = true, and never touch fully transparent pixels. On indexed sprites indexedMode chooses between snapping the recoloured pixels to the nearest palette entry ("remap") and editing the palette entries the cels use ("palette"). (16 functions)
  • Images: The fast way to work pixel by pixel. app.getImage reads a cel (or part of it) into an Image, a pixel buffer that lives inside the script: img:getPixel and img:setPixel never leave the script, so a loop over every pixel of a 512 x 512 cel takes about a tenth of a second. app.putImage writes it back as one change, however many pixels you set. A pixel is one packed integer (app.pixelColor.rgba(r, g, b, a), as in Aseprite); setPixel also takes "#rrggbb[aa]" and { r, g, b, a } (alpha defaults to 255). On indexed sprites img:getIndex and img:setIndex work with palette indices, so duplicate colours stay apart. The usual pattern: local img = app.getImage(layer, frame); for y = 0, img.height - 1 do for x = 0, img.width - 1 do img:setPixel(x, y, ...) end end; app.putImage(layer, frame, img). (17 functions)
  • Pixels: Pixel reads and writes as flat RGBA lists (built on Images; never selection-clipped or mirrored), whole-selection fills and flips, and the eyedropper. (7 functions)
  • Selection: Selection shapes combine with the current selection (replace, add, subtract, intersect). The selection mask is readable and writable. (19 functions)
  • Layers: Create, arrange and configure layers. Groups are folders of layers: a layer's parentId names its group (nil at the top level) and app.moveLayer positions count within a group (0 = bottom). Hiding or locking a group hides or locks everything inside it. A group at full opacity with blend mode "normal" is pass-through (its layers blend straight into what's below) unless its isolated flag is set; an isolated group, or any other opacity or mode, composites its layers together first. (25 functions)
  • Frames & tags: The timeline: frames, their durations, and animation tags. (25 functions)
  • Cels: A cel is one layer's image on one frame. Move, clear and link them in blocks. (9 functions)
  • Tilemaps: Tilemap layers place tiles from their own tileset on a grid. Tiles are numbered from 1 (0 is the empty tile). (19 functions)
  • Palette: The sprite's palette. (21 functions)
  • Slices: Named rectangles, e.g. for export regions or hitboxes. A slice can also be a 9-slice (a center rectangle) and carry a pivot point; both are relative to the slice's top-left corner, as in Aseprite, and move with it when the canvas is resized, cropped or rotated. (5 functions)
  • Brushes: Define your own brushes by name, then paint with them. They live only in the engine for the session: not in the document, not undoable, not saved, and separate from the editor's brush library. (6 functions)
  • Editing context: The current colors, brush, symmetry and tool options — shared with the editor's toolbar. Draw commands use them for colors, brush and tool options they aren't given; symmetry is information only and is applied only when you pass it. (3 functions)
  • Random: Seeded random numbers, computed inside the script (no round trip per call). The same seed gives the same results, so a script can be re-run, undone and redone, or shared, and look identical. Prefer these to math.random, which differs on every run. Generators are objects: call their methods with a colon (rng:int(1, 6)). (11 functions)
  • Color: Color math without hand-rolled hex parsing and RGB mixing. Colors in are "#rrggbb" / "#rrggbbaa" strings or { r, g, b, a } tables (0-255, a optional); colors out are { r, g, b, a } tables unless the name says hex. Mixing and adjusting default to OKLab, a perceptual space where equal steps look equal; `space` can be "oklab", "oklch", "rgb" or "hsl". Results are clamped to sRGB by reducing chroma, so hue and lightness hold. Lists (ramp, shades) are built in one call. (11 functions)
  • Dialogs and commands: Script UI, modelled on Aseprite: Dialog{ title = ... } builds a window from widgets (label, entry, number, slider, check, radio, combobox, color, shades, button, separator, file) and plugin:newCommand adds commands to the menus, the command palette and the Keyboard Shortcuts editor. The window is drawn by the editor, as text only, with a title bar that names the script and marks it "script". Callbacks (onclick, onchange, onclose) run in the script, one at a time, each as a single undo step; a script with a dialog open stays alive, idle, while the user keeps drawing, and stopping it closes its dialogs. Not supported: canvas widgets and dlg:repaint. (18 functions)
  • Config forms: The pre-run form a script declares: number, text, checkbox, select, color, brush and palette fields.
  • Context settings: The colors, brush and tool options draw commands default to, plus the UI's symmetry toggle (information only).
  • Types: Every named set of values (FillMode, BlendMode, SymmetryAxis, ...), each value explained.
  • Reading the document: How each part of the document model is read by a script.
  • Internal commands: Engine commands scripts can't call, and why.
  • Stays in the editor: What a script can't reach: zoom, panels, playback, saving.

As Markdown: scripting.md; the whole documentation in one file: llms-full.txt.