# pxstud.io documentation > pxstud.io is a pixel art and animation editor that runs in the browser: layers, frames, tags, tilemaps, palettes and Aseprite files, with no install and no account. It is local-first: work is saved in the browser and the editor works offline. Lua scripts drive the same engine the editor uses. A free account (never needed for the editor) opens the shared script library, and Sync keeps work in sync across devices. The whole manual, page by page in reading order; the scripting guides, then the whole Lua scripting API reference; then the FAQ. The index of every page, each also on its own: https://pxstud.io/llms.txt. # pxstud.io manual How to draw, animate and build tilesets in pxstud.io, the pixel art editor that runs in your browser. pxstud.io is a pixel art and animation editor that runs in your browser. It needs no account and no install: open the editor and start drawing. Your work is saved in the browser as you go, and the editor keeps working offline. This manual explains what every part of the editor does and how to use it. If you've used Aseprite, most of it will feel familiar: pxstud.io follows the same ideas (layers, frames, cels, tags, tilemaps), and opens and saves `.aseprite` files. ## Where to start - **New to pxstud.io?** [Your first sprite](https://pxstud.io/docs/manual/start/first-sprite.md) walks through making, drawing, saving and exporting a sprite. - **How it all fits together:** [How your work is organized](https://pxstud.io/docs/manual/concepts/how-work-is-organized.md) explains projects, sprites, layers, frames and cels, and what's kept where. - **Arranging the editor:** [The workspace](https://pxstud.io/docs/manual/guides/workspace.md) covers panels, layouts and multiple editors. - **Guides** for each part of the editor: [layers](https://pxstud.io/docs/manual/guides/layers.md), [color and palettes](https://pxstud.io/docs/manual/guides/color.md), [selecting and transforming](https://pxstud.io/docs/manual/guides/selection.md), [animation](https://pxstud.io/docs/manual/guides/animation.md), [tilemaps](https://pxstud.io/docs/manual/guides/tilemaps.md), [opening, importing and exporting](https://pxstud.io/docs/manual/guides/import-export.md), [slices](https://pxstud.io/docs/manual/guides/slices.md), [resizing, cropping and effects](https://pxstud.io/docs/manual/guides/document.md), the [library](https://pxstud.io/docs/manual/guides/library.md) of scripts, palettes and brushes, and [inviting people to view your work](https://pxstud.io/docs/manual/guides/share-links.md). - **Keyboard, screen reader or touch screen?** [Keyboard, accessibility and touch](https://pxstud.io/docs/manual/guides/accessibility.md) covers the command palette, keyboard drawing and touch gestures. - **Looking something up?** The [Tools](https://pxstud.io/docs/manual/reference/tools.md), [Tool options](https://pxstud.io/docs/manual/reference/tool-options.md), [Menus](https://pxstud.io/docs/manual/reference/menus.md), [Keyboard shortcuts](https://pxstud.io/docs/manual/reference/shortcuts.md), [Settings](https://pxstud.io/docs/manual/reference/settings.md) and [File formats](https://pxstud.io/docs/manual/reference/file-formats.md) references list everything, generated from the editor itself. - **Automating with Lua?** [Your first script](https://pxstud.io/docs/scripting/first-script.md) writes one in a few minutes, the [scripting guide](https://pxstud.io/docs/scripting/guide.md) covers the rest, and the [Scripting API](https://pxstud.io/docs/scripting.md) documents every Lua function. - **Every shortcut on one page:** the [keyboard quick reference](https://pxstud.io/quick-reference). - **Rather have an app?** [The desktop app](https://pxstud.io/docs/manual/guides/desktop-app.md) is the editor as a native app for macOS, Windows and Linux. # Your first sprite Open the editor, make a sprite, draw, and save and export your work. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ This walks through making a small sprite from start to finish: creating it, drawing, undoing mistakes, and getting a PNG out. It takes about five minutes. ## Open the editor Go to [app.pxstud.io](https://app.pxstud.io). There's nothing to install and no account to make: the editor starts with a project called "Untitled Project" holding one blank sprite, "Untitled 1" (64×64 pixels, transparent, with a 24-color starter palette). The default layout has: - the **menu bar** across the top; - the **palette**, **color picker** and **foreground/background colors** on the left; - the **tool options** bar, the **canvas** and the **timeline** in the middle; - the **toolbar** on the right. On a phone or a small window, the editor uses a compact layout instead, with the side panels folded into tabs. Everything is still there; see [The workspace](https://pxstud.io/docs/manual/guides/workspace.md). ## Make a new sprite Choose File > New Sprite... (`Ctrl+Alt+N`), or click **+** at the end of the sprite tabs. The New Sprite dialog opens on 64×64. ![The New Sprite dialog with the Blank presets tab open, square sizes from 8x8 to 256x256, and the Name, Width, Height, Color Mode, Background and Palette fields on the right](https://pxstud.io/manual/images/start/new-sprite-dialog.light.webp) _The New Sprite dialog_ - **Presets** across the top fill in common sizes: **Blank** (squares and game screens), **Social** (YouTube, Instagram and more), **Consoles** (the native screens of classic systems and fantasy consoles like PICO-8), **Stores** (Steam, itch.io and the app stores) and **Recent** (sizes you've used). - For Social and Stores sizes, a **Pixel Size** row offers smaller canvases that scale up exactly. Pick "320x180 ×4", draw at 320×180, and export at 400% to get a crisp 1280×720 image. - **Width** and **Height** set the size yourself. - **Color Mode** is RGBA Color (any color, with transparency), Grayscale, or Indexed Color (every pixel is one of the palette's colors). - **Background** is Transparent, White, or your background color. A non-transparent background becomes a [Background layer](https://pxstud.io/docs/manual/guides/layers.md#the-background-layer). - **Palette** starts the sprite with the default palette, one of My Palettes, or a built-in palette. Click **Create**. The sprite opens in a new tab, in the current project. ## Draw 1. Pick a color: click a swatch in the palette to make it the **foreground** color, or right-click it to make it the **background** color. Press `X` to swap the two. 2. Pick the Pencil (`B`) and drag on the canvas. The right mouse button draws with the background color. 3. Try the Paint Bucket (`G`) to fill an area, the Rectangle (`U`) for boxes, and the Eraser (`E`) to clear pixels. Holding `Alt` over the canvas picks up a color with the Eyedropper. Every tool is described in the [Tools reference](https://pxstud.io/docs/manual/reference/tools.md). **Undo** with `Ctrl+Z` and **redo** with `Ctrl+Shift+Z` or `Ctrl+Y`. Each sprite has its own undo history, and the history button under them in the toolbar (or Edit > Undo History) shows every step as a tree so you can jump back to any of them, including steps you undid and then replaced with something else. ## Move around - **Zoom** with the mouse wheel or a trackpad pinch (it zooms around the pointer), or with `Ctrl+=` and `Ctrl+-`. `Ctrl+Shift+0` fits the whole sprite in view. - **Pan** with a two-finger trackpad scroll, the middle mouse button, or the Hand (`H`). - On a touch screen, one finger draws and two fingers pinch to zoom and pan. Tap with two fingers to undo, or three to redo. ## Saving You don't have to save: every change is kept in your browser as you work, and the header shows **Saved locally** once it is. Close the tab and come back later, and your project, its sprites and the tabs you had open are still there. The editor also works offline once you've opened it. You can also install the editor as an app, with its own window and icon: in Chrome, Edge and other Chromium browsers, use the install icon at the end of the address bar; on iPhone and iPad, tap Share, then **Add to Home Screen**. On a computer or an Android phone, the app and the browser tab share the same work. On iPhone and iPad, the Home Screen app keeps its own copy, apart from Safari's: sign in to Sync in both to have the same sprites. - File > Save (`Ctrl+S`) saves right away instead of waiting a moment. - File > Save As... (`Ctrl+Shift+S`) downloads the sprite as a `.pxstudio` file, a copy you can keep, share, or open again with File > Open... (`Ctrl+O`). > **Warning:** Work saved in the browser stays in that browser. Clearing your browser's site data deletes it, and > it isn't on your other devices. Download a copy with File > Save As... to keep it safe, or use > Sync on pxstud.io to keep your work in your account. ## Export a PNG Choose File > Export... (`Ctrl+Alt+Shift+S`). The dialog shows a live preview of the file you'll get. - **Type:** PNG, Animated GIF, Animated PNG, Sprite Sheet, Aseprite file, WebP, MP4 video and more. - **Resize:** scale the image up by whole steps (100% to 1600%) so every pixel stays a sharp square. Pixel art is usually drawn small and exported big. - **Frames:** all of them, only the current one, or one tag's frames. Click **Export** and the file downloads. ## Find any command Press `Ctrl+K` to open the command palette and type what you want ("flip", "onion", "export"). It lists every command with its menu and shortcut, and the ones you used recently come first. ## Next - [Layers](https://pxstud.io/docs/manual/guides/layers.md): draw the outline, colors and shading separately. - [The workspace](https://pxstud.io/docs/manual/guides/workspace.md): arrange the panels the way you like. - [Menus](https://pxstud.io/docs/manual/reference/menus.md): everything in the menu bar. # How your work is organized Projects, sprites, layers, frames and cels, and your libraries of palettes, brushes and scripts, and where each is kept. Everything you make in pxstud.io fits into a few nested pieces. Knowing them helps with almost everything else: what undo can take back, what a downloaded file contains, and what Sync copies to your other devices. ## The pieces ``` Project └── Sprite one image or animation (a .pxstudio file) ├── Layers stacked drawings; groups hold other layers ├── Frames the steps of the animation │ └── Cels what one layer shows on one frame ├── Tags named ranges of frames ├── Slices named rectangles (hitboxes, 9-slices, export areas) ├── Palette the sprite's colors └── Tilesets the tiles of tilemap layers Your library: palettes, brushes and scripts, used across all your sprites ``` **Project.** A named folder of sprites. You can have as many as you like and switch between them in the **Project** menu. A project holds nothing but its sprites. **Sprite.** One image or animation: its size, color mode, layers, frames and everything else below. Each sprite opens in its own tab. Its name belongs to the project, not the drawing, so a downloaded `.pxstudio` file takes the sprite's name as its file name. **Layer.** Layers stack from the bottom up, and each one can be shown, hidden, locked and blended on its own ([Layers](https://pxstud.io/docs/manual/guides/layers.md)). There are four kinds: - **image layers**, the usual kind, holding pixels; - **groups**, which hold other layers; - **reference layers**, a picture to trace over, never exported; - **tilemap layers**, painted with tiles from a tileset. The **Background** layer is an image layer with a few rules: it's opaque, at the bottom, and can't be moved. **Frame.** One step of the animation, with how long it shows (in milliseconds). A still image is a sprite with one frame. **Cel.** The meeting point of a layer and a frame: what that layer shows on that frame. A sprite with 3 layers and 4 frames has up to 12 cels. A cel can be **empty** (nothing drawn there yet), and cels on one layer can be **linked** so several frames share one picture: drawing on any of them changes all of them. Pixel art usually has empty space, so a cel only stores the part of the canvas that's been drawn on. **Tag.** A named, colored range of frames ("walk", "jump") with a playback direction: forward, reverse, ping-pong or reverse ping-pong, and how many times it repeats. Exports can use one tag's frames. **Slice.** A named rectangle on the canvas with no pixels of its own, used to mark hitboxes, export regions or 9-slice scaling guides. A slice can change from one frame to the next. **Palette.** The sprite's list of colors. What it means depends on the sprite's **color mode**: - **RGBA:** every pixel has its own color, with transparency. The palette is a set of swatches to pick from. - **Grayscale:** shades of gray, with transparency. - **Indexed:** every pixel _is_ a palette entry, so recoloring an entry recolors every pixel that uses it. One entry (the first, unless you change it) counts as transparent. A palette can also change partway through an animation, from a given frame on. **Tileset.** The tiles a tilemap layer paints with. Repainting a tile updates everywhere it's placed, and several tilemap layers can share one tileset. **Your library.** Palettes, brushes and scripts you make, save or fork live in your own library, outside any sprite, so you can use them in all of them. The editor also comes with built-in palettes and brushes. Choosing a library palette copies its colors into the sprite; changing it later doesn't change sprites that already use it. ## The editing context Your **foreground and background colors**, the **brush** and each **tool's options** aren't part of any sprite. One set is shared by every tab, so switching sprites keeps your brush and colors. They reset when you reload the page. (A [saved layout](https://pxstud.io/docs/manual/guides/workspace.md#layouts) can bring your tool settings back.) ## What undo covers Each sprite has **its own undo history**. Anything that changes the sprite can be undone: drawing, layers, frames, cels, tags, slices, the palette, resizing, and the selection too. These **can't** be undone: - renaming a project or a sprite; - changes to palettes, brushes and scripts in your library; - your colors, brush and tool options; - which layer or frame is active, and how the workspace is arranged; - the names you give undo steps. The history is saved with the sprite, so it's still there after you close its tab or reload the page, and with Sync on your other devices too. The oldest steps are dropped after 1,000. ## Versions Undo goes back step by step, and its oldest steps are dropped. To keep a state of a sprite for good, **name it**: Sprite > Version History... > **Name this version...** keeps the sprite as it is now on this device, until you delete the version. From the same list you can **Compare** a version with the sprite as it is now (side by side, or with every changed pixel marked), **Restore** it (one step you can undo) or download it as a `.pxstudio` file (without undo history: a version has none). Naming a step in the [Undo History](https://pxstud.io/docs/manual/guides/document.md#undo-history) panel keeps a version too: the sprite as it was at that step. With Sync, the cloud keeps earlier versions of each sprite too: every save of the last day, then one a day for 30 days, then one a week for a year. Name one of those (the pin button) and it's kept for good on every device; its storage counts toward your plan until you remove the name. ## The same sprite in two tabs A sprite can be edited in only one browser tab at a time. Open it in a second tab and it's read-only there, with a note at the top; whatever the first tab saves shows up in it. Choose **Edit here** to move the editing to the second tab: the first one saves and becomes read-only. When the editing tab closes the sprite, another tab that has it open takes over. ## Where things are kept | What | Kept | | - | - | | Projects and sprites | In this browser, saved as you work. With Sync, also in your account. | | Your palettes, brushes and scripts | In this browser. With Sync, also in your account; published script versions are in the script library. | | Built-in palettes and brushes | In the editor itself. | | Hearted scripts | In your account, with a copy in this browser so they work offline. | | Layouts, preferences and shortcuts | In this browser only. | | Colors, brush and tool options | Only while the page is open. | | Undo history | With the sprite: in this browser, with Sync in your account, and in the `.pxstudio` files you download. | | Named versions | In this browser. Cloud versions you name are in your account. | ## Files you can download - **A `.pxstudio` file** (File > Save As...) is one sprite: its size and color mode, palette, layers, frames, cels, tags, slices, tilesets, grid and reference images, and its undo history. It doesn't include named versions, the selection or anything outside the sprite. Open it again with File > Open..., and undo carries on where it was. - **Everything at once:** on [your account page](https://app.pxstud.io/account), **This browser > Export everything (.zip)** downloads a zip of every project's sprites (as `.pxstudio` files, with their undo history) and your palettes (`.gpl`), brushes and scripts. It works offline, and signed out. # The workspace Move, dock, float and hide panels, switch between layouts, and open more than one editor. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ Everything around the canvas is a **panel**, and every panel can be moved, resized, stacked with others or closed. Arrange the editor for drawing, for animating or for a small laptop screen, save the arrangement as a layout, and switch between layouts from the View menu. ![The editor in its Default layout: the menu bar on top, the palette and color picker on the left, the canvas with a green slime sprite in the middle above the timeline, and the tools down the right](https://pxstud.io/manual/images/guides/workspace-default.light.webp) _The Default layout_ ## The panels | Panel | What it's for | | - | - | | **Menu bar** (bar) | The menus, plus the save status, your account and the theme and full-screen buttons. | | **Tools** (bar) | The tools. | | **Tool options** (bar) | Settings for the current tool. | | **Editor** | The sprite tabs and the canvas. You can open several. | | **Timeline** | Layers, frames, cels and tags. | | **Palette** | The sprite's colors. | | **Color picker** | Picks any color for the foreground or background. | | **Colors** (bar) | The foreground and background colors and your recent colors. | | **Tileset** | The tiles of the active tilemap layer; it comes to the front when you select one. | | **Preview** | A second, separate view of the sprite. | | **Scripts** | The script editor; it opens when you create or open a script. | The menu bar, Tools, Tool options and Colors are **bars**: they size themselves to their contents and dock only along edges. ## Moving panels Drag a panel by any empty part of it, or by its tab. (Fields, sliders, the canvas and lists don't start a drag, so they keep working normally.) While you drag, the drop target highlights: - **onto another panel's edge** to split the space with it; - **onto another panel's middle or its tabs** to stack it there as a tab; - **along the edge of the workspace** to dock it across that whole side; - **with `Alt` held** to float it in its own window, which you can resize from any edge. `Esc` cancels the drag. On a touch screen, hold your finger on the panel for a moment before dragging. Drag the lines between panels to resize them. They also work from the keyboard: Tab to a line, then use the arrow keys (`Shift` for bigger steps). Double-click a line, or press `Enter` on it, to fit the panels beside it to their contents. No panel can be made smaller than its contents. ### The panel menu Right-click a panel's empty space or its tab (or long-press it, or press `Shift+F10`) for its menu: - **Float**, or **Dock to** the left, right, top or bottom edge; - **Collapse to Tabs** (show just the tabs; clicking one opens the panel over the workspace) or **Expand**; - **Stack with** another panel; - **New editor beside this one** (editors only); - **Close**. ## Showing and hiding panels View > Panels > Timeline, View > Panels > Palette and the other items in **View > Panels** show and hide each panel. A panel you reopen goes back where it was. If you close the menu bar and the Colors bar too, a **☰ Workspace** button appears in the corner to bring the menu bar back or reset the layout. ## Layouts **View > Workspace** lists the layouts. Choose one to switch to it. | Layout | Arrangement | | - | - | | **Default** | Palette, color picker and colors on the left; tool options, canvas and timeline in the middle; tools on the right. | | **Animation** | Like Default, with a taller timeline and the Preview docked beside the canvas. | | **Minimal** | Just the menu bar, tool options, the canvas and the tools. | | **Compact** | For phones held upright: the canvas with tools down the side, and the other panels as tabs that open as sheets. | | **Compact (Landscape)** | The same for phones held sideways. | - View > Workspace > Save Layout As... saves the current arrangement under a name. Tick **Include these settings** to have the layout also bring your view preferences, theme, keyboard shortcuts or tool defaults with it whenever you switch to it. - View > Workspace > Update Current Layout saves changes to the active saved layout. - View > Workspace > Reset Layout puts the panels back the way the active layout was saved. - View > Workspace > Manage Layouts... lets you rename, delete, export and import layouts (as `.pxlayout.json` files, to share them or move them to another browser). - View > Workspace > Lock Layout stops panels being moved by accident. Layouts are saved in this browser, on this device. They aren't synced to your account. ## Small screens and phones When the window is narrower than 768 pixels or shorter than 500, the editor switches to the Compact layout (Compact (Landscape) when the window is wider than it is tall). It switches back when the window grows again. This goes by window size, so a narrow desktop window gets the compact layout too. Each size keeps its own arrangement, so rotating a phone or resizing a window never undoes how you set up the other. A desktop arrangement that no longer fits the window also gives way to the compact one until there's room again. In the compact layouts, panels shown as tabs open as **sheets** over the workspace: tap the tab again, tap elsewhere or press `Esc` to close one, and drag its edge to make it bigger. The menus are under the **☰** in the Colors bar. Compact layouts start locked; unlock them from the panel menu or View > Workspace > Lock Layout. ## More than one editor View > New Editor opens another editor beside the current one. Use it to see the same sprite at two zoom levels, or two sprites side by side. - Each editor has its own **zoom, pan, rotation, tiled mode, pixel grid and onion skin**, and shows its own sprite. - **Tools, brush and colors** are shared by every editor. - The editor you last clicked is the **active** one: menus and shortcuts act on its sprite. ## Zoom, pan and rotate | To | Do | | - | - | | Zoom in or out | Mouse wheel or trackpad pinch (around the pointer, or the middle of the view), `Ctrl+=` / `Ctrl+-`, or the Zoom | | See the whole sprite | View > Fit to Window (`Ctrl+Shift+0`) | | See pixels at 100% | View > Actual Size (`Ctrl+0`) | | Pan | Hold `Space` and drag (with any tool, as in Aseprite), two-finger trackpad scroll, the middle mouse button, `Shift` + mouse wheel to go sideways, or the Hand | | Rotate the view | `Ctrl+Alt+]` / `Ctrl+Alt+[`; click the angle badge on the canvas, or press `Ctrl+Alt+0`, to straighten it | Rotating the view only turns how you see the sprite, never the sprite itself. Zoom goes from 1/64 (about 2%) to 64×, Aseprite's range, so even a 4096-pixel sheet fits on screen. **Zoom snapping** (Settings > Zoom Snapping > Whole Numbers, under Settings) decides which zoom levels you can land on: - **Whole Numbers** (the default): Aseprite's zoom levels, 1×, 2×, 3×, 4×, 5×, 6×, 8×, 12×, 16×, 24×, 32×, 48× and 64×, and below 100% 1/2, 1/3, 1/4, 1/5, 1/6, 1/8, 1/12 and so on down to 1/64. Every pixel is the same size on screen: at 2.5× some pixels would be two screen pixels wide and others three. - **Powers of Two:** 1/64, 1/32 ... 1/2, 1×, 2×, 4×, 8×, 16×, 32× and 64×, for bigger jumps. - **Off (Smooth):** any level in between, in small steps. While snapping is on, each wheel notch, zoom key or zoom-tool click moves one level. Trackpad and two-finger pinches jump from level to level as you go. View > Fit to Window picks the largest level that still fits. The setting applies to every editor and to the preview, and is kept on this device. **Mouse and trackpad** (Settings > Mouse and Trackpad...) changes how the mouse works on the canvas: - **Mouse wheel:** **Zoom** (the default) zooms with the wheel and pans with a trackpad's two-finger scroll. **Scroll the view** pans with the wheel instead (`Shift` + wheel goes sideways) and zooms with `Ctrl` + wheel (`⌘` + wheel on a Mac). A trackpad pinch zooms either way. - **Zoom around:** the point under the pointer stays still as you zoom (the default), or the middle of the view. - **Brush size:** `Ctrl+Shift` + wheel (`⌘⇧` + wheel on a Mac) makes the active tool's brush, or a line's width, bigger with the wheel going up. **Invert brush-size wheel** turns that round, as in older Aseprite. - **Right button:** the tool's own right-click (the default: most tools paint with the background color, as in Aseprite), or **Erase while held**, **Pick color while held** or **Pan the view**. The Zoom, Slice and Text tools keep their own right-click. A pen's side button counts as the right button. - **Back and forward buttons** open the [tool wheel](https://pxstud.io/docs/manual/reference/tools.md) while you hold one over the canvas; let go over a tool to pick it. The browser doesn't go back while the pointer is over the canvas. - **Cursor size:** 100%, 150% or 200%, still sharp. The middle button always pans. Any mouse button but the left one, and either wheel direction, with or without modifier keys, can also run a command: add it in Settings > Keyboard Shortcuts..., where it replaces what the button or wheel did on the canvas ([Keyboard shortcuts](https://pxstud.io/docs/manual/reference/shortcuts.md)). **Full screen:** the button at the right of the menu bar makes the whole editor full screen, and the button in the canvas's top-right corner shows just the canvas. `Esc` leaves either. **Canvas info:** View > Show Canvas Info shows a readout in a corner of the canvas (pointer position, selection size, tool, layer, frame, zoom and more). Choose its corner and what it shows with Settings > Canvas Info.... # Layers Stack your art on layers, group them, lock them, and control how they blend. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ A sprite is built from **layers** stacked on top of each other. Each layer holds one image per frame (a **cel**), so you can draw the outline, the colors and the shading separately and change one without touching the others. Layers are listed down the left side of the timeline, top layer first. ![The timeline with four layers: Outline, Colors, Shading and Background, with their visibility and lock buttons](https://pxstud.io/manual/images/guides/layers-timeline.light.webp) _The layer list on the left of the timeline_ ## Adding and removing layers - **New layer:** Layer > New Layer (`Shift+N`) adds an empty layer above the active one. - **Duplicate:** Layer > Duplicate Layer copies the active layer and its cels. - **Delete:** Layer > Delete Layer (`Ctrl+Alt+Shift+L`). - **Rename:** press `F2` on a layer, or right-click it and choose **Rename Layer**. Click a layer to make it the **active layer**: drawing tools paint on the active layer at the current frame. Right-click any layer for a menu of everything you can do with it. `Alt`+click a layer to select its painted pixels instead ([Selecting](https://pxstud.io/docs/manual/guides/selection.md#making-a-selection)). ## Ordering layers Drag a layer by its handle (_grip icon_) to move it up or down the stack, or use Layer > Move Layer Up and Layer > Move Layer Down. Layers higher in the list are drawn on top. ## Showing, hiding and locking Each layer row starts with three buttons, in columns, before its name: | Button | What it does | | - | - | | _eye icon_ Visible | Hides or shows the layer. Hidden layers aren't drawn on the canvas or in exports, and can't be painted on. | | _padlock icon_ Lock | Locks the layer's content: nothing on it can be painted, erased or moved. | | _two joined dots icon_ Continuous | Frame > New Frame links the layer's cel to the one it copies instead of making a separate copy: handy for a background that stays the same across frames. Click it to make the layer **discontinuous** (_two dots icon_), the default, so new frames get their own copy. The Background layer starts out continuous. Groups have none. | The icons above the columns set every layer at once: click the eye to show all layers (or hide them all when they're all shown), and the same for the lock and continuity. **Lock movement** keeps a layer's cels in place (the Move tool, timeline drags and nudges leave them alone), while painting still works. Turn it on in Layer > Layer Properties...; the layer then shows a pin (_pin icon_) before its name. The Background layer's movement is always locked. To show or hide several layers at once, press one eye and drag over the others while holding the button down. Each layer you pass over takes the same visibility as the first one, and the whole drag is undone in one step. To look at one layer on its own, `Alt`+click its eye, or choose Layer > Solo Layer. Every other layer is hidden (the groups it's in stay shown). Do it again to bring back exactly the layers that were hidden. Each step can be undone. ## Finding layers Type in the **Search layers** box above the layer list to show only the layers whose name contains what you typed, with the groups they're in (even collapsed ones). Point at the box to see how many layers match. Press `Esc` or clear the box to see every layer again. You can't drag layers to reorder them while searching. ## Opacity and blend modes Each layer has an **opacity** (0 to 100%) and a **blend mode**, which decides how its colors mix with the layers below. Click the opacity number on a layer, or choose Layer > Layer Properties..., to change them. pxstud.io has the same 19 blend modes as Aseprite, from Normal and Multiply to Hue and Divide. The Layer Properties dialog also holds the layer's name, its visibility and locks, and its **user data**: a color that tints the layer's row in the timeline, and a note. ## Groups A **group** holds layers so you can treat them as one: hide, lock, move, duplicate or delete them together. ![The timeline with a group named Character, expanded, holding the Outline and Colors layers](https://pxstud.io/manual/images/guides/layers-group.light.webp) _A group holding two layers_ - **Make a group:** select cels on the layers you want in the timeline (or just click one layer) and choose Layer > Group Layers (`Ctrl+G`), or start an empty one with Layer > New Group. - **Add or take out layers:** drag them into or out of the group. - **Ungroup:** Layer > Ungroup (`Ctrl+Shift+G`) puts the layers back where the group was. - **Fold it away:** the arrow (_down arrow icon_) beside the group collapses or expands it. Hiding or locking a group hides or locks everything in it. A group at 100% opacity with the Normal blend mode is **pass-through**: its layers blend with the layers below as if they weren't grouped. An **isolated** group combines its layers first and blends the result as one image, so a Multiply or Screen layer inside only mixes with the group's own layers. Tick **Isolate** in Layer > Layer Properties... to isolate a group at 100% Normal (like Photoshop's Normal group, where pass-through is Photoshop's Pass Through). Any other opacity or blend mode always isolates the group, and the box shows ticked. ## Clipping masks A **clipping mask** shows a layer only where the layer below it has pixels. Paint shading, highlights or a texture on a clipped layer and it stays inside the shape underneath, however loosely you paint. To clip the active layer, or every selected layer, choose Layer > Clipping Mask (`Ctrl+Alt+G`). Choose it again to release them. You can also tick **Clipping mask** in Layer > Layer Properties.... A clipped layer shows a bent arrow (_bent down arrow icon_) before its name. - A clipped layer clips to the nearest **unclipped** layer below it in the same group, its **base**. Several clipped layers in a row all clip to the same base. - Each clipped layer keeps its own opacity and blend mode, which mix with the base and the clipped layers under it. The base's opacity and blend mode apply to the whole stack. - Hiding the base hides its clipped layers too. On a frame where the base is empty, its clipped layers show nothing. - A group can be a base, or be clipped. It's then combined on its own first, as if it were isolated. - A clipped layer at the bottom of its group, or one directly above a reference layer, has nothing to clip to. It draws normally, and its arrow is dimmed. - Reference layers and the Background layer can't be clipped. Merging a clipped layer down into its base keeps only the part inside the base. Duplicating a base puts the copy inside its clipping stack. **Split layers** exports show a clipped layer on its own, still trimmed to its base. Aseprite has no clipping masks. pxstud.io keeps them in `.aseprite` files as a property on the layer, so they come back when the file is reopened in pxstud.io. In Aseprite those layers draw unclipped. ## Draft layers A **draft layer** is for sketches, notes and construction lines: you see it on the canvas while you work, but it's never part of the finished art. Exports, Layer > Flatten Sprite and Layer > Stamp Visible all leave it out. Turn the active layer (or every selected layer) into a draft with Layer > Draft Layer, or tick **Draft layer** in Layer > Layer Properties.... A draft layer shows a pencil (_pencil and line icon_) before its name. - A draft group makes everything inside it a draft. - A clipped layer whose base is a draft layer is left out of exports too, as if its base were hidden. - Reference layers and the Background layer can't be drafts. - `.aseprite` files keep draft layers as a property on the layer. Aseprite shows and exports them like any other layer. ## Layer effects A layer or group can carry **effects**: adjustments, outlines, shadows and screen looks that are drawn over its pixels without changing them. Open Layer > Layer Effects... (or the layer's right-click menu), then **Add effect** and pick one: - **Style:** Drop Shadow / Glow (also an inner shadow), Outline, Motion Echo (fading copies trailing behind), Edge Detect, Smoother, Auto-Shade (highlights and shadows from a light direction, see [Auto-Shade](https://pxstud.io/docs/manual/guides/document.md#auto-shade)); - **Color:** Levels, Modulate, Color Balance, Gradient Map, Hue/Saturation, Grayscale, Brightness/Contrast, Color Curve, Invert Color, Replace Color; - **Palette:** Posterize, Bit Crusher, Threshold, Dither (an ordered pattern), Palette Swap (swap one list of colors for another), Anti-Alias (in-between colors on the stair steps where two colors meet, see [Anti-Alias](https://pxstud.io/docs/manual/guides/document.md#anti-alias)); - **Screen:** Noise, CRT Scanlines, Chromatic Aberration, Pixelate, Vignette; - **Distort:** Wave, Bend, Distortion (shifted bands), Parallax (brighter pixels move further); - **Lens:** Blur and Bloom. These soften edges, so they make half-transparent pixels and in-between colors. For pixel art, set **Edges** to **Hard** (every pixel fully opaque or fully transparent) or **Dithered** (the soft edge becomes a dither pattern that stays put as the layer moves), and **Colors** to **Palette only** (every pixel takes the nearest color of the frame's palette, as baking an indexed sprite does). Indexed sprites start with **Palette only**. A Drop Shadow / Glow can have a soft edge too: **Softness** fades it over up to 8 pixels, and its **Edges** are **Dithered** by default (the one shadow color in a dither pattern that stays put), or **Hard** or **Smooth** (see [Drop Shadow](https://pxstud.io/docs/manual/guides/document.md#drop-shadow)). Softness can be animated. Three entries are ready-made settings of another effect: **Grayscale** is Hue/Saturation with the saturation all the way down (in OKLCH, so each gray keeps its color's lightness), **Modulate** is Levels with each channel's output white set to a tint color (the colors are multiplied by it), and **Bit Crusher** is Posterize with 4 levels per channel. The effects list shows them under that effect's name; change the settings as you like. The Color effects have the same controls as their [Filter menu](https://pxstud.io/docs/manual/guides/document.md#effects-and-adjustments) dialogs. Every effect is deterministic: the same sprite always draws the same pixels, noise and distortion included (they take a **Seed**). Each effect has a checkbox to switch it off without losing its settings, buttons to move it up or down the list and to edit or remove it, and the canvas shows the result while its dialog is open. Effects run in order, bottom to top. A layer with effects shows **fx** in the timeline. - Every change is one undo step, and effects can be edited on a locked layer. - A **group's** effects act on its layers after they are combined, which also makes the group composite on its own, like a group with an opacity or blend mode. A clipped layer's effects run before it is clipped. - The Background layer takes the effects that change a pixel on its own (colors, Noise, Dither, Scanlines, Vignette, Palette Swap), not the ones that read around it. Reference layers take none. - Anything that composites the sprite includes them: exports, Layer > Flatten Sprite, Layer > Stamp Visible, the preview and merging (which bakes the merged layers' effects). A script reading a layer's pixels sees the cels, not the effects. - **Bake into pixels** (Layer > Bake Layer Effects) writes an image layer's effects into all of its cels and removes them, so the layer looks the same but can't be changed back. A shadow or outline grows the cel up to the canvas edge; indexed sprites snap the result to the palette. Groups and tilemap layers can't be baked: merge a group, or convert a tilemap layer to an image layer, first. - `.pxstudio` files keep effects. Aseprite, Photoshop, OpenRaster and Piskel files have no such thing, so those exports write an image layer's effects into its pixels; effects on groups and tilemap layers are left out (the Export dialog says so). ### Animating an effect Press **Animate** on an effect to make its settings change over the frames. Each animatable setting has **keys**: press **Key the active frame** to add one with the value it has there, then change a key's frame, value and easing. Before a setting's first key and after its last, it holds; between two keys it eases (the easing belongs to the earlier key: Linear, Hold, Ease in/out, Overshoot, Elastic, Bounce, steps), either by frame count or, with **Time**, by how long the frames last. Colors animate too (a shadow shifting from red to green). A setting with no keys keeps the value from the effect's own dialog. Every key is on a frame, so it follows that frame if you move it. Tweening uses the same easings and the same notion of progress. Baking an animated layer writes each frame its own picture (frames that shared a linked cel stop sharing it). ## Adjustment layers Layer > New Adjustment Layer adds a layer with no pixels of its own, only effects: they change everything below it, on every frame, at the layer's opacity. Use it for a color grade, a vignette or a CRT look over a whole animation (add the effect, then **Animate** it for a fade). - Inside a group it changes the layers below it in that group; inside a group that composites on its own (an opacity, a blend mode, effects or **Isolated**) nothing outside the group changes. - **Clipped**, it changes only the layer it clips to. - It takes the effects that change a pixel on its own (the Color and Palette effects, Noise, Scanlines, Vignette, Palette Swap), and always the Normal blend mode. - Layer > Merge Down bakes it into the layer below. Aseprite, Photoshop, OpenRaster and Piskel files can't hold one, so those exports leave it out (the Export dialog says so); PNG, GIF and sprite sheets include it. ## Merging and flattening - Layer > Merge Down (`Ctrl+E`) combines the active layer with the one below it, exactly as they look together (even if one is hidden). The layer below ends up at 100% opacity and the Normal blend mode with its old opacity built in, so the picture doesn't change, unless it used another blend mode: then it's combined as it looks on its own, as in Aseprite. A layer with clipped layers on it takes them along. You can't merge an unclipped layer onto a clipped one. - Layer > Merge Up combines the active layer with the one above it. The result looks the same as Merge Down of that layer, but keeps the upper layer's name and color. - Layer > Merge Selected Layers combines the selected layers into one, exactly as they look together, at the topmost one's place and with its name. They must all be in the same group. Selected layers that are hidden are shown while merging and merged too. If the Background layer is one of them, the result is the Background. - Layer > Stamp Visible (`Ctrl+Alt+Shift+E`) adds a new layer named "Visible" holding everything you see, flattened, on every frame, and keeps all your layers. It goes above the active layer (above its whole group, if it's in one). Draft and reference layers are left out. - Layer > Flatten Group turns the active group into a single layer with the group's name, opacity and blend mode, holding everything in it as it looks. Hidden, draft and reference layers inside it are deleted. A group at 100% Normal whose layers use other blend modes can look different afterwards, since its layers now blend with what's below as one image. - Layer > Flatten Sprite combines every layer into a single layer named "Flattened" (or into the Background layer, if the sprite has a visible one), as in Aseprite. Hidden layers are shown while flattening and merged too (except hidden layers inside a group, which are deleted with it). **Draft and reference layers are deleted**, not kept. Scripts can flatten only the visible layers and keep the hidden ones with `app.flattenSprite(true)` (Aseprite's Flatten Visible). All of these can be undone. ## The Background layer A **Background** layer is opaque and always at the bottom, like the canvas of a painting. Use Layer > Background from Layer to turn the bottom layer into one, and Layer > Layer from Background to make it an ordinary layer again. It can't be moved up the stack. ## Other kinds of layers - **Reference layers** (_picture icon_) hold an image to trace over. They're shown on the canvas but never exported. Add one with Layer > New Reference Layer.... - **Tilemap layers** (_grid icon_) are painted with tiles from a tileset. Add one with Layer > New Tilemap Layer.... # Color and palettes Pick colors, build and edit the sprite's palette, use palettes from your library, and work in indexed color. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ You paint with two colors at a time, the **foreground** and the **background**, and keep the colors a sprite uses in its **palette**. Palettes you like can be saved to your library and used in any sprite. ## Foreground and background The left mouse button paints with the foreground color and the right button with the background color. They start as black and white. - **Click** a palette swatch to make it the foreground color; **right-click** it for the background. From the keyboard, `Enter` picks the foreground and `Shift+Enter` the background. On a touch screen, tap the swatch, then the palette's **background** button (below). - Press `X` to swap the two. - Hold `Alt` over the canvas, or use the Eyedropper, to pick a color off the sprite. - The **Colors** panel shows both. Click one to choose which the color picker edits; the picker's heading says **Editing Foreground** or **Editing Background**. A color that matches a palette entry shows its index (`Idx-3`); otherwise it reads **Custom**. - **Drag** a foreground or background color in the Colors panel to adjust it without opening the picker: right shifts the hue, up lightens and down darkens. Hold `Shift` to shift the saturation instead of the hue. - **Recent** keeps the last ten foreground colors you chose. Click one to use it again. Picks follow the sprite's color mode: in a grayscale sprite a color becomes its gray, and in an indexed sprite it snaps to the nearest palette color. ## The color picker The **Color picker** panel picks any color. Its tabs show different ways of choosing: | Tab | Shows | | - | - | | **Shades** | a saturation and brightness square, with a hue slider | | **Spectrum** | hue across, lightness down, with a saturation slider | | **Wheel** | a hue and saturation wheel, with a brightness slider | | **Sliders** | channel sliders and number fields, in RGB, HSV, HSL or OKLCH | | **Normal map** | colors for normal maps (turn it on in the settings) | - **Opacity** sets the color's transparency. - The text field takes any color you type or paste (`#ff8800`, `rgb(...)`, `oklch(...)`); the button beside it switches between HEX, RGB, HSL and OKLCH. - The pipette button picks a color from anywhere on your screen, in browsers that allow it. - The **settings** (the cog) choose which tabs show, the color space (HSV, a painter's RYB wheel, or OKLCH, where equal steps look equally bright), a **harmony** (complementary, triadic and more) shown as color chips, and whether the picker snaps to steps. In a grayscale sprite the picker is a single brightness slider. ## The palette The **Palette** panel holds the sprite's colors. | Button | Does | | - | - | | **+** | Adds the foreground color (if it isn't there already). | | **Swap** | Swaps the foreground and background colors. | | **Sort** | Sorts the palette, or the selected range, by hue, saturation, brightness, luminance, a channel or alpha, ascending or descending, in the same order as Aseprite. Fully transparent colors always go first, and sorting by hue puts the grays first (dark to light) when ascending and last when descending. | | **Create ramp** | With a range selected: fills the colors between its two ends with an even gradient, or inserts new in-between colors. | | **Shading ramp** | With a range selected: makes it the Shades list for the Shading ink. With the Gradient tool, it makes the range the gradient's Stops instead. | | **Palette Lock** (the padlock, RGBA sprites) | Turns [Palette Lock](https://pxstud.io/docs/manual/guides/color.md#palette-lock) on or off. | | **Palette tools** (**...**) | [Palette Audit](https://pxstud.io/docs/manual/guides/color.md#palette-audit), [Clean Up Palette](https://pxstud.io/docs/manual/guides/color.md#cleaning-up-the-palette), Palette Lock, and the [palette groups](https://pxstud.io/docs/manual/guides/color.md#palette-groups). | | **Library** | Opens the Palette Library. | Below them, the selected swatch can be **edited**, made the **background** color, **replaced** with the foreground color, or **removed**. Working with swatches: - **Shift+click** (or `Shift` and the arrow keys) selects a range. - **Drag** a swatch, or press `Alt` and an arrow key, to reorder. - **Double-click** or press `F2` to edit a color. - `Del` removes the selected color. Palette changes are part of the sprite, so they can be undone. The same palette tools are in Sprite > Palette > Palette Audit..., Sprite > Palette > Clean Up Palette... and Sprite > Palette > Palette Lock. ### Palette groups A group gives a name to a run of palette entries, such as "Skin" or "Foliage". Its name shows as a chip above the swatches. - To make one, select a swatch or **Shift+click** a range, then choose **New Group from Range...** in the **...** menu and name it. A new group trims any group it overlaps. - **Click** a chip to select the group's colors as a range, ready to sort, make a ramp from, or use as Shades. - **Double-click** a chip (or choose **Edit Group...** with one of its swatches selected) to rename or remove it. **Remove All Groups** clears them. Groups name palette positions, not colors: moving, sorting or removing colors doesn't move a group. Groups are saved with the sprite and apply to every palette when the palette changes over time. ### Palette Audit Sprite > Palette > Palette Audit... lists every color the artwork uses that isn't in the palette, most-used first, with its pixel count and the palette color it is closest to. For each one you can: - **Add** it to the palette. - **Remap** it: its pixels become the palette color on the right. That is the nearest color unless you pick another from the list. **Add all to palette** and **Remap all** do every color at once. The list updates as you go, and says when every color is in the palette. Remapping changes the cels of every layer you can edit (locked and hidden layers are skipped) in the frames that share the active frame's palette. It is one undo step. The audit is for RGBA sprites; an indexed sprite only uses palette colors. ### Cleaning up the palette Sprite > Palette > Clean Up Palette... tidies the palette in effect at the active frame: - **Merge duplicate colors** keeps the first of each set of identical colors. - **Merge similar colors** also merges colors closer than **Distance** to an earlier one (about 2 is barely visible, 10 is clearly different). - **Remove unused colors** removes colors no pixel uses. The dialog shows the palette that will be left before you apply. In an indexed sprite the pixels follow their colors to their new positions, pixels of a merged similar color take the color that was kept, and the transparent entry is never removed. In RGBA and grayscale sprites only the swatches change. ### Palette Lock With **Palette Lock** on (the padlock in the Palette panel, or Sprite > Palette > Palette Lock), an RGBA sprite behaves like an indexed one while you draw: every pixel a tool paints snaps to the nearest palette color, including soft brush edges, blended inks and gradients. The foreground and background colors snap to the palette too. Pixels you don't paint over keep their colors; use the [Palette Audit](https://pxstud.io/docs/manual/guides/color.md#palette-audit) to bring existing artwork onto the palette. Palette Lock is saved with the sprite. Pasting, moving and transforming pixels, and the adjustments in the Filter menu, aren't snapped. ## The Palette Library The **Library** button opens the Palette Library, with two tabs. **Built-in** lists the palettes that come with the editor: classic consoles and computers, and popular palettes from Lospec, each credited to its author. They work offline and signed out. Search by name or tag, then: - **Use** replaces the sprite's palette with it; **Append** adds its colors. - The save button saves a copy to My Palettes, keeping the credit. - **Find more on Lospec** opens [Lospec's palette list](https://lospec.com/palette-list); import one from My Palettes. **My Palettes** holds palettes you've saved. They're yours alone; with Sync they're kept in your account too. - **Save to My Palettes** saves the sprite's current palette under a name. - **Load** replaces the sprite's palette with a saved one; **Append** adds only the colors it doesn't have yet. - **Import...** reads `.gpl` (GIMP), `.pal` (JASC), `.hex`, `.txt` (Paint.NET), `.act`, `.aco` and `.ase` (Adobe Photoshop and Swatch Exchange; CMYK and Lab swatches are converted without a color profile), Aseprite files, and images (every distinct color, in reading order). - **Import from Lospec** takes a palette's name or address on lospec.com, for example `endesga-32`. **Find more on Lospec** beside it opens Lospec's palette list. - **Export** saves the palette as `.gpl`, `.pal`, `.hex`, `.act`, `.aco`, `.ase`, or a `.png` strip with one pixel per color. `.hex`, `.png`, `.gpl` and `.pal` keep transparency (`.gpl` and `.pal` the way Aseprite writes it); `.act` keeps only one transparent color, and `.aco` and `.ase` keep none. Loading a library palette copies its colors into the sprite: changing the library palette later doesn't change sprites that already use it. On an indexed sprite, loading replaces the colors slot by slot and every pixel keeps its entry number, so the art shows the new colors (a palette swap). If the loaded palette is shorter than the entries the pixels use, those entries keep their old colors. ## Color modes A sprite's color mode decides how its pixels store color. Choose it when you make the sprite, or convert later with Sprite > Convert to RGB Color, Sprite > Convert to Grayscale or Sprite > Convert to Indexed Color... (also in File > Rename Sprite...). - **RGBA** stores a full color, with transparency, in every pixel. The palette is a set of swatches to pick from. - **Grayscale** stores a shade of gray and its transparency. - **Indexed** stores, in every pixel, the number of a palette entry (up to 256). Change an entry and every pixel that uses it changes color; reorder or sort the palette and the pixels follow their colors; remove an entry and its pixels become transparent. Sprite > Convert to Indexed Color... opens a dialog with a live preview of the active cel: - **Palette:** **Keep the current palette** (set it up first; if its transparent entry, the first one by default, is a visible color, the palette's first fully transparent color becomes the transparent entry instead, or, with none, a transparent entry is inserted before it so that color stays; only the first 256 colors are kept), or **Generate from the sprite** with up to **Colors** (2 to 256) picked from the sprite's own pixels. A generated palette replaces the current one, including palettes that change over time; its first entry is transparent unless the Background is the only layer. - **Dithering:** **None** snaps every pixel to its nearest palette color. The ordered patterns (**Bayer 2x2**, **4x4**, **8x8**) mix the two nearest colors in a regular grid that stays put from frame to frame, which suits animation. **Floyd-Steinberg** and **Atkinson** spread each pixel's error to its neighbors for a smoother, grainier look; Atkinson keeps highlights and shadows cleaner. Error diffusion can shimmer in animation, because a small change moves the grain. - **Amount** (0 to 100): how strongly to dither. Pixels that are almost fully transparent (alpha under 8 out of 255) become transparent, and a visible pixel never lands on a fully transparent palette color. Converting to grayscale drops the color: each pixel keeps its brightness as Aseprite measures it (the Rec. 709 weights, so pure red becomes 54, green 182 and blue 18 out of 255). It also replaces the palette with 256 grays, black to white, and drops palettes that change over time and palette groups, so converting on to indexed maps grays onto grays. Both can be undone; converting from indexed to RGBA loses nothing. In an indexed sprite, one palette entry stands for **transparent**: the first, unless you change **Transparent Color** in File > Rename Sprite.... It moves with its entry when you add, remove, reorder or sort colors. If you remove the transparent entry itself, its pixels stay transparent: another fully transparent color takes over, or, with none, the slot just after the last color. ## Color depth Sprite > Color Depth... pins the sprite to a hardware color profile, the way a Mega Drive could only show 8 levels of each of red, green and blue. Pick a preset (generic depths like RGB565, consoles by maker, home computers), or **Custom** for your own number of **bits** or **levels** per channel, in color or grayscale. The dialog shows a banded preview of what the profile allows, how many colors that is, and, for a level count of 16 or fewer, the exact values. Applying folds every pixel on every layer and frame, the tiles, the palette (and palettes that change over time) and the foreground and background colors into the profile, as one undo step. In an indexed sprite only the palette changes: the pixels keep their entries. A grayscale profile turns every color into a gray; a grayscale sprite takes grayscale profiles only. After that the sprite stays on its profile: whatever you draw, fill, paste, transform, adjust, import or run from a script lands on an allowed color, and so do palette edits, the color picker (its sliders show the steps as bands), the eyedropper and Replace Color. Layer effects and adjustment layers show only allowed colors too, so baking them changes nothing on screen. The active profile shows above the color picker and in File > Rename Sprite.... **None (8-bit)** removes the profile. Colors stay as they are: the precision the fold dropped doesn't come back (use Undo for that). Reference layers are left alone, and blending translucent pixels or layers with opacity and blend modes can still mix colors between the steps on screen, as in an indexed sprite. Choosing a console in File > New Sprite...'s **Consoles** tab offers its color depth and, for systems with a fixed palette such as the NES or PICO-8, its palette. The profile is saved in `.pxstudio` files; an Aseprite export writes the pixels as they are, and opening it here pins the sprite again. Each level of an n-bit channel is evenly spaced from 0 to 255 (3 bits: 0, 36, 73, 109, 146, 182, 219, 255). Some hardware isn't quite linear; the notes say where the profile approximates it. | Profile | Group | Color depth | Colors | Notes | | - | - | - | - | - | | **RGB565** | Generic | 5-6-5 bits | 65536 | 16-bit high color: 5 bits of red and blue, 6 of green. | | **RGB444** | Generic | 4-4-4 bits | 4096 | 12-bit color, 4 bits a channel (the Amiga's). | | **RGB332** | Generic | 3-3-2 bits | 256 | 8-bit color: 3 bits of red and green, 2 of blue. | | **Grayscale 8-bit** | Generic | 8-bit gray | 256 | 256 grays. | | **Grayscale 4-bit** | Generic | 4-bit gray | 16 | 16 grays. | | **Grayscale 2-bit** | Generic | 2-bit gray | 4 | 4 grays. | | **Grayscale 1-bit** | Generic | 1-bit gray | 2 | Black and white. | | **Game Boy** | Nintendo | 2-bit gray | 4 | 4 shades of an LCD green: 2-bit grayscale, or the green palette. | | **Game Boy Color** | Nintendo | 5-5-5 bits | 32768 | 15-bit color, 5 bits a channel. | | **Game Boy Advance** | Nintendo | 5-5-5 bits | 32768 | 15-bit color, 5 bits a channel. | | **NES** | Nintendo | Fixed palette | | A fixed palette of about 54 colors. | | **SNES** | Nintendo | 5-5-5 bits | 32768 | 15-bit color, 5 bits a channel. | | **Nintendo DS** | Nintendo | 5-5-5 bits | 32768 | 15-bit color for 2D graphics, 5 bits a channel. | | **Master System** | Sega | 2-2-2 bits | 64 | 6-bit color, 2 bits a channel: 64 colors. | | **Mega Drive / Genesis** | Sega | 3-3-3 bits | 512 | 9-bit color, 3 bits a channel: 512 colors. Evenly spaced levels; the real ramp bows a little. | | **Game Gear** | Sega | 4-4-4 bits | 4096 | 12-bit color, 4 bits a channel. | | **PlayStation** | Sony | 5-5-5 bits | 32768 | 15-bit color, 5 bits a channel (24-bit only for video). | | **Atari Lynx** | Atari | 4-4-4 bits | 4096 | 12-bit color, 4 bits a channel. | | **Neo Geo Pocket Color** | Other Handhelds | 4-4-4 bits | 4096 | 12-bit color, 4 bits a channel. | | **WonderSwan Color** | Other Handhelds | 4-4-4 bits | 4096 | 12-bit color, 4 bits a channel. | | **Playdate** | Other Handhelds | 1-bit gray | 2 | Black and white. | | **PC Engine / TurboGrafx-16** | Other Consoles | 3-3-3 bits | 512 | 9-bit color, 3 bits a channel: 512 colors. | | **Neo Geo** | Other Consoles | 5-5-5 bits | 32768 | Approximated as 15-bit color: the hardware adds a shared dark bit. | | **Commodore 64** | Home Computers | Fixed palette | | A fixed palette of 16 colors. | | **Amiga OCS/ECS** | Home Computers | 4-4-4 bits | 4096 | 12-bit color, 4 bits a channel: 4096 colors. | | **Atari ST** | Home Computers | 3-3-3 bits | 512 | 9-bit color, 3 bits a channel: 512 colors. | | **Atari STE** | Home Computers | 4-4-4 bits | 4096 | 12-bit color, 4 bits a channel. | | **Amstrad CPC** | Home Computers | 3-3-3 levels | 27 | 27 colors: each gun off, half or full. | | **IBM CGA** | Home Computers | Fixed palette | | A fixed palette of 16 colors (4 at a time at 320x200). | | **PC VGA (Mode 13h)** | Home Computers | 6-6-6 bits | 262144 | 18-bit color, 6 bits a channel (256 at a time). | | **PICO-8** | Fantasy Consoles | Fixed palette | | A fixed palette of 16 colors. | | **TIC-80** | Fantasy Consoles | Fixed palette | | 16 colors, Sweetie 16 by default. | ## Palettes that change over time In a sprite with more than one frame, the palette panel can start a **new palette from this frame**: a copy of the current one that applies from that frame until the next change. Edits then change only that stretch of the animation. **Remove** goes back to the palette before it. **Color cycling** (indexed sprites) rotates a range of palette colors while the animation plays, for water, fire and flashing lights, without touching any pixels. Add a cycle, choose its first and last palette index, its direction and how many frames each step lasts, and press Play to see it. Cycles are saved in `.pxstudio` files only; **Bake into frames** turns them into per-frame palettes (adding frames if needed) so they show up in exported GIFs and videos. ## Shades for the Shading ink The Shading ink moves pixels along a list of colors, its **Shades**. With the ink set to Shading, the tool options show the list: drag palette colors into it, press **+ FG** to add the foreground color, drag to reorder, and click a color to remove it. Selecting a range in the palette while the list is empty fills it, as does the palette's **Shading ramp** button. The left button shifts pixels forward along the list and the right button back. # Selecting and transforming Select part of a sprite, move, scale, rotate and flip it, copy and paste, draw with symmetry, and snap to the grid and to guides. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ A **selection** marks the pixels you want to work on. Painting stays inside it, and the Edit and Filter commands act on it. With nothing selected, they act on the whole cel. ## Making a selection | Tool | Key | How | | - | - | - | | Rectangular Selection | `M` | Drag from corner to corner. **Radius** rounds the corners, together or one by one. | | Elliptical Selection | `Shift+M` | Drag the box the ellipse fits in. | | Lasso Selection | `Q` | Drag around the area; it closes by itself. | | Polygonal Lasso Selection | `Shift+Q` | Click each corner; double-click or press `Enter` to close. | | Magic Wand | `W` | Click a color on the active layer, or drag to add every area you pass over. **Contiguous** takes the connected area, **Global** every matching pixel; **Tolerance** sets how close a color must be. These settings are the wand's own, separate from the Paint Bucket's. | Hold a key **as you start** to combine with the current selection: - no key replaces it, and a click without a drag clears it; - `Shift` adds to it; - `Alt+Shift` takes away from it (as in Aseprite; `Alt` alone does too); - `Ctrl+Shift` keeps only the overlap. **Grid cells.** Double-click with any selection tool to select the grid cell under the pointer. Keep the button down after the second click and drag to select every whole cell between where you started and the pointer. This works on rectangular, isometric and hexagonal grids, and selects each cell's exact shape. The keys above combine the cells with the selection you had before the double-click. It uses the sprite's grid when one is set up in View > Grid > Grid Settings... or the grid is shown; otherwise a double-click is just two clicks. With the Polygonal Lasso Selection, once you've placed a corner a double-click closes the shape as usual. The selection shows as a moving dashed outline ("marching ants") that follows its exact shape. **The Select menu:** - Select > Select All (`Ctrl+A`) selects the whole canvas. - Select > Deselect (`Ctrl+D`) clears the selection, as does `Esc`. - Select > Reselect (`Ctrl+Shift+D`) brings back the last selection you cleared. - Select > Inverse Selection (`Ctrl+Shift+I`) swaps what's selected and what isn't. - Select > Quick Mask lets you paint the selection instead ([Quick Mask](https://pxstud.io/docs/manual/guides/selection.md#quick-mask)). - **Select Opaque** selects every painted pixel of the active layer; **Select Transparent** selects every pixel with no paint. For a group, that means the pixels of every layer inside it. - **Modify > Expand...**, **Contract...** and **Border...** change the selection's shape by a number of pixels (up to 256). Expand grows it outward. Contract shrinks it inward, and anything thinner than the amount disappears. Border keeps only a band of that width along the inside of the edge, which you can then fill to outline a shape. **Corners** picks **Round** (the circle brush of the same reach, as in Aseprite; at 1 pixel only the pixels straight above, below and to the sides count) or **Square** (diagonals count too). The edge of the selection counts as an edge, so contracting Select All leaves a margin and its border is a frame around the canvas. A selection that hangs off the canvas keeps its off-canvas part: contracting it doesn't pull it in from the canvas edge. The dialog remembers your last amount and corners. **From a layer.** Hold `Alt` and click a layer in the timeline to select its painted pixels at the current frame, without making it the active layer. Add `Shift` to add them to the selection, `Ctrl` to take them away, or both to keep only the overlap. Selections can be undone like any other change. Each sprite has its own. ## Quick Mask Select > Quick Mask lets you paint the selection instead of drawing its outline. It has no shortcut of its own; you can give it one in Settings > Keyboard Shortcuts.... Turning it on makes the selection paint: the canvas is tinted red where nothing is selected and stays clear where something is. With nothing selected, the whole canvas is tinted. While it's on: - the paint tools paint the selection. The pencil, brushes, shapes, the Paint Bucket and the gradient add to it, and the eraser takes away. Brush shapes, symmetry, pressure and dynamics work as usual. The color doesn't matter, only how opaque the paint is; - your layers can't be changed: the other tools, and the commands that would edit the sprite, do nothing until you turn it off; - Undo and Redo step through your strokes; undoing past the first one turns Quick Mask off. Choose Select > Quick Mask again to turn it off: every pixel painted at half opacity or more becomes the selection. Turning it on and turning it off are one undo step each. Each sprite has its own Quick Mask, and it stays on while you change frames, layers or tabs. It is never saved: it isn't in the file, an export or Sync, and closing the sprite while it's on loses what you painted. Running a script turns it off first, so the script gets what you painted as the selection. ## What uses the selection - **Painting.** Every drawing tool stays inside the selection. - **Edit commands.** Edit > Fill fills the selection with the foreground color; Edit > Stroke Selection... paints a line of a chosen width and color along its edge (inside, centered or outside); Edit > Delete clears it (a Background layer clears to the background color) and shrinks the cel to what's left, removing it when nothing is. Delete and Edit > Cut then drop the selection, as in Aseprite (one undo brings both back, and Select > Reselect the selection); Edit > Flip Horizontal and Edit > Flip Vertical flip it in place: every selected pixel moves to its mirror position within the selection's bounds, the spots it leaves clear (to the background color on a Background layer), and the selection outline flips with it. With nothing selected, Fill and Flip act on the whole cel, and Delete removes the cel (on a linked cel, only this frame's). - **Adjustments and effects** (the Filter menu) work inside the selection. Their **Apply to** option chooses which cels: the active one, the cels selected in the timeline, all frames of the layer, all layers in the frame, or everything. ## Moving and transforming The arrow keys move the selected pixels one pixel at a time, and `Shift` with an arrow one grid cell, as in Aseprite; each press is one undo step. With nothing selected, `←` and `→` step through the frames and `↑` and `↓` through the layers. With the Move (`V`), the selection, or the whole cel if nothing is selected, gets a frame with handles. Then: | To | Do | | - | - | | Move | Drag inside. `Shift` keeps to one direction; hold `Alt` to move a copy. | | Scale | Drag a corner or edge handle. `Shift` keeps the proportions. | | Skew | Hold `Ctrl` and drag an edge handle. | | Distort | Hold `Ctrl` and drag a corner handle: that corner moves on its own, for any four-sided shape. | | Perspective | Hold `Ctrl` and `Shift` and drag a corner handle: the opposite corner on that edge moves the other way, like a floor or wall going into the distance. | | Rotate | Drag just outside a corner. `Shift` snaps to 15° steps; the canvas info shows the angle. | | Change the center | Drag the dot in the middle, or pick a point in the **Pivot** grid in the tool options. | Press `Enter`, click outside, or switch tools to **apply** the change as one undo step; `Esc` **cancels** it. The tool options choose how rotation and scaling redraw the pixels: - **Fast Rotation** keeps every pixel as it is (the usual choice for pixel art); - **RotSprite** rotates through an 8× upscale for smoother, cleaner edges; - **Bilinear** blends in-between colors. You can distort and then still scale, rotate or move the result. The shape has to stay convex: a corner can't be dragged across the shape to fold it over. Art moved or transformed past the canvas edge isn't lost: - With **nothing selected**, the whole cel moves and keeps everything, so dragging it half off the canvas and back brings it all back. Scaling or rotating it keeps what lands within the canvas and the cel's old area, wherever you move it. - **Selected or pasted pixels** are kept within the canvas and the area the cel already covers; what goes beyond that is cut when you apply. - On the **Background** layer, anything past the canvas edge is cut. The Move tool's options also have number fields: **X** and **Y** (the left and top of the transformed shape), **W** and **H** (its size in pixels, scaled about the pivot) and the **angle** in degrees. Type a value and press `Enter` to start or edit the pending transform. With **nothing selected** and several cels selected in the timeline, applying the transform moves, scales or rotates every selected cel the same way, as one undo step. A layer with **Lock movement** on can't be moved, though it can still be scaled, rotated and distorted in place. With **Snap to grid** on, moving snaps the corner to the grid and scaling snaps the edge you drag; a distorted corner snaps to grid points. **Edit > Transform** also has: - Edit > Transform > Rotate 90° Clockwise, Edit > Transform > Rotate 90° Counterclockwise and Edit > Transform > Rotate 180°, pixel-exact; - **Align** (left, center, right, top, middle, bottom) puts the selection, or the cel's pixels, against the canvas edge or center, and **Distribute Horizontally / Vertically** spaces the content of three or more selected cels evenly between the outermost two; - the rotate, flip and iso commands, like Align, cover every cel selected in the timeline when nothing is selected on the canvas (with a selection they act on the active cel); - Edit > Transform > Iso Left, Edit > Transform > Iso Right and Edit > Transform > Iso Floor, which project flat art onto the sides or top of an isometric cube, following the sprite's isometric ratio. To turn or flip the **whole sprite** or a **whole layer**, use the Sprite and Layer menus instead (Sprite > Rotate 90° CW, Sprite > Flip Canvas Horizontal, Layer > Flip Layer Horizontal and their neighbors). ## Copy and paste | Command | Does | | - | - | | Edit > Copy (`Ctrl+C`) | Copies the selected pixels on the active layer. | | Edit > Cut (`Ctrl+X`) | Copies them, then clears them. | | Edit > Copy Merged | Copies what you see: every visible layer, within the selection or the whole canvas. | | Edit > Paste (`Ctrl+V`) | Pastes as a floating image you can move and transform, then apply. | | Edit > Paste as New Layer | Pastes onto a new layer called "Pasted". | | Edit > Paste as New Sprite | Opens the clipboard image as a new sprite. | A paste lands where the selection is (so copy and paste puts it back in the same spot), or in the middle of the view. Transparent pixels in it don't erase what's underneath. Press `Enter` or click away to place it, or `Esc` to throw it away. Copies also go to your computer's clipboard as a PNG, and images copied in other apps paste in. When the **timeline** has the keyboard focus, these shortcuts copy and paste whole cels instead ([Animation and the timeline](https://pxstud.io/docs/manual/guides/animation.md#cels)). ## Symmetry Turn on symmetry with the **Symmetry** button at the right of the tool options bar, and every drawing tool mirrors what you draw. Its menu picks the axes, which can be combined: - **Horizontal** mirrors top to bottom; **Vertical** mirrors left to right; - the two **Diagonals** mirror across the corners; - Horizontal and Vertical together give four-way symmetry. Drag the dashed guides on the canvas to move an axis (they snap to half pixels, so odd and even sizes both mirror cleanly), and **Reset** puts them back in the middle. Each sprite remembers its own symmetry. The Move tool and the selection tools aren't mirrored. The mirrored strokes work on their own side's pixels: a translucent, shading, blur or dodge/burn stroke blends with what is under each mirror, and the Paint Bucket fills again from the mirrored click point. ## The grid **View > Grid** has: - View > Grid > Show Grid (`Ctrl+'`) and View > Grid > Show Pixel Grid (`Ctrl+Shift+'`); - View > Grid > Snap to Grid (`Shift+S`), which snaps shapes, lines, rectangular and elliptical selections, slices and gradients to the grid's corners; - View > Grid > Isometric Grid, which switches to diamond cells; - View > Grid > Hexagonal Grid, which switches to hexagonal cells; - View > Grid > Grid Settings..., with the grid's type (rectangular, isometric at 2:1, 1:1, 3:2 or 4:3, or hexagonal), offset, cell size, color and opacity. **Selection as Grid** uses the selection's bounds as one cell. **Hexagonal grids** can be **pointy-top** (a corner at the top, straight sides left and right) or **flat-top** (a corner at the left). **Width** and **Height** are one hexagon's bounding box and **Edge length** is the length of its straight edges. The pixels have to work out evenly: the width (pointy) or height (flat) must be even, and the box must be longer than the edge by an even number of pixels, so each pointed end is a whole number of pixels tall. Choosing the type, or flipping the orientation, fits a regular-looking hexagon; the dialog tells you what's wrong if you type a combination that doesn't fit. Every other row sits half a cell over, and the lines are drawn pixel-exact. **Snap to grid** snaps to the hexagons' corners, and **Stop at grid** fills a single hexagon. The grid's type, offset and size are saved with the sprite (and can be undone); its color and opacity, and whether it shows or snaps, apply to every sprite. `.aseprite` files keep only the bounding box, as they do for isometric grids. ## Rulers and guides View > Guides > Show Rulers (`Ctrl+R`) puts a ruler along the top and the left of every editor, marked in sprite pixels. The sprite's own extent is shaded on each, and the pixel under the pointer is highlighted. Guides are lines you place across the sprite to line things up. They never show in the art or in exports. - **Add one** by dragging out of a ruler onto the canvas: out of the top ruler for a horizontal guide, out of the left ruler for a vertical one. The position shows while you drag, and guides always sit on a pixel edge. For an exact spot, use View > Guides > New Guide.... - **Move one** with the Move tool: drag the guide. - **Remove one** by dragging it off the canvas (back onto a ruler), or all of them with View > Guides > Clear Guides. - `Esc` cancels a guide drag. Also in **View > Guides**: - View > Guides > Show Guides (`Ctrl+;`) hides or shows them; - View > Guides > Snap to Guides (`Ctrl+Shift+;`) pulls shapes, lines, rectangular and elliptical selections, slices and gradients onto a guide within a few screen pixels, and moving or scaling with the Move tool too (a moved selection lands whichever edge is nearer the guide). A guide in reach wins over the grid; - View > Guides > Lock Guides (`Ctrl+Alt+;`) stops guides being moved or removed by dragging. Guides are saved with the sprite, also in `.aseprite` files (Aseprite keeps them but doesn't show them), and adding, moving or removing one can be undone. They move with the art when you change the canvas size, crop, trim, resize, rotate or flip the sprite. Whether they show, snap or are locked, and whether the rulers show, apply to every sprite. ### Perspective grid **View > Perspective Grid** turns on a drawing aid for perspective: rays fan out of one, two or three vanishing points, with a horizon line through the first two. Like guides it never shows in the art. With the Move tool, drag a point to move it (a point can sit well outside the canvas). View > Perspective Grid > Perspective Grid Settings... sets 1-, 2- or 3-point, the points' positions and how many rays fan out of each. It's saved with the sprite, moves with the art when you resize, rotate or flip the canvas, and can be undone; it's dropped from `.aseprite` files. Nothing snaps to it. Choose **Fisheye** as the type for a 5-point curvilinear grid: a circle with a center point, and curved horizontal and vertical lines that all meet at the circle's left, right, top and bottom points, with straight lines through the center. Set the center, **radius** and how many lines run each way in the dialog, or drag the center and the handle on the circle with the Move tool. In a rotated view each ruler measures the sprite's axis that runs closer to it, so at 90° the top ruler shows y (counting the other way at some angles). At angles between quarter turns the rulers read along the line through the middle of the view, and the highlight follows the pointer. Guides can be added and dragged at any angle. # Animation and the timeline Add and time frames, play them back, mark loops with tags, work with cels, tween between keyframes, and see neighboring frames with onion skin. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ An animation is a sprite with more than one **frame**. The **timeline** shows them as columns, with the layers as rows; each square where a layer meets a frame is a **cel** ([How your work is organized](https://pxstud.io/docs/manual/concepts/how-work-is-organized.md)). ![The timeline with a green 'bounce' tag over frames 1 to 4, each 120 ms; a Slime layer with the slime at different heights, a Shading layer with a shadow only on frame 1 and empty dots elsewhere, and a Background layer whose cels are linked across all four frames](https://pxstud.io/manual/images/guides/animation-timeline.light.webp) _A four-frame animation with a tag_ ## The timeline Along the top of the timeline: | Control | Does | | - | - | | First, Previous, Play/Pause, Next, Last | Move through the frames (`Home`, `←`, `Enter`, `→`, `End`; the arrows move the selected pixels instead while there's a selection). Previous and Next go round from the last frame to the first and back, as in Aseprite. `,` and `.` step inside the current frame's tag instead, looping from its end back to its start, for flipping through one animation. | | Loop | Whether playback starts over at the end. | | Speed | Plays at 0.25× to 4× without changing the frames' timing. | | Playback mode | How tags are played (below). | | **+ Layer**, reference image, delete | Add a layer (`Shift+N`), add a reference layer from a file, delete the active layer. | | **+ Frame**, duplicate, delete | Add an empty frame after the current one (`Ctrl+Shift+F`), duplicate it (`Alt+N`), delete it (`Ctrl+Alt+Shift+F`). | | **Tag** | Make a tag over the selected frames. | Each **frame column** has the frame's number and, under it, how long it shows in milliseconds: click the number box to type a new duration. **Layer rows** run down the left, top layer first. In the grid, a cel with something on it shows a **filled dot**, and an **empty cel** shows a **hollow dot**. A line joins **linked cels**, the cels that share one picture. Hover a cel to see which layer and frame it is, and what it's linked to. ## Frames The **Frame** menu, and right-clicking a frame number, have everything for frames: - Frame > New Empty Frame adds an empty frame after the current one. - Frame > New Empty Frame Before adds an empty frame right before the current one. It is how you put a frame ahead of frame 1. - Frame > New Frame copies the current frame (or the selected frames) and its cels. On a [continuous layer](https://pxstud.io/docs/manual/guides/layers.md#showing-hiding-and-locking), the copy's cel is linked instead. - Frame > New Linked Frame copies it with its cels **linked**, so the copies share their pictures with the originals: draw on one and both change. - Frame > Delete Frame deletes the current or selected frames (a sprite keeps at least one). - Frame > Reverse Frames reverses the selected frames, or all of them if fewer than two are selected. **Timing.** Each frame has its own duration, 10 ms to 65,535 ms (about 65 seconds, as in Aseprite). Type it in the box under the frame's number, or use Frame > Frame Properties.... Frame > Set All Frames to Current Duration... gives every frame the current one's duration. **Color and lock.** Frame > Frame Properties... also gives a frame a **color label**, shown as a stripe on its header so you can spot key poses or frames that need work, and a **lock**. A locked frame (marked with a padlock) refuses edits to its cels, and can't be removed, moved or re-timed, by tools and scripts alike. Unlock it in the same dialog. Both are saved with the sprite and synced, but `.aseprite` has no equivalent, so they're dropped when you export to it. Scripts use `app.setFrameProps`. **Reordering.** Drag a frame by the grip that appears beside its number, or select it and press `Alt` with `←` or `→`. Several selected frames move together: drag any one of them and the whole selection moves as a block, and frames that aren't next to each other are gathered into one run in their timeline order. Hold `Alt` while you drop to **copy** the selected frames there instead; the copies are inserted at the drop point and become the selection. Either way it is one undo step. (Stop playback first.) Tags follow the frames by position, as in Aseprite: frames dragged out of a tag leave it, and a copy dropped right after a tag's last frame joins the tag, like any new frame added there. ## Playback Press `Enter` to play and pause. Each frame shows for its own duration, scaled by the speed setting; playback keeps to the clock, skipping a frame if the computer falls behind. Editing anything, or starting to draw, stops playback, as in Aseprite. The **playback mode** decides how tags are played: | Mode | Plays | | - | - | | **Tags** (Play tags & repetitions) | Every frame in order. Each tag plays in its own direction and as many times as its repeat count, tags inside tags too, before playback moves on. | | **All frames** | Every frame in order, ignoring tags. | | **Once** | Like Tags from the first frame to the last, then stops and goes back to the frame you started on. With a tag chosen (below), that tag once: a ping-pong tag goes there and back. | | **Subtags** | Like Tags, but when the playhead is in a tag with a repeat of 0, that tag loops (with the tags inside it). | A tag's **repeat** counts each pass, and each way of a ping-pong is one pass: a ping-pong tag with a repeat of 2 plays 1, 2, 3, 2, 1. A repeat of 0 plays a tag once on the way through (a ping-pong there and back) and loops it when it's the tag being played. Tags play the same way in exports, as in Aseprite. To play **just one tag**, click its band above the frames (click it again to go back to the whole animation). It loops, with the tags inside it playing their own way. With **Loop** off, it plays its repeat count and stops; so does the whole animation at its last frame. The Preview panel plays at the same speed, so you can watch the animation while you draw. Its **1:1** button shows the sprite at full size, one sprite pixel per screen pixel. ## Tags A **tag** names a range of frames ("idle", "walk") so you can play it on its own and export it on its own. Select the frames and press the **Tag** button, or choose Frame > New Tag.... The dialog has: - **Name** and **Color**; - **From frame** and **To frame**; - **Direction**: Forward, Reverse, Ping-pong or Ping-pong Reverse; - **Repeat**: how many times it plays before playback continues; 0 loops it forever. Drag a tag's band to move it, or its ends to change its frames. From the keyboard, Tab to the band and press `Alt+Left` or `Alt+Right` to move it a frame, or Tab to one of its ends and press `Left` or `Right`. Double-click the band, or use the pencil that appears when you point at it, to edit the tag; right-click it to delete it. As in Aseprite, a tag covers frame positions: a frame you move out of a tag leaves it, one you drop between two of its frames joins it, and a new frame added right after a tag's last frame extends the tag. A new frame added before a tag's first frame is not part of the tag: the tag stays on its frames and shifts along with them. ## Cels Click a cel to select it. Then: - **Shift+click** selects a range, **`Ctrl`+click** adds or removes one cel, and **dragging** from an unselected cel selects a rectangle; - clicking a frame number or a layer name selects that whole column or row; - `Shift` with the arrow keys extends the selection, and `Esc` clears it; - on a touch screen, press and hold for half a second, then drag. With cels selected and the timeline focused, the usual shortcuts work on whole cels: `Ctrl+X`, `Ctrl+C` and `Ctrl+V` cut, copy and paste them, and `Del` clears them. The same commands are in **Edit > Cel** and in the right-click menu: - Edit > Cel > Link Cels makes the selected cels on each layer share one picture; Edit > Cel > Unlink Cels gives each its own copy again (unlinking the cel the others were linked to leaves the others linked together). - **Drag** selected cels to move them, or hold `Alt` while dragging to copy them. The outline turns green where they can go and red where they can't. `Alt+Shift` with an arrow key nudges them one step. To paint on several cels at once, for example to fix the same outline in every frame, turn on **Paint across cels** in the tool options bar and select the cels. Painting on one of them paints them all as one undo step; selecting frame numbers or layer names works too. Locked and hidden layers are skipped, and linked cels are painted once. See [Tools](https://pxstud.io/docs/manual/reference/tools.md#how-tools-behave). Cels only go where they mean the same thing: pixels onto image layers, tiles onto a tilemap layer with the same tileset. Locked and hidden layers are skipped. **Cel properties** (right-click a cel) set its **Opacity**, which combines with the layer's, its **Z-index**, which draws it that many layers higher or lower on that frame only (counting every layer and group, as Aseprite does; it can leave a plain group, but not a group with its own opacity or blend mode), and its user data. Linked cels share one opacity and one user data (as in Aseprite); the z-index is each cel's own. The Background layer's cels are always fully opaque, and a locked layer's cel opacity can't change. ## Tweening A **tween** draws the frames between two keyframes for you: it moves, scales, turns or fades the start keyframe's picture on its way to the end keyframe, and draws each frame in between as an ordinary cel. The end keyframe keeps your own drawing. Because the in-betweens are plain pixels, exports, onion skin and scripts see ordinary frames, and so does Aseprite when you save as `.aseprite` (the tween's settings ride along in the cels' user data, so pxstud.io picks the tween up again when you open the file). Tweens work on image layers, not on the Background. ### The Tween inspector Draw the start and end keyframes on the same layer, select the start keyframe and choose Edit > Cel > Tween... (`Shift+T`, or right-click the cel). The inspector tweens it to the next cel on that layer with a drawing, or, if you selected several frames, to the last selected one with a drawing. With no later drawing on the layer, a selection over several frames ends on the last selected frame even if it's empty (for a fade or pop out). Opened on a tween's start keyframe or on one of its in-betweens, it edits that tween. The inspector is a window you can drag by its title bar and leave open: you can still scrub the timeline, draw, and undo while it's there. `Esc` closes it. - **Preview**: plays the start keyframe, the in-betweens and the end keyframe in a loop, at the frames' own durations and with the other visible layers around them. It follows your changes as you make them. Pause it and step frame by frame with the buttons under it. A big sprite shows just the part the tween moves through. The line beside it says how many in-betweens there are and on which frames ("6 in-betweens, frames 3–8"). - **Presets**: ready-made tweens (see "Tween presets" below). **Use preset** fills the form; Apply makes it. - **Several layers**: shown when your selection covers more than one layer with a drawing on the start frame (see "Tweening several layers" below). - **Pose**: the **From** and **End** columns set where the picture is at the start and at the end: **Position** X and Y in pixels, **Scale** X and Y (1 is the original size, a negative one flips it), **Rotation** in degrees (clockwise; 360 is a whole turn, so 720 spins twice), **Skew** X and Y in degrees, and **Opacity** in percent. **Lock scale aspect** keeps X and Y in proportion as you change one. **Rotate the short way round** turns 350° into a 10° step back rather than a nearly full turn; turn it off to spin the long way. **Match end keyframe** sets the End position and scale so the start picture lands where the end keyframe's drawing is, at its size. A new tween starts with this filled in. **Reset From** puts From back to no change. - **Easing**: how the motion speeds up and slows down. Each small curve is one choice: **Linear** (steady), **Hold** (stays put, then jumps at the end keyframe), **Steps** (moves in equal jumps; set how many), and Sine, Quad, Cubic, Quart, Quint, Expo, Circ, Back, Elastic and Bounce, each easing **in** (starts slow), **out** (ends slow) or **in-out** (both). Use the arrow keys to move through them. **Back** has an **Overshoot**, **Elastic** an **Amplitude** and a **Period** (how long each wobble is), and **Bounce** a number of **Bounces**. Under **Advanced: per-track easing** you can give position, scale, rotation, skew, opacity or the color track an easing of its own, for example a bounce on position while the fade stays steady. **Draw curve** turns the chosen easing into a curve you edit yourself (see below). - **Keyframes**: **End keyframe** picks the frame the tween ends on; move it and Apply to lengthen or shorten the tween (in-betweens beyond the new end are removed, or kept as plain cels if you painted on them). **Chain through several keyframes** makes a tween from each keyframe to the next, up to the one you pick, in one step; **Land each tween on its end keyframe** gives every one its own Match end keyframe pose. **Extend** adds one more tween from this tween's end keyframe to the next keyframe, with these settings. - **Path**: how the position travels. **Straight**; an **Arc** of a given **Height** (a jump; negative goes the other way); a **Wave** with an **Amplitude**, a number of **Cycles** and a **Phase** where it starts; a **Zigzag** with an **Amplitude** and **Cycles**; or a **Shake** of up to **Amplitude** pixels each frame. A shake is the same every time for one **Seed**; **Re-roll** picks another. **Drawn** is a route you draw on the canvas (see "Drawing a motion path" below). **Orient to path** turns the picture to face the way it travels, along any path. **Show the path on the canvas** draws the route and a dot where each in-between lands. - **Squash and stretch**: stretches the picture along its motion while it moves fast and squashes it across, keeping its area. 0% is off. - **Pivot**: the point that scale, rotation and skew turn about. Click one of the nine squares (corners, edges or center of the start keyframe's cel), or type X and Y, where 0 is the left or top edge and 1 the right or bottom. With **Pivot in: Canvas pixels** X and Y are a fixed point on the canvas instead, so several layers tweened about the same point move together as one piece. - **Fade**: how opacity changes show (see "Dither fades and crossfades" below). **Alpha** changes the cel's opacity; **Dither** makes whole pixels appear or vanish through a **Pattern**. **Crossfade into the end keyframe** turns the picture into the end keyframe's drawing. - **Color**: tick **Animate color** to change the picture's colors across the tween. **Hue** (degrees), **Saturation** and **Lightness** (percent) work like Hue/Saturation, and **Tint amount** mixes every color toward the **Tint color**; each has a From and an End value. The color track's easing is the one called "Color and ramp" under per-track easing. Each in-between is recolored from the start keyframe's picture, so edits don't pile up. On indexed sprites the recolored pixels snap to the nearest palette color. - **Palette ramp** (indexed sprites): tick **Shift palette indices along a ramp** to cycle colors without recoloring. Pixels that use palette entries **First** to **Last** (the transparent entry is left out) move **Shift** places along that run by the end of the tween, either **wrapping around** past the ends or stopping at them. Other pixels stay as they are. The swatches show the ramp. - **Resample**: how turned or resized pixels are drawn. **Automatic** uses **Nearest** (exact pixels) and switches to **RotSprite** once anything rotates or skews; picking one yourself keeps it. **Bilinear** blends colors, for painterly art. - **Subpixel**: how in-between positions become whole pixels. **Monotonic** never steps back against the direction of travel, **Round** takes the nearest pixel, and **Floor** rounds down. - **Timing**: **Frames** spaces the motion evenly over the in-betweens; **Time** follows the frames' durations, so a longer frame covers more of the motion. - **Delay (frames)**: holds the start pose for that many frames, then the motion fills the rest of the tween and still lands on the end keyframe. Under per-track easing each track can wait a different number of frames, for example a fade that starts after the move. - **Exposure**: **On ones** draws a new pose every frame; **on twos** draws one every second frame and holds it for two, and so on up to eight. - **Ping-pong (out and back)**: the motion reaches its end pose halfway through the tween and returns to the start pose by the end keyframe, so a loop that ends where it began needs no second drawing. The easing shapes each half. - **Palette Lock**: when it's on for an RGBA sprite (Sprite > Palette > Palette Lock), resampled and recolored in-betweens snap to the palette, like anything you paint. A plain move copies the picture exactly. Turning the lock on or off redraws tweens that have Regenerate automatically on. - **Regenerate automatically**: redraws the in-betweens whenever the start keyframe or the frames change. Turn it off to redraw them yourself with Edit > Cel > Regenerate Tween. **Apply** makes the tween (or changes it) and keeps the inspector open on it; **Apply & Close** also closes it. Either is one undo step. Problems with a value, or a tween that would overlap another one on the layer, show at the bottom of the window, and Apply waits until they're fixed. If the keyframes are next to each other there is no room for in-betweens: tick **Add** and choose how many empty frames to insert between them when you apply. Frames are shared by every layer, so the new frames are empty on the other layers too. If the start keyframe is a linked cel, the inspector offers **Unlink**, because a tween needs the start keyframe's own picture. Tips: - **On twos** reads as hand-drawn and halves the poses to clean up if you paint over them. - Keep **Monotonic** subpixel for slow, eased moves: plain rounding can make the picture jitter back and forth by a pixel. It acts as Round for easings that overshoot (Back, Elastic, Bounce) and for paths other than Straight. - For anything that rotates, **RotSprite** keeps pixel-art edges clean and never invents colors. Nearest is faster but leaves jagged diagonals. - To fix one in-between by hand, just paint on it: your edit is kept when the tween redraws. - The end keyframe shows your own drawing. For a move that comes to rest, draw (or copy) the resting picture on the end frame; leave it empty for a fade or pop out. ### Tween presets **Presets** at the top of the inspector holds ready-made tweens: fades, dither in and out, a dither crossfade, pop in and out, drop and bounce, slides in from each side, a swoop along a drawn path, shake, a squash-and-stretch hop, squash and stretch, float, swing, spin and blink. Pick one and choose **Use preset**: it fills the form, which you can still change, and **Apply** (or **Apply to layers**) makes it. Presets are relative, so one preset fits any drawing and any length: - distances are measured in the start drawing's size: a slide "from one width to the left" moves a 16-pixel-wide drawing 16 pixels and a 40-pixel one 40, an arc's height and a shake's size follow the drawing's smaller side, and a drawn path stretches with the drawing's width and height. Distances land on whole pixels; - the motion fills the tween whatever its number of frames, and a delay is a share of it; - the pivot is a share of the start drawing's box; - everything else (easing, scale, rotation, opacity, color, squash, exposure, ping-pong, fade) is used as it is. The resample, subpixel and timing settings stay yours. On an indexed sprite, a preset that fades uses a dither fade, since partial opacity can't show on a palette. Under **Save and manage presets**, type a name and **Save as preset** to keep the current form as your own preset (saving under a name you already used replaces it); pick one of yours to **Rename** or **Delete** it. Your presets live on this device, like saved curves: they aren't in the file and aren't synced. **Export** downloads them as a JSON file and **Import…** reads one back (on another device, say), skipping anything that isn't a valid preset. ### Dither fades and crossfades Pixel art often can't show half-transparent pixels, and indexed sprites can't at all: an alpha fade there only changes the cel's opacity, which the editor blends off the palette. Set **Fade** to **Dither** and the opacity track dissolves the picture instead: whole pixels appear or vanish through a **Bayer 2×2**, **4×4**, **8×8** or **Cluster** (halftone) pattern, the share of pixels kept following the eased opacity. The pattern is fixed to the canvas, not to the picture, so it doesn't crawl as the picture moves, and a fade in only ever adds pixels. Only whole pixels of the drawing's own colors are kept, so indexed sprites and sprites with a color depth stay exact. New tweens on indexed sprites start with Dither. **Crossfade into the end keyframe** changes the start picture into the end keyframe's drawing over the tween (by the opacity track's easing): with Dither, each pixel shows one drawing or the other; with Alpha, the two are blended. Painting on the end keyframe redraws the crossfade. ### Tweening several layers Select cels on several layers (a head, a body and a hat, or the letters of a title) from the start frame to the end frame, and open the inspector. **Several layers** lists how many layers have a drawing on the start frame; set the tween as usual and choose **Apply to layers** to give every one of them the same tween in one undo step. - **Stagger by** starts each layer that many frames after the previous one, in the **Order** you pick: top layer first, bottom layer first, or the order you selected them in. A later layer holds its start pose until its turn. - **Length**: **Finish together** keeps every tween ending on the same end keyframe, so later layers move a little faster; **Extend** keeps the same motion length, so each later layer ends later (frames are added at the end if needed). - **Shared group pivot** scales, rotates and skews every layer about one point (the Pivot setting taken over the box around all the layers' start drawings), so the layers move as one rigid piece instead of each turning about its own center. In the timeline, the frames where a layer waits are drawn with a dashed arrow and a paler tint. ### Drawing a motion path Set **Path** to **Drawn** to steer the picture along a curve you draw. It starts as the route you had (an arc becomes a curved path), from the start position to the end position. While the inspector is open, the canvas shows the path with its anchors, their handles and a dot for each in-between: - drag an **anchor** (a circle) to move it; the first and last anchors are the start and end positions, so dragging them sets the End position too; - drag a **handle** (a square) to bend the curve; the opposite handle turns with it, or hold `Alt` to move one alone; - click the line to add an anchor there, `Alt`-click an anchor to remove it, and double-click one to switch it between smooth and a corner; - **Add anchor** and **Remove anchor** in the inspector do the same from the keyboard. Anchors and handles snap to pixel centers. The picture travels the path at a constant speed: the position easing sets the pace along it, not the curve's shape. Tick **Orient to path** to turn the picture to face its direction of travel (it starts unturned); RotSprite keeps its edges clean. The path is part of the tween, so it's kept in `.pxstudio` and `.aseprite` files. Changes on the canvas are staged like the rest of the form until you Apply. Untick **Edit the path on the canvas** to see the path without its handles. ### Drawing your own easing curve **Draw curve** (next to Linear, Hold and Steps) changes the easing into a curve you shape: a square where the horizontal axis is how far through the tween you are and the vertical axis is how far the motion has got. It starts from the easing you had chosen. Drag a dot (an **anchor**) to move it, drag the squares beside the selected dot (its **handles**) to bend the curve, double-click empty space to add an anchor, or double-click a dot (or press `Del`) to remove it. The arrow keys nudge the focused anchor, and the **Progress** and **Amount** boxes set it exactly. **Corner** takes the handles away so the curve turns sharply there. Amounts above 1 or below 0 overshoot. **Flip** mirrors the curve; **Start from** replaces it with any ready-made easing to edit. Give the curve a name and **Save to library** to keep it for other tweens: saved curves live on this device only, and choosing one from **Saved curves** loads it into the editor. Saving under a name you already used replaces that curve. ### Chains and moving a tween's end A chain is a row of tweens through several keyframes of one layer, each ending on the keyframe where the next begins, so one drawing can end one tween and start the next. Select the first keyframe and choose Edit > Cel > Tween Through Keyframes to chain through every later keyframe on the layer, each tween landing on its own end keyframe, or use **Chain through several keyframes** in the inspector to pick where it stops and keep your own settings. It is one undo step. Tweens that already start on those keyframes are replaced. To move where a tween ends, select its start keyframe through the frame it should end on and choose Edit > Cel > Set Tween End to Selection, or change **End keyframe** in the inspector and Apply. A tween can't be lengthened into the next one in a chain. ### Tweens in scripts Scripts get the same tweens: `app.setTween(layerId, frameId, { to = endFrameId, end = { dx = 12 }, ease = "quad", mode = "out", path = "arc", height = 6 })` tweens a layer's cel at `frameId` to a later frame and returns the tween's id, and `app.regenerateTween`, `app.bakeTween` and `app.removeTween` take the layer and start frame. `app.setTweenChain(layerId, { frameA, frameB, frameC }, opts)` tweens through those keyframes in one step and returns the tweens' ids, and `app.setTweenEnd(layerId, frameId, newEndFrameId)` moves a tween's end. The options also take `curve` (a drawn easing), `trackEasing`, `color` and `colorFrom` (hue, saturation, lightness, tint), `tintColor` (a hex string or `{ r, g, b }`), `ramp` (`{ first, last, shift, wrap }`), `fade`, `ditherPattern` and `crossfade`, `delay` and `trackDelay`, `pivotSpace`, `orient`, and `path = "custom"` with `points` (the drawn path's anchors). `app.tweenPresets()` lists the built-in presets and `app.tweenPreset("popIn")` returns one; pass its `recipe` as the `preset` option to scale it to the start cel. `app.setTweenStagger({ layerA, layerB }, frameId, { to = endFrameId, preset = app.tweenPreset("slideInTop").recipe, step = 2, groupPivot = true })` tweens several layers in one step. See the scripting reference for every option. ### Tweens in the timeline - An **arrow** runs along the bottom of the cels from the start keyframe to an arrowhead on the end keyframe. A keyframe can end one tween and start the next. - The **in-between** frames are tinted blue. Frames where the tween is still waiting out its **delay** are paler, with a dashed arrow. - A small dark **diamond** marks an in-between you've painted on or moved. Your edit stays when the tween is redrawn, until you use Edit > Cel > Revert to Tween. - When the start keyframe changes, or the frames or their timing do, the tween is **stale**: its in-betweens no longer match. The arrow turns amber, and so does the corner of the start keyframe. Use Edit > Cel > Regenerate Tween to redraw it. A tween set to regenerate on its own never stays stale, except on a locked layer. - A **red corner** on a start keyframe means the tween is **broken**: its end frame was deleted or moved before it. Give it a new end, or remove it. Hover a cel to read the same thing in words ("tween start, to frame 9, stale", "tweened, edited by hand"); screen readers announce it too. ### Tween commands These are in **Edit > Cel** and in a cel's right-click menu. They act on the selected cels (or the active cel), and any cel of a tween, its start keyframe or an in-between, stands for the whole tween. Each is one undo step, and locked and hidden layers are skipped. - Edit > Cel > Regenerate Tween redraws the in-betweens from the start keyframe, leaving the ones you edited by hand alone. - Edit > Cel > Revert to Tween puts the selected hand-edited in-betweens back to what the tween draws. - Edit > Cel > Bake Tween keeps every in-between as a plain cel and removes the tween, so nothing redraws them again. - Edit > Cel > Remove Tween removes the tween and the in-betweens it drew. Ones you edited by hand stay, as plain cels. - Edit > Cel > Copy Tween Settings copies a tween's settings: easing, motion, path and timing, but not where it ends. Edit > Cel > Paste Tween Settings applies them to each selected keyframe. A keyframe that already starts a tween keeps its end; otherwise the tween runs to the next frame on that layer with a drawn cel. Keyframes with nothing after them are skipped. ## Onion skin Onion skin shows faded copies of the frames before and after the current one, so you can see the motion while you draw. Turn it on with View > Show Onion Skin or the onion skin button in the tool options bar. It's set per editor view. Settings > Onion Skin... chooses how many frames before and after to show (up to 16 each), their opacity and how much it fades per frame, whether earlier frames are tinted red and later ones blue, whether to show only the current tag's frames, and whether the copies draw behind or in front of the sprite. Behind, they draw over the Background layer and under the other layers, so they show on a sprite with a Background. The tint recolors each copy by its brightness, so dark outlines show as dark red or dark blue too. These settings apply to every sprite. Onion skin follows playback, as in Aseprite. It doesn't wrap around past the first or last frame. With **Current tag only**, it uses the smallest tag around the current frame, in that tag's direction: in a reverse tag, the "before" frame is the next one along the timeline. With **Loop through tag frames** it wraps inside the tag, and a ping-pong tag bounces at its ends. # Tilemaps Build levels and repeating art from tiles: tilemap layers, tilesets, the three tile modes, shared tilesets, and exporting to Tiled and game engines. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ A **tilemap layer** is painted with **tiles**: small pictures, all the same size, kept in the layer's **tileset**. The layer itself only records which tile sits in each cell of its grid (and whether it's flipped or rotated). Repaint a tile and every cell that uses it changes, which is what makes tiles good for walls, floors and terrain. ## Making a tilemap layer - **New:** Layer > New Tilemap Layer... asks for a name and a **tile width and height** (16×16 unless you change it). It starts with an empty tileset. If the sprite already has a tileset, you can choose **Use existing** to share it (below). - **From an image layer:** Layer > Tilemap > Convert to Tilemap... cuts the layer's art, on every frame, into tiles and replaces it with tile placements. **Share identical tiles** makes repeated squares one tile; **Also share flipped and rotated tiles** catches mirrored copies too. The grid starts at the canvas's top-left corner, and art past the canvas edges is kept as tiles too. Each cel keeps its opacity, z-index and user data, which you can change in **Cel properties** (right-click the cel), as on any layer. - **Back to pixels:** Layer > Tilemap > Convert to Normal Layer turns the tiles into ordinary pixels on every frame, tiles outside the canvas included. ## The three modes With a tilemap layer active, the tool options bar shows **Tiles**, **Pixels: Auto** and **Pixels: Edit**, and a **Tile grid** checkbox that draws the tile boundaries. Press `Alt+T` to switch between Tiles and the pixel mode you used last. (Aseprite uses Tab for this; here Tab only moves between controls.) | Mode | What painting does | | - | - | | **Tiles** | Place whole tiles with the Tile Stamp. The pixel tools don't paint in this mode. | | **Pixels: Auto** | Paint pixels as on any layer; only the cells you touch change. A cell that ends up identical to an existing tile reuses it; a new picture becomes a new tile; a tile used elsewhere is copied for this cell, so the other cells keep theirs. A cell you erase completely becomes empty again. Tiles your edit leaves unused are removed. | | **Pixels: Edit** | Paint straight into the tiles themselves: every cell, on every frame, that uses a tile changes with it. Empty cells stay empty. | Choosing Tiles switches to the Tile Stamp, and picking a drawing tool switches back to a pixel mode, so the tool and the mode always match. ## The Tileset panel The **Tileset** panel shows the active tilemap layer's tiles, with how many cells use each. Click a tile to select it: the selected tile is the one the Tile Stamp places. **Tile 0** is the empty tile and is always there. - The buttons add an empty tile, **duplicate** the selected tile (the copy goes at the end), **clear** it to transparent, or **delete** it (if cells use it, you're asked first, and those cells are emptied). Every change can be undone. - Drag a tile, or press `Alt` with an arrow key, to **reorder** the tileset. Cells keep showing the same pictures. - **Auto match** (Flip H, Flip V, Rotate) lets Pixels: Auto reuse a tile when the new picture matches it flipped or rotated. - **Stamp** (Flip H, Flip V, Rotate 90°) flips or turns the tile the Tile Stamp places. Only square tiles can be rotated. - Layer > Tilemap > Tileset Properties... names the tileset and sets its **Base Index**: the number shown for the first tile (1, as in Aseprite; from -32768 to 32767). It only changes the numbering. Its **User Data** section holds a color, a note and custom properties for the tileset ([User data and custom properties](https://pxstud.io/docs/manual/guides/document.md#user-data-and-custom-properties)). ## Stamping tiles With the Tile Stamp, click or drag across the layer to put the selected tile in each cell you pass over. Hold `Alt` as you start to remove tiles instead. A whole drag is one undo step. The layer must be visible and unlocked. ## Placing tiles with other tools With a tilemap layer active, the pencil, eraser, line, rectangle, ellipse, triangle, hexagon, star and bucket have a **Place tiles** checkbox. With it on, they place the selected tile (and its Flip and Rotate settings, as set for the Tile Stamp) on tilemap cells instead of painting pixels, whatever the Tiles / Pixels mode: - A cell is covered when any pixel the tool would paint lies inside it: a one-pixel pencil dot places one tile, an outline rectangle the cells its outline crosses, a filled one every cell it fills. Brush size counts. The eraser empties the cells it touches. - The **bucket** fills the connected cells (up and down, left and right) that hold the same tile as the one you click, with the selected tile. An empty cell counts as tile 0. **Global** fills every cell holding that tile instead. It stays inside the canvas's grid of cells, so on an empty layer it fills the visible grid and no further. - **Symmetry** mirrors the placement. A cell goes to the cell holding the mirror of its center, and the tile is flipped to match: a vertical axis toggles Flip H, a horizontal axis Flip V, and a diagonal axis turns the tile (Rotate 90° plus a flip, square tiles only). A cell on an axis keeps the tile as you placed it. The bucket fills again from each mirrored click. - A **selection** limits the placement to cells with at least one selected pixel (the bucket also stops at the others). - Each stroke or fill is one undo step, and one that changes nothing leaves none. The Tile Stamp follows symmetry and the selection too. ## Sharing a tileset Several tilemap layers can use **the same tileset**: choose **Use existing** in Layer > New Tilemap Layer.... Editing, adding, removing or reordering tiles then reaches every layer that shares it, which is how you keep a background layer and a foreground layer in step. The panel's header shows how many layers share the tileset. Locking one of them protects it: a tile it shows can't be repainted, cleared or deleted (or edited with Pixels: Edit) through another layer until you unlock it. ## Importing tiles Layer > Tilemap > Import Tiles from Image... cuts a picture into tiles of the tileset's size and adds them. The picture can be an **image file**, the **active frame**, or the **selection** on it. It can **skip duplicate tiles** (also flipped and rotated ones), **skip empty tiles**, and **place the tiles** on the active frame as well. ## Animated tilemaps Each frame has its own tile placements, so a tilemap can animate. Frames can be **linked** on a tilemap layer too (Frame > New Linked Frame, Edit > Cel > Link Cels): linked frames share their placements. Remember that Pixels: Edit changes the tiles themselves, so it shows on every frame that uses them, linked or not. ## Exporting File > Export... with the type **Tilemap (JSON / Tiled)** writes a PNG image of each tileset plus either: - **Tiled** (`.tmj` map with `.tsj` tilesets), for the Tiled editor and engines that read its format. Tiled maps have no frames, so only the selected frame is exported. - **JSON (all frames)**, a simple file with every frame's tile numbers and durations. In both, a cell of 0 is empty, and flips and rotation are stored in the top bits of the number, as Tiled does. Tilemaps are also kept in **Aseprite** files, shared tilesets included. # Opening, importing and exporting Open sprites and images, turn pictures into pixel art, cut up sprite sheets, and export images, animations, sheets, video and timelapses. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ pxstud.io keeps your work in the browser as you go ([Your first sprite](https://pxstud.io/docs/manual/start/first-sprite.md#saving)). This page covers getting files in and out. Which formats can do what is in [File formats](https://pxstud.io/docs/manual/reference/file-formats.md). ## Opening files File > Open... (`Ctrl+O`) opens a `.pxstudio` or Aseprite file, a file from another editor (Photoshop `.psd`, OpenRaster `.ora`, Piskel, Pixilart, Pixelorama, Pyxel Edit), or an image (PNG or animated PNG, GIF, JPEG, WebP, AVIF, BMP, TGA, PCX, QOI, ICO, CUR, Netpbm, FLI/FLC), as a new sprite in the current project, named after the file. You can also **drop** files onto the editor: - Several images at once ask whether to open them as **separate sprites** or as **one animation** (one frame per image, in file-name order). - A large image (over 512 pixels on a side) asks whether to **import it as pixel art** or open it as it is. - Other pictures the browser can show (SVG and so on) go to Import as pixel art. When something in a file can't be kept, a notice says what, for example an Aseprite file's color profile, a blend mode pxstud.io doesn't have, or a Photoshop adjustment layer. What each editor's files keep is in [File formats](https://pxstud.io/docs/manual/reference/file-formats.md). File > Save As... (`Ctrl+Shift+S`) downloads the sprite as a `.pxstudio` file, which keeps everything and opens again exactly as it was. ## Import ### Image as pixel art File > Import > Import image as pixel art... turns a photo or drawing into pixel art. 1. **Choose image...** or drop one in. (Pictures larger than 4096 pixels are shrunk first.) 2. Set the **size**: width and height, or a long side of 16, 32, 64 or 128. 3. Pick an **algorithm**: - **Balanced (SLIC + k-means)**, the default, groups pixels into regions of similar color; - **Smooth with outlines (bilateral)** smooths areas and can darken the edges between them; - **Crisp shapes (Kopf-Lischinski)** keeps sharp shapes and outlines. 4. Pick a **palette**: **Auto** finds the best colors (2 to 64 of them), or use the sprite's palette, one of My Palettes or a built-in palette. 5. Press **Start** to see the result, adjust, and press **Start again** to update it. 6. **Import as** a new sprite, or as a new layer in the current sprite (centered), and click **Import**. **Add the palette colors to the sprite palette** keeps the colors handy for touch-ups. ### Image sequence File > Import > Image sequence... picks several images and makes one sprite with a frame per image, in natural file-name order (`run2` before `run10`). The sprite is named after the first file without its number. Images of different sizes sit at the top-left of the largest. ### Sprite sheet File > Import > Sprite sheet... cuts a sheet into frames. Choose an image, or **Use current sprite**. - **Auto-detect grid** looks for the empty gaps between frames (transparent, or the sheet's background color) and sets the type, frame size, offset and padding to match. It runs when you choose an image, and again whenever you press it. It needs gaps between the frames, so a sheet whose frames touch keeps your settings and says so. - **Find frames by** **Detecting each sprite** is for sheets that aren't on a grid. See [Detecting sprites](https://pxstud.io/docs/manual/guides/import-export.md#detecting-sprites) below. - **Type:** Horizontal strip, Vertical strip, By rows or By columns. - **Frame W** and **Frame H**, **Offset**, **Padding**, and whether to include partial cells and skip empty ones. A grid over the sheet shows where the cuts fall. - **Aseprite JSON...** reads the frames, durations and tags from the JSON file Aseprite exports with a sheet, instead of the grid. Trimmed frames go back to their place on the full canvas, and Aseprite's inner padding is left out of each frame. - **Create** a new sprite, or **replace the current sprite's frames** (one undo step). #### Detecting sprites For packed or unevenly spaced sheets, choose **Detecting each sprite**. Each cluster of non-background pixels becomes one frame, shown as a numbered box over the sheet. - **Background:** Automatic uses transparency if the sheet has any, otherwise its most common color. Or choose Transparent, or a color. - **Tolerance** (0 to 255): how far from the background a pixel may be and still count as empty. Raise it to ignore faint noise or JPEG fuzz. - **Merge gap:** boxes with at most this many empty pixels between them join into one sprite, so a detached sword or spark stays with its character. 0 joins only boxes that overlap. - **Min pixels:** sprites with fewer pixels than this are dropped as specks. - **Joined:** whether pixels touching only at a corner belong to the same sprite. - **Order:** frames run row by row or column by column. - **Frame size** is the largest sprite's size, or a custom size (larger sprites are clipped). The **Anchor** decides where each sprite sits on that canvas: bottom center keeps feet on one line, which keeps a walk cycle from jittering. To correct the result by hand, click a box to select it. You can change its X, Y, W and H, shift where it sits on the frame (**Shift X** and **Shift Y**), move it **Earlier** or **Later** in the order, step to the **Previous** or **Next** box, or **Delete** it. Drag on an empty part of the sheet to draw a new box, which is added last. Changing a detection setting or pressing **Re-detect** runs detection again and discards these edits. Sprites that touch can't be told apart and come out as one box; split them by hand, or use the grid instead. ## Export File > Export... (`Ctrl+Alt+Shift+S`) opens one dialog for every format, with a preview of the real file. Hidden layers, reference layers and [draft layers](https://pxstud.io/docs/manual/guides/layers.md#draft-layers) are never exported (an Aseprite file keeps every layer). **For every type:** - **Resize (%)** scales by whole steps, 100% to 1600%, so pixels stay sharp squares. - **Apply pixel ratio** stretches non-square pixels to their shape. - **Frames:** all, the active frame, or one tag's frames. **Animation Direction:** forward, backward or ping-pong (for a tag, forward keeps the tag's own direction). - **Play subtags & repetitions** (when the sprite has tags, on by default as in Aseprite): the frames are exported the way playback plays them, through every tag in its direction, nested tags and repeat counts included, so a frame can appear more than once. Turn it off to export each frame once, in order (one file per frame for a PNG sequence). - **Area** (when the sprite has [slices](https://pxstud.io/docs/manual/guides/slices.md)): the whole canvas, one slice, or **Each slice**, which writes one export per slice. A slice that moves or resizes between frames exports at the box that covers it on every exported frame, so all its frames are the same size. With each slice, `{slice}` in the filename becomes the slice name (`{slice}.png` gives `head.png`); without it the name goes before the extension (`sprite-head.png`, `walk-head001.png`). Not for sprite sheets or the whole-file types. - **Filename.** For a PNG sequence, a number in the name sets the numbering: `walk001.png` gives `walk001.png`, `walk002.png` and so on; without one, frames are numbered `sprite-001.png`. **Per type:** | Type | Options | | - | - | | **PNG** | One image, or one per frame. | | **Animated GIF** | **Loop count** (0 loops forever, or uses the tag's repeat). **Dithering**: a GIF holds 256 colors, so an animation with more gets a palette picked from all its frames, and this chooses how the in-between colors are drawn (the same choices as [converting to indexed](https://pxstud.io/docs/manual/guides/color.md#color-modes); ordered patterns don't flicker between frames). Pixels under half opacity become transparent; GIF stores durations in 10 ms steps, 20 ms at least. | | **Animated PNG (APNG)** | **Loop count**. Keeps every color, soft transparency and exact durations; saved as `.png`. Apps without APNG support show the first frame. | | **WebP** | Still or animated; **Lossless**, or a **Quality** from 0 to 100; loop count. | | **Sprite Sheet** | Below. | | **Aseprite file** | The whole sprite, with layers, groups, linked cels, tags, slices and tilemaps. Group opacity and blend modes show in Aseprite with **Experimental > Compose groups** on. | | **Photoshop file (PSD)** | The active frame with its layers, groups, blend modes, opacity, visibility and clipping, plus the merged image. Opens in Photoshop, Krita, GIMP and Photopea. | | **OpenRaster (ORA)** | The active frame with its layers and groups, for Krita, GIMP, MyPaint and Pixelorama. | | **Piskel file** | Every frame and visible layer. Groups are flattened, and the file gets one frame rate (from the most common duration). | | **Tilemap (JSON / Tiled)** | Tileset images plus a Tiled map or a JSON file ([Tilemaps](https://pxstud.io/docs/manual/guides/tilemaps.md#exporting)). | | **MP4 video** | **Frame rate** (Auto keeps every duration exact), how many times it **plays**, and a **background color** (video has no transparency). Only offered where the browser can make video. | | **WebM video (VP9)** | How many times it **plays** and a **background color** (video has no transparency). Keeps every frame's exact duration. Only offered where the browser can make video. | | **FLC** | Autodesk Animator's format: up to 256 colors, transparency becomes black. | | **JPEG** | **Quality** and a **background color**. | | **AVIF** | **Quality**: 100 (the default) is lossless; lower makes a smaller file that isn't pixel-exact. | | **ICO, CUR** | Windows icons and cursors, up to 256×256. **All sizes** also stores each larger whole-number size up to 256 (a 32×32 sprite gives 32, 64, 96 … 256), so Windows picks a sharp one. A cursor's click point is the pivot of a slice named `hotspot`. | | **ANI** | An animated Windows cursor, up to 256×256. Every selected frame becomes one cursor image shown for the frame's duration (rounded to 1/60 s); repeated frames (ping-pong) are stored once. Set the click point with **Hotspot X / Y** (it starts at the `hotspot` slice pivot). | | **Crafts PDF** | A printable cross-stitch, bead or perler pattern of the current frame: a cover with a preview and the color count, the chart split into gridded pages, and a legend listing each color's symbol, swatch, hex, palette-group name and stitch count. Set the **cell size** (mm), **page size** (A4 or Letter), a heavier **grid line every** N cells, and **symbols** or **color fills**. Transparent cells are skipped; symbols mode takes up to 56 colors. | | **Netpbm** | Uncompressed `.pgm` (opaque grayscale), `.ppm` (opaque color) or `.pam` (with transparency); the extension follows the image. | | **TIFF** | Lossless, LZW-compressed. Indexed sprites keep their palette (unless they use their transparent color); other sprites save RGB, or RGBA when anything is transparent. One file per frame. | | **JPEG XL** | Lossless, with transparency. Limited to 1024×1024 pixels, so lower the resize for big exports. One file per frame. | | **BMP, TGA, PCX, QOI, SVG** | One file per frame. SVG draws each run of same-colored pixels as a rectangle. | ### Presets and queue **Presets and queue**, at the bottom of the Export dialog, saves and batches exports. Nothing in it asks for confirmation. - **Presets** keep the type and every option on that tab (resize, frames, area, filename, and the per-type options such as quality, background, hotspot, cell size or the sprite sheet settings) under a name you choose. **Save preset** with an existing name replaces it. **Apply** fills the dialog with a preset, and **Rename** and **Delete** do what they say. Presets stay on this device and need no account. A preset's tag, slice or frame choice that the current sprite doesn't have falls back to all frames or the whole canvas. - **Queue:** **Add current to queue** (or a preset's **Add to queue**) lines up an export with the settings as they are now. **Run queue** makes every file in one go: one download, or a single `pxstudio-export.zip` when the items make several files. Each item shows Waiting, Done or the error that stopped it; a failed item doesn't stop the others, and **Run queue** again retries the failed ones. **Remove** takes an item out. The queue is for the sprite and dialog you have open, and is empty again when the dialog closes. ### Sprite sheets The **Sprite Sheet** type has four tabs, laid out like Aseprite's: - **Layout:** strip, rows, columns or **Packed**; a fixed number of columns or rows, or a fixed width or height; best fit; merge duplicate frames; ignore empty ones. - **Sprite:** which layers and frames, whether to split layers or tags, and trimming. - **Borders:** border, spacing and inner padding, and **Extrude** (repeats edge pixels, to stop seams in games). - **Output:** an **Engine Preset**, the **Data File** (Aseprite JSON as a Hash or an Array, or a Godot 4 SpriteFrames `.tres`) and, for JSON, how each frame is named. The layouts work like Aseprite's, so the same options give the same sheet: - Strips, rows and columns place each frame at its own size, so trimmed frames don't take the space of the largest one. With no constraint, **By rows** and **By columns** make a near-square grid. - **Split Layers** puts each layer's frames one after the other, and **Split Tags** each tag's. **By rows** starts a new row for each layer and tag (**By columns**, a new column), unless a fixed width (height) wraps them. - **Packed** fits the frames, largest first, into the smallest sheet it can, close to square (100 trimmed frames of 4 to 43 px give 258×236 px, as in Aseprite). **Best fit: Smallest area** tries a few more sizes, and **Smallest power of two** gives a power-of-two sheet. - A fixed width or height is the sheet's width or height, even when the frames use less. - The preview warns when the sheet is over 16384 px on a side, which many GPUs and engines can't load. Trimming: - **Trim Sprite** crops every frame to the area any frame uses. **Trim Cels** crops each frame to its own content. With a visible Background layer, both trim away the background color (the color of the top-left pixel) instead of transparency. - **Trim by Grid** grows the trimmed area out to the grid lines, for either kind of trim. - The JSON keeps the frames in canvas coordinates: `sourceSize` is the canvas size and `spriteSourceSize` is where the frame's pixels sit on it, so engines and re-imports keep each frame in place. Frame names: leave **Item Filename** empty for Aseprite's names, `{title} {frame}.{extension}`, with ` ({layer})` added for one layer and ` #{tag}` for one tag (`hero (Body) #walk 0.aseprite`). `{frame}` counts from 0 for each layer and tag. The tokens are Aseprite's: | Token | Becomes | | - | - | | `{title}`, `{extension}`, `{name}` | The file name without its extension, the extension (`aseprite`), and both. | | `{layer}`, `{group}` | The layer, when the frames come from one layer, and its group. | | `{tag}`, `{innertag}`, `{outertag}` | The exported tag, or the shortest tag holding the frame; `{outertag}` is the longest. | | `{frame}`, `{tagframe}` | The frame number, and its number inside the tag. Digits set the start and width: `{frame1}` counts 1, 2, …; `{frame001}` gives 001, 002, …. | | `{duration}` | The frame's duration in milliseconds. | You get the sheet's PNG and a data file with the same name. The JSON lists each frame's position and duration, the tags, the exported layers and the slices, and is readable by game engines and tools that read Aseprite's JSON. Unlike Aseprite, a frame's `frame` rectangle doesn't include the inner padding, and tag ranges count the frames in the sheet. As in Aseprite, a tag's frames are in timeline order on the sheet, and its direction and repeat count are in the JSON for the game engine to apply. **Engine presets** set the layout for a game engine in one click: **Godot 4** and **Phaser / PixiJS** pack the frames, trim each one to its content, merge duplicates and add 1 px of extrude, so frames don't pick up their neighbors' edges when the camera moves or the texture is filtered. Godot 4 also picks the `.tres` data file. **Aseprite defaults** puts the layout back to a plain strip. You can change any option afterwards. #### Godot 4 With the **Godot 4 SpriteFrames (.tres)** data file: - Each tag becomes an animation with the tag's name. A sprite without tags gets one animation called `default`. With **Split Layers**, animations are named `layer/tag`. - Frame durations carry over exactly: the animation's speed comes from its most common frame duration, and longer or shorter frames get a matching relative duration. - Godot has no reverse, ping-pong or repeat setting, so the frames are written out in play order. A tag with a repeat count plays that many times (each way of a ping-pong counts once) and doesn't loop; other tags loop. - Trimmed frames keep their full size and position, so the sprite doesn't jump between frames. Put the PNG and the `.tres` in the same folder of your Godot project, then pick the `.tres` as the **Sprite Frames** of an **AnimatedSprite2D**. For crisp pixels, set **Project Settings > Rendering > Textures > Default Texture Filter** to **Nearest**. ## Export Timelapse File > Export Timelapse... makes a short GIF or MP4 of your sprite being drawn, for sharing. - **Animate:** **Draw in the finished sprite** (pixels appear in an order you choose: a scan, random dissolve, diagonal sweep, spiral, from the center, or color by color), or **Replay my edits** (your actual undo history from this session, stroke by stroke: the steps that led to where you are now, without undone steps or other branches). Pick a range of edits (**From edit** / **To edit**) and **Show every** Nth edit to speed up long builds. - With several layers or frames, choose whether they're drawn together or one at a time. - **Timing:** drawing time, frame rate, pace (ease in and out, steady, speed up, slow down), a pause between frames, a hold at the end, and how many times the finished animation then plays. - **Output:** GIF or MP4, scale (it aims for 1080 pixels by default), margin, a square canvas, and the background color (or transparent, for GIF). # Keyboard, accessibility and touch Run every command from the keyboard, draw without a mouse, use a screen reader, and work on a touch screen. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ Everything in pxstud.io can be done from the keyboard: every command is in the command palette, every shortcut can be changed, and the canvas itself can be drawn on with the arrow keys. On a touch screen, one finger draws, two fingers zoom and pan, and a two-finger tap undoes. ## Finding and running any command Press `Ctrl+K` (or choose Edit > Command Palette...) and type part of a command's name. The list shows each command's menu path and shortcut, and dims the ones that can't run right now. Use the arrow keys to pick one and `Enter` to run it. With nothing typed, the commands you ran most recently come first. ## Changing shortcuts Settings > Keyboard Shortcuts... lists every command with its shortcuts. Search by command, menu or key, then: - click **Add** (or an existing shortcut) and press the new key combination; `Esc` cancels; - if the combination already belongs to another command, choose **Reassign** to move it, or **Cancel**; - **Reset** puts one command back to its default, and **Reset all** puts back every one; - **Export...** saves your set as a JSON file, and **Import...** loads one, for example in another browser. Tick **Disable single-key shortcuts** to switch off every shortcut that is a single letter, digit or punctuation key (with or without `Shift`), so a stray key press never changes tool. The full list of defaults is in [Keyboard shortcuts](https://pxstud.io/docs/manual/reference/shortcuts.md). `Tab` is never a shortcut and can't be assigned to one: it always moves to the next control, so you can never get stuck in a panel or on the canvas. (That's why switching a tilemap layer between Tiles and Pixels is `Alt+T` rather than Aseprite's Tab; see [Tilemaps](https://pxstud.io/docs/manual/guides/tilemaps.md).) ## Moving around the editor - **Skip to canvas.** The first thing `Tab` reaches on the page is a **Skip to canvas** link. Press `Enter` on it to put the keyboard focus straight on the canvas. - **Landmarks.** The menu bar is the page's header, and every panel is a labelled region (Tools, Tool options, Timeline, Palette, Color picker and so on), so a screen reader's landmark list jumps straight to it. - **The menu bar** is one stop for `Tab`. `Left` and `Right` (or `Home` and `End`) move between the menus, and `Down`, `Enter` or `Space` opens one. Inside a menu, `Up` and `Down` move, typing a letter jumps to the next item starting with it, `Right` opens a submenu and `Left` closes it, and `Esc` closes the menu. With a menu open, `Left` and `Right` hop to the neighboring menus. When the menu bar is docked down a side, `Up` and `Down` move between the menus instead. - **Panel menus.** In any panel, `Shift+F10` or the Menu key opens the panel's menu, the same one a right-click opens ([The workspace](https://pxstud.io/docs/manual/guides/workspace.md#the-panel-menu)). - **Resizing panels.** `Tab` to the line between two panels and use the arrow keys ([The workspace](https://pxstud.io/docs/manual/guides/workspace.md#moving-panels)). ## Drawing with the keyboard Put the focus on the canvas (with `Tab` or the **Skip to canvas** link), and a pixel cursor appears where the pointer last was, or in the middle of the sprite. Then: | Key | What it does | | - | - | | Arrow keys | Move the cursor one pixel. | | `Shift` with an arrow | Moves the cursor one grid cell. | | Hold `Space` | Presses the active tool at the cursor; move with the arrows to draw, and let go of `Space` to finish. | | `Enter` | Puts the pen down; move with the arrows, then press `Enter` (or `Space`) again to lift it. Use this if holding two keys at once is hard. | | `Shift` or `Alt` with `Space` or `Enter` | Acts like `Shift` or `Alt` with a click, for example to add to a selection. | | `Esc` | Cancels the stroke in progress. | | `Tab` | Leaves the canvas. | Every tool works this way except the Hand: brushes paint, shapes stretch from where the pen went down to where it comes up, selection tools select, and the Move drags. The canvas scrolls to keep the cursor in view, and in tiled mode the cursor wraps around the edges. When a move or transform is waiting to be applied, or a multi-step tool such as the Polygon is waiting for its last point, `Enter` finishes that instead of putting the pen down. Keyboard drawing starts when the canvas gets the focus from the keyboard: `Tab` to it, or use the skip link. Clicking the canvas with the mouse turns it off again, so mouse users keep the arrow keys, `Space` and `Enter` for their usual jobs, as in Aseprite: the arrows move the selected pixels, or step through frames and layers; holding `Space` pans; and `Enter` plays and pauses the animation. After a click, `Tab` away and back to draw with the keyboard again. **With a screen reader,** the canvas is announced with the sprite's name and size, the active tool and the zoom. The cursor's position ("x 12, y 7") is read once it stops moving, then "Pen down", "Pen up", or "Applied at..." for a single press, and "edge" when the cursor can't go further. ## Keyboard in the panels **Sprite tabs.** The tabs above the canvas are one stop for `Tab`: - `Left`, `Right`, `Home` and `End` switch sprites; - `Alt+Left` and `Alt+Right` move the tab along the row (and say where it went); - `F2` renames the sprite, and `Del` closes it; - `Enter` on a sprite whose cloud copy changed lets you choose which copy to keep. **The palette** is also one stop for `Tab`, on the selected swatch: - the arrow keys, `Home` and `End` move between swatches, and `Shift` with an arrow selects a range (to sort, for example); - `Enter` or `Space` makes the swatch the foreground color, and `Shift+Enter` the background color; - `Alt` with an arrow moves the swatch in the palette; - `F2` edits the swatch's color, and `Del` removes it. The new foreground or background color, and a swatch's new place, are read out to screen readers. See [Color and palettes](https://pxstud.io/docs/manual/guides/color.md). **The timeline.** The cels form a grid with one `Tab` stop, the active cel: - the arrow keys move to the next cel (and make its layer and frame active); - `Shift` with an arrow extends the cel selection, and `Esc` clears it; - `Alt+Left` and `Alt+Right` move the cel's frame (or the selected frames) one place, and say where they went; - `Alt+Shift` with an arrow nudges the selected cels; - the Menu key opens the cel's right-click menu. On a frame number, `Alt+Left` and `Alt+Right` move that frame too. On a layer's name, `F2` renames it. On a tag, `Enter` or `Space` makes it the tag that plays, and `Alt+Left` and `Alt+Right` move it a frame (its ends take `Left` and `Right` to resize it). See [Animation and the timeline](https://pxstud.io/docs/manual/guides/animation.md). ## Notifications Messages appear in the bottom-left corner and are read out by screen readers. Errors, and messages with a button, stay until you close them. Other messages stay for at least 8 seconds, longer the more there is to read (up to 20 seconds), and they wait while your pointer or focus is on them, or while the browser tab is in the background. At most five show at once. ## Touch screens - **Drawing:** one finger (or a pen) draws with the active tool. A pen's pressure sets the brush size from the start (only a pen's: a finger draws at the full size). It can also drive the brush size, angle and a color gradient (**Dynamics** in the tool options; see [Tools](https://pxstud.io/docs/manual/reference/tools.md#brush-and-ink-options)). A pen's eraser end erases: the editor switches to the Eraser while you use it and back afterwards. - **Drawing with a pen only:** the first time you draw with a pen on a touch screen, the editor asks whether to draw with the pen only. Choose **Pen only** (or turn off **Settings > Draw with Finger** later) and one finger pans the canvas instead of drawing, so a hand resting on the screen leaves no marks. Pinching and the two- and three-finger taps work the same either way. - **Zoom and pan:** put two fingers on the canvas and pinch to zoom, or move them together to pan. A single finger waits a moment before it draws, so starting a pinch never leaves a mark. With the Hand, a second finger turns the pan into a pinch. Other fingers and your palm are ignored while a stroke is in progress. - **Undo and redo:** tap the canvas with two fingers to undo, or with three fingers to redo. Tap quickly and keep your fingers still; a tap that moves the view slightly puts it back. - **Colors:** tap a palette swatch for the foreground color. For the background, tap it and then the palette's background button (there's no right-click on a touch screen). - **The timeline** scrolls with your finger. A tap selects a cel or frame. Press and hold for half a second, then drag, to select a range; press and hold and let go without moving to open the right-click menu. - **Panels:** press and hold a panel for a moment before dragging it. On a phone or a small window the editor switches to a compact layout, where panels open as sheets over the canvas; see [Small screens and phones](https://pxstud.io/docs/manual/guides/workspace.md#small-screens-and-phones). ## The keystroke display View > Show Keystrokes shows the keys you press on screen, which helps when you record a tutorial or teach a class. Settings > Keystrokes... chooses whether it shows and which corner of the window or the canvas it sits in. ## Known limits - The canvas has no keyboard equivalent of a right-click, so keyboard drawing always uses the foreground color. # Slices Mark named regions of a sprite for export, game engines and 9-slice scaling, with a pivot and per-frame changes. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ A **slice** is a named rectangle on the sprite: a button's area, a character's hitbox, the part of a sheet a game should cut out. Slices hold no pixels, so drawing never changes them. They're saved with the sprite, written into Aseprite files and sprite sheet data, and a game engine can read them from there. A slice can also carry a **9-slice center**, for art that stretches without distorting its corners, and a **pivot**, a point the game treats as the slice's origin. ![A green game button on the canvas with two slices: Button, selected, with eight handles, dotted 9-slice lines and a pivot crosshair at its bottom edge, and a dashed Icon slice around the star in the middle](https://pxstud.io/manual/images/guides/slices-canvas.light.webp) _A selected 9-slice with a pivot, and a second slice inside it_ ## Making a slice Pick the Slice (`Shift+C`) and drag on an empty spot of the canvas, from one corner to the opposite one; a dashed outline shows it as you drag. Hold `Shift` to make it square. The new slice is named "Slice 1", "Slice 2" and so on, and is selected, ready to adjust. Every slice is drawn on the canvas as a dashed outline with its name, whatever tool you're using. A slice with a color in its user data (below) is outlined in that color; the others get colors of their own in turn, so neighbors are easy to tell apart. With **Snap to grid** (`Shift+S`) on, slice edges snap to the document grid, both when you make a slice and when you drag it. With **Snap to guides** (`Ctrl+Shift+;`) on, making, moving and resizing slices, and moving their 9-slice lines and pivots, also snap onto nearby [guides](https://pxstud.io/docs/manual/guides/selection.md#rulers-and-guides) (a moved slice lands whichever edge is nearer). ## Auto-slice To cut a sprite sheet or an icon set into slices in one go, choose Sprite > Auto-slice.... It adds one slice around each separate painted region, found in the frame's visible layers or only in the active cel. Pixels that touch at a corner count as one region. Set: - **Merge gap:** regions whose bounding boxes are this many pixels apart or closer (or overlap) become one slice. 0 keeps every separate region. - **Min width** and **Min height:** smaller regions are skipped. The dialog shows how many slices it will add. They are named "Slice 1", "Slice 2" and so on, top to bottom then left to right, after the slices you already have, and the whole run is one undo step. Each one exports like any other slice. ## Selecting, moving and resizing With the Slice tool: | To | Do | | - | - | | Select a slice | Click it. A selected slice has a solid outline and eight square handles. | | Select more than one | `Shift`+click or `Ctrl`+click to add a slice or take it out of the selection. | | Deselect | Click an empty spot, or press `Esc`. | | Move | Drag inside a slice. Every selected slice moves together. | | Resize | Drag one of the handles. | | Open its properties | Double-click it. | Where slices overlap, a click picks the one made last. Each drag is one undo step, however many slices it moved. ## 9-slice center A **9-slice** slice is split into a grid of nine parts by its **center** rectangle. When the art is stretched (a button or a dialog frame in a game), the corners stay as drawn, the edges stretch along one direction, and the center fills the rest. - **Turn it on:** right-click the slice and choose **Set 9-slice**. The middle third of the slice becomes the center, shown as four dotted lines across the slice. - **Adjust it:** select the slice and drag any of the four dotted lines. - **Set it exactly:** use **9-slice (center)** in the Slice Properties dialog. - **Turn it off:** right-click and choose **Remove 9-slice**. The center must lie inside the slice. ## Pivot The **pivot** is a point your game can use as the slice's anchor: a character's feet, the spot a sprite rotates around. It shows as a small crosshair. - **Set it:** right-click the slice where you want the pivot and choose **Set Pivot**. The pivot goes on the pixel you clicked. - **Move it:** select the slice and drag the crosshair. - **Remove it:** right-click and choose **Remove Pivot**. The pivot can lie outside the slice. ## Slices that change over the animation A slice's position, size, center and pivot can change from frame to frame, while its name stays the same. Go to the frame where it should change and edit the slice there (drag it, or change it in Slice Properties). That frame gets its own **key**: the new geometry holds from that frame on, until the next frame with a key of its own. Editing on the first frame changes the slice's own geometry. To undo a change from one frame on, go to that frame, right-click the slice and choose **Remove Slice Key** (shown only on frames that have their own key), or delete the key in the Slice Properties dialog. The geometry before it then carries on. ## Slice Properties Double-click a slice with the Slice tool, choose **Slice Properties...** from its right-click menu, or choose Sprite > Slice Properties... (which opens the slice made last; the dialog can switch to any other). It has: - **Slice:** which slice you're editing, when the sprite has more than one. - **Name**, and the bounds: **X**, **Y**, **Width** and **Height**, in sprite pixels. - **9-slice (center)**, with **Center X**, **Center Y**, **Center W** and **Center H**. - **Pivot**, with **Pivot X** and **Pivot Y**. - **Keys**, when the slice changes over time: each frame with its own geometry, with **Go to** and **Delete key**. - **User data:** a color and a note, written to the sprite sheet data with the slice. - **Delete Slice**. The center and the pivot are measured from the slice's top-left corner, as in Aseprite. The dialog edits the slice as it is on the current frame; saving a change on a later frame gives that frame a key. ## The right-click menu Right-click a slice with the Slice tool for: | Item | Does | | - | - | | **Slice Properties...** | Opens the dialog above. | | **Duplicate Slice** | Adds a copy named "_name_ copy", with all its keys, and selects it. | | **Delete** | Deletes the slice. | | **Set 9-slice** / **Remove 9-slice** | Adds a center or removes it. | | **Set Pivot** / **Remove Pivot** | Puts the pivot where you clicked, or removes it. | | **Remove Slice Key** | Removes this frame's key (only on frames that have one). | Each of these is one undo step. ## Deleting slices Select slices with the Slice tool and press `Del` or `Backspace`, use **Delete** in the right-click menu, or **Delete Slice** in Slice Properties. With the Slice tool active and slices selected, Edit > Delete deletes the slices instead of pixels. ## Slices when the canvas changes Resizing, cropping, trimming, rotating and flipping the sprite move and resize its slices along with the art ([Resizing, cropping and effects](https://pxstud.io/docs/manual/guides/document.md)). ## Where slices go - **Export** can save just one slice, or every slice as its own file: choose it under **Area** in File > Export... ([Opening, importing and exporting](https://pxstud.io/docs/manual/guides/import-export.md#export)). - **Aseprite files** keep every slice, with its keys, center, pivot and user data, both ways ([Opening, importing and exporting](https://pxstud.io/docs/manual/guides/import-export.md)). - **Sprite sheets:** the JSON data lists each slice in Aseprite's format: its name, its user data color and note, and a key per frame where it changes, with the bounds, center and pivot, scaled with the sheet. - **Scripts** can add, read, change and remove slices ([Slices in the scripting reference](https://pxstud.io/docs/scripting/slices.md)). # Resizing, cropping and effects Resize, crop, trim, rotate and flip the whole sprite, apply effects and color adjustments, and step back through the undo history. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ The **Sprite** menu changes the sprite as a whole: its size, its canvas, its orientation. The **Filter** menu's effects and adjustments recolor and filter the pixels of one cel or many. Every one of them is a single undo step. ## Sprite size and canvas size - Sprite > Sprite Size... (`Ctrl+Alt+I`) **scales** the sprite to a new **Width** and **Height** (1 to 4096 pixels). Every cel is resized with nearest-neighbor scaling, so pixels stay sharp and no new colors appear; slices, tilesets and the selection scale with it. Width and height are separate: work out the other side yourself to keep the proportions. - Sprite > Canvas Size... (`Alt+C`) changes the size of the **canvas** without scaling the art. Pick an **Anchor** in the 3×3 grid: where the existing art sits in the new canvas (the middle keeps it centered, the top-left corner grows or shrinks the canvas to the right and down). With the middle anchor an odd difference puts the extra column on the right and the extra row on top, so shrinking back undoes it exactly. Art that ends up outside a smaller canvas isn't lost: it's kept past the edge, out of view, and comes back if the canvas grows again (as in Aseprite). The Background layer is cut to the canvas, and filled out with the background color when it grows. ## Crop and trim - Sprite > Crop to Selection makes the canvas the selection's bounding box. It needs a selection ([Selecting and transforming](https://pxstud.io/docs/manual/guides/selection.md)). A selection that reaches past the canvas grows it on that side. - Sprite > Trim cuts the canvas down to the smallest rectangle that holds what's drawn, across every frame. It looks at the sprite as shown: hidden layers don't count, and a solid border color (such as an opaque Background) is trimmed like transparency. When two opposite edges are solid in different colors, or nothing is drawn, the sprite is left as it is. Like Canvas Size, crop and trim keep the art outside the new canvas rather than deleting it. ## Rotate and flip the canvas Sprite > Rotate 90° CW and Sprite > Rotate 90° CCW turn the whole sprite by a quarter turn (width and height swap), and Sprite > Flip Canvas Horizontal and Sprite > Flip Canvas Vertical mirror it. These act on every layer and frame, locked layers included, and take the slices and the selection along. Art past the canvas edge turns and mirrors with the rest, so turning back restores it. To turn or flip only the selection or one cel, use **Edit > Transform** or the Layer menu's flips instead ([Selecting and transforming](https://pxstud.io/docs/manual/guides/selection.md#moving-and-transforming)). ## Sprite properties and pixel ratio File > Rename Sprite... shows the sprite's name, size and its count of layers and frames, and has: - **Color Mode**: RGB, Grayscale or Indexed ([Color and palettes](https://pxstud.io/docs/manual/guides/color.md#color-modes)); - **Transparent Color**, the palette index that stands for transparent (indexed sprites); - **Pixel Ratio**: 1:1, 2:1, 1:2, or **Custom** (each side 1 to 255); - **user data**: a color, a note and custom properties for the sprite ([below](https://pxstud.io/docs/manual/guides/document.md#user-data-and-custom-properties)). Nothing changes until you choose **Save**, which applies everything at once; one undo step takes back the color mode, transparent color, pixel ratio and user data together. **Cancel** leaves the sprite as it was. The **pixel ratio** makes pixels non-square, as some old consoles and computers showed them: at 2:1 each pixel is twice as wide as it is tall. The canvas shows the sprite stretched; the pixels themselves don't change. Exports stay one file pixel per sprite pixel unless you turn on **Apply pixel ratio** in the Export dialog ([Opening, importing and exporting](https://pxstud.io/docs/manual/guides/import-export.md#export)). You can also set the pixel ratio when you make a sprite. ## User data and custom properties Anything that can be annotated has a **User Data** section in its properties dialog: the sprite, a layer or group, a cel, a tag, a slice and a tileset (**Tileset Properties**). It holds a **color** (not on tags, which have their own), a **note**, and **custom properties**: named values that a game or tool reads from the exported data. Slices export theirs in the sprite sheet JSON, and `.aseprite` files keep all of them, so they come back when the file is reopened. Properties are grouped by **namespace**. **Yours** is the plain list; **Add extension namespace** makes a list that belongs to a named extension, as Aseprite's extensions do. In each list: - **Add property** adds a row with a name, a type and a value; **Add item** does the same inside a vector. The arrows move a row up or down and the bin removes it. - Pick the **type** of a value: Boolean, Null, String, UUID, a whole number of a given size (Int8 to UInt64), Fixed, Float or Double, Point (X, Y), Size (W, H), Rect (X, Y, W, H), or a **Vector** (a list) or **Map** (named properties) that holds more rows. Changing a row's type keeps its value where the new type allows it. - A value that doesn't fit its type (a UInt8 of 300, a UUID with the wrong shape, a repeated name) is marked on its row, and **Save** waits until it's fixed. Whole numbers are checked against the range of their type. **Edit as JSON** shows the same properties as text, for copying and pasting. Everything except text, true/false and plain numbers is written as `{ "$type": "uint8", "value": 7 }`; scripts use the same form with `app.setUserData`. Switching back to rows needs the JSON to be valid. ## Duplicating a sprite Sprite > Duplicate Sprite copies the sprite, with all its layers, frames and slices, into a new sprite named "_name_ copy" in the same project. ## Effects and adjustments The Filter menu has the same filters as Aseprite's Edit menu (Brightness/Contrast, Hue/Saturation and Color Curve work out their colors their own way; see each one below): - **Tone and color:** Filter > Brightness/Contrast..., Filter > Hue/Saturation..., Filter > Color Curve..., Filter > Levels..., Filter > Threshold..., Filter > Gradient Map... and Filter > Color Balance...; - **Replacing and reducing colors:** Filter > Invert Color... (`Ctrl+I`), Filter > Replace Color..., Filter > Posterize... and Filter > Reduce Colors...; - **Shapes:** Filter > Outline..., Filter > Drop Shadow\..., Filter > Anti-Alias... and Filter > Auto-Shade...; - **Neighborhood filters:** Filter > Despeckle... and Filter > Convolution Matrix.... Each opens a dialog. While it's open, the canvas shows a **live preview** of the active cel; turn **Preview** off to compare. **OK** applies the effect as one undo step, **Cancel** leaves the sprite alone. **What they change.** With a selection, an effect only changes pixels inside it; without one, it changes the whole cel. The dialog says which. **Apply to** chooses the cels: | Apply to | Cels | | - | - | | **Active cel** | The cel you're on (the default, when its layer can be edited). | | **Selected cels in timeline** | The cels selected in the timeline. | | **All frames of this layer** | The active layer's cel in every frame. | | **All layers in this frame** | Every layer's cel in the current frame. | | **Everything** | Every cel of the sprite. | The effects work on image layers. Brightness/Contrast, Hue/Saturation, Color Curve, Invert Color, Replace Color and Posterize also work on tilemap layers: they edit the tiles the cel places, so every placement of those tiles changes. The last three choices skip layers that are locked or hidden, and layers the effect can't take, and tell you how many cels they left out. The menu items are grayed out only when no layer can take the effect. When the active layer can't take the effect (it's locked, hidden, a group, or a tilemap for the other effects), the dialog starts at **Everything**. On the Background layer the effects never change alpha, because it has no transparency: the alpha options are ignored there, and Replace Color changes only the color. ### Indexed sprites In an indexed sprite, the color adjustments (everything except Despeckle, Convolution Matrix, Reduce Colors, Drop Shadow, Anti-Alias and Auto-Shade, which always remap) have an **Indexed** choice: - **Remap to nearest** (the default) changes the pixels, then snaps each one to its nearest palette color. The palette stays as it is. - **Modify palette** changes the palette entries the chosen cels use, instead of the pixels. Every pixel that uses those entries changes, in any cel, and the selection is ignored. In a sprite whose palette changes over time, each frame's own palette is edited. ## The effects ### Outline Draws an outline around shapes, edging them against transparent pixels. ![The Outline dialog: a color, Place set to Outside, Apply to set to Active cel, a 3 by 3 matrix with the four side neighbors on, Circle and Square presets, and the Also treat a color as background and Preview checkboxes](https://pxstud.io/manual/images/guides/document-outline.light.webp) _The Outline dialog_ - **Color:** the outline color; it starts as the foreground color. In an indexed sprite, pick a palette entry. - **Place:** **Outside** paints the transparent pixels touching the shape, so the shape grows; **Inside** repaints the shape's own edge pixels, so it keeps its size. The canvas edge doesn't count as transparent, so a shape that runs off the canvas isn't outlined along it. - **Matrix:** which of the eight neighbors count as touching. **Circle** uses the four above, below and to the sides (no corners); **Square** uses all eight. Click a cell to switch it. - **Also treat a color as background:** a **Bg Color** that counts as empty, like transparency, for outlining art drawn on a solid color. ### Posterize **Levels** (2 to 32, default 4): how many steps each color channel keeps. Fewer levels give flatter, bolder color. ### Despeckle Removes stray single pixels with a median filter: each pixel takes the middle value of the pixels around it. **Window** sets the area, from 1 × 1 to 9 × 9 (default 3 × 3); larger windows smooth more. ### Convolution Matrix Runs a kernel filter over the pixels. - **Preset:** Identity, Gaussian Blur 3x3 (the default, the same as Aseprite's Blur 3x3), Box Blur 3x3, Gaussian Blur 5x5, Box Blur 5x5, Sharpen, Edge Detect, Emboss, Sobel Horizontal and Sobel Vertical. A gaussian blur weighs the middle pixels most, so it is softer than a box blur, which weighs every pixel alike. Editing a weight makes it **Custom**. - **Size:** 1 × 1 to 9 × 9, and the grid of weights to edit. - **Divisor:** what the weighted sum is divided by; **Auto (kernel sum)** works it out. - **Bias:** added to every result (-255 to 255). - **Red**, **Green**, **Blue**, **Alpha:** which channels it changes (alpha is off by default). - **Edges:** at the cel's border, **Clamped** repeats the edge pixels, **Tiled** wraps around to the other side. ### Reduce Colors Maps the art onto a few colors, optionally dithered, without changing the color mode: a quick way to give painted or imported art a limited palette, or to preview how it would look in indexed color. Transparency is kept. - **Colors:** **Generate from the cels** picks the best **Colors** (2 to 256) from every cel **Apply to** covers, shown as swatches; **The sprite's palette** uses the palette instead (its transparent entry left out). - **Dithering** and **Amount:** the same choices as converting to indexed ([Color modes](https://pxstud.io/docs/manual/guides/color.md#color-modes)). Pick an ordered pattern for animation. ### Brightness/Contrast **Brightness** and **Contrast**, each from -100 to 100. Brightness adds the same amount to every channel (at 100, 255 levels: black becomes white), then contrast spreads the channels away from mid-gray or pulls them toward it (at -100 everything is mid-gray, at 100 almost every channel is 0 or 255). Aseprite's version differs: it scales brightness, so black stays black. ### Hue/Saturation - **Mode:** **HSV**, **HSL** or **OKLCH**. In HSV the third slider is **Value**, which moves the brightest channel toward full or zero; in HSL and OKLCH it's **Lightness**, which moves toward white or black. - **OKLCH** is perceptual: turning the hue keeps every color's perceived lightness, so yellows don't jump brighter and blues darker the way they do in HSV or HSL, and **Saturation** scales chroma. A color pushed past what the screen can show loses saturation rather than shifting hue or lightness. Use it to recolor a whole sprite while keeping its shading readable. - **Hue** (-180 to 180), **Saturation**, **Value** or **Lightness**, and **Alpha** (each -100 to 100\). Positive Lightness, Value and Alpha move that share of the remaining way to full: +50 takes black to mid-gray and half-transparent pixels three quarters opaque. Negative ones scale down, so -100 reaches black or transparent. Aseprite's multiplies instead, so black stays black there. ### Levels Sets where black, white and the midtones sit, per channel: **Input black** and **Input white** (0 to 255) are the values that become the darkest and lightest; **Midtones** (gamma, 0.10 to 5) brightens (above 1) or darkens (below 1) what's between; **Output black** and **Output white** limit the range the result can use, and an output white below the black inverts the channel. Pick **RGB** to change all three channels together, or **Red**, **Green** or **Blue** to change one after that (not on grayscale sprites, which have one gray channel). ### Threshold Turns every pixel one of two colors by its brightness: **Level** (0 to 255) is the brightness a pixel needs to become the **At or above** color instead of the **Below** color (black and white by default). Brightness is the same luma Aseprite uses for grayscale. Pixels keep their own alpha. ### Gradient Map Recolors the art by brightness through a gradient: the darkest pixels take the first stop's color, the lightest the last, and the rest what lies between. Add stops, set their **Position** and color, and choose the space they **Blend in**: **RGB**, **OKLab** (even-looking steps, the default) or **OKLCH**. **Reverse** runs the gradient from light to dark. Pixels keep their own alpha. ### Color Balance Shifts the color of one tonal range: pick **Shadows**, **Midtones** or **Highlights** and move the **Cyan - Red**, **Magenta - Green** and **Yellow - Blue** sliders (-100 to 100). Each pixel is shifted by how dark or light it is, so shadows fade into midtones and midtones into highlights. **Preserve luminosity** (on by default) puts back the lightness the shift changed, so only the color moves. On grayscale sprites there is a single **Lightness** slider. ### Drop Shadow Writes a shadow into the cels. **Style** is **Drop shadow** (offset **Across** and **Down**, up to 10 pixels either way), **Glow** (no offset) or **Inner shadow** (over the art, along the edge facing away from the offset). **Spread** grows it up to 10 pixels, in a **Round**, **Square** or **Diamond** shape, and the color's **Opacity** sets how strongly it shows. **Softness** (up to 8 pixels) fades its edge; at 0 the edge is hard. **Edges** sets how a soft edge is drawn: **Dithered** (the default: a dither pattern of the one shadow color that stays put as the art moves), **Hard** (cut at half strength) or **Smooth** (half-transparent pixels). Dithered and Hard add no in-between colors. A shadow outside the art grows the cel (up to the canvas edge) and sits underneath it. On an indexed sprite the result snaps to the palette. **Add as Layer Effect** in this dialog and in the other adjustments keeps the same settings live instead of writing them: see [Layer effects](https://pxstud.io/docs/manual/guides/layers.md#layer-effects). ### Anti-Alias Smooths the stair steps where one color meets another, the way pixel artists do it by hand: not a blur, but a few in-between colors placed next to each step. Pick **Color A** and **Color B** (they start as the foreground and background colors; the swatches under each are the layer's most used colors). Straight edges, corners and 45-degree diagonals stay crisp. The longer the straight run before a step, the more pixels get an in-between color, up to **In-between colors** (1 to 4, blended from A to B in OKLCH). **Smooth into** chooses the side that is repainted: **Color B** (the default: around the shape, best for a line on a known background), **Color A** (inside the shape, so its silhouette doesn't grow; a one-pixel line is left alone) or **Both**. **Strength** sets how far along each run the smoothing reaches (100: up to half the run). **Colors** set to **Palette only** uses the nearest palette colors instead; indexed sprites always do. To smooth an outline against a transparent background, set Color B's **Opacity** to 0 (any transparent pixel) and tick **Any visible color is A**: the in-betweens are the edge's own colors fading out. Palette colors can't fade, so with **Palette only** (and on indexed sprites) they become solid or nothing. **Add as Layer Effect** keeps it live; on a sprite with a color depth the result stays on the depth. ### Auto-Shade Shades flat colors in one pass, the way pixel artists add highlights and shadows by hand. **Shade** is **Base color only** (the pixels of **Base color**; the swatches under **Most common colors** are the cel's most used colors) or **Every flat color** (each color gets a ramp of its own). The light comes from **Light angle**, in degrees counterclockwise from the right: 90 is above, 135 the top left (the default). Pixels near the edge facing the light get up to **Highlights** lighter steps (0 to 5) and pixels near the far edge up to **Shadows** darker steps, the edge itself the strongest; each step is **Step width** pixels deep (1 to 4). A part 1 pixel thick keeps its color and one 2 pixels thick gets one highlight and one shadow, so thin parts never turn all dark. The steps are worked out from the color in OKLCH. **Hue shift** turns highlights toward a warm yellow and shadows toward a cool blue, as pixel artists do, and **Chroma per step** makes each step more (or less) colorful. **Rim light** adds a thin highlight along the outer edge of the shadow side; **Darkest outline** paints the shape's outer edge one step darker than the deepest shadow. **Colors** set to **Palette only** takes the nearest palette colors; indexed sprites always do. The swatches under **Steps (dark to light)** show the result. **Edit These Colors** copies them into **Ramp**, where you can change any of them: a ramp is walked for every color it holds, whichever way it runs. **Use Shading Ink Shades** copies the Shading ink's ramp (a new Auto-Shade starts from it when it holds the base color). **Add as Layer Effect** keeps it live, so redrawn art is shaded again; its light angle can be animated to turn the light over an animation. On a sprite with a color depth the result stays on the depth. ### Color Curve Remaps values through a curve you draw. Pick a **Channel**: **Value** (red, green and blue alike), **Red**, **Green**, **Blue** or **Alpha**. Each channel has its own curve, a smooth one that bends through the points without overshooting them (Aseprite joins its points with straight lines). In a grayscale sprite the channels are **Value** and **Alpha**. In the curve editor, click to add a point, drag a point to move it, and double-click one to remove it. Below it, the **Points** list has each point's input and output as number fields, with **Remove** buttons and **Add point** (in the middle of the widest gap), so the curve can be edited from the keyboard too. **Reset channel** straightens the current channel's curve. The Value curve is applied first, then the single-channel ones. ### Invert Color Inverts the channels you tick: **Red**, **Green** and **Blue** are on, **Alpha** is off. Fully transparent pixels stay transparent. In a grayscale sprite there's one **Gray** channel instead of the three colors (Convolution Matrix too). ### Replace Color Swaps colors for others. - **From** and **To**: the colors (palette entries in an indexed sprite). Or tick **Replace with transparent**. **Add row** adds another From and To pair; **Remove row** takes one away. All rows apply at once, a pixel takes the first row it matches, and a replaced color is never replaced again by a later row, so rows can swap two colors. - **Tolerance** (0 to 255): how far a pixel may be from the From color and still be replaced; 0 takes only that exact color. ## Undo History The history button under **Undo** and **Redo** in the toolbar (or Edit > Undo History) opens the Undo History panel beside the toolbar: to its left when the toolbar is on the right, otherwise on whichever side has room. It lists every step you can undo, oldest first, with **Original** (the sprite before the oldest step) at the top. The current step is highlighted with a filled dot, and steps you've undone (which redo would bring back) are grayed. Each step shows the time it was made (and the date, if it wasn't today); hover over it for the full date and time. Click any row to take the sprite to that point. Click the button again, or press **Esc** in the panel, to close it. Undoing never throws work away. If you undo a few steps and then make a new change, the steps you undid stay in the tree as a **branch**, drawn off the step where you went a different way. Each level of branching has its own color. A step with branches has an arrow beside it: click it to fold them away (the step then shows how many steps are hidden) or unfold them. Branches start unfolded up to three levels deep; deeper ones start folded. Click any row in a branch to go back to it. Redo follows the branch you visited most recently. The history is kept with the sprite, and goes with it in the `.pxstudio` files you download: open one and its history is back. **Name a step** to find it again later: hover over it and click its **...** button, or right-click it (from the keyboard, press the Menu key or Shift+F10 on it), then choose **Name step...**. A named step shows its name in front of its usual label, with a tag and a colored edge, so it stands out in a long history. Naming a step doesn't change the sprite and adds nothing to undo. The names are kept with the history: after a reload, on your other devices with Sync, and in the `.pxstudio` files you download. The same menu has **Rename step...** and **Remove name**. Naming a step also keeps the sprite as it was at that step as a named version, under the same name, in Sprite > Version History.... The step leaves the history once it's among the oldest dropped, but the version stays until you delete it. Renaming a step or removing its name leaves its version as it is. The bottom line shows how much memory the history uses and how many steps it holds across every branch, out of 1,000 at most. Past that, the oldest steps are dropped first. [How your work is organized](https://pxstud.io/docs/manual/concepts/how-work-is-organized.md#what-undo-covers) explains what undo covers. ## In scripts Scripts can do all of this too: see [Document](https://pxstud.io/docs/scripting/document.md) and [Adjustments](https://pxstud.io/docs/scripting/adjustments.md) in the scripting reference (the outline is under [Drawing](https://pxstud.io/docs/scripting/drawing.md)). # The library Find and run scripts other people shared, heart the ones you use, keep your own scripts, palettes and brushes, and publish scripts. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ Your **library** is the scripts, palettes and brushes you made, saved or forked. They live outside any sprite, so you can use them in every sprite. The **script library** is the shared side: scripts people have published, including the **Official** set made by pxstud.io. Palettes and brushes aren't shared: the editor comes with a **Built-in** set of each, and the ones you save are yours alone. | | Yours | Shared or built in | | - | - | - | | Scripts | Scripts > Open My Script... | Scripts > Browse Script Library... | | Palettes | The **My Palettes** tab of the Palette Library | Its **Built-in** tab | | Brushes | The **My Brushes** tab of the Brush Library | Its **Built-in** tab | Open the **Palette Library** with the library button at the top of the palette, and the **Brush Library** with the **Custom brushes** button in the tool options of the Pencil and the Eraser. Your own items are stored in this browser and work offline, signed out. With Sync they're also kept in your account and synced to your other devices. The built-in palettes and brushes are part of the editor, so they work offline and signed out too. ## Browsing the script library The script library needs a free account on pxstud.io: sign in with your email address and open the link we send. A free account stores nothing in the cloud, and the rest of the editor never needs one. - **Search** by name or tag. Results update as you type. - **Tags** in the sidebar narrow the list, with how many scripts use each. Pick up to 3; a script must have all of them. Clicking a tag on a card filters by it too, and **Clear filters** starts over. - **All scripts** and **Official** choose between everything and just the Official scripts. Results are ranked by how many people ran or forked each script, with each person counted once (your own runs don't count). Your hearted scripts come first. Each card shows the script's name, its author and version, an **Official** or **Yours** badge (**edited** when your draft is newer than what's published), its tags, and how many people ran and forked it. A card lists the latest published version. To use an older one, open **Versions** and pick it from the **Version** list, which shows each version's date and release note; the card's buttons then act on that version. ## Running and forking | Button | Does | | - | - | | **Run** | Runs it on the current sprite, asking for its options first if it has any (an **Options** badge on the card says so). | | **Fork** | Copies it into your library and opens it in the script panel, where you can change it. The copy keeps the credit to the original. | | **View code** | Opens the code read-only, or in the script editor if it's yours. | ## Hearts The heart on a card **hearts** a script: gray when it isn't hearted, red when it is. Hearted scripts are listed first in every search, and are saved in this browser, so you can still list, run and fork them **offline**: without a connection the script library shows just your hearted scripts, and says so. Your hearted scripts are also on your account page, on the **Hearted** shelf of the Scripts group in its **Library** section. Right-click one there for **Fork to yours** or **Unheart**. ## Built-in palettes and brushes The Palette Library's **Built-in** tab lists classic console palettes and popular ones from [Lospec](https://lospec.com/palette-list), each credited to its author. **Use** replaces the sprite's palette, **Append** adds its colors, and the save button saves a copy to My Palettes, keeping the credit. **Find more on Lospec** opens Lospec's palette list; import one by name in My Palettes. See [Color and palettes](https://pxstud.io/docs/manual/guides/color.md#the-palette-library). The Brush Library's **Built-in** tab has a set of brush shapes. **Select** draws with one, and the save button saves a copy to My Brushes. ## Custom brushes The Brush Library's **My Brushes** tab has a small editor for making brushes: 1. Pick a grid size: **8×8**, **16×16**, **32×32** or **64×64**. 2. Click a cell to fill or empty it, or drag across cells to paint several. **Clear** empties the grid. 3. Name the brush and choose **Save to My Brushes**. Click **Select** on a saved brush to draw with it. A custom brush always draws at its own size, whatever the **Size** option says; pick one of the built-in shapes in the tool options to go back. The pencil button renames a brush (type the name, then `Enter`; `Esc` cancels), and the trash button deletes it from My Brushes, after asking. Deleting a palette from My Palettes asks the same way. ## Your palettes and scripts - **Palettes:** the My Palettes tab saves, loads, imports and exports palettes. See [Color and palettes](https://pxstud.io/docs/manual/guides/color.md#the-palette-library). - **Scripts:** Scripts > New Script... starts one and Scripts > Open My Script... lists yours, with rename and delete. Scripts you opened recently are at the bottom of the Scripts menu. The scripting API is documented in the [scripting reference](https://pxstud.io/docs/scripting.md). Your [account page](https://app.pxstud.io/account) (its **Library** section) also lists everything of yours, signed out too: right-click an item (or press `Shift+F10`) to rename, duplicate, download or delete it. ## Publishing scripts Publishing shares a script in the script library. Every script has a **draft**, the version you edit, and **published versions**. Each time you publish, the draft becomes the next numbered version, and published versions never change. Edits after that stay in your draft (marked **edited**) until you publish again, so people using your script aren't affected by work in progress. Each script also has a **visibility**. Everything starts **Private**. A **shared** script is listed in the script library from its first published version; a private one that you published isn't listed. Scripts can have up to 3 tags (each up to 24 characters); while you type one, matching tags already in use are suggested, with how many scripts use them. In the script panel, the save button saves your draft and the publish button publishes it. The **Private** or **Shared** badge beside the script's name (or the settings button) opens **Script Settings**, with its tags, who can see it, and its version history, where you can read an older version or **restore** it into your draft. The account page and the Open Script dialog use the same badges: **Private**, **Shared v2**, or **Private v2** for a published script that's private. Changing tags or visibility of a published script takes effect in the script library right away. Deleting a published script takes it out of the script library too. ## Accounts | | Without an account | Free account | With Sync | | - | - | - | - | | Built-in palettes and brushes | Yes | Yes | Yes | | Your own library | In this browser | In this browser | Synced too | | Browse, run, fork and heart scripts | No | Yes | Yes | | Publish scripts | No | No | Yes | A year of Sync comes with the desktop app ($20 once), which you get from your [account page](https://app.pxstud.io/account). Sync is optional, and it only renews if you turn on **Auto-renew**. **When Sync ends**, your account stays a free one, your published, shared scripts stay in the script library, and you can still unpublish or delete them. Your own items stay in your browsers. The cloud copy of everything else (unpublished or private scripts, your palettes and brushes) is kept read-only for 180 days, then deleted from the server. # The desktop app pxstud.io's desktop app for macOS, Windows and Linux, and how it differs from the editor in your browser. The desktop app is the editor in its own window, with its files built in, so it opens without a connection. It costs $20 once, and that includes a year of Sync. Sync is optional: the app works and updates without it, you can turn off **Use Sync** any time on your account page, and nothing renews unless you turn on **Auto-renew** ($20 a year). It's signed for Windows, signed and notarized by Apple for macOS, and updates itself. ## What's different from the browser The desktop app is the same editor as the one in your browser, with a few differences: - **Your work is kept by the app**, not by your browser: the sprites in your browser aren't in the desktop app, and the other way around, unless you sign in to both with Sync. - **On macOS, the menus are in the menu bar** at the top of the screen. On Windows and Linux they stay in the window, as in the browser. - **You sign in through your browser.** Choose **Sign in**, then **Sign in with your browser**: app.pxstud.io opens in your browser, where you sign in with an emailed link (or are already signed in) and approve the app. Your browser may ask whether to open pxstud.io; allow it, and the app is signed in. It stays signed in until you sign out, keeping its key in your system's keychain (Keychain on macOS, Credential Manager on Windows, the Secret Service on Linux). As in the browser, everything works without an account; signing in adds the script library and, with Sync, your work on every device. - **Billing opens in your browser**: buying the app, renewing Sync and managing your plan happen on Stripe's pages there, then the app picks up the change when you come back to it. - **The editor draws the same way everywhere**, in your system's web view (WebKit on macOS, WebView2 on Windows, WebKitGTK on Linux): no particular graphics card or driver is needed. # Invite people to view your work Send an invite link so people can look at a sprite or a whole project, without changing anything, until you remove them. An **invite link** lets people see your work in the editor exactly as it is: every layer and frame, playing as an animation, at any zoom. They can look, but they can't change, copy, export or keep anything. Anyone who opens the link within **72 hours** becomes a **viewer**. A viewer keeps access after the link expires, until you remove them, you delete the work, or they leave. They always see the latest synced version. | | Who | | - | - | | Invites people | You, with Sync, for your own synced work | | Views | Anyone signed in to pxstud.io: a free account is enough | ## Invite people 1. Open the sprite in the editor and choose **File > Share View Link...**. On your account page, it's **Share view link…** in a sprite's or a project's menu (right-click it, or use its **⋯** button). 2. Choose **This sprite**, or **The whole project** to share every sprite in it. 3. Select **Create invite link**, then **Copy**, and send the link however you like. The link is shown only once. If you lose it, make another; each one is open for its own 72 hours. Viewers see what's in the cloud, so the work has to have synced first. If it hasn't, the dialog says so instead of making a link that would show nothing. A sprite you've changed since its last sync shows as it was then, and in a shared project, sprites that haven't synced yet appear once they do. Sharing comes with Sync. Without it, **Share View Link...** offers Sync instead; signed out, it asks you to sign in. ## Manage who can view The same dialog lists, for the project: - **Open invites**, with when each expires. **Revoke** closes one at once, so nobody new can join with it. People who already joined keep their access. - **People who can view**, by @username (or "Someone without a username" and the day they joined). **Remove** takes someone's access away at once; they'd need a new invite to view it again. If your Sync ends, nobody can open your shared work until you have Sync again. Your viewers keep it in their list, marked unavailable. ## Open an invite Opening an invite link takes you to the editor. If you aren't signed in, you're asked to sign in or create a free account first: enter your email address, open the link we send, and you land on the shared work. Open that email link on the same device and browser; on another device, signing in takes you to the editor, and you can open the invite link again from there. Opening your own invite link does nothing: it's for the people you send it to. ## Work shared with you Everything shared with you is on your account page, under **Shared with you** (in the editor: **Project > Shared with You...**). Each entry shows who shared it, with: - **Open**, to view it. It's greyed out while the owner doesn't have Sync. - **Leave**, to remove it from your list. Only a new invite brings it back. Shared work opens in the editor with a banner across the top: what it is, who shared it, and that it's **view only**. Select **Back to my work** to return to the editor as you left it. While you're viewing, you can: - switch between the shared sprites with their tabs; - go through the frames and play the animation, with onion skin; - pan and zoom with the Hand and Zoom, rotate the view, and show the grids; - open the timeline's layers to see how the work is built. Everything that would change the work is unavailable and shown as such: drawing tools, layer and frame changes, the palette and colors, undo, paste and scripts. Copy, export and save are off too. Shared work is never saved in your browser, never added to your projects and never synced, so viewing it leaves your own work untouched. # Tools Every tool in the toolbar, what it does, its keyboard shortcut and its options. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ The toolbar has 33 tools. Some share a button: a small corner mark shows a button holds more than one tool, and right-clicking it lists them. Pressing a shared button's key again switches to the next tool on it (for example, `B` picks the Pencil, pressing `B` again picks the Spray, then the Scatter and then the Dither). Each tool's settings appear in the tool options bar above the canvas, and each entry below links to them in [Tool options](https://pxstud.io/docs/manual/reference/tool-options.md). Each tool keeps its own brush size, shape and ink, so switching from the pencil to the eraser and back brings the pencil's brush back; turn off Settings > Remember Brush per Tool to share one brush between every tool. Your other settings are kept as you switch tools. ## How tools behave - **Left and right mouse buttons.** The left button draws with the foreground color and the right button with the background color. With the Shading ink, the right button steps the other way along the shade ramp. - **Alt picks a color.** Holding `Alt` over the canvas switches to the Eyedropper until you let go, except on tools that use Alt themselves: the selection tools, Magic Wand, Move, Hand, Line, Rectangle, Ellipse, Triangle, Hexagon, Star, Tile Stamp and Iso Shape. - **Selections and symmetry.** When part of the sprite is selected, painting stays inside the selection. With symmetry on, strokes and shapes are mirrored across the symmetry axes. - **Painting several cels at once.** Turn on **Paint across cels** in the tool options bar, then select cels in the timeline: each stroke, fill or shape you make on one of them lands on all of them, and one undo takes it all back. The badge next to the button says how far it reaches, for example "2 layers × 4 frames". While you draw you see the stroke on the cel you're painting; the others update when you let go. - **Snap to grid** (`Shift+S`) snaps shapes, lines, selections, slices and gradients to the document grid. A drag across cells covers them exactly, as in Aseprite: a rectangle or selection dragged over one 8×8 cell is 8×8, not 9×9. **Snap to guides** (`Ctrl+Shift+;`) snaps them onto a nearby [guide](https://pxstud.io/docs/manual/guides/selection.md#rulers-and-guides), which wins over the grid. - **Shapes and lines.** `Shift` makes a square, circle or square box (on the shorter side of the drag, as in Aseprite) and snaps a line's angle. `Alt` or `Ctrl` draws from the center: the point you pressed becomes the middle. Pressing or letting go of these keys mid-drag updates the shape straight away, except that on the Rectangle `Alt` pressed after the drag has begun rotates it instead. - **Straight lines from the last point.** Shift-click with any freehand tool (Pencil, Eraser, Dither, Spray, Scatter, Blur, Jumble, Dodge/Burn) draws a straight line from where the last freehand stroke ended, whichever tool made it and on whichever layer or frame. The stabilizer doesn't apply to that line, so it reaches the click. - **Locked and hidden layers** can't be painted on, including layers inside a locked or hidden group. - **Tilemap layers.** The pixel tools paint a tilemap layer's tiles in the Pixels modes; picking one while in Tiles mode switches back to Pixels. The Tile Stamp switches to Tiles mode. - **Tools with several steps** (Polygonal Lasso Selection, Polygon, Curve, Iso Shape) finish with `Enter` or a double-click. `Esc` cancels. The Text tool finishes with `Ctrl+Enter` instead, since `Enter` starts a new line. - **The tool wheel.** Hold `` ` `` (the backquote key, under `Esc`) and two rings of tools open around the pointer: the inner ring close by, the outer ring behind it with each icon between two inner ones. Move toward one and let go of the key to switch to it; letting go in the middle, or pressing `Esc`, changes nothing. Choose the tools and where they sit in Settings > Tool Wheel...: drag tools into the eight slots of each ring (slots may stay empty), drag one slot onto another to swap, and right-click a slot or drag it onto the trash at the end of its row to empty it. - **Borrowing a tool.** Turn on Settings > Hold Tool Keys to Borrow and a tool's key does two things: tapping it switches to the tool as usual, while holding it for a moment only borrows the tool, and letting go returns to the tool you had (hold `E` to erase, release to carry on with the pencil). It is off by default so that a key held while you think never changes the tool. - **Panning.** Hold `Space` and drag, or drag with the middle mouse button, with any tool (Space is the hand tool's quick key, as in Aseprite). While [keyboard drawing](https://pxstud.io/docs/manual/guides/accessibility.md#drawing-with-the-keyboard) is on, Space draws instead. ## Brush and ink options The drawing tools share their brush and ink options: the brush **Size** from 1 to 64 pixels (which the Spray, Scatter, Dither, Blur, Jumble and Dodge/Burn use too), **Brush shape** (square, circle, line, diamond, cross, X or star) and **Angle** (it turns the line and square), a **Custom brushes** button for the Brush Library (its built-in brushes and My Brushes), **Pixel-perfect**, **Dynamics** (a stabilizer, and pen pressure or speed mapped to size, angle and color; pen pressure to size is on from the start, and mouse and finger strokes ignore pressure) and the **Ink** with its **Shades**. Each tool's list, with the values and defaults, is in [Tool options](https://pxstud.io/docs/manual/reference/tool-options.md). ## Selection tools Double-click with any of them to select the grid cell under the pointer, and keep the button down after the second click to drag across more cells ([grid cells](https://pxstud.io/docs/manual/guides/selection.md#making-a-selection)). ### Rectangular Selection Shortcut: `M`. On the toolbar's Marquee Selection button, with Elliptical Selection. Drag to select a rectangular area; double-click selects a grid cell. Drag from one corner to the opposite one. Hold a key when you start to combine with the current selection, as in Aseprite: `Shift` adds to it, `Alt+Shift` (or just `Alt`) subtracts from it, and `Ctrl+Shift` keeps only the overlap. A click without a drag deselects. During the drag, `Shift` makes a square and `Ctrl` grows it from the point you pressed; a key you held when you started only does this once you press it again. **Radius** rounds the selection's corners, all four together or each on its own (see the Rectangle below); the marquee keeps its own radius, separate from the Rectangle's. ### Elliptical Selection Shortcut: `Shift+M`. On the toolbar's Marquee Selection button, with Rectangular Selection. Drag to select an elliptical area; Shift makes it a circle and double-click selects a grid cell. Works like the Rectangular Selection, with the same keys when you start and during the drag: `Shift` during the drag makes a circle and `Ctrl` grows it from the center. A click without a drag deselects. ### Lasso Selection Shortcut: `Q`. On the toolbar's Lasso Selection button, with Polygonal Lasso Selection. Drag a freehand outline to select the area inside it; double-click selects a grid cell. Drag around the area; the outline closes by itself when you let go. The keys combine with the current selection as above, and a click without a drag deselects. ### Polygonal Lasso Selection Shortcut: `Shift+Q`. On the toolbar's Lasso Selection button, with Lasso Selection. Click to place corners; double-click or press Enter to close the shape and select it. Each click adds a corner, and a line follows the pointer to show the next edge. Pressing and dragging before you let go adds two corners, where you pressed and where you let go, as in Aseprite. Double-click or press `Enter` to close the shape. `Esc` cancels. ### Magic Wand Shortcut: `W`. Click to select pixels of a similar color on the active layer; drag to add every area you pass over. Selects by color on the active layer only. **Contiguous** selects the connected area you click; **Global** selects every matching pixel on the layer. **Tolerance** (0 to 255) sets how different a color can be and still match. On an indexed sprite it matches palette indices instead: two entries of the same color are different, and Tolerance is how many indices apart a pixel may be. **8-connected** and **Stop at grid** work as with the paint bucket. These settings are the wand's own: changing them leaves the paint bucket's alone (in Aseprite the two share them). Press and drag to sweep: every area the pointer passes over joins one selection, the outline grows as you go, and letting go makes it a single step to undo. The wand ignores the current selection while it looks for matching pixels; the selection keys held as you press then combine the whole result with the current selection. A click outside the canvas does nothing. ## Drawing tools ### Pencil Shortcut: `B`. On the toolbar's Pencil button, with Spray, Scatter and Dither. Draw freehand with the current brush; right-drag draws with the background color. Drag to draw; the whole stroke is one undo step. Shift-click draws a straight line from where the last freehand stroke ended. In tiled view, strokes wrap around the edges. As in Aseprite, a tilted line brush skips pixel-perfect (removing corners would leave holes in its band). Options: brush size, shape and angle, pixel-perfect, ink and dynamics. On a tilemap layer, **Place tiles** puts the selected tile on the cells the stroke touches. ### Spray Shortcut: `Shift+B`. On the toolbar's Pencil button, with Pencil, Scatter and Dither. Spray random dots around the pointer, building up while you hold it. Holding the button still keeps spraying. **Rate** (1 to 64) sets how many dots each burst adds; moving, it sprays a burst per pixel of path, so a fast mouse or pen doesn't spray thicker. The spray's radius follows the brush size. ### Scatter Shortcut: `Alt+Shift+B`. On the toolbar's Pencil button, with Pencil, Spray and Dither. Stamp the brush along the path, each stamp nudged by a random offset: foliage, grass, sparkles. Drag to stamp the brush along your path, about one brush width apart, with each stamp landing at a random spot near the path: good for leaves, grass, sparkles and rubble, especially with a custom brush. **Scatter X** and **Scatter Y** (0 to 64 pixels) set how far a stamp can land to either side of the path and above or below it, and **Count** (1 to 16) sets how many stamps land at each step. With both offsets at 0 you get a dotted line. The whole stroke is one undo step, and unlike the spray, holding still adds nothing. Options: brush size, shape and angle, ink, Scatter X, Scatter Y and Count. ### Dither On the toolbar's Pencil button, with Pencil, Spray and Scatter. Draw freehand with the current brush, painting only the pixels of an ordered pattern (a checkerboard at 50%) and leaving the others as they are; right-drag paints the background color. Draws like the pencil, but paints only the pixels of an ordered pattern and leaves the pixels in between as they are, for dithered shading and textures. **Pattern** picks the pattern (Bayer 2×2, 4×4 or 8×8, halftone dots, horizontal, vertical or diagonal lines, or noise) and **Density** (0 to 100%) how much of it gets painted: 50% of Bayer 2×2 is a checkerboard, 100% paints every pixel. The pattern lines up with the sprite's top-left corner, not with your stroke, so strokes that overlap, mirrored strokes and other frames all stay on one pattern. With the brush preview on, the cursor shows the pixels a stroke would paint. Shift-click draws a straight line from where the last freehand stroke ended. Options: brush size, shape and angle, pixel-perfect, ink, dynamics, Pattern and Density. ### Eraser Shortcut: `E`. Erase pixels to transparent with the current brush. Erases to transparent with the current brush. Shift-click erases a straight line from the end of the last freehand stroke. The **Right-click** option chooses what the right button does: **Paint BG** paints the background color, and **Replace FG** turns only pixels of the foreground color into the background color. With a pen that has an eraser end, turn the pen over: the editor switches to the Eraser while the eraser end is on or over the canvas, and back to your tool once you draw with the tip again or move the pen away. A stroke always finishes with the tool it started with. ### Eyedropper Shortcut: `I`. Click to pick the foreground color from the canvas; right-click picks the background color. Click, or drag to keep picking as you move. Transparent pixels are skipped. **Composite** picks the color you see, from all visible layers; **Layer** picks from the active layer only. ## View tools ### Zoom Shortcut: `Z`. Click to zoom in on a point; right-click to zoom out. Each click zooms in a step, keeping the point under the pointer where it is (in tiled view too, on whichever copy you clicked); right-click zooms out. ### Hand Shortcut: `H`. Drag to pan the view. Drag to move the view. Double-click zooms in 2x; `Alt` and double-click zooms out. ## Moving and slicing ### Move Shortcut: `V`. Drag to move the selection or the whole cel; its handles scale, rotate and skew it, and Ctrl/Cmd-dragging a corner distorts it (add Shift for perspective). Drag inside to move the pixels; `Shift` keeps the move to one direction and `Alt` moves a copy. The handles transform them: - drag a corner or edge handle to scale (`Shift` keeps the proportions); - `Ctrl` and drag an edge handle to skew; - `Ctrl` and drag a corner handle to distort it on its own (add `Shift` for perspective); - drag just outside a corner to rotate (`Shift` snaps to 15° steps); - drag the dot in the middle to move the point everything turns and scales around. Press `Enter` or click outside to apply the change as one undo step, or `Esc` to cancel it. The options choose the rotation method (**Fast Rotation**, **RotSprite** or **Bilinear**) and place the pivot. Layers with locked movement can't be moved. The Move tool also drags [guides](https://pxstud.io/docs/manual/guides/selection.md#rulers-and-guides) (unless View > Guides > Lock Guides is on): drag one to move it, or off the canvas to remove it. With View > Guides > Snap to Guides on, moving lands the content's nearer edge on a guide in reach, and scaling and the pivot snap to guides too. ### Slice Shortcut: `Shift+C`. Drag to create a slice, a named region for export or 9-slice scaling; click a slice to edit it. Drag on an empty spot to make a slice; `Shift` makes it square. Click a slice to select it (`Shift` or `Ctrl` adds to the selection), then drag it, its handles, its 9-slice guides or its pivot. Double-click a slice to open its properties, or right-click it for its menu. Changes apply from the current frame on. ## Fill tools ### Paint Bucket Shortcut: `G`. On the toolbar's Paint Bucket button, with Gradient. Fill the clicked area, or every matching pixel, with the foreground color. **Contiguous** fills the connected area you click; **Global** recolors every matching pixel on the layer. **Tolerance** (0 to 255) sets how close a color must be to count as matching (on an indexed sprite, how close a palette index must be, as with the magic wand). Right-click fills with the background color. On a tilemap layer, **Place tiles** makes the bucket fill cells instead of pixels: Contiguous fills the connected cells holding the clicked cell's tile with the selected tile, Global every cell holding it (Tolerance and the other pixel options don't apply). With a selection, a Contiguous fill stays inside it: unselected pixels act as walls, so the fill doesn't flow from one selected area into another, and clicking outside the selection fills nothing. A Global fill recolors the matching pixels inside the selection wherever you click. **8-connected** (Contiguous only) also spreads to diagonal neighbors, so the fill leaks through 1 px diagonal lines; off, only up, down, left and right count as connected (Aseprite's default). **Stop at grid**, while the grid is shown, keeps the fill inside the grid cell you click. On an isometric grid that's the diamond the grid draws: it includes the line pixels along its two upper edges and stops at the lines along its lower edges, which belong to the diamonds below. A hexagonal grid works the same way: a hexagon includes the line pixels along its left side and its two upper edges. **Close gap** (0 to 5, Contiguous only) lets you fill a sketchy outline that isn't quite closed: gaps up to that many pixels wide count as closed, so the fill stops at them instead of leaking out, and the gap itself is filled. Keep it low on small shapes, where a high setting can also stop the fill at narrow necks inside the shape. **Pattern** fills with a repeating pattern of the foreground and background colors (Checker, Dither 25% and 75%, stripes, Diagonal, Grid, Dots, Bricks) instead of a solid color; right-click swaps the two. Patterns line up from the canvas's top-left corner, so fills next to each other match. ### Gradient Shortcut: `Shift+G`. On the toolbar's Paint Bucket button, with Paint Bucket. Drag from inside an area to fill it, as the bucket would, with a foreground-to-background gradient. Drag from where the gradient starts to where it ends; right-drag runs it the other way. Like the paint bucket, it fills only the area you start in: the connected area of the color you press on (or, with **Global**, every pixel of that color), with the bucket's **Tolerance**, **8-connected**, **Stop at grid** and **Close gap**, and inside the selection, so you can shade a shape without selecting it first. To fill the whole layer, use Global with Tolerance 255. With symmetry on, the mirrored areas get the same gradient. A fade to a transparent color keeps the other color and only fades it out. Options: **Linear** or **Radial**; **Colors**, either foreground to background (**FG → BG**) or through up to 32 **Stops** placed along the drag; **Dither** for a stepped pixel-art blend (Bayer 2×2, 4×4 or 8×8 checkers, halftone **Dots**, horizontal, vertical or diagonal **lines**, or an even **Noise** scatter); and **Snap**, where **Palette** paints only palette colors, so with Dither on the gradient steps between neighboring palette colors. To make a multi-color gradient, set **Colors** to **Stops**. The Stops bar shows the ramp, with a handle under it for each stop; its left end is where you start dragging on the canvas. - **Add** a stop: click the bar (it takes the foreground color), or drop a palette swatch on it. Shift+clicking a range of the palette loads the whole range, evenly spaced. - **Move** a stop: drag its handle. Stops can't pass each other, but two can meet for a hard edge between their colors. With a handle selected, the arrow keys nudge it (`Shift` for bigger steps). - **Recolor** a stop: drop a palette swatch on its handle, or select it and press **FG**. - **Remove** a stop: drag its handle well off the bar and let go, or select it and press `Del`. - **Even** spreads the stops out evenly; **Clear** removes them all. ### Tile Stamp Stamp the selected tile onto a tilemap layer's cells; Alt-drag removes tiles. Click or drag across a tilemap layer to place the selected tile in each cell you pass; `Alt` removes tiles instead. Flip and rotate the stamped tile with the **Flip H**, **Flip V** and **Rot 90°** options. Works on visible, unlocked tilemap layers. It mirrors with symmetry and stays inside a selection, as the other tools do when they **Place tiles** (see [Tilemaps](https://pxstud.io/docs/manual/guides/tilemaps.md#placing-tiles-with-other-tools)). ## Shape tools Pressing `U` repeatedly steps through the rectangle and ellipse, outlined and filled. `Shift+U` starts the same cycle from the ellipse. The triangle, hexagon and star have no key of their own: pick them from the Shape button's flyout, or give them one in Keyboard Shortcuts (pressing it again switches between outlined and filled). ### Rectangle Shortcut: `U`. On the toolbar's Shape button, with Ellipse, Triangle, Hexagon, Star and Iso Shape. Drag to draw a rectangle, outlined or filled. Drag from corner to corner; `Shift` makes a square and `Ctrl` draws from the center, as does `Alt` if it is already down when you press. Press `Alt` part-way through the drag to rotate the rectangle about its center: move the cursor around the center to turn it (`Shift` snaps to 15° steps), and let go of `Alt` to keep the angle and carry on sizing. A turned rectangle is painted as a four-corner polygon, so its corners are not rounded. Options: **Outline** or **Filled**, and **Radius** to round the corners. With the link button on, one Radius sets all four corners; turn it off for a field per corner (top-left, top-right, bottom-right, bottom-left), so you can round just one side or make a tab or a speech bubble. ### Ellipse Shortcut: `Shift+U`. On the toolbar's Shape button, with Rectangle, Triangle, Hexagon, Star and Iso Shape. Drag to draw an ellipse, outlined or filled. Drag the box the ellipse fits in; `Shift` makes a circle and `Alt` or `Ctrl` draws from the center. The pixels are Aseprite's, so an ellipse always touches all four sides of its box, and the circle brush is the same filled circle. Options: **Outline** or **Filled**. ### Triangle On the toolbar's Shape button, with Rectangle, Ellipse, Hexagon, Star and Iso Shape. Drag to draw a triangle pointing up, outlined or filled. Drag the box the triangle fits in: it points up, with its base along the bottom of the box. `Shift` makes the box square and `Alt` or `Ctrl` draws from the center. Options: **Outline** or **Filled**. ### Hexagon On the toolbar's Shape button, with Rectangle, Ellipse, Triangle, Star and Iso Shape. Drag to draw a flat-topped hexagon, outlined or filled. Drag the box the hexagon fits in: its flat sides run along the top and bottom of the box. `Shift` makes the box square and `Alt` or `Ctrl` draws from the center. Options: **Outline** or **Filled**. ### Star On the toolbar's Shape button, with Rectangle, Ellipse, Triangle, Hexagon and Iso Shape. Drag to draw a five-pointed star, outlined or filled. Drag the box the five-pointed star fits in: it points up and its outer points touch every side of the box. `Shift` makes the box square and `Alt` or `Ctrl` draws from the center. Options: **Outline** or **Filled**. ### Iso Shape Shortcut: `K`. On the toolbar's Shape button, with Rectangle, Ellipse, Triangle, Hexagon and Star. Draw an isometric box, ramp, stairs, pyramid, prism, cylinder, cone, sphere or dome, adjust it with its handles, then press Enter. 1. Drag from the shape's back corner to its front corner to set its footprint. `Shift` keeps it square; hold `Alt` to freeze the footprint and move the pointer up or down to set the height. A click without dragging places a shape the size of the last one. 2. Adjust it: drag a handle to change that dimension (`Shift` scales all three together), drag the shape to move it, or nudge it with the arrow keys. 3. Press `Enter` or double-click to draw it. Clicking elsewhere also draws it; `Esc` throws it away. Pressing `K` again switches the shape. Options: - **Shape:** Box, Ramp, Stairs, Pyramid, Prism (a roof), Cylinder, Cone, Sphere or Dome. The round shapes (cylinder, cone, sphere, dome) have a circular footprint, so their two side handles both set the diameter. The cone rises to a point and the dome to a rounded top at the height you set. A sphere's height follows its width, so it has no height handle. - **Cap:** the width of the flat tip at the top and bottom corners, 2 (pointed) to 4 (flat). - **Bevel** (boxes): chamfers the top edges by that many pixels, so a slope runs between the top and the sides. 0 keeps the edges square. - **Style:** fill and outline, fill only, or outline only. In outline style, **Show back edges** draws the hidden edges too. - **Rotate 90°** turns ramps, stairs and prisms to slope the other way. - **Auto-shade from foreground:** the top face uses the foreground color and the sides are shaded darker, following the Shades ramp when the color is on it. Turn it off to pick the outline, inner edge and face colors yourself. The shape follows the sprite's isometric grid ratio (2:1 unless you change it). ### Contour Shortcut: `D`. On the toolbar's Contour button, with Polygon. Drag a freehand outline; it closes by itself and its inside is filled. Fills the shape you drag, outline included. The shape needs at least three points. ### Polygon Shortcut: `Shift+D`. On the toolbar's Contour button, with Contour. Click to place corners; double-click or press Enter to finish the closed polygon. Each click places a corner, and the shape always closes back to the first one; pressing and dragging places two, where you pressed and where you let go (Aseprite's way). Double-click or press `Enter` to finish. Options: **Outline** or **Filled**. ## Line tools ### Line Shortcut: `L`. On the toolbar's Line / Curve button, with Curve. Drag to draw a straight line; Shift snaps it to the pixel-art angles and Ctrl (Cmd on Mac) draws it from its middle. Hold `Shift` to snap to 16 angles (0°, 90°, 45° and 5:2 / 2:5 for 22.5° / 67.5°; an isometric grid adds its own slope), with even pixel steps, and `Ctrl` or `Alt` to draw from the middle: the point you pressed becomes the line's midpoint. Option: **Width**, 1 pixel by default; wider lines get round ends. ### Curve Shortcut: `Shift+L`. On the toolbar's Line / Curve button, with Line. Click to place corner points, drag to place smooth ones, and drag a placed point or handle to reshape it; click the first point to close the path, or press Enter or double-click to finish it open. Draws a path through as many points as you like, like a pen tool: - **Click** to place a corner point. Two corner points in a row are joined by a straight line. - **Press and drag** to place a smooth point: the drag pulls out its handles, which bend the path on both sides. Hold `Alt` while dragging to move only the outgoing handle, for a sharp turn. `Shift` snaps a handle, or the next point, to the pixel-art angles. - **Click the first point** to close the path (it turns green when the pointer is over it). Drag there to shape the closing curve. - `Enter` or a double-click finishes the path open; `Backspace` removes the last point; `Esc` throws the path away. Until you finish it, the path stays editable. Press on a point you've already placed to change it instead of adding a new one: - **Drag a point** to move it; its handles come with it. Hold `Ctrl` to move the first point (a plain click there closes the path). - **Drag a handle** to reshape the curve. The handle on the other side turns with it so the point stays smooth; hold `Alt` to move one handle on its own. Drop a handle on its point to remove it. - **Alt-click a point** to make it a sharp corner, or **Alt-drag** it to pull out new handles. Zoomed out, a press only grabs a point within two pixels of it, so you can still place points close together. Options: **Width** (shared with the Line), **Pixel perfect** (on by default: removes the L-shaped corners a 1-pixel curve picks up), and **Outline** or **Filled**, which fills a closed path. ## Adjusting tools ### Blur Shortcut: `R`. On the toolbar's Blur button, with Jumble and Dodge/Burn. Brush over pixels to soften them, averaging each with its neighbours. Each pixel is softened once per stroke. **Strength** (1 to 4) sets how far it looks for neighbors. ### Jumble On the toolbar's Blur button, with Blur and Dodge/Burn. Brush over pixels to scramble them with colors from nearby pixels. Each pixel the brush touches takes the color of a random nearby pixel. **Strength** (0 to 100%) is the chance that a pixel changes. ### Dodge/Burn Shortcut: `O`. On the toolbar's Blur button, with Blur and Jumble. Brush over pixels to lighten them, keeping their hue; right-drag darkens them instead. Drag to lighten (dodge) the pixels under the brush; right-drag to darken (burn) them. By default it changes perceptual (OKLCH) lightness only, so hue and saturation hold steady instead of washing out to gray, and transparent pixels are left alone. Each pixel changes once per stroke: stroke again to push it further. **Strength** (1 to 100, default 25) sets how far: 100 moves half way from black to white, 25 about one step of a typical ramp. **Hue shift** (0 to 60 degrees, default 0 = off) shades the way pixel artists do: lightening turns the hue toward warm yellow and softens the color, darkening toward cool blue and deepens it, never past those hues; gray pixels stay gray. With **Show Brush Preview** on, the cursor shows the result before you click (hold the right button to see the burn). On an indexed sprite the result snaps to the nearest palette color, so a low strength may not change anything. ## Ruler and text ### Ruler Shortcut: `Shift+R`. Drag to measure between two points: the length, angle and offsets show in the info bar and nothing is painted; Shift snaps the angle. Drag between two points to measure them. The info bar shows the length in pixels, the angle (counter-clockwise from the right, so up is 90°) and the horizontal and vertical offsets, and a line with two handles marks the span. Nothing is painted and nothing goes on the undo stack. The measurement stays after you let go until you press again to start another, press `Esc` or switch tool; `Shift` snaps the end to the 16 pixel-art angles. ### Text Shortcut: `T`. Click and type; Mod+Enter or a click outside lands it on a new text layer. Click text later to edit it. Click where the text should start and type. The text appears as you type, in the foreground color, inside a dashed box with a caret. `Enter` starts a new line, and the arrow keys, Home and End move the caret as in any text field. Click inside the box to move the caret there, or drag the box to move the text. Press `Ctrl+Enter`, or click outside the box, to finish: the text lands as one undo step on a new **text layer** directly above the active layer, named after its first line. Switching to another tool lands it too. `Esc` drops it at any point before it lands. **Editing text.** Click text on a text layer with the Text tool to open it again: its words come back in the box with the caret at the end, and its font, size, style, spacing, alignment and color come back into the options and the foreground color. Change any of them and finish to redraw it; delete all of it to clear the layer's cel. Text layers show _letter T icon_ in the timeline. Use the Move tool to move a text layer's cel: it stays editable. A text layer is ordinary pixels to everything else: it exports, merges and blends like any layer, in `.aseprite` files too. Painting on it, or anything that changes its pixels (a transform, a color-mode change, resizing the sprite), turns it into plain pixels, which you then edit like any drawing; undoing back to the drawn text makes it editable again. Text keeps the name of its font, not the font itself, so text drawn in a font you loaded opens only in a browser where that font is loaded too. **Fonts.** Six pixel fonts come with pxstud.io: Tom Thumb (a tiny 4x6), Spleen 5x8, 6x12 and 8x16, and Scientifica in regular and bold. Bitmap fonts are drawn exactly as designed, the same on every device, and **Scale** makes their pixels bigger by whole steps. **Load font...** adds a font file from your computer: a bitmap font (`.bdf`) or an outline font (`.ttf`, `.otf`, `.woff`, `.woff2`). Outline fonts are drawn at the **Size** you choose without anti-aliasing, so every pixel is solid. Each line is shaped as a whole, so kerning, ligatures and scripts that join letters (Arabic, for example) come out as the font intends; the exact pixels can differ slightly between browsers. Pixel fonts sold as TTF look right at their design size or a whole multiple of it. A loaded font stays in this browser only: it doesn't sync, and the trash button next to the font list removes it. **Bold** and **Italic** use a loaded font's own bold and italic (or ones the browser makes from it). A bitmap font gets them from its own pixels: bold doubles every pixel to its right, and italic shifts rows right a pixel at a time going up, leaving the baseline where it is. The bundled fonts' authors and licenses are listed on the editor's [Open-source licenses](https://app.pxstud.io/licenses) page. Options: font, scale or size, letter spacing, line spacing and alignment. # Menus Every item in the menu bar, what it does and its keyboard shortcut. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ The menu bar runs along the top of the editor. Every item is also in the command palette (`Ctrl+K`), and every shortcut below can be changed in Settings > Keyboard Shortcuts.... Items that are checked or unchecked turn a setting on or off. Right-clicking a layer, a frame, a cel or a slice opens a menu of the same commands for the thing you clicked. ## Project | Item | Shortcut | What it does | | - | - | - | | _Your projects: choose one to switch to it._ | | | | **New Project...** | | Opens a dialog to name a new project, then creates it and switches to it. | | **Rename Project...** | | Opens a dialog to rename the current project; sprite names stay unchanged. | | **Manage Projects...** | | Opens your account page, where you can see and manage all your projects. | | **Shared with You...** | | Opens your account page's list of work other people invited you to view, to open it or leave it. | ## File | Item | Shortcut | What it does | | - | - | - | | _The sprites in this project: choose one to show it._ | | | | **New Sprite...** | `Ctrl+Alt+N` | Opens a dialog to create a sprite: pick a size preset, color mode, background and palette. | | **Rename Sprite...** | | Opens a dialog to rename the sprite, convert its color mode, and set its pixel ratio, transparent index and user data. | | **Open...** | `Ctrl+O` | Opens a sprite or image file from your computer, such as .pxstudio, .aseprite, PNG, GIF or JPEG. | | Import > **Import image as pixel art...** | | Converts an image to pixel art at your chosen size and palette, as a new sprite or layer. | | Import > **Image sequence...** | | Combines several images into one new sprite, one frame per file in file-name order. | | Import > **Sprite sheet...** | | Cuts a sprite sheet into frames by grid or Aseprite JSON, as a new sprite or replacing this one. | | **Save** | `Ctrl+S` | Saves the current sprite right away and, with Sync, syncs it to your account. | | **Save As...** | `Ctrl+Shift+S` | Downloads the current sprite to your computer as a .pxstudio file. | | **Export...** | `Ctrl+Alt+Shift+S` | Opens a dialog to export as PNG, GIF, sprite sheet, video, Aseprite and other formats, with a preview. | | **Export Timelapse...** | | Makes a shareable GIF or MP4 timelapse showing how the sprite was drawn. | | **Share View Link...** | | With Sync: makes an invite link to this sprite or its whole project. Anyone signed in who opens it within 72 hours can view it, but not change it, until you remove them. | | **Close** | `Ctrl+Alt+W` | Closes the current sprite's tab and keeps it in the project, asking first if changes couldn't be saved. | | **Close All** | | Closes every open sprite tab and keeps them in the project, asking first if changes couldn't be saved. | | **Delete Current Sprite...** | | Permanently deletes the current sprite from the project after you confirm; this can't be undone. | ## Edit | Item | Shortcut | What it does | | - | - | - | | **Undo** | `Ctrl+Z` | Undoes your last change, and goes to the frame and layer it changed. | | **Redo** | `Ctrl+Shift+Z` or `Ctrl+Y` | Redoes the last change you undid, and goes to the frame and layer it changed. | | **Undo History** | | Shows or hides the undo history as a tree beside the toolbar; clicking a step takes the sprite back to that point. | | **Cut** | `Ctrl+X` | Copies the selected pixels on the active cel to the clipboard, then erases them and drops the selection. | | **Copy** | `Ctrl+C` | Copies the selected pixels on the active cel to the clipboard. | | **Copy Merged** | | Copies all visible layers in the current frame as one image, within the selection or the whole canvas. | | **Paste** | `Ctrl+V` | Pastes the clipboard image onto the active layer as a floating image you can move before placing. | | **Paste as New Layer** | | Pastes the clipboard image onto a new layer above the active one. | | **Paste as New Sprite** | | Opens the clipboard image as a new sprite sized to fit it. | | **Delete** | `Del` or `Backspace` | Erases the selected pixels on the active cel and drops the selection (the whole cel if nothing is selected; the Background layer clears to the background color), or deletes the selected slices when the Slice tool is active. | | **Fill** | `F` | Fills the selection, or the whole active cel if nothing is selected, with the foreground color. | | **Stroke Selection...** | | Opens a dialog that paints a line of a given width and color along the selection's edge, inside, centered on it or outside, on the active cel. | | **Flip Horizontal** | `Shift+H` | Flips the selection, or the whole active cel if nothing is selected, left to right. | | **Flip Vertical** | `Shift+V` | Flips the selection, or the whole active cel if nothing is selected, upside down. | | Transform > **Free Transform (Move Tool)** | | Switches to the Move tool so you can scale, rotate, skew and distort (perspective) the selection with its handles. | | Transform > **Rotate 90° Clockwise** | | Rotates the selection, or the whole active cel if nothing is selected, 90° clockwise. | | Transform > **Rotate 90° Counterclockwise** | | Rotates the selection, or the whole active cel if nothing is selected, 90° counterclockwise. | | Transform > **Rotate 180°** | | Rotates the selection, or the whole active cel if nothing is selected, 180°. | | Transform > Align > **Align Left** | | Moves the selection, or the content of the active cel (of each selected cel in the timeline), against the canvas's left edge. | | Transform > Align > **Align Center** | | Moves the selection, or the content of the active cel (of each selected cel in the timeline), against the canvas's horizontal center. | | Transform > Align > **Align Right** | | Moves the selection, or the content of the active cel (of each selected cel in the timeline), against the canvas's right edge. | | Transform > Align > **Align Top** | | Moves the selection, or the content of the active cel (of each selected cel in the timeline), against the canvas's top edge. | | Transform > Align > **Align Middle** | | Moves the selection, or the content of the active cel (of each selected cel in the timeline), against the canvas's vertical center. | | Transform > Align > **Align Bottom** | | Moves the selection, or the content of the active cel (of each selected cel in the timeline), against the canvas's bottom edge. | | Transform > Align > **Distribute Horizontally** | | Spaces the content of three or more selected cels evenly left to right, keeping the outermost two where they are. | | Transform > Align > **Distribute Vertically** | | Spaces the content of three or more selected cels evenly top to bottom, keeping the outermost two where they are. | | Transform > **Iso Left** | | Projects the selection, or the whole active cel, onto the left face of an isometric cube. | | Transform > **Iso Right** | | Projects the selection, or the whole active cel, onto the right face of an isometric cube. | | Transform > **Iso Floor** | | Turns the selection, or the whole active cel, into an isometric floor diamond. | | **Command Palette...** | `Ctrl+K` | Opens a search box where you can find and run any command by name. | | Cel > **Cut Cels** | | Copies the cels selected in the timeline, or the active cel, then clears them. | | Cel > **Copy Cels** | | Copies the cels selected in the timeline, or the active cel, as whole cels. | | Cel > **Paste Cels** | | Pastes copied cels into the timeline, starting at the active cel. | | Cel > **Clear Cels** | | Empties the cels selected in the timeline, or the active cel. | | Cel > **Link Cels** | | Links the selected cels on each layer so they share the first cel's image. | | Cel > **Unlink Cels** | | Gives each selected linked cel its own copy of the image. | | Cel > **Move Cels Up** | | Moves the selected cels up one layer in the timeline. | | Cel > **Move Cels Down** | | Moves the selected cels down one layer in the timeline. | | Cel > **Move Cels Left** | | Moves the selected cels one frame earlier in the timeline. | | Cel > **Move Cels Right** | | Moves the selected cels one frame later in the timeline. | | Cel > **Select All Cels** | | Selects every cel in the timeline. | | Cel > **Tween...** | `Shift+T` | Opens the Tween inspector for the active cel's tween, or to tween it to the next keyframe on its layer (or to the last selected one), with a live preview. | | Cel > **Tween Through Keyframes** | | Tweens from the selected keyframe through every later keyframe on its layer, one tween between each pair of neighbours, each landing on its next keyframe, in one step. | | Cel > **Set Tween End to Selection** | | Moves the end of the selected tween to the last frame in the cel selection on its layer; in-betweens outside the new range are removed (or kept as plain cels if you edited them). | | Cel > **Regenerate Tween** | | Redraws the in-between frames of the selected tweens from their start keyframes, keeping cels you edited by hand. | | Cel > **Revert to Tween** | | Puts the selected in-between cels you edited by hand back to what their tween generates. | | Cel > **Bake Tween** | | Turns the selected tweens into plain frames: the in-betweens stay as ordinary cels and the tween is removed. | | Cel > **Remove Tween** | | Removes the selected tweens and their generated in-betweens; cels you edited by hand stay as plain cels. | | Cel > **Copy Tween Settings** | | Copies the selected tween's settings (easing, motion, path and timing), without its end keyframe. | | Cel > **Paste Tween Settings** | | Applies copied tween settings to each selected keyframe, tweening it to its next keyframe on the layer. | ## Sprite | Item | Shortcut | What it does | | - | - | - | | **Properties...** | | Opens a dialog to rename the sprite, convert its color mode, and set its pixel ratio, transparent index and user data. | | **Version History...** | | Name the sprite as it is now to keep it on this device; with Sync, the cloud's versions too. Compare any of them with now, download one, or restore it (Undo brings back what was there). | | **Sprite Size...** | `Ctrl+Alt+I` | Opens a dialog to set a new width and height, resizing every cel with nearest-neighbor scaling. | | **Canvas Size...** | `Alt+C` | Opens a dialog to resize the canvas and pick where existing art is anchored, without scaling it. | | **Rotate 90° CW** | | Rotates the whole canvas 90° clockwise, across every layer and frame. | | **Rotate 90° CCW** | | Rotates the whole canvas 90° counterclockwise, across every layer and frame. | | **Flip Canvas Horizontal** | | Mirrors the whole canvas left to right: every layer and frame, plus slices and the selection. | | **Flip Canvas Vertical** | | Mirrors the whole canvas top to bottom: every layer and frame, plus slices and the selection. | | **Crop to Selection** | | Crops the canvas to the bounds of the current selection. | | **Trim** | | Crops the canvas to the smallest box that holds every non-transparent pixel in every layer and frame. | | **Auto-slice...** | | Opens a dialog that adds a slice around each separate painted region of the frame or the active cel, with an optional gap to merge nearby regions and a minimum size. | | **Slice Properties...** | | Opens a dialog to edit a slice's name, bounds, 9-slice center and pivot. | | **Duplicate Sprite** | | Makes a copy of this sprite as a new sprite in the project. | | **Convert to Indexed Color...** | | Opens a dialog that converts the sprite to indexed color: keep the palette or generate one, with optional dithering. | | **Convert to Grayscale** | | Converts the sprite to grayscale, replacing every color with its brightness. | | **Convert to RGB Color** | | Converts the sprite to RGBA color, where each pixel stores its own color. | | **Color Depth...** | | Pins the sprite to a hardware color profile (Mega Drive 9-bit, RGB565, 2-bit grayscale...): its colors fold into it, and everything drawn later stays on it. | | Palette > **Palette Audit...** | | Lists the colours in the artwork that aren't in the palette, with how many pixels use each: add them to the palette or remap them to a palette colour. | | Palette > **Clean Up Palette...** | | Opens a dialog that merges duplicate and similar palette colours and removes colours no pixel uses. | | Palette > **Palette Lock** | | RGBA sprites: everything you draw snaps to the nearest palette colour, so the artwork stays on-palette. Saved with the file. | ## Layer | Item | Shortcut | What it does | | - | - | - | | **Layer Properties...** | | Opens a dialog to edit the layer's name, opacity, blend mode, visibility, locks and user data. | | **Layer Effects...** | | Opens the active layer's effects: non-destructive adjustments, outlines and shadows that are shown over its pixels without changing them. | | **Bake Layer Effects** | | Writes the active layer's effects into its pixels and removes them, so they can no longer be changed. | | **Visible** | | Shows or hides the selected layers. | | **New Layer** | `Shift+N` | Adds a new blank layer above the active layer and makes it active. | | **New Group** | | Adds a new empty group above the active layer to hold and organize layers. | | **New Adjustment Layer** | | Adds a layer with no pixels of its own: its effects (levels, gradient map, noise, vignette, ...) change everything below it, on every frame. | | **New Reference Layer...** | | Adds an image from your computer as a reference layer to trace over. | | **New Tilemap Layer...** | | Opens a dialog to create a tilemap layer, choosing its name, tile size and a new or shared tileset. | | Tilemap > **Tiles Mode** | | Switches the tilemap layer to placing whole tiles, selecting the Tile Stamp tool. | | Tilemap > **Pixels Mode (Auto Tiles)** | | Switches to painting pixels where painted cells reuse or create tiles automatically. | | Tilemap > **Pixels Mode (Edit Tileset)** | | Switches to painting pixels directly into existing tiles, updating every place each tile is used. | | Tilemap > **Toggle Tiles/Pixels** | `Alt+T` | Switches the tilemap layer between placing tiles and your last-used pixel mode. | | Tilemap > **Convert to Tilemap...** | | Opens a dialog to turn the active image layer into a tilemap layer, choosing tile size and tile sharing. | | Tilemap > **Convert to Normal Layer** | | Converts the tilemap layer into a regular image layer, flattening its tiles into pixels on every frame. | | Tilemap > **Tileset Properties...** | | Opens a dialog to edit the active tilemap's tileset name and base index. | | Tilemap > **Import Tiles from Image...** | | Opens a dialog to slice an image, the current frame or the selection into tiles, skipping empty or duplicate ones. | | **Delete Layer** | `Ctrl+Alt+Shift+L` | Deletes the selected layers and everything painted on them; deleting a group removes its contents too. | | **Duplicate Layer** | | Copies the selected layers with all their cels, placing each copy directly above the original. | | **Move Layer Up** | | Moves the active layer one step up within its group. | | **Move Layer Down** | | Moves the active layer one step down within its group, never below the Background. | | **Flip Layer Horizontal** | | Mirrors the active layer, or every layer in the group, left to right on every frame. | | **Flip Layer Vertical** | | Mirrors the active layer, or every layer in the group, top to bottom on every frame. | | **Group Layers** | `Ctrl+G` | Puts the selected layers into a new group at the topmost layer's position, keeping their order. | | **Ungroup** | `Ctrl+Shift+G` | Removes the active group and moves its layers out, dropping the group's opacity, blend mode and visibility. | | **Clipping Mask** | `Ctrl+Alt+G` | Clips the selected layers to the nearest unclipped layer below, so they only show where it has pixels; again to release them. | | **Draft Layer** | | Marks the selected layers as draft layers: you still see them while you work, but export, Flatten and Stamp Visible leave them out; again to make them ordinary layers. | | **Solo Layer** | | Hides every other layer so only the active layer (with its group) shows; again to bring the others back. Alt-click a layer's eye does the same. | | **Merge Down** | `Ctrl+E` | Merges the active layer or group into the image layer below it, then deletes the merged layer. | | **Merge Up** | | Merges the active image layer into the layer above it: the result looks the same and takes the upper layer's name and colour. | | **Merge Selected Layers** | | Merges the selected layers (all in one group) into one layer at the topmost one's position, exactly as they look together. Hidden ones are left alone. | | **Stamp Visible** | `Ctrl+Alt+Shift+E` | Adds a new layer holding everything visible, flattened, per frame, and keeps every layer as it is. Draft and reference layers are left out. | | **Flatten Group** | | Turns the active group into one image layer with the same name, opacity and blend mode, holding everything in it as it looks. Hidden, draft and reference layers inside are deleted. | | **Flatten Sprite** | | Merges all visible layers into one; hidden, draft and reference layers and groups are deleted. | | **Background from Layer** | | Turns the active layer into the Background: moves it to the bottom and fills transparency with the background color. | | **Layer from Background** | | Turns the Background into a regular layer that can have transparency and be moved again. | | **Reference Transform...** | | Opens a dialog to set the reference image's position and size on the current frame, or clear it. | ## Frame | Item | Shortcut | What it does | | - | - | - | | **Frame Properties...** | | Opens a dialog to set how long the current frame is shown, in milliseconds, plus its colour label and lock. | | **New Empty Frame** | `Ctrl+Shift+F` | Adds a new empty frame right after the current frame. | | **New Empty Frame Before** | | Adds a new empty frame right before the current frame, which is how to put one ahead of frame 1. | | **New Frame** | `Alt+N` | Copies the selected frames and their cels, placing the copy right after them. | | **New Linked Frame** | | Copies the selected frames right after them, with cels linked so edits show in both. | | **Delete Frame** | `Ctrl+Alt+Shift+F` | Deletes the selected frames and everything on them; the last remaining frame can't be deleted. | | **Reverse Frames** | | Reverses the order of the selected frames, or every frame if fewer than two are selected. | | **Play/Pause** | `Enter` | Starts or stops playing the animation. | | **First Frame** | `Home` | Jumps to the first frame. | | **Previous Frame** | | Steps back to the previous frame, from the first frame round to the last. | | **Next Frame** | | Steps forward to the next frame, from the last frame round to the first. | | **Last Frame** | `End` | Jumps to the last frame. | | **Set All Frames to Current Duration...** | | Gives every frame the current frame's duration, after asking you to confirm. | | **New Tag...** | | Opens a dialog to create an animation tag over the selected frames, with name, color and playback direction. | ## Select | Item | Shortcut | What it does | | - | - | - | | **Select All** | `Ctrl+A` | Selects the whole canvas. | | **Deselect** | `Ctrl+D` | Clears the current selection. | | **Reselect** | `Ctrl+Shift+D` | Brings back the selection you last cleared. | | **Inverse Selection** | `Ctrl+Shift+I` | Flips the selection so everything that was unselected becomes selected, and the reverse. | | **Quick Mask** | | Turns Quick Mask on or off: while it's on, you paint the selection with the paint tools (the eraser removes), and when you turn it off what you painted becomes the selection. | | **Select Opaque** | | Selects every painted pixel of the active layer (every layer inside it for a group). Alt+click a layer in the timeline does the same for that layer. | | **Select Transparent** | | Selects every pixel of the active layer that has no paint. | | Modify > **Expand...** | | Grows the selection outward by a number of pixels, with round or square corners. | | Modify > **Contract...** | | Shrinks the selection inward by a number of pixels; parts thinner than that disappear. | | Modify > **Border...** | | Keeps only a band of the given width along the inside edge of the selection. | ## Filter | Item | Shortcut | What it does | | - | - | - | | **Brightness/Contrast...** | | Opens a dialog that adjusts brightness and contrast, with a live preview. | | **Hue/Saturation...** | | Opens a dialog that shifts hue, saturation, lightness and alpha, with a live preview. | | **Color Curve...** | | Opens a dialog where you draw curves to remap the value, each color channel or alpha. | | **Levels...** | | Opens a dialog that sets the black, white and midtone points of each channel, with a live preview. | | **Threshold...** | | Opens a dialog that turns every pixel one of two colours by its brightness, with a live preview. | | **Gradient Map...** | | Opens a dialog that recolours the art by brightness through a gradient, with a live preview. | | **Color Balance...** | | Opens a dialog that shifts the colour of the shadows, midtones and highlights, with a live preview. | | **Invert Color...** | `Ctrl+I` | Opens a dialog that inverts the red, green, blue or alpha channels you choose. | | **Replace Color...** | | Opens a dialog that swaps one color for another or for transparency, within a tolerance. | | **Posterize...** | | Opens a dialog that reduces each color channel to fewer levels, with a live preview. | | **Reduce Colors...** | | Opens a dialog that maps the art onto a few generated colours or the palette, optionally dithered, with a live preview. | | **Outline...** | | Opens a dialog that adds a colored outline inside or outside shapes, with a live preview. | | **Drop Shadow\...** | | Opens a dialog that adds a drop shadow, glow or inner shadow, with a live preview. | | **Anti-Alias...** | | Opens a dialog that smooths the stair steps where two colors meet, pixel-art style, with a live preview. | | **Auto-Shade...** | | Opens a dialog that shades flat colours from a light direction, with highlights on the lit side and shadows on the other, with a live preview. | | **Despeckle...** | | Opens a dialog that removes stray pixels with a median filter, with a live preview. | | **Convolution Matrix...** | | Opens a dialog that applies a blur, sharpen or custom kernel filter, with a live preview. | ## View | Item | Shortcut | What it does | | - | - | - | | **Zoom In** | `Ctrl+=` | Zooms the current editor view in one step. | | **Zoom Out** | `Ctrl+-` | Zooms the current editor view out one step. | | **Actual Size** | `Ctrl+0` | Sets the current editor view to 100% zoom, one screen pixel per sprite pixel. | | **Fit to Window** | `Ctrl+Shift+0` | Zooms and centers the current editor view so the whole sprite fits with a small margin. | | **Rotate View 15° Clockwise** | `Ctrl+Alt+]` | Turns the current editor view 15° clockwise without changing the sprite itself. | | **Rotate View 15° Counterclockwise** | `Ctrl+Alt+[` | Turns the current editor view 15° counterclockwise without changing the sprite itself. | | **Reset Rotation** | `Ctrl+Alt+0` | Puts the current editor view back upright, undoing any view rotation. | | Grid > **Show Grid** | `Ctrl+'` | Shows or hides the sprite's grid in every editor view; the choice is saved on this device. | | Grid > **Show Pixel Grid** | `Ctrl+Shift+'` | Shows or hides thin lines between pixels in the current editor view when zoomed in. | | Grid > **Snap to Grid** | `Shift+S` | Turns snapping on or off so tools line up with the grid; the choice is saved on this device. | | Grid > **Isometric Grid** | | Switches this sprite's grid between square and isometric cells, and shows the grid when turning isometric on. | | Grid > **Hexagonal Grid** | | Switches this sprite's grid between square and hexagonal cells (pointy-top at first; flip the orientation in Grid Settings), and shows the grid when turning hexagonal on. | | Grid > **Grid Settings...** | | Opens Grid Settings to set the sprite's grid type, cell size and offset, plus grid color and opacity. | | Guides > **Show Rulers** | `Ctrl+R` | Shows or hides rulers along the top and left of every editor view; drag out of a ruler to add a guide. | | Guides > **Show Guides** | `Ctrl+;` | Shows or hides the sprite's guides in every editor view; hidden guides don't snap. | | Guides > **Snap to Guides** | `Ctrl+Shift+;` | Turns snapping to guides on or off for shapes, lines, selections, slices, gradients and the Move tool. | | Guides > **Lock Guides** | `Ctrl+Alt+;` | Locks the guides so the Move tool can't drag or remove them; the choice is saved on this device. | | Guides > **New Guide...** | | Adds a guide at an exact position, across or down the sprite. | | Guides > **Clear Guides** | | Removes every guide from this sprite, in one undoable step. | | Perspective Grid > **Perspective Grid** | | Shows or removes a perspective grid on this sprite: rays fanning out of vanishing points, as a drawing aid. Drag the points with the Move tool. | | Perspective Grid > **Perspective Grid Settings...** | | Opens Perspective Grid Settings to choose a 1-, 2- or 3-point grid or a fisheye grid, and set its points or centre and radius, and the number of lines. | | **Tile Grid** | | Shows or hides tile boundaries over the canvas while a tilemap layer is active. | | **Show Brush Preview** | | Shows or hides an outline of the brush's shape and size that follows the pointer. | | **Show Canvas Info** | | Shows or hides a readout in a corner of the canvas with the pointer position, tool, frame and more. | | Tiled Mode > **Both Axes** | | Repeats the sprite around itself in both directions in this view, for drawing seamless patterns. | | Tiled Mode > **Horizontal Only** | | Repeats the sprite to its left and right in this view, for drawing seamless horizontal patterns. | | Tiled Mode > **Vertical Only** | | Repeats the sprite above and below itself in this view, for drawing seamless vertical patterns. | | **Show Onion Skin** | | Shows or hides faded neighboring frames around the current one in this view, to help with animation. | | **New Editor** | | Opens another editor view, with its own zoom, rotation and display options. | | Panels > **Menu Bar** | | Shows or hides the menu bar panel. | | Panels > **Tools** | | Shows or hides the Tools panel. | | Panels > **Tool Options** | | Shows or hides the tool options bar, where you adjust the current tool's settings. | | Panels > **Timeline** | | Shows or hides the Timeline panel with layers, frames and tags. | | Panels > **Preview** | | Shows or hides the Preview panel. | | Panels > **Tileset** | | Shows or hides the Tileset panel. | | Panels > **Palette** | | Shows or hides the Palette panel. | | Panels > **Color Picker** | | Shows or hides the Color Picker panel. | | Panels > **Foreground/Background Colors** | | Shows or hides the panel with the foreground and background colors. | | Panels > **Scripts** | | Shows or hides the Scripts panel, the editor for the open Lua script. | | Workspace > _The built-in layouts, then the ones you saved: choose one to switch to it._ | | | | Workspace > **Save Layout As...** | | Saves the current panel arrangement as a named layout, optionally with settings to bring along. | | Workspace > **Update Current Layout** | | Overwrites the active saved layout with your current panel arrangement and chosen settings. | | Workspace > **Reset Layout** | | Puts panels back the way the active layout was saved, undoing moves and resizes. | | Workspace > **Manage Layouts...** | | Opens the Layouts dialog to rename, delete, export and import your saved layouts. | | Workspace > **Lock Layout** | | Locks or unlocks the layout so panels can't be dragged or docked by accident. | | **Show Keystrokes** | | Shows or hides an on-screen display of the keys you press, handy for recording tutorials. | ## Scripts Scripts can add their own items to this menu, and to others (see [Scripting](https://pxstud.io/docs/scripting/dialogs.md)). | Item | Shortcut | What it does | | - | - | - | | **New Script...** | | Opens a dialog to name a new script and start writing it. | | **Open My Script...** | | Opens a list of your scripts to load one into the script panel, or rename or delete it. | | _Scripts you opened recently._ | | | | **Browse Script Library...** | | Opens the script library to search, run, fork or heart public and official scripts. | | **Script Commands...** | | Installs script commands from your scripts so they appear in menus and can have shortcuts. | | **Scripting Reference** | | Opens the scripting reference in a new browser tab. | ## Settings | Item | Shortcut | What it does | | - | - | - | | Zoom Snapping > **Off (Smooth)** | | Lets the canvas zoom smoothly, to any level between 1/64 and 64x. | | Zoom Snapping > **Whole Numbers** | | Zooms only to Aseprite's levels (1x, 2x, 3x, 4x, 5x, 6x, 8x, 12x ... 64x, and 1/2, 1/3 ... 1/64 below 100%), so every pixel is the same size on screen. | | Zoom Snapping > **Powers of Two** | | Zooms only to powers of two: 1/64 ... 1/2, 1x, 2x, 4x, 8x, 16x, 32x and 64x. | | **Draw with Finger** | | Lets a finger draw on a touch screen; turn it off to draw only with a pen or mouse, and pan with one finger. | | **Remember Brush per Tool** | | Lets each tool keep its own brush size, shape and ink; turn it off to share one brush between every tool. | | **Hold Tool Keys to Borrow** | | Tap a tool's key to switch to it; hold it for a moment to borrow the tool, and letting go returns you to the tool you had. | | **Mouse and Trackpad...** | | Opens the mouse and trackpad settings: what the wheel and the right button do, where zoom is anchored, the brush-size wheel's direction and the cursor size. | | **Keyboard Shortcuts...** | `?` | Opens the shortcut editor to view, change, reset, import or export keyboard shortcuts. | | **Onion Skin...** | | Opens onion skin settings: frame counts, opacity, tint, tag-only mode, and whether ghosts draw behind or in front. | | **Checkerboard...** | | Opens settings for the transparency checkerboard's square size and two colors. | | **Tool Wheel...** | | Opens the tool wheel's layout: drag tools into the slots of its two rings, or leave slots empty. | | **Keystrokes...** | | Turns the keystroke display on or off and chooses its corner on the window or canvas. | | **Canvas Info...** | | Turns the canvas info display on or off, chooses its corner, and picks what it shows. | ## Help Every item but the last opens a page of these docs in a new browser tab. | Item | Shortcut | What it does | | - | - | - | | **Manual** | | Opens the "Manual" docs page in a new browser tab. | | **Your First Sprite** | | Opens the "Your First Sprite" docs page in a new browser tab. | | **How Your Work Is Organized** | | Opens the "How Your Work Is Organized" docs page in a new browser tab. | | Guides > **The Workspace** | | Opens the "The Workspace" docs page in a new browser tab. | | Guides > **Layers** | | Opens the "Layers" docs page in a new browser tab. | | Guides > **Color and Palettes** | | Opens the "Color and Palettes" docs page in a new browser tab. | | Guides > **Selecting and Transforming** | | Opens the "Selecting and Transforming" docs page in a new browser tab. | | Guides > **Animation and the Timeline** | | Opens the "Animation and the Timeline" docs page in a new browser tab. | | Guides > **Tilemaps** | | Opens the "Tilemaps" docs page in a new browser tab. | | Guides > **Opening, Importing and Exporting** | | Opens the "Opening, Importing and Exporting" docs page in a new browser tab. | | Guides > **Keyboard, Accessibility and Touch** | | Opens the "Keyboard, Accessibility and Touch" docs page in a new browser tab. | | Guides > **Slices** | | Opens the "Slices" docs page in a new browser tab. | | Guides > **Resizing, Cropping and Effects** | | Opens the "Resizing, Cropping and Effects" docs page in a new browser tab. | | Guides > **The Library** | | Opens the "The Library" docs page in a new browser tab. | | Reference > **Tools** | | Opens the "Tools" docs page in a new browser tab. | | Reference > **Menus** | | Opens the "Menus" docs page in a new browser tab. | | Reference > **Keyboard Shortcuts** | | Opens the "Keyboard Shortcuts" docs page in a new browser tab. | | Reference > **File Formats** | | Opens the "File Formats" docs page in a new browser tab. | | Reference > **Tool Options** | | Opens the "Tool Options" docs page in a new browser tab. | | Reference > **Settings** | | Opens the "Settings" docs page in a new browser tab. | | Scripting > **Your First Script** | | Opens the "Your First Script" docs page in a new browser tab. | | Scripting > **Scripting Guide** | | Opens the "Scripting Guide" docs page in a new browser tab. | | Scripting > **Scripting API** | | Opens the scripting reference in a new browser tab. | | **Keyboard Quick Reference** | | Opens the "Keyboard Quick Reference" docs page in a new browser tab. | | **FAQ** | | Opens the "FAQ" docs page in a new browser tab. | | _In a browser that can install it: Install App..., which installs the editor as an app (on iPhone and iPad, it explains how to add it to the Home Screen)._ | | | | _In the desktop app: Check for Updates..., which downloads a newer version._ | | | | **About pxstud.io...** | | Shows what this editor is, and links to its website, documentation and open-source licenses. | # Keyboard shortcuts The default keyboard shortcut for every tool and command, and how to change them. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ These are the default shortcuts. **Mod** is Ctrl on Windows and Linux, and ⌘ on a Mac; this page shows them for your system. Change any of them in Settings > Keyboard Shortcuts..., where you can also export your set and import it in another browser. The [keyboard quick reference](https://pxstud.io/quick-reference) has the default list on one printable page, with a practice mode. Shortcuts you've changed are listed in Settings > Keyboard Shortcuts.... A few keys are worth knowing first: - Pressing a tool's key again switches to the next tool on the same toolbar button. - Holding `Alt` over the canvas picks a color with the Eyedropper until you let go, and holding `Space` pans with a drag, whatever the tool. - The arrow keys work as in Aseprite: with a selection they move its pixels (`Shift`: one grid cell); without one, `←` and `→` step through the frames (going round at the ends) and `↑` and `↓` through the layers. `,` and `.` loop inside the current tag. With the canvas focused from the keyboard, the arrows draw instead ([keyboard drawing](https://pxstud.io/docs/manual/guides/accessibility.md#drawing-with-the-keyboard)). - Undo and redo go to the frame and layer they changed, so you see what they did. - `Tab` only ever moves between controls; it's never a shortcut. - `?` (whichever keys type a question mark on your keyboard) opens Settings > Keyboard Shortcuts.... - Number fields take maths: type `32*2`, `(16+8)*2` or `2^5` and press `Enter` (or leave the field) for the result. They accept + − × ÷, `^` (power), `%` (remainder), `!` (factorial) and brackets, with `.` or `,` for decimals. `↑` and `↓`, or the mouse wheel over the focused field, step the value (`Shift`, `PageUp` and `PageDown`: by 10). - Number fields also scrub: drag sideways from a field's label or its small arrows to change the value (`Shift` for fine steps, `Alt` for big ones, `Esc` puts it back); clicking the number itself still edits it. Sizes in sprite pixels, such as the brush size and the line width, stop at the sprite's size. - Browsers keep some keys for themselves, so a few commands use another: a new sprite is `Ctrl+Alt+N`, not Mod+N. - A command can also run from a mouse button (middle, right, back or forward) or a wheel direction, with or without modifier keys: in Settings > Keyboard Shortcuts..., click **Add**, then click that button or turn the wheel on the field. They act over the canvas, where a binding replaces what the button or wheel does there. ## Tools | Tool | Shortcut | | - | - | | Rectangular Selection | `M` | | Elliptical Selection | `Shift+M` | | Lasso Selection | `Q` | | Polygonal Lasso Selection | `Shift+Q` | | Magic Wand | `W` | | Pencil | `B` | | Spray | `Shift+B` | | Scatter | `Alt+Shift+B` | | Dither | none | | Eraser | `E` | | Eyedropper | `I` | | Zoom | `Z` | | Hand | `H` | | Move | `V` | | Slice | `Shift+C` | | Paint Bucket | `G` | | Gradient | `Shift+G` | | Tile Stamp | none | | Rectangle | `U` | | Ellipse | `Shift+U` | | Triangle | none | | Hexagon | none | | Star | none | | Iso Shape | `K` | | Contour | `D` | | Polygon | `Shift+D` | | Blur | `R` | | Jumble | none | | Dodge/Burn | `O` | | Line | `L` | | Curve | `Shift+L` | | Ruler | `Shift+R` | | Text | `T` | ## File | Command | Shortcut | | - | - | | New Sprite... | `Ctrl+Alt+N` | | Open... | `Ctrl+O` | | Save | `Ctrl+S` | | Save As... | `Ctrl+Shift+S` | | Export... | `Ctrl+Alt+Shift+S` | | Close | `Ctrl+Alt+W` | ## Edit | Command | Shortcut | | - | - | | Undo | `Ctrl+Z` | | Redo | `Ctrl+Shift+Z` or `Ctrl+Y` | | Cut | `Ctrl+X` | | Copy | `Ctrl+C` | | Paste | `Ctrl+V` | | Delete | `Del` or `Backspace` | | Fill | `F` | | Flip Horizontal | `Shift+H` | | Flip Vertical | `Shift+V` | | Command Palette... | `Ctrl+K` | | Cel > Tween... | `Shift+T` | ## Sprite | Command | Shortcut | | - | - | | Sprite Size... | `Ctrl+Alt+I` | | Canvas Size... | `Alt+C` | ## Layer | Command | Shortcut | | - | - | | New Layer | `Shift+N` | | Tilemap > Toggle Tiles/Pixels | `Alt+T` | | Delete Layer | `Ctrl+Alt+Shift+L` | | Group Layers | `Ctrl+G` | | Ungroup | `Ctrl+Shift+G` | | Clipping Mask | `Ctrl+Alt+G` | | Merge Down | `Ctrl+E` | | Stamp Visible | `Ctrl+Alt+Shift+E` | ## Frame | Command | Shortcut | | - | - | | New Empty Frame | `Ctrl+Shift+F` | | New Frame | `Alt+N` | | Delete Frame | `Ctrl+Alt+Shift+F` | | Play/Pause | `Enter` | | First Frame | `Home` | | Last Frame | `End` | | Next Frame in Tag | `.` | | Previous Frame in Tag | `,` | ## Select | Command | Shortcut | | - | - | | Select All | `Ctrl+A` | | Deselect | `Ctrl+D` | | Reselect | `Ctrl+Shift+D` | | Inverse Selection | `Ctrl+Shift+I` | | Move Selection Down | `↓` | | Move Selection Down One Cell | `Shift+↓` | | Move Selection Left | `←` | | Move Selection Left One Cell | `Shift+←` | | Move Selection Right | `→` | | Move Selection Right One Cell | `Shift+→` | | Move Selection Up | `↑` | | Move Selection Up One Cell | `Shift+↑` | ## Filter | Command | Shortcut | | - | - | | Invert Color... | `Ctrl+I` | ## View | Command | Shortcut | | - | - | | Zoom In | `Ctrl+=` | | Zoom Out | `Ctrl+-` | | Actual Size | `Ctrl+0` | | Fit to Window | `Ctrl+Shift+0` | | Rotate View 15° Clockwise | `Ctrl+Alt+]` | | Rotate View 15° Counterclockwise | `Ctrl+Alt+[` | | Reset Rotation | `Ctrl+Alt+0` | | Grid > Show Grid | `Ctrl+'` | | Grid > Show Pixel Grid | `Ctrl+Shift+'` | | Grid > Snap to Grid | `Shift+S` | | Guides > Show Rulers | `Ctrl+R` | | Guides > Show Guides | `Ctrl+;` | | Guides > Snap to Guides | `Ctrl+Shift+;` | | Guides > Lock Guides | `Ctrl+Alt+;` | ## Settings | Command | Shortcut | | - | - | | Keyboard Shortcuts... | `?` | ## Colors | Command | Shortcut | | - | - | | Swap Foreground/Background Color | `X` | ## Mouse and trackpad What the mouse does on the canvas with the default [Mouse and Trackpad settings](https://pxstud.io/docs/manual/guides/workspace.md#zoom-pan-and-rotate). | Command | Shortcut | | - | - | | Pan the view (drag) | `Middle button` | | Zoom (or pinch a trackpad) | `Wheel` | | Scroll sideways | `Shift+Wheel` | | Change the brush size | `Ctrl+Shift+Wheel` | | The tool's right-click (most paint with the background color) | `Right button` | | Open the tool wheel (hold) | `Back button` or `Forward button` | # File formats Which formats pxstud.io opens and exports, and what each one keeps. Open files with File > Open... (or drop them on the editor) and export with File > Export.... How to use each option is in [Opening, importing and exporting](https://pxstud.io/docs/manual/guides/import-export.md). | Format | Opens | Exports | Animation | Layers | | - | - | - | - | - | | **pxstud.io** (`.pxstudio`) | yes | with File > Save As... | yes | everything is kept | | **Aseprite** (`.aseprite`, `.ase`) | yes | yes | yes | layers, groups, linked cels, tags, slices, tilemaps and the palette | | **PNG** | one image | one image, or one per frame | as separate files | flattened | | **APNG** (animated PNG, `.png` or `.apng`) | yes, with each frame's timing | animated | yes | flattened | | **GIF** | yes, with each frame's timing | animated | yes | flattened | | **WebP** | still or animated | still or animated | yes | flattened | | **JPEG** | yes (no transparency) | one image per frame | no | flattened | | **AVIF** | one image | one image per frame (lossless by default) | no | flattened | | **BMP**, **TGA**, **PCX** | one image | one image per frame | no | flattened | | **QOI** | one image | one image per frame | no | flattened | | **ICO** (Windows icon) | the largest image in the file | one icon per frame, up to 256×256, optionally with every larger whole-number size | no | flattened | | **CUR** (Windows cursor) | the largest image, with its click point | like ICO, with the click point | no | flattened | | **Netpbm** (`.pbm`, `.pgm`, `.ppm`, `.pnm`, `.pam`) | one image | one image per frame (`.pgm`, `.ppm`, or `.pam` with transparency) | no | flattened | | **Photoshop** (`.psd`, `.psb`) | yes | the active frame | no | layers, groups, blend modes, opacity, visibility, clipping | | **OpenRaster** (`.ora`) | yes | the active frame | no | layers, groups, blend modes, opacity, visibility | | **Piskel** (`.piskel`) | yes | yes | yes | layers (groups flattened on export) | | **Pixilart** (`.pixil`) | yes | no | yes | layers | | **Pixelorama** (`.pxo`, version 1.0 and later) | yes | no | yes, with tags | layers, groups, tilemaps, linked cels | | **Pyxel Edit** (`.pyxel`) | yes | no | animations become frames and tags | layers | | **FLI/FLC** (Autodesk Animator) | yes | yes | yes | flattened, up to 256 colors | | **SVG** | through Import as pixel art | one image per frame | no | flattened | | **MP4 video** | no | where the browser can make video | yes | flattened | | **Sprite sheet** (PNG + JSON) | with File > Import > Sprite sheet... | yes | frames in one image | can split layers and tags into rows | | **Godot 4 SpriteFrames** (PNG + `.tres`) | no | yes, as a sprite sheet's data file | one animation per tag | can split layers | | **Tilemap** (Tiled `.tmj`/`.tsj`, JSON) | no | yes | JSON: every frame; Tiled: one frame | tilemap layers only | | **Palettes** (`.gpl`, `.pal`, `.hex`, `.act`, `.aco`, `.ase`, …) | in the [Palette Library](https://pxstud.io/docs/manual/guides/color.md#the-palette-library) | in the Palette Library | | | **Size limits.** Animated formats and Aseprite files open up to 4096 pixels on a side. **Opening images.** An image opens as a sprite with one layer. Indexed images (GIF, FLC, 8-bit PNG, BMP, TGA, PCX, ICO, indexed Photoshop files) open as indexed sprites with their palette (an 8-bit PNG's partly transparent colors included); grayscale images (PGM, PBM, gray PNG, gray Photoshop files) open as grayscale sprites; a GIF whose frames have different palettes keeps a palette per frame. A PNG that is animated (an APNG) opens with all its frames and their timings, whether it's named `.png` or `.apng`. **Files from other editors.** Photoshop, OpenRaster (Krita, GIMP, MyPaint), Piskel, Pixilart, Pixelorama and Pyxel Edit files open with their layers. Anything that doesn't carry over is listed in a notice: - A blend mode pxstud.io doesn't have becomes Normal. - Photoshop's Pass Through groups open as pass-through groups and its other groups as isolated ones, and export the same way. A Pass Through group below 100% opacity can only be drawn isolated: when a layer inside uses another blend mode, the notice says it may look different. - OpenRaster groups keep their isolation (`isolation="isolate"` is an isolated group). In Pixelorama, a group set to Pass Through opens pass-through and any other group isolated. - Photoshop layer masks are applied to the layer's pixels. Adjustment layers, text and effects come in as their pixels, or not at all. - Pixelorama tilemap layers open as tilemap layers: layers that share a tileset still share it, and flipped and transposed tiles keep their transform. A tilemap with isometric, hexagonal or half-offset tiles, or one the square tile grid can't hold, opens as an ordinary layer of its pixels. An indexed Pixelorama project opens as an indexed sprite, with its palette's colors in the same slots; if the file's indices and colors don't agree it opens as RGBA. Linked cels stay linked, and guides and user data (project, layer, cel, tag and tile notes, layer and cel colors) come along. - Pixelorama's layer effects, 3D and audio layers and frame user data are skipped (the notice names the layers). Files from before Pixelorama 1.0 need saving again in a newer Pixelorama. - A Pyxel Edit file with animations opens at tile size, with one frame per animation cell and a tag per animation; the rest of the canvas isn't kept. - Piskel keeps one frame rate for the whole file, so on export it uses the most common frame duration. Its export also leaves out hidden layers and has no groups or blend modes. **Icons and cursors.** An ICO or CUR file holds one image per size; pxstud.io opens the largest. A cursor's click point (its hotspot) comes in as a slice named `Hotspot` with its pivot on that pixel. Exporting a cursor uses the pivot of a slice named `hotspot`, or the top-left pixel when there is none. **FLC timing.** FLC players show every frame for the same time, so pxstud.io's FLC export repeats a frame as many times as its duration needs (as Aseprite does), and opening an FLC joins repeated frames back into one. Two neighboring frames that look the same become one frame either way. **APNG or GIF?** An animated PNG keeps every color, soft transparency and exact frame timings, and plays in every current browser. GIF has at most 256 colors, only fully clear or fully solid pixels, and timings in 10 ms steps, but almost everything can show it. Apps without APNG support show an animated PNG's first frame. **Aseprite files** keep almost everything. When opening one, a notice lists what couldn't be kept: an embedded color profile (pxstud.io works in sRGB), a blend mode pxstud.io doesn't have (set to Normal), a tilemap layer that isn't on its tile grid (opened as an image layer), a reference layer that shows different images on different frames (the first is kept), or a damaged cel or an unknown kind of layer (left out, as Aseprite does, while the rest of the file opens). Aseprite has no clipping masks. pxstud.io saves a layer's clipping mask in the `.aseprite` file as a property under its own `pxstudio` extension name, which Aseprite keeps but ignores. The layer draws unclipped in Aseprite and is clipped again when the file is reopened in pxstud.io. Aseprite isolates either every group or none: groups are pass-through unless **Compose groups separately** (in its Experimental preferences) is on. When a sprite has an isolated group, pxstud.io's `.aseprite` file asks for composed groups (and keeps the groups' opacity and blend modes), and marks each group that isn't isolated with `isolated: false` under the `pxstudio` extension name. Aseprite then draws those groups isolated too when that option is on, while pxstud.io reopens them pass-through. A file with composed groups from Aseprite opens with every group isolated. # Tool options Every option in the tool options bar: which tools show it, its values and its default. The tool options bar above the canvas shows the active tool's options. Change one and it stays that way as you switch tools and back; options that several tools share, like the brush size, are the same for all of them. Options last until you reload the page, unless you save them in a workspace layout (View > Workspace > Save Layout As..., with "Tool defaults" checked). Where the bar is narrow its options wrap onto more rows, or on a phone scroll sideways. What each tool does with them is in the [Tools](https://pxstud.io/docs/manual/reference/tools.md) reference. ## With every tool These sit at the right-hand end of the bar whatever tool is active (Paint across cels only with the painting tools). Each is an on/off button; the small arrow next to it opens its settings. | Option | Values | Default | What it does | | - | - | - | - | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | | **Symmetry** | Horizontal, vertical and the two diagonals, in any combination | None | Mirrors what you draw across the chosen axes. The arrow next to it picks the axes and recenters them; drag an axis on the canvas to move it. Each sprite keeps its own. | | **Grid** | On or off; Snap to grid, Isometric, Grid settings | Off | Shows the sprite's grid. The arrow next to it turns on snapping, switches to an isometric grid and opens the grid settings. | | **Tiled view** | Off, both axes, horizontal, vertical | Off | Repeats the sprite around itself to check that it tiles; each click steps to the next mode. | | **Onion skinning** | On or off, with its settings | Off | Shows the frames before (red) and after (blue) the current one, faded. The arrow next to it holds the onion skin settings. | While a tilemap layer is active, the bar starts with its mode and the tile grid. See [Tilemaps](https://pxstud.io/docs/manual/guides/tilemaps.md). | Option | Values | Default | What it does | | - | - | - | - | | **Tilemap** | Tiles, Pixels: Auto, Pixels: Edit | Pixels: Auto | How tools treat the tilemap layer: Tiles places whole tiles, Pixels: Auto gives each painted cell its own tile, Pixels: Edit changes the tile everywhere it's used. While a tilemap layer is active. | | **Tile grid** | On or off | On | Shows the tile boundaries over the canvas. While a tilemap layer is active. | ## By tool ### Rectangular Selection | Option | Values | Default | What it does | | - | - | - | - | | **Radius** | 0 to 1024 px per corner | 0 px (square) | Rounds the corners; 0 keeps them square. With the link on, one value sets all four; turn it off to give each corner (top-left, top-right, bottom-right, bottom-left) its own. The Rectangle and the Rectangular Marquee keep separate radii. | ### Elliptical Selection No options of its own. ### Lasso Selection No options of its own. ### Polygonal Lasso Selection No options of its own. ### Magic Wand | Option | Values | Default | What it does | | - | - | - | - | | **Fill mode** | Contiguous, Global | Contiguous | Selects only the connected area you click, or every matching pixel on the layer. | | **Tolerance** | 0 to 255 | 0 | How different a color can be and still be selected (per color channel). Separate from the Paint Bucket's. | | **8-connected** | On or off | Off | Also spreads to diagonal neighbours, so the selection leaks through 1 px diagonal lines. Off, only up, down, left and right count as connected. With Contiguous. | | **Stop at grid** | On or off | Off | Keeps the selection inside the grid cell you click. While the grid is shown. | ### Pencil | Option | Values | Default | What it does | | - | - | - | - | | **Size** | 1 px to 64 px | 1 px | Brush size in pixels; the Spray's splash is twice as wide. A custom brush keeps its own size. | | **Brush shape** | Square, Circle, Line, Diamond, Cross, X, Star, Custom | Circle | The brush's footprint: a square, a circle, a 1-pixel line, a diamond, a cross, an X, a star, or the custom brush picked in the Brush Library. | | **Custom brushes** | The built-in brushes and yours | None | Opens the Brush Library to pick a built-in brush or draw and save your own to paint with. | | **Angle** | 0° to 180° | 0° | Turns the brush around its center. With the Line or Square brush. | | **Pixel-perfect** | On or off | Off | Removes the doubled corner pixels that make a freehand line look jagged. | | **Ink** | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Dynamics** | Stabilizer; pressure and speed mapped to size, angle and gradient | Pressure to size (pen only) | Smooths the stroke (Stabilizer) and lets pen pressure or speed vary the size, the angle, or the color from foreground to background. Pressure only comes from a pen: mouse and finger strokes ignore it. | | **Place tiles** | On or off | Off | Places the tile picked in the Tileset panel on the tilemap cells the tool covers (the Eraser empties them, the Bucket fills the connected cells holding the clicked tile) instead of painting pixels. Symmetry mirrors the tiles and a selection limits them. While a tilemap layer is active. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Spray | Option | Values | Default | What it does | | - | - | - | - | | **Size** | 1 px to 64 px | 1 px | Brush size in pixels; the Spray's splash is twice as wide. A custom brush keeps its own size. | | **Ink** | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Rate** | 1 to 64 | 8 | How many dots each burst adds. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Scatter | Option | Values | Default | What it does | | - | - | - | - | | **Size** | 1 px to 64 px | 1 px | Brush size in pixels; the Spray's splash is twice as wide. A custom brush keeps its own size. | | **Brush shape** | Square, Circle, Line, Diamond, Cross, X, Star, Custom | Circle | The brush's footprint: a square, a circle, a 1-pixel line, a diamond, a cross, an X, a star, or the custom brush picked in the Brush Library. | | **Custom brushes** | The built-in brushes and yours | None | Opens the Brush Library to pick a built-in brush or draw and save your own to paint with. | | **Angle** | 0° to 180° | 0° | Turns the brush around its center. With the Line or Square brush. | | **Ink** | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Scatter X** | 0 px to 64 px | 8 px | How far each stamp can land left or right of the path. | | **Scatter Y** | 0 px to 64 px | 8 px | How far each stamp can land above or below the path. | | **Count** | 1 to 16 | 2 | How many stamps land at each step along the path. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Dither | Option | Values | Default | What it does | | - | - | - | - | | **Size** | 1 px to 64 px | 1 px | Brush size in pixels; the Spray's splash is twice as wide. A custom brush keeps its own size. | | **Brush shape** | Square, Circle, Line, Diamond, Cross, X, Star, Custom | Circle | The brush's footprint: a square, a circle, a 1-pixel line, a diamond, a cross, an X, a star, or the custom brush picked in the Brush Library. | | **Custom brushes** | The built-in brushes and yours | None | Opens the Brush Library to pick a built-in brush or draw and save your own to paint with. | | **Angle** | 0° to 180° | 0° | Turns the brush around its center. With the Line or Square brush. | | **Pixel-perfect** | On or off | Off | Removes the doubled corner pixels that make a freehand line look jagged. | | **Ink** | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Dynamics** | Stabilizer; pressure and speed mapped to size, angle and gradient | Pressure to size (pen only) | Smooths the stroke (Stabilizer) and lets pen pressure or speed vary the size, the angle, or the color from foreground to background. Pressure only comes from a pen: mouse and finger strokes ignore it. | | **Pattern** | Bayer 2×2, Bayer 4×4, Bayer 8×8, Dots, Horizontal lines, Vertical lines, Diagonal lines, Noise | Bayer 2×2 | The pattern the brush paints through: Bayer checkers (2×2 coarsest, 8×8 finest), halftone dots, lines, or an even noise scatter. It lines up with the sprite's top-left corner, so strokes that overlap stay on one pattern. | | **Density** | 0% to 100% | 50% | How much of the pattern gets painted: 50% of Bayer 2×2 is a checkerboard, 100% paints every pixel. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Eraser | Option | Values | Default | What it does | | - | - | - | - | | **Size** | 1 px to 64 px | 1 px | Brush size in pixels; the Spray's splash is twice as wide. A custom brush keeps its own size. | | **Brush shape** | Square, Circle, Line, Diamond, Cross, X, Star, Custom | Circle | The brush's footprint: a square, a circle, a 1-pixel line, a diamond, a cross, an X, a star, or the custom brush picked in the Brush Library. | | **Custom brushes** | The built-in brushes and yours | None | Opens the Brush Library to pick a built-in brush or draw and save your own to paint with. | | **Angle** | 0° to 180° | 0° | Turns the brush around its center. With the Line or Square brush. | | **Pixel-perfect** | On or off | Off | Removes the doubled corner pixels that make a freehand line look jagged. | | **Right-click** | Paint BG, Replace FG | Paint BG | What the right button does: paint the background color, or turn only pixels of the foreground color into the background color. | | **Dynamics** | Stabilizer; pressure and speed mapped to size, angle and gradient | Pressure to size (pen only) | Smooths the stroke (Stabilizer) and lets pen pressure or speed vary the size, the angle, or the color from foreground to background. Pressure only comes from a pen: mouse and finger strokes ignore it. | | **Place tiles** | On or off | Off | Places the tile picked in the Tileset panel on the tilemap cells the tool covers (the Eraser empties them, the Bucket fills the connected cells holding the clicked tile) instead of painting pixels. Symmetry mirrors the tiles and a selection limits them. While a tilemap layer is active. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Eyedropper | Option | Values | Default | What it does | | - | - | - | - | | **Sample** | Composite, Layer | Composite | Picks the color you see from all visible layers, or from the active layer only. | ### Zoom No options of its own. ### Hand No options of its own. ### Move | Option | Values | Default | What it does | | - | - | - | - | | **Rotation algorithm** | Fast Rotation, RotSprite, Bilinear | RotSprite | How rotated and scaled pixels are resampled: Fast Rotation keeps them sharp, RotSprite rotates pixel art cleanly, Bilinear blends them smoothly. | | **Pivot** | A corner, an edge's middle, or the center | Center | Where the point everything turns and scales around starts; you can also drag it on the canvas. | ### Slice No options of its own. ### Paint Bucket | Option | Values | Default | What it does | | - | - | - | - | | **Ink** | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Place tiles** | On or off | Off | Places the tile picked in the Tileset panel on the tilemap cells the tool covers (the Eraser empties them, the Bucket fills the connected cells holding the clicked tile) instead of painting pixels. Symmetry mirrors the tiles and a selection limits them. While a tilemap layer is active. | | **Fill mode** | Contiguous, Global | Contiguous | Only the connected area you click, or every matching pixel on the layer. | | **Tolerance** | 0 to 255 | 0 | How different a color can be and still match (per color channel). | | **8-connected** | On or off | Off | Also spreads to diagonal neighbours, so the area leaks through 1 px diagonal lines. Off, only up, down, left and right count as connected. With Contiguous. | | **Stop at grid** | On or off | Off | Keeps the area inside the grid cell you click. While the grid is shown. | | **Close gap** | 0 px to 5 px | 0 px | Treats gaps in an outline up to this many pixels wide as closed, so the fill stops there instead of leaking out (the gap itself is filled). 0 is off. With Contiguous. | | **Pattern** | Solid, Checker, Dither 25%, Dither 75%, Horizontal stripes, Vertical stripes, Diagonal, Grid, Dots, Bricks | Solid | Fills with a repeating pattern of the foreground and background colors (swapped on right-click) instead of a solid color. Patterns line up from the canvas's top-left corner, so neighbouring fills match. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Gradient | Option | Values | Default | What it does | | - | - | - | - | | **Ink** | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Type** | Linear, Radial | Linear | A straight ramp along the drag, or rings around its start. | | **Colors** | FG → BG, Stops | FG → BG | Run from the foreground to the background color, or through the Stops. | | **Stops** | Up to 32 colors, each at a position along the drag | Empty | The colors the gradient runs through, left = where you start dragging: click the bar to add the foreground color, drag a handle to move its stop (meeting a neighbour makes a hard edge) or off the bar to remove it, drop palette colors on the bar or a handle; Even spreads them out, FG recolors the selected stop. Shift+click a palette range to load it. With fewer than two, it runs foreground to background. With Colors set to Stops. | | **Dither** | None, Bayer 2×2, Bayer 4×4, Bayer 8×8, Dots, Horizontal lines, Vertical lines, Diagonal lines, Noise | Bayer 4×4 | None blends smoothly; the others step between the colors in a pattern, for a pixel-art look: Bayer checkers (2×2 coarsest, 8×8 finest), halftone dots, lines, or an even noise scatter. | | **Snap** | Free, Palette | Free | Free blends any colors; Palette paints only palette colors, each pixel the nearest one, and with Dither steps between neighbouring palette colors. | | **Fill mode** | Contiguous, Global | Contiguous | Only the connected area you click, or every matching pixel on the layer. | | **Tolerance** | 0 to 255 | 0 | How different a color can be and still match (per color channel). | | **8-connected** | On or off | Off | Also spreads to diagonal neighbours, so the area leaks through 1 px diagonal lines. Off, only up, down, left and right count as connected. With Contiguous. | | **Stop at grid** | On or off | Off | Keeps the area inside the grid cell you click. While the grid is shown. | | **Close gap** | 0 px to 5 px | 0 px | Treats gaps in an outline up to this many pixels wide as closed, so the fill stops there instead of leaking out (the gap itself is filled). 0 is off. With Contiguous. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Tile Stamp | Option | Values | Default | What it does | | - | - | - | - | | **Tile** | Any tile in the layer's tileset | 1 | The tile being stamped (0 is the empty tile); pick it in the Tileset panel. | | **Flip H** | On or off | Off | Flips the stamped tile horizontally. | | **Flip V** | On or off | Off | Flips the stamped tile vertically. | | **Rot 90°** | On or off | Off | Rotates the stamped tile 90° clockwise. | ### Rectangle | Option | Values | Default | What it does | | - | - | - | - | | **Ink** | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Fill** | Outline, Filled | Outline | Draws the shape's outline, or fills it. A curve only fills when it's closed. | | **Radius** | 0 to 1024 px per corner | 0 px (square) | Rounds the corners; 0 keeps them square. With the link on, one value sets all four; turn it off to give each corner (top-left, top-right, bottom-right, bottom-left) its own. The Rectangle and the Rectangular Marquee keep separate radii. | | **Place tiles** | On or off | Off | Places the tile picked in the Tileset panel on the tilemap cells the tool covers (the Eraser empties them, the Bucket fills the connected cells holding the clicked tile) instead of painting pixels. Symmetry mirrors the tiles and a selection limits them. While a tilemap layer is active. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Ellipse | Option | Values | Default | What it does | | - | - | - | - | | **Ink** | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Fill** | Outline, Filled | Outline | Draws the shape's outline, or fills it. A curve only fills when it's closed. | | **Place tiles** | On or off | Off | Places the tile picked in the Tileset panel on the tilemap cells the tool covers (the Eraser empties them, the Bucket fills the connected cells holding the clicked tile) instead of painting pixels. Symmetry mirrors the tiles and a selection limits them. While a tilemap layer is active. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Triangle | Option | Values | Default | What it does | | - | - | - | - | | **Ink** | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Fill** | Outline, Filled | Outline | Draws the shape's outline, or fills it. A curve only fills when it's closed. | | **Place tiles** | On or off | Off | Places the tile picked in the Tileset panel on the tilemap cells the tool covers (the Eraser empties them, the Bucket fills the connected cells holding the clicked tile) instead of painting pixels. Symmetry mirrors the tiles and a selection limits them. While a tilemap layer is active. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Hexagon | Option | Values | Default | What it does | | - | - | - | - | | **Ink** | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Fill** | Outline, Filled | Outline | Draws the shape's outline, or fills it. A curve only fills when it's closed. | | **Place tiles** | On or off | Off | Places the tile picked in the Tileset panel on the tilemap cells the tool covers (the Eraser empties them, the Bucket fills the connected cells holding the clicked tile) instead of painting pixels. Symmetry mirrors the tiles and a selection limits them. While a tilemap layer is active. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Star | Option | Values | Default | What it does | | - | - | - | - | | **Ink** | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Fill** | Outline, Filled | Outline | Draws the shape's outline, or fills it. A curve only fills when it's closed. | | **Place tiles** | On or off | Off | Places the tile picked in the Tileset panel on the tilemap cells the tool covers (the Eraser empties them, the Bucket fills the connected cells holding the clicked tile) instead of painting pixels. Symmetry mirrors the tiles and a selection limits them. While a tilemap layer is active. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Iso Shape | Option | Values | Default | What it does | | - | - | - | - | | **Ink** | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Shape** | Box, Ramp, Stairs, Pyramid, Prism (roof), Cylinder, Cone, Sphere, Dome | Box | The solid to draw. Pressing the tool's key again also switches it. | | **Cap** | 2 to 4 | 3 | How wide the flat tip at the top and bottom corners is: 2 is a sharp point, 4 is flat. Also the size step when dragging. | | **Bevel** | 0 to 1024 px | 0 px (square edges) | Chamfers the box's four top edges with a 45° slope: the top shrinks and the sides stop short of it. The front slopes take the side colors, the back ones the top color. It never grows past the box's height or half its footprint. For boxes. | | **Style** | Fill + outline, Fill, Outline | Fill + outline | Fill and outline, fill only, or outline only; click to step through them. | | **Show back edges** | On or off | Off | Also draws the edges hidden behind the shape, like a wireframe. With the Outline style. | | **Rotate 90°** | A button | Sloping down to the right | Turns the shape a quarter turn, so it slopes the other way. For ramps, stairs and prisms. | | **Auto-shade from foreground** | On or off | On | Colors the top face with the foreground color and shades the sides darker, along the Shades when the color is on them. Off, five swatches set the outline, inner edge and face colors. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Contour | Option | Values | Default | What it does | | - | - | - | - | | **Ink** | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Polygon | Option | Values | Default | What it does | | - | - | - | - | | **Ink** | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Fill** | Outline, Filled | Outline | Draws the shape's outline, or fills it. A curve only fills when it's closed. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Blur | Option | Values | Default | What it does | | - | - | - | - | | **Size** | 1 px to 64 px | 1 px | Brush size in pixels; the Spray's splash is twice as wide. A custom brush keeps its own size. | | **Brush shape** | Square, Circle, Line, Diamond, Cross, X, Star, Custom | Circle | The brush's footprint: a square, a circle, a 1-pixel line, a diamond, a cross, an X, a star, or the custom brush picked in the Brush Library. | | **Custom brushes** | The built-in brushes and yours | None | Opens the Brush Library to pick a built-in brush or draw and save your own to paint with. | | **Angle** | 0° to 180° | 0° | Turns the brush around its center. With the Line or Square brush. | | **Strength** | 1 px to 4 px | 1 px | How far each pixel looks for neighbors to blend with. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Jumble | Option | Values | Default | What it does | | - | - | - | - | | **Size** | 1 px to 64 px | 1 px | Brush size in pixels; the Spray's splash is twice as wide. A custom brush keeps its own size. | | **Brush shape** | Square, Circle, Line, Diamond, Cross, X, Star, Custom | Circle | The brush's footprint: a square, a circle, a 1-pixel line, a diamond, a cross, an X, a star, or the custom brush picked in the Brush Library. | | **Custom brushes** | The built-in brushes and yours | None | Opens the Brush Library to pick a built-in brush or draw and save your own to paint with. | | **Angle** | 0° to 180° | 0° | Turns the brush around its center. With the Line or Square brush. | | **Strength** | 0% to 100% | 100% | The chance that a pixel the brush touches changes. Needs a brush bigger than 1 pixel. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Dodge/Burn | Option | Values | Default | What it does | | - | - | - | - | | **Size** | 1 px to 64 px | 1 px | Brush size in pixels; the Spray's splash is twice as wide. A custom brush keeps its own size. | | **Brush shape** | Square, Circle, Line, Diamond, Cross, X, Star, Custom | Circle | The brush's footprint: a square, a circle, a 1-pixel line, a diamond, a cross, an X, a star, or the custom brush picked in the Brush Library. | | **Custom brushes** | The built-in brushes and yours | None | Opens the Brush Library to pick a built-in brush or draw and save your own to paint with. | | **Angle** | 0° to 180° | 0° | Turns the brush around its center. With the Line or Square brush. | | **Strength** | 1 to 100 | 25 | How much each stroke lightens (or, with the right button, darkens) the pixels it touches, in perceptual lightness. 100 moves half way from black to white. | | **Hue shift** | 0° to 60° | 0° | Also turns the hue of each pixel it touches: lightening shifts toward warm yellow and softens the color, darkening (right button) shifts toward cool blue and deepens it, the way pixel artists shade. 0 keeps the hue. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Line | Option | Values | Default | What it does | | - | - | - | - | | **Ink** | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Width** | 1 px to 64 px | 1 px | The stroke's thickness; wider than 1 px it gets round ends. Shared by the Line and the Curve. | | **Place tiles** | On or off | Off | Places the tile picked in the Tileset panel on the tilemap cells the tool covers (the Eraser empties them, the Bucket fills the connected cells holding the clicked tile) instead of painting pixels. Symmetry mirrors the tiles and a selection limits them. While a tilemap layer is active. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Curve | Option | Values | Default | What it does | | - | - | - | - | | **Ink** | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Fill** | Outline, Filled | Outline | Draws the shape's outline, or fills it. A curve only fills when it's closed. | | **Width** | 1 px to 64 px | 1 px | The stroke's thickness; wider than 1 px it gets round ends. Shared by the Line and the Curve. | | **Pixel perfect** | On or off | On | Removes the L-shaped corners a 1 px curve picks up, for clean pixel-art lines. At width 1. | | **Paint across cels** | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | ### Ruler No options of its own. ### Text | Option | Values | Default | What it does | | - | - | - | - | | **Font** | The bundled pixel fonts (Tom Thumb, Spleen 5x8, Spleen 6x12, Spleen 8x16, Scientifica, Scientifica Bold) and any .bdf, .ttf, .otf, .woff or .woff2 you load | Spleen 5x8 | The typeface. Load font... adds a font file from your computer; it stays in this browser, and Remove forgets it. | | **Scale** | 1x to 16x | 1x | Draws each of the font's pixels as a square this many pixels wide. With a bitmap font. | | **Size** | 4 to 256 px | 16 px | The font size in pixels. Each glyph is drawn without anti-aliasing; pixel fonts look right at their design size or a multiple of it. With a loaded TTF, OTF or WOFF font. | | **Bold** | On or off | Off | Heavier letters: a loaded font's bold, or, for a bitmap font, every pixel doubled to its right. | | **Italic** | On or off | Off | Slanted letters: a loaded font's italic, or, for a bitmap font, rows shifted right a pixel at a time going up. | | **Letter spacing** | -16 to 64 px | 0 px | Extra pixels between characters; negative pulls them closer. | | **Line spacing** | -32 to 128 px | 0 px | Extra pixels between lines; negative pulls them closer. | | **Align** | Left, Center, Right | Left | Lines line up on their left edges, centers or right edges; the point you clicked is that edge or center. | ## Every option Each option once, with the tools that show it. | Option | Tools | Values | Default | What it does | | - | - | - | - | - | | **Size** | Pencil, Eraser, Spray, Scatter, Dither, Blur, Jumble, Dodge/Burn | 1 px to 64 px | 1 px | Brush size in pixels; the Spray's splash is twice as wide. A custom brush keeps its own size. | | **Brush shape** | Pencil, Eraser, Scatter, Dither, Blur, Jumble, Dodge/Burn | Square, Circle, Line, Diamond, Cross, X, Star, Custom | Circle | The brush's footprint: a square, a circle, a 1-pixel line, a diamond, a cross, an X, a star, or the custom brush picked in the Brush Library. | | **Custom brushes** | Pencil, Eraser, Scatter, Dither, Blur, Jumble, Dodge/Burn | The built-in brushes and yours | None | Opens the Brush Library to pick a built-in brush or draw and save your own to paint with. | | **Angle** | Pencil, Eraser, Scatter, Dither, Blur, Jumble, Dodge/Burn | 0° to 180° | 0° | Turns the brush around its center. With the Line or Square brush. | | **Pixel-perfect** | Pencil, Eraser, Dither | On or off | Off | Removes the doubled corner pixels that make a freehand line look jagged. | | **Right-click** | Eraser | Paint BG, Replace FG | Paint BG | What the right button does: paint the background color, or turn only pixels of the foreground color into the background color. | | **Ink** | Pencil, Spray, Scatter, Dither, Paint Bucket, Gradient, Rectangle, Ellipse, Triangle, Hexagon, Star, Iso Shape, Contour, Polygon, Line, Curve | Copy, Simple, Lock Alpha, Shading | Copy | How color is applied: Copy replaces pixels (transparency too), Simple blends over them, Lock Alpha recolors only visible pixels, Shading steps pixels along the Shades. | | **Shades** | Pencil, Spray, Scatter, Dither, Paint Bucket, Gradient, Rectangle, Ellipse, Triangle, Hexagon, Star, Iso Shape, Contour, Polygon, Line, Curve | A list of colors | Empty | The ramp the Shading ink steps along: drag palette colors in, drag to reorder, click one to remove it; + FG adds the foreground color. With the Shading ink. | | **Dynamics** | Pencil, Eraser, Dither | Stabilizer; pressure and speed mapped to size, angle and gradient | Pressure to size (pen only) | Smooths the stroke (Stabilizer) and lets pen pressure or speed vary the size, the angle, or the color from foreground to background. Pressure only comes from a pen: mouse and finger strokes ignore it. | | **Fill** | Rectangle, Ellipse, Triangle, Hexagon, Star, Polygon, Curve | Outline, Filled | Outline | Draws the shape's outline, or fills it. A curve only fills when it's closed. | | **Radius** | Rectangle, Rectangular Selection | 0 to 1024 px per corner | 0 px (square) | Rounds the corners; 0 keeps them square. With the link on, one value sets all four; turn it off to give each corner (top-left, top-right, bottom-right, bottom-left) its own. The Rectangle and the Rectangular Marquee keep separate radii. | | **Width** | Line, Curve | 1 px to 64 px | 1 px | The stroke's thickness; wider than 1 px it gets round ends. Shared by the Line and the Curve. | | **Pixel perfect** | Curve | On or off | On | Removes the L-shaped corners a 1 px curve picks up, for clean pixel-art lines. At width 1. | | **Shape** | Iso Shape | Box, Ramp, Stairs, Pyramid, Prism (roof), Cylinder, Cone, Sphere, Dome | Box | The solid to draw. Pressing the tool's key again also switches it. | | **Cap** | Iso Shape | 2 to 4 | 3 | How wide the flat tip at the top and bottom corners is: 2 is a sharp point, 4 is flat. Also the size step when dragging. | | **Bevel** | Iso Shape | 0 to 1024 px | 0 px (square edges) | Chamfers the box's four top edges with a 45° slope: the top shrinks and the sides stop short of it. The front slopes take the side colors, the back ones the top color. It never grows past the box's height or half its footprint. For boxes. | | **Style** | Iso Shape | Fill + outline, Fill, Outline | Fill + outline | Fill and outline, fill only, or outline only; click to step through them. | | **Show back edges** | Iso Shape | On or off | Off | Also draws the edges hidden behind the shape, like a wireframe. With the Outline style. | | **Rotate 90°** | Iso Shape | A button | Sloping down to the right | Turns the shape a quarter turn, so it slopes the other way. For ramps, stairs and prisms. | | **Auto-shade from foreground** | Iso Shape | On or off | On | Colors the top face with the foreground color and shades the sides darker, along the Shades when the color is on them. Off, five swatches set the outline, inner edge and face colors. | | **Rate** | Spray | 1 to 64 | 8 | How many dots each burst adds. | | **Scatter X** | Scatter | 0 px to 64 px | 8 px | How far each stamp can land left or right of the path. | | **Scatter Y** | Scatter | 0 px to 64 px | 8 px | How far each stamp can land above or below the path. | | **Count** | Scatter | 1 to 16 | 2 | How many stamps land at each step along the path. | | **Strength** | Blur | 1 px to 4 px | 1 px | How far each pixel looks for neighbors to blend with. | | **Strength** | Jumble | 0% to 100% | 100% | The chance that a pixel the brush touches changes. Needs a brush bigger than 1 pixel. | | **Strength** | Dodge/Burn | 1 to 100 | 25 | How much each stroke lightens (or, with the right button, darkens) the pixels it touches, in perceptual lightness. 100 moves half way from black to white. | | **Hue shift** | Dodge/Burn | 0° to 60° | 0° | Also turns the hue of each pixel it touches: lightening shifts toward warm yellow and softens the color, darkening (right button) shifts toward cool blue and deepens it, the way pixel artists shade. 0 keeps the hue. | | **Pattern** | Dither | Bayer 2×2, Bayer 4×4, Bayer 8×8, Dots, Horizontal lines, Vertical lines, Diagonal lines, Noise | Bayer 2×2 | The pattern the brush paints through: Bayer checkers (2×2 coarsest, 8×8 finest), halftone dots, lines, or an even noise scatter. It lines up with the sprite's top-left corner, so strokes that overlap stay on one pattern. | | **Density** | Dither | 0% to 100% | 50% | How much of the pattern gets painted: 50% of Bayer 2×2 is a checkerboard, 100% paints every pixel. | | **Type** | Gradient | Linear, Radial | Linear | A straight ramp along the drag, or rings around its start. | | **Colors** | Gradient | FG → BG, Stops | FG → BG | Run from the foreground to the background color, or through the Stops. | | **Stops** | Gradient | Up to 32 colors, each at a position along the drag | Empty | The colors the gradient runs through, left = where you start dragging: click the bar to add the foreground color, drag a handle to move its stop (meeting a neighbour makes a hard edge) or off the bar to remove it, drop palette colors on the bar or a handle; Even spreads them out, FG recolors the selected stop. Shift+click a palette range to load it. With fewer than two, it runs foreground to background. With Colors set to Stops. | | **Dither** | Gradient | None, Bayer 2×2, Bayer 4×4, Bayer 8×8, Dots, Horizontal lines, Vertical lines, Diagonal lines, Noise | Bayer 4×4 | None blends smoothly; the others step between the colors in a pattern, for a pixel-art look: Bayer checkers (2×2 coarsest, 8×8 finest), halftone dots, lines, or an even noise scatter. | | **Snap** | Gradient | Free, Palette | Free | Free blends any colors; Palette paints only palette colors, each pixel the nearest one, and with Dither steps between neighbouring palette colors. | | **Rotation algorithm** | Move | Fast Rotation, RotSprite, Bilinear | RotSprite | How rotated and scaled pixels are resampled: Fast Rotation keeps them sharp, RotSprite rotates pixel art cleanly, Bilinear blends them smoothly. | | **Pivot** | Move | A corner, an edge's middle, or the center | Center | Where the point everything turns and scales around starts; you can also drag it on the canvas. | | **Font** | Text | The bundled pixel fonts (Tom Thumb, Spleen 5x8, Spleen 6x12, Spleen 8x16, Scientifica, Scientifica Bold) and any .bdf, .ttf, .otf, .woff or .woff2 you load | Spleen 5x8 | The typeface. Load font... adds a font file from your computer; it stays in this browser, and Remove forgets it. | | **Scale** | Text | 1x to 16x | 1x | Draws each of the font's pixels as a square this many pixels wide. With a bitmap font. | | **Size** | Text | 4 to 256 px | 16 px | The font size in pixels. Each glyph is drawn without anti-aliasing; pixel fonts look right at their design size or a multiple of it. With a loaded TTF, OTF or WOFF font. | | **Bold** | Text | On or off | Off | Heavier letters: a loaded font's bold, or, for a bitmap font, every pixel doubled to its right. | | **Italic** | Text | On or off | Off | Slanted letters: a loaded font's italic, or, for a bitmap font, rows shifted right a pixel at a time going up. | | **Letter spacing** | Text | -16 to 64 px | 0 px | Extra pixels between characters; negative pulls them closer. | | **Line spacing** | Text | -32 to 128 px | 0 px | Extra pixels between lines; negative pulls them closer. | | **Align** | Text | Left, Center, Right | Left | Lines line up on their left edges, centers or right edges; the point you clicked is that edge or center. | | **Sample** | Eyedropper | Composite, Layer | Composite | Picks the color you see from all visible layers, or from the active layer only. | | **Tile** | Tile Stamp | Any tile in the layer's tileset | 1 | The tile being stamped (0 is the empty tile); pick it in the Tileset panel. | | **Flip H** | Tile Stamp | On or off | Off | Flips the stamped tile horizontally. | | **Flip V** | Tile Stamp | On or off | Off | Flips the stamped tile vertically. | | **Rot 90°** | Tile Stamp | On or off | Off | Rotates the stamped tile 90° clockwise. | | **Place tiles** | Pencil, Eraser, Line, Rectangle, Ellipse, Triangle, Hexagon, Star, Paint Bucket | On or off | Off | Places the tile picked in the Tileset panel on the tilemap cells the tool covers (the Eraser empties them, the Bucket fills the connected cells holding the clicked tile) instead of painting pixels. Symmetry mirrors the tiles and a selection limits them. While a tilemap layer is active. | | **Fill mode** | Magic Wand | Contiguous, Global | Contiguous | Selects only the connected area you click, or every matching pixel on the layer. | | **Tolerance** | Magic Wand | 0 to 255 | 0 | How different a color can be and still be selected (per color channel). Separate from the Paint Bucket's. | | **8-connected** | Magic Wand | On or off | Off | Also spreads to diagonal neighbours, so the selection leaks through 1 px diagonal lines. Off, only up, down, left and right count as connected. With Contiguous. | | **Stop at grid** | Magic Wand | On or off | Off | Keeps the selection inside the grid cell you click. While the grid is shown. | | **Fill mode** | Paint Bucket, Gradient | Contiguous, Global | Contiguous | Only the connected area you click, or every matching pixel on the layer. | | **Tolerance** | Paint Bucket, Gradient | 0 to 255 | 0 | How different a color can be and still match (per color channel). | | **8-connected** | Paint Bucket, Gradient | On or off | Off | Also spreads to diagonal neighbours, so the area leaks through 1 px diagonal lines. Off, only up, down, left and right count as connected. With Contiguous. | | **Stop at grid** | Paint Bucket, Gradient | On or off | Off | Keeps the area inside the grid cell you click. While the grid is shown. | | **Close gap** | Paint Bucket, Gradient | 0 px to 5 px | 0 px | Treats gaps in an outline up to this many pixels wide as closed, so the fill stops there instead of leaking out (the gap itself is filled). 0 is off. With Contiguous. | | **Pattern** | Paint Bucket | Solid, Checker, Dither 25%, Dither 75%, Horizontal stripes, Vertical stripes, Diagonal, Grid, Dots, Bricks | Solid | Fills with a repeating pattern of the foreground and background colors (swapped on right-click) instead of a solid color. Patterns line up from the canvas's top-left corner, so neighbouring fills match. | | **Paint across cels** | Pencil, Eraser, Spray, Scatter, Dither, Blur, Jumble, Dodge/Burn, Paint Bucket, Gradient, Rectangle, Ellipse, Triangle, Hexagon, Star, Line, Curve, Polygon, Contour, Iso Shape | On or off | Off | Paints every cel selected in the timeline at once (the cel you paint on must be one of them): one stroke, fill or shape lands on each, and one undo takes it back. The badge counts the cels it reaches; locked and hidden layers are skipped. With the painting tools. | # Settings Every setting in pxstud.io: where to change it, its values, its default and where it's kept. Most settings are in the **Settings** menu and the **View** menu, and a few sit next to what they change (the color picker's own settings button, the timeline's playback controls). Tool options have [their own reference](https://pxstud.io/docs/manual/reference/tool-options.md). Settings are kept in one of these places, shown in each table's **Kept** column: - **This browser, every sprite:** saved in this browser and the same for every sprite. Another browser or device has its own. A saved [workspace layout](https://pxstud.io/docs/manual/guides/workspace.md) can carry some of them, as noted under their tables. - **Each sprite, in this browser:** each sprite has its own, saved in this browser. - **In the sprite file:** part of the sprite, so it's saved and synced with it and undo covers it. - **Each editor view:** each editor (View > New Editor) has its own, until you reload. - **Until you reload:** shared by every view and back to the default when the page reloads. ## Appearance | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Theme** | The theme button at the right end of the menu bar | Match system, Light, Dark | Match system | This browser, every sprite | Light or dark, or whichever your system uses; every page of pxstud.io follows it. | A saved workspace layout can carry these: Theme. ## Keyboard shortcuts | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Keyboard shortcuts** | Settings > Keyboard Shortcuts... | Any keys for any command, or none | The defaults | This browser, every sprite | Changes, adds or removes the keys for any command or tool. | A saved workspace layout can carry these: Keyboard shortcuts. How to change them is in [Keyboard shortcuts](https://pxstud.io/docs/manual/reference/shortcuts.md). ## View | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Pixel grid** | View > Grid > Show Pixel Grid | On or off | Off | Each editor view | Outlines every pixel when you zoom in far enough. | | **Tiled mode** | View > Tiled Mode | Off, Both axes, Horizontal only, Vertical only | Off | Each editor view | Repeats the sprite around itself, to check that it tiles; you can paint across the copies. | | **Onion skin** | View > Show Onion Skin | On or off | Off | Each editor view | Shows the frames around the current one, faded, as set in the onion skin settings. | | **Tile grid** | View > Tile Grid | On or off | On | Until you reload | Outlines the tiles while a tilemap layer is active. | | **Brush preview** | View > Show Brush Preview | On or off | On | Until you reload | Shows the brush's footprint under the pointer, or the Spray's splash circle. | | **Zoom snapping** | Settings > Zoom Snapping | Off (smooth), Whole numbers, Powers of two | Whole numbers | This browser, every sprite | Whole numbers keeps every pixel the same size on screen, on Aseprite's levels (1x, 2x, 3x, 4x, 5x, 6x, 8x, 12x ..., and 1/2, 1/3 ... below 100%); powers of two zooms ... 1/2, 1x, 2x, 4x ...; off zooms smoothly. | ## Touch and pen For drawing on a tablet or touch screen. A pen's eraser end always switches to the Eraser while you use it (see [Tools](https://pxstud.io/docs/manual/reference/tools.md#tool-eraser)). | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Draw with finger** | Settings > Draw with Finger | On or off | On | This browser, every sprite | When off, one finger pans the canvas instead of drawing, so only a pen or mouse draws; pinching and two- and three-finger taps work either way. | ## Mouse and trackpad What the mouse, its wheel and a trackpad do on the canvas; [The workspace](https://pxstud.io/docs/manual/guides/workspace.md#zoom-pan-and-rotate) explains them. Commands bound to mouse buttons and wheel directions are kept with the [keyboard shortcuts](https://pxstud.io/docs/manual/reference/shortcuts.md). | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Mouse wheel** | Settings > Mouse and Trackpad... | Zoom, Scroll the view | Zoom | This browser, every sprite | Zoom: the wheel zooms and a trackpad's two-finger scroll pans. Scroll the view: the wheel pans (Shift+wheel sideways) and Ctrl+wheel (⌘+wheel on a Mac) zooms. A trackpad pinch zooms either way. | | **Zoom around** | Settings > Mouse and Trackpad... | Pointer, Center of the view | Pointer | This browser, every sprite | Whether wheel and trackpad zoom keeps the point under the pointer still, or the middle of the view. | | **Invert brush-size wheel** | Settings > Mouse and Trackpad... | On or off | Off | This browser, every sprite | Ctrl+Shift+wheel (⌘⇧+wheel on a Mac) changes the active tool's size, wheel up for bigger; this makes wheel up smaller, as in older Aseprite. | | **Right button** | Settings > Mouse and Trackpad... | Tool's right-click (background color), Erase while held, Pick color while held, Pan the view | Tool's right-click (background color) | This browser, every sprite | What a right-button drag on the canvas does: the tool's own right-click (most paint with the background color), erase, pick a color, or pan. A pen's side button counts as the right button. | | **Back and forward buttons open the tool wheel** | Settings > Mouse and Trackpad... | On or off | On | This browser, every sprite | Hold the mouse's back or forward button over the canvas to open the tool wheel, and let go over a tool to pick it; the browser doesn't go back. | | **Cursor size** | Settings > Mouse and Trackpad... | 100%, 150%, 200% | 100% | This browser, every sprite | Draws the pixel cursors at 150% or 200%, still crisp, for big or dense screens. | ## Tools | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Remember brush per tool** | Settings > Remember Brush per Tool | On or off | On | This browser, every sprite | Each tool keeps its own brush size, shape and ink, so switching back to a tool brings back what it last used; when off, every tool shares one brush. | | **Hold tool keys to borrow** | Settings > Hold Tool Keys to Borrow | On or off | Off | This browser, every sprite | Tap a tool's key to switch to it; hold the key for a moment and the tool is only borrowed, so letting go returns you to the tool you had (hold E to erase, release to carry on with the pencil). | | **Tool wheel: inner ring** | Settings > Tool Wheel... | Up to eight tools, in slots you choose | 8 tools | This browser, every sprite | The tools on the ring nearest the pointer, the easiest to reach; slots may stay empty. | | **Tool wheel: outer ring** | Settings > Tool Wheel... | Up to eight tools, in slots you choose | 8 tools | This browser, every sprite | The tools on the ring behind the inner one, turned half a slot so each sits between two inner tools; slots may stay empty. | ## Grid The grid's look and snapping are yours; its type, offset and cell size belong to the sprite. | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Show grid** | View > Grid > Show Grid | On or off | Off | This browser, every sprite | Draws the sprite's grid over the canvas. | | **Snap to grid** | View > Grid > Snap to Grid | On or off | Off | This browser, every sprite | Snaps shapes, lines, selections, slices and gradients to the grid. | | **Color and opacity** | View > Grid > Grid Settings... | Any color | #000000 at 50% | This browser, every sprite | The grid lines' color and how opaque they are. | | **Type** | View > Grid > Grid Settings... | Rectangular, Isometric | Rectangular | In the sprite file | A grid of rectangles, or of diamonds for isometric art (View > Grid > Isometric Grid switches too). | | **Ratio** | View > Grid > Grid Settings... | 2:1, 1:1, 3:2, 4:3 | 2:1 | In the sprite file | An isometric grid's slope: how many pixels across for each pixel down. | | **Offset (X, Y)** | View > Grid > Grid Settings... | -32767 px to 32767 px | 0, 0 | In the sprite file | Where the grid starts, so it can line up with art that isn't at the corner. | | **Cell size** | View > Grid > Grid Settings... | 1 px to 32767 px | 16 × 16 px | In the sprite file | Each cell's width and height; an isometric cell's height follows from its width and ratio. | A saved workspace layout can carry these: View preferences (grid, checkerboard, onion skin, playback). ## Rulers and guides Whether rulers and guides show, snap or are locked is yours; the guides themselves belong to the sprite. | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Show rulers** | View > Guides > Show Rulers | On or off | Off | This browser, every sprite | Rulers along the top and left of every editor view, in sprite pixels. Drag out of one to add a guide. | | **Show guides** | View > Guides > Show Guides | On or off | On | This browser, every sprite | Draws the sprite's guides over the canvas. Hidden guides don't snap. | | **Snap to guides** | View > Guides > Snap to Guides | On or off | On | This browser, every sprite | Pulls shapes, lines, selections, slices, gradients and Move tool transforms onto a guide within a few screen pixels. | | **Lock guides** | View > Guides > Lock Guides | On or off | Off | This browser, every sprite | Stops guides being moved or removed by dragging. New ones can still come out of the rulers. | A saved workspace layout can carry these: View preferences (grid, checkerboard, onion skin, playback). ## Checkerboard The checkerboard shows through transparent pixels. | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Square size** | Settings > Checkerboard... | 1 px, 2 px, 4 px, 8 px, 16 px, 32 px, 64 px | 16 px | This browser, every sprite | The side of each square of the checkerboard behind transparent pixels, in sprite pixels. | | **Colors** | Settings > Checkerboard... | Two colors of your choice | The theme's | This browser, every sprite | The two colors of the squares; Reset to Theme follows the light or dark theme again. | A saved workspace layout can carry these: View preferences (grid, checkerboard, onion skin, playback). ## Onion skin | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Previous frames** | Settings > Onion Skin... | 0 to 16 | 1 | This browser, every sprite | How many frames before the current one show. | | **Next frames** | Settings > Onion Skin... | 0 to 16 | 1 | This browser, every sprite | How many frames after the current one show. | | **Opacity** | Settings > Onion Skin... | 0% to 100% | 50% | This browser, every sprite | How opaque the nearest frames are. | | **Opacity step** | Settings > Onion Skin... | 0% to 100% | 10% | This browser, every sprite | How much fainter each frame further away gets. | | **Red/Blue tint** | Settings > Onion Skin... | On or off | On | This browser, every sprite | Tints earlier frames red and later frames blue. | | **Current tag only** | Settings > Onion Skin... | On or off | Off | This browser, every sprite | Shows only frames in the tag the current frame belongs to. | | **Loop through tag frames** | Settings > Onion Skin... | On or off | Off | This browser, every sprite | With Current tag only, wraps around the tag's ends, as the animation does when it loops. | | **Position** | Settings > Onion Skin... | Behind sprite, In front of sprite | Behind sprite | This browser, every sprite | Draws the other frames behind the current one, or over it. | A saved workspace layout can carry these: View preferences (grid, checkerboard, onion skin, playback). ## Playback | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Playback speed** | The timeline's and the preview's playback controls | 0.25x, 0.5x, 0.75x, 1x, 1.5x, 2x, 3x, 4x | 1x | This browser, every sprite | Plays the animation faster or slower; the frame durations in the sprite don't change. | | **Playback mode** | The timeline's and the preview's playback controls | Play tags & repetitions, Play all frames (ignore tags), Play once, Play subtags | Play tags & repetitions | This browser, every sprite | Whether playback follows the tags and their repeats, plays every frame, plays once, or plays the subtags. | A saved workspace layout can carry these: View preferences (grid, checkerboard, onion skin, playback). ## Canvas info | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Show canvas info** | View > Show Canvas Info | On or off | On | This browser, every sprite | Shows a readout of the cursor position, tool, frame and so on in a corner of each canvas. | | **Position** | Settings > Canvas Info... | Top left, Top right, Bottom left, Bottom right | Bottom left | This browser, every sprite | The corner of the canvas it sits in. | | **Items** | Settings > Canvas Info... | Any of Cursor position, Selection size, Shape size / rotation angle, Foreground color, Active tool, Active layer, Frame, Canvas size, Color mode, Zoom | Cursor position, Selection size, Shape size / rotation angle, Active tool, Frame, Canvas size | This browser, every sprite | What the readout lists; the selection and shape sizes show only while there's one. | ## Keystrokes | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Show keystrokes** | View > Show Keystrokes | On or off | Off | This browser, every sprite | Shows each key you press and the command it runs, for recording tutorials. | | **Position** | Settings > Keystrokes... | Top left, Top center, Top right, Bottom left, Bottom center, Bottom right | Bottom center | This browser, every sprite | Where on screen the keys appear. | | **Relative to canvas** | Settings > Keystrokes... | On or off | Off | This browser, every sprite | Places them in a corner of the canvas instead of the window. | ## Color picker | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Modes** | The Color picker's settings button | Any of Shades, Spectrum, Wheel, Sliders, Normal map | Shades, Spectrum, Wheel, Sliders | This browser, every sprite | The pickers offered in the picker's mode bar; at least one stays on. | | **Mode** | The Color picker's mode bar | Shades, Spectrum, Wheel, Sliders, Normal map | Shades | This browser, every sprite | The picker showing. | | **Color space** | The Color picker's settings button | HSV, RYB (painter's wheel), OKLCH (even lightness) | HSV | This browser, every sprite | The color space of the Shades, Spectrum and Wheel pickers. | | **Slider channels** | The Sliders mode's channel buttons | RGB, HSV, HSL, OKLCH | RGB | This browser, every sprite | The channels the Sliders mode shows. | | **Harmony** | The Color picker's settings button | None, Complementary, Analogous, Split complementary, Triadic, Tetradic, Square, Monochromatic | None | This browser, every sprite | Marks the colors that go with the current one on the pickers, by a color-wheel rule. | | **Discrete steps** | The Color picker's settings button | On or off | Off | This browser, every sprite | Snaps the pickers to a fixed number of hues and tones. | | **Hue steps** | The Color picker's settings button | 6 hues, 8 hues, 10 hues, 12 hues, 16 hues, 18 hues, 24 hues, 36 hues | 12 hues | This browser, every sprite | How many hues Discrete steps allows. | | **Tone steps** | The Color picker's settings button | 3 levels, 4 levels, 5 levels, 6 levels, 8 levels, 10 levels, 12 levels, 16 levels | 8 levels | This browser, every sprite | How many saturation and lightness levels Discrete steps allows. | | **Wheel start** | The Color picker's settings button | 0° to 330° | 0° | This browser, every sprite | Where red sits on the wheel, turning counterclockwise from the right, in 30° steps. | | **Normal map** | The Color picker's settings button | Green up, Y+ (OpenGL), Green down, Y− (DirectX) | Green up, Y+ (OpenGL) | This browser, every sprite | Which way green points in the Normal map picker. | | **Text format** | The format button next to the Color picker's text field | HEX, RGB, HSL, OKLCH | HEX | This browser, every sprite | How the color is written in the picker's text field; click to step through them. | ## Symmetry | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Axes** | The Symmetry button in the tool options bar | Horizontal, vertical and the two diagonals, in any combination | None (off) | Each sprite, in this browser | The lines strokes and shapes are mirrored across. | | **Axis position** | Drag an axis on the canvas | Anywhere on the canvas, on a pixel edge or a pixel's middle | The canvas center | Each sprite, in this browser | Where the axes cross; the Symmetry button's arrow has Reset to center. | ## Text tool The Text tool's options are kept in this browser, so the last font you used is still chosen next time. Fonts you load are kept in this browser too. | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Font** | The Text tool's options bar | A bundled pixel font, or a font file loaded in this browser | Spleen 5x8 | This browser, every sprite | The typeface new text is drawn in. | | **Scale** | The Text tool's options bar | 1x to 16x | 1x | This browser, every sprite | How many pixels wide each of a bitmap font's pixels is drawn. | | **Size** | The Text tool's options bar | 4 px to 256 px | 16 px | This browser, every sprite | The size a loaded TTF, OTF or WOFF font is drawn at. | | **Letter spacing** | The Text tool's options bar | -16 px to 64 px | 0 px | This browser, every sprite | Extra pixels between characters. | | **Line spacing** | The Text tool's options bar | -32 px to 128 px | 0 px | This browser, every sprite | Extra pixels between lines. | | **Align** | The Text tool's options bar | Left, Center, Right | Left | This browser, every sprite | Whether lines line up on their left edges, centers or right edges. | | **Bold** | The Text tool's options bar | On or off | Off | This browser, every sprite | Heavier letters: a loaded font's bold, or, for a bitmap font, every pixel doubled to its right. | | **Italic** | The Text tool's options bar | On or off | Off | This browser, every sprite | Slanted letters: a loaded font's italic, or, for a bitmap font, rows shifted right a pixel at a time going up. | ## Workspace Which panels are open and where they're docked is covered in [The workspace](https://pxstud.io/docs/manual/guides/workspace.md). | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Lock layout** | View > Workspace > Lock Layout | On or off | Off | This browser, every sprite | Stops panels from being moved, docked or resized by accident. | ## New sprites | Setting | Where | Values | Default | Kept | What it does | | - | - | - | - | - | - | | **Palette** | File > New Sprite... | The starter palette, one of My Palettes, or a built-in one | The starter palette | This browser, every sprite | The palette a new sprite starts with: the New Sprite dialog remembers the last one you chose. | # Your first script Write a short Lua script that draws a sun, run it, undo it, and save it. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ Scripts are small Lua programs that draw and edit your sprite for you: anything you can do with the tools, a script can do with code. This walks through writing one that draws a sun on a new layer. It takes about five minutes, and you don't need to have programmed before. ## Make a script Open a sprite (a new 32×32 one is fine) and choose Scripts > New Script.... Name the script "Sun" and press **Create**. The **Scripts** panel opens with a few comment lines (lines starting with `--`, which Lua ignores). It has a code editor, a toolbar along the top, and an output area at the bottom. In the default layout it docks to the right of the canvas, so you can see both at once (you can move it like any panel: see [The workspace](https://pxstud.io/docs/manual/guides/workspace.md)). ![The Scripts panel showing the Sun script's code, with Run, Save draft and Publish buttons at the top and the line it printed in the output area below](https://pxstud.io/scripting/images/first-script-panel.light.webp) _The Scripts panel with the sun script, after running it_ ## Write it Replace the comments with this: ```lua -- Draws a sun on a new layer. local layer = app.addLayer("Sun") local frame = app.getActiveFrameId() local w, h = app.getCanvasWidth(), app.getCanvasHeight() local cx, cy = w // 2, h // 2 local r = math.min(w, h) // 4 -- the rays: eight short lines around the middle for i = 0, 7 do local a = i * math.pi / 4 app.drawLine(layer, frame, cx + math.floor(math.cos(a) * (r + 2) + 0.5), cy + math.floor(math.sin(a) * (r + 2) + 0.5), cx + math.floor(math.cos(a) * (r + 5) + 0.5), cy + math.floor(math.sin(a) * (r + 5) + 0.5), { color = "#f4a52e" }) end -- the disc app.drawEllipse(layer, frame, cx - r, cy - r, cx + r, cy + r, { color = "#ffd34e", fill = "filled" }) print("Drew a sun " .. (r * 2 + 1) .. " pixels across.") ``` What it does: - `app.addLayer` adds a layer named "Sun" and gives back its id; `app.getActiveFrameId` gives the frame you're on. Every drawing command takes a layer and a frame, like a tool paints on the active layer at the current frame. - `app.getCanvasWidth` and `app.getCanvasHeight` read the sprite's size, so the sun fits any canvas. Coordinates are pixels, with (0, 0) at the top-left corner. - `app.drawLine` and `app.drawEllipse` work exactly like the Line and Ellipse tools. Anything you don't pass (here, the brush) comes from the current tool settings. - `print` writes a line to the output area. As you type, the editor suggests `app.` functions; point at one to read what it does. ## Run it Press **Run** (the play button) in the panel's toolbar. The output area says "Drew a sun 17 pixels across.", and the timeline has a new layer, "Sun", with the sun on the canvas beside the panel. If something's wrong, the output area shows the error and the line it happened on is highlighted. Fix it and run again. ## Undo it Everything a run changes is **one undo step**: press `Ctrl+Z` and the layer and the sun are gone together. Change a color or the `+ 5` that sets the ray length, and run it again. ## Save it Press **Save draft** (the disk button). The script is saved in this browser, like your sprites, and a `*` after its name in the panel shows unsaved changes. To open it again later, choose Scripts > Open My Script...; the last five scripts you opened are also listed right under it in the Scripts menu. **Publish** shares a script in the script library; it needs an account with Sync (see [the scripting guide](https://pxstud.io/docs/scripting/guide.md#saving-and-publishing)). ## Next steps - [The scripting guide](https://pxstud.io/docs/scripting/guide.md) covers running, stopping, script windows, script commands and the official generators. - The [Scripting API](https://pxstud.io/docs/scripting.md) lists every `app.` function, grouped by what they do: [drawing](https://pxstud.io/docs/scripting/drawing.md), [layers](https://pxstud.io/docs/scripting/layers.md), [frames](https://pxstud.io/docs/scripting/frames.md), [color math](https://pxstud.io/docs/scripting/color.md) and more. # Scripting guide Write Lua scripts that draw and edit your sprites, run library scripts and generators, and add your own windows and menu commands. _Shortcuts are written for Windows and Linux. On a Mac, use ⌘ Cmd where they say Ctrl, and ⌥ Option for Alt._ Scripts are Lua programs that work on the open sprite. They drive the same engine as the editor: every tool, layer, frame, cel, tag, slice, tile and palette entry you can change by hand, a script can change with code. Use them to generate art (trees, rocks, textures, starfields), to repeat a tedious edit, or to add commands of your own to the menus. New to scripting? [Your first script](https://pxstud.io/docs/scripting/first-script.md) walks through writing and running one. This page covers everything around it. ## The Scripts menu | Item | What it does | | - | - | | Scripts > New Script... | Names a new script and opens it in the Scripts panel. | | Scripts > Open My Script... | Lists your scripts: choose one to open it, or rename or delete it. The last five you opened are listed in the menu under it. | | Scripts > Browse Script Library... | The script library: search, run, fork and heart shared scripts, including the official generators. See [The library](https://pxstud.io/docs/manual/guides/library.md). | | Scripts > Script Commands... | Installs menu commands from your scripts ([below](https://pxstud.io/docs/scripting/guide.md#script-commands)). | | Scripts > Scripting API | Opens the [Scripting API](https://pxstud.io/docs/scripting.md) reference in a new tab. | Every one of these is also in the command palette (`Ctrl+K`). ## The Scripts panel The **Scripts** panel opens when you create or open a script (in the default layouts, docked to the right of the canvas, so you can watch a script draw; on a phone, in front of it). View > Panels > Scripts shows and hides it, and a reload reopens the script you had open (its last saved draft). It has a code editor with Lua highlighting, suggestions for every `app.` function as you type, and a description when you point at one. Below the code, an output area shows what the script prints and any error. ![The Scripts panel showing a script's code, with its toolbar at the top and its printed output below](https://pxstud.io/scripting/images/first-script-panel.light.webp) _The Scripts panel_ Along the top, from left to right: - **The script's name.** Double-click it to rename the script. A `*` means there are unsaved changes; next to it, "draft" for a script you haven't published, or the version you're editing ("v2", "v2 · edited"). - **Private** or **Shared**: who can see the script in the library. Click it to change. - **Scripting API reference** (the book) opens the reference in a new tab. - **Run** (the play button) runs the script on the active sprite; a script with a [config form](https://pxstud.io/docs/scripting/guide.md#config-forms) asks for its values first. While it runs, the run controls take its place ([below](https://pxstud.io/docs/scripting/guide.md#running-pausing-and-stopping)). - **Save draft** (the disk) saves the script in this browser. - **Publish** asks for an optional release note, then saves the script and publishes it as a new version. - **Script settings** (the sliders) opens the script's settings: its tags, who can see it, its config form and its published versions. - **Hide panel** closes the panel. The open script stays listed in the File menu, with the sprites: choose its name there to show the panel again. Someone else's script (opened with **View code** in the library) is read-only. Its toolbar has **Fork** instead of Save draft and Publish: that copies it into a new script of yours to edit. ## Running, pausing and stopping A script runs on the sprite you're looking at, and you watch it draw as it goes. However much it changes, a run is **one undo step**: `Ctrl+Z` takes the whole run back. Scripts have **no time limit**. While one runs, its run controls let you end it early: | Control | What it does | | - | - | | **Pause** / **Resume** | Stops the script where it is, even in the middle of a loop, and carries on later. | | **Stop** | Ends the script and keeps what it has done so far. | | **Stop & Revert** | Ends the script and undoes everything it changed. | In the Scripts panel these are small buttons in the toolbar. A script run from the library covers the editor with a window showing its name, its output and the same three controls, until it finishes. When a script fails, the output shows the error; in the panel, the line it failed on is highlighted. A script can't paint on a locked layer: the command is refused and the script stops with an error. ## Writing scripts Every function is on the `app` table. The [Scripting API](https://pxstud.io/docs/scripting.md) documents each one, generated from the editor itself, in sections: - [Drawing](https://pxstud.io/docs/scripting/drawing.md): the tools as commands (`app.drawLine`, `app.floodFill`, `app.drawIsoShape`...), [Images](https://pxstud.io/docs/scripting/images.md) for working pixel by pixel, and [Pixels](https://pxstud.io/docs/scripting/pixels.md) for flat pixel lists, fills and the eyedropper; - [Document](https://pxstud.io/docs/scripting/document.md), [Layers](https://pxstud.io/docs/scripting/layers.md), [Frames](https://pxstud.io/docs/scripting/frames.md), [Cels](https://pxstud.io/docs/scripting/cels.md), [Selection](https://pxstud.io/docs/scripting/selection.md), [Slices](https://pxstud.io/docs/scripting/slices.md) and [Tilemaps](https://pxstud.io/docs/scripting/tilemaps.md); - [Palette](https://pxstud.io/docs/scripting/palette.md), [Adjustments](https://pxstud.io/docs/scripting/adjustments.md) and [Brushes](https://pxstud.io/docs/scripting/brushes.md); - helpers: [Color](https://pxstud.io/docs/scripting/color.md) math, seeded [Random](https://pxstud.io/docs/scripting/random.md) numbers, and the [Editing context](https://pxstud.io/docs/scripting/context.md) (the current colors, brush and tool options). A few things to know: - **Start with `print(app.describe())`.** `app.describe()` returns the whole sprite's structure: layers, frames, cels, tags, slices, the selection and the current settings. - **Settings you leave out come from the editor.** A drawing command without a color uses the foreground color; without a brush, the current brush; and so on for each tool option. - **The selection and symmetry apply only when you ask.** Pass `respectSelection = true` to keep painting inside the selection, and `symmetry = app.getContext().symmetry` to mirror it. - **Work pixel by pixel in an Image.** `app.getImage(layer, frame)` reads a cel into an Image that lives inside the script; `img:getPixel` and `img:setPixel` are cheap, so loop over as many pixels as you like, then write it back once with `app.putImage(layer, frame, img)`. Every `app.*` call is a trip to the editor, an Image's methods aren't. On indexed sprites, `img:getIndex` and `img:setIndex` work with palette indices. - **Use `app.random` for randomness.** A generator made with `app.random.new(seed)` gives the same numbers for the same seed, so a run can be repeated exactly. `pairs()` visits a table's keys in the same order on every run too. - **Use `app.color` for color math.** Mixing, ramps and shades are worked out in a perceptual color space, which looks better than averaging hex values. - **Print to check things.** `print(...)` writes to the output area; tables print in full. For example, this scatters stars on a new layer; change the seed for a different sky: ```lua local layer = app.addLayer("Stars") local frame = app.getActiveFrameId() local rng = app.random.new(42) -- same seed, same stars local colors = app.color.ramp({ "#5a6b9e", "#ffffff" }, 4) local img = app.getImage(layer, frame) -- the layer's pixels, inside the script for i = 1, img.width * img.height // 40 do img:setPixel(rng:int(0, img.width - 1), rng:int(0, img.height - 1), rng:pick(colors)) end app.putImage(layer, frame, img) -- written back as one change ``` ## Config forms A script can ask for settings before it runs: when you choose **Run** (in the library, or in the Scripts panel), a form opens with its fields (numbers, text, checkboxes, choices, colors, and a brush or palette from your own libraries), and the script reads what you entered as the `app.config` table. Fields can show or hide depending on earlier choices. **Randomize** rolls new values within the ranges the script's author chose, for quick variations. The official generators all work this way. To give one of your scripts a form, write it as JSON in **Config form** in the script's settings, in the shape the [Config forms](https://pxstud.io/docs/scripting/config-forms.md) reference describes; the settings say what's wrong with it before saving. The form is part of the draft and of each published version. A script should still read every field as optional and fall back to its own defaults. For input while a script runs, open a script window instead. ## Script windows A script can open a window of its own with `Dialog{}`, built the way Aseprite scripts build them: labels, text and number boxes, sliders, checkboxes, radio buttons, drop-down lists, color fields, rows of color swatches, buttons and a file picker (small text files only). ```lua local dlg = Dialog{ title = "Quick Fill" } dlg:color{ id = "paint", label = "Color", color = "#3a8f4a" } dlg:button{ id = "fill", text = "Fill layer", onclick = function() local layer, frame = app.getActiveLayerId(), app.getActiveFrameId() app.floodFill(layer, frame, 0, 0, { color = dlg.data.paint }) end } dlg:show{ wait = false } ``` - `dlg:show{ wait = false }` opens a **floating window** and the script waits, idle, while you keep drawing; each button press or change it reacts to is its own undo step. Without `wait = false`, the window is modal: the editor is locked until you close it. - The window's title bar names the script and marks it as a script's, and everything in it is plain text, so a script can't pass its window off as part of the editor. - Closing the window ends the script; stopping the script closes its windows. The [Dialogs and commands](https://pxstud.io/docs/scripting/dialogs.md) reference has every widget and its options. ## Script commands A script can add **commands** to the editor's menus, the same way an Aseprite plugin does. Define an `init(plugin)` function that registers them with `plugin:newCommand`: ```lua local function mirror() app.flip("horizontal", app.getActiveLayerId()) end function init(plugin) plugin:newCommand{ id = "mirror", title = "Mirror Layer", group = "layer", onclick = mirror } end ``` Save the script, then choose Scripts > Script Commands... and press **Install** next to it. The command now appears: - in the menu its `group` names (`file`, `file_import`, `edit`, `edit_transform`, `edit_fx`, `edit_adjustments`, `sprite`, `layer`, `frame`, `select` or `view`), shown with the script's name; without a group, under Scripts, in a submenu named after the script; - in the command palette; - in Settings > Keyboard Shortcuts..., where you can give it a shortcut. Running the command runs your saved script and then the command's `onclick`, as one undo step. After changing which commands a script registers, press **Reinstall**; **Remove** takes them out of the menus. A script can add up to 32 commands. Installed commands are kept in this browser. To install commands from someone else's script, fork it into your scripts first. ## Saving and publishing Your scripts are saved in this browser, like your sprites, and work offline. With [Sync](https://pxstud.io/docs/manual/concepts/how-work-is-organized.md#where-things-are-kept), they're also kept in your account and on your other devices. **Publishing** shares a script in the script library. Every script starts **Private**, so only you can see it; choose **Shared in the library** in its settings (**Script settings**, or the Private badge) to list it. Then: - **Save draft** keeps your edits private. People using the library always get your last published version, never the draft. - **Publish** saves the draft and turns it into the next numbered version, with the release note you type (shown in the version history). Published versions never change; publish again to make a new one. - The script's settings list every published version. **View code** opens an old one read-only, and **Restore to draft** copies it into your draft. - A script can have up to 3 tags, which help people find it in the library. Publishing needs an account with Sync on pxstud.io. Running, forking and hearting scripts (with a free account) is covered in [The library](https://pxstud.io/docs/manual/guides/library.md). ## What scripts can't do Scripts are sandboxed, so running a script someone else wrote is safe: - **No network, files or code loading.** A script can't reach the internet or your files (apart from a text file you pick in its window), and can't load other code. Lua's `io`, `os` and `debug` libraries aren't there; `string`, `math`, `table`, `utf8` and `coroutine` are. - **Only the open sprite.** A script edits the sprite you ran it on, as one undo step. It can't open, save, export, close or delete sprites, use the clipboard, or change the view and panels. - **Your libraries only when you pick from them.** A script sees one of your saved brushes or palettes only when its config form asks and you choose it. - **A memory budget.** A run can't use more than a set amount of memory: for its Images, its Lua data, the pixels it adds to the sprite and the undo step it builds. Past it the script is stopped with an error that says which, and what it did so far stays as one undo step, so a runaway script can't crash the editor or push your earlier steps out of the undo history. There's no time limit; a script that runs away can always be paused or stopped. ## The official generators pxstud.io's script library has a set of official scripts, marked **Official**: generators for gradients, checkerboards, noise fills, brick walls, cobblestones, wood planks, grass and water textures, lightning, tornadoes, rainbows, comets, galaxies, planets, stars, trees, bushes and rocks. Sign in with a free account, open Scripts > Browse Script Library..., choose **Official**, and press **Run** on one: fill in its form (or press **Randomize**) and it draws on your sprite. Fork one to read how it's made, or to change it. # pxstud.io 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. ## Option groups Sets of options shared by several functions, defined once here. A function's `opts` table says which groups it also takes — "also takes the Painting options" means every option below is accepted, with the same meaning and defaults, in addition to the function's own. ### Painting options (`paint`) Taken by every command that paints pixels (the drawing functions and the stroke tools). Each is optional. By default a draw command ignores the selection and does not mirror, whatever the editor currently shows; opt in with respectSelection = true and symmetry = app.getContext().symmetry (or an axes list). tilemapMode is filled from the editing context. Taken by: `app.blur`, `app.dodgeBurn`, `app.drawContour`, `app.drawCurve`, `app.drawEllipse`, `app.drawGradient`, `app.drawHexagon`, `app.drawIsoShape`, `app.drawLine`, `app.drawPolygon`, `app.drawRectangle`, `app.drawStar`, `app.drawStroke`, `app.drawTriangle`, `app.floodFill`, `app.jumble`, `app.scatter`, `app.spray`. - `respectSelection` (boolean; default false) — Pass true to paint only inside the current selection (read it with app.getSelection / app.isSelected). By default the selection is ignored. - `symmetry` (list of SymmetryAxis: "horizontal" | "vertical" | "diagonal" | "antidiagonal"; default none) — Mirror axes through the canvas center (or through symmetryX / symmetryY); combinations (horizontal + vertical) give 4-way symmetry. Default none: pass symmetry = app.getContext().symmetry to follow the editor's symmetry toggle, or an axes list such as { "vertical" }. - `symmetryX` (number ≥ 0; default the canvas center (width / 2)) — Where the vertical axis (and the point both diagonals cross) sits: pixels from the canvas's left edge, a multiple of 0.5 (whole = between pixels, x.5 = through a pixel's middle). Clamped to the canvas. Pass app.getContext().symmetryX to follow the editor's guide (nil there means centered). - `symmetryY` (number ≥ 0; default the canvas center (height / 2)) — Where the horizontal axis (and the diagonals' crossing) sits: pixels from the canvas's top edge, a multiple of 0.5. Clamped to the canvas. - `tilemapMode` (TilemapEditMode: "pixels-edit" | "pixels-auto"; default context.tilemapMode) — How a tilemap layer takes pixel paint: auto gives each painted cell its own tile, edit changes the shared tile in place. Ignored on image layers. ### Placement options (`tilePlace`) Each is optional. By default tiles go on every given cell, whatever the editor shows. Mirrored cells get the mirrored tile: a vertical axis toggles flipH, a horizontal one flipV, a diagonal one transposes it (rotate90 plus a flip). A cell on an axis keeps the tile as placed. Taken by: `app.fillTiles`, `app.stampTiles`. - `respectSelection` (boolean; default false) — Pass true to skip cells with no selected pixel (a fill also stops at them). - `symmetry` (list of SymmetryAxis: "horizontal" | "vertical" | "diagonal" | "antidiagonal"; default none) — Also place on the mirrored cells across these axes (and their combinations). Pass app.getContext().symmetry to follow the editor's toggle. - `symmetryX` (number ≥ 0; default the canvas center (width / 2)) — Where the vertical axis sits, in pixels from the canvas's left edge (a multiple of 0.5). - `symmetryY` (number ≥ 0; default the canvas center (height / 2)) — Where the horizontal axis sits, in pixels from the canvas's top edge (a multiple of 0.5). ### Brush options (`brush`) Taken by the stroke tools (drawStroke, blur, jumble). Pass a built-in brush by size and shape, or a custom brush by name or inline. Taken by: `app.blur`, `app.dodgeBurn`, `app.drawStroke`, `app.jumble`, `app.scatter`. - `brushSize` (integer 1–64; default context.brushSize) — Brush size in pixels. - `brushShape` (BrushShape: "circle" | "square" | "line" | "diamond" | "cross" | "x" | "star" | "horizontal"; default context.brushShape) — Brush shape. For a custom brush use `brush`. - `brushAngle` (integer 0–180; default context.brushAngle) — Brush angle in degrees: tilts the "line" shape (0 = horizontal, 90 = vertical) and rotates "square". - `brush` (brush name, or an inline { rows } / { width, height, mask }) — A custom brush: the name of one you defined with app.defineBrush, or an inline definition. Replaces brushSize, brushShape and brushAngle. ### Ink options (`ink`) Taken by every painting command except erase: how the paint color combines with the pixels already there. Omitted options come from the editing context (ink, shades). With ink = "shading" the color is ignored and each touched pixel whose color is in `shades` moves one step along that list (once per command, however often the shape covers it). Taken by: `app.drawContour`, `app.drawCurve`, `app.drawEllipse`, `app.drawGradient`, `app.drawHexagon`, `app.drawIsoShape`, `app.drawLine`, `app.drawPolygon`, `app.drawRectangle`, `app.drawStar`, `app.drawStroke`, `app.drawTriangle`, `app.floodFill`, `app.scatter`, `app.spray`. - `ink` (Ink: "copy" | "simple" | "lock-alpha" | "shading"; default context.ink) — copy replaces pixels, simple alpha-composites, lock-alpha recolors only visible pixels, shading steps colors along `shades`. Ignored by erase. - `shades` (a list of colors ("#rrggbb[aa]" or { r, g, b, a }), in ramp order; default context.shades) — The ramp shading steps along. A pixel whose color is not in the list is untouched; a pixel at the end of the list stays there. - `shadeReverse` (boolean; default false) — Shading steps backward in `shades` (the GUI's right mouse button) instead of forward. ## Functions ### Document The sprite as a whole: start here with app.describe(), then size, crop, rotate and color mode. #### `app.convertColorMode(colorMode, opts?)` — changes, undoable Converts the sprite between color modes (rgba, grayscale, indexed), optionally dithering and generating a palette when going to indexed. - `colorMode` (ColorMode: "rgba" | "grayscale" | "indexed") — Conversions out of rgba (and into grayscale) are lossy: color quantizes to its nearest palette entry or its Rec. 709 luma (Aseprite's, truncated: red is 54). Into grayscale the palette becomes 256 grays, black to white (per-frame palettes and palette groups go, the transparent index becomes 0). No-op if the sprite is already in that mode. - `opts` (table, optional) - `dithering` (DitherMethod: "none" | "bayer2" | "bayer4" | "bayer8" | "floydSteinberg" | "atkinson"; default "none") — Into indexed only: how colours between palette entries are approximated. - `ditherAmount` (integer 0–100; default 100) — Into indexed only: dithering strength (0 = nearest colour only). - `paletteColors` (integer 1–256; default keep the current palette) — Into indexed only: replace the palette with this many colours generated from the sprite (plus a transparent entry at index 0 unless the Background is the only layer; per-frame palettes are dropped). Returns nothing. #### `app.cropToSelection()` — changes, undoable Crops the canvas to the current selection's bounds; a selection reaching past the canvas grows it there. No-op if nothing is selected. Returns nothing. #### `app.describe()` — reads Everything about the document's structure in one call (no pixel data): { sprite, activeLayerId, activeFrameId, layers, frames, cels, tags, slices, selection (bounds or nil), context } — each part in the shape of its own query (app.getSprite, app.getLayers, ...). Start here to see what you're working with. Returns (table): Document table. #### `app.flip(axis, layerId?)` — changes, undoable, refused on a locked layer Mirrors the whole canvas, or one layer (a group's descendants too) across every frame: cel pixels and positions, tilemap tiles, reference images. Unlike app.flipCelRegion it ignores the selection. - `axis` (FlipAxis: "horizontal" | "vertical") - `layerId` (string, optional) — The layer (or group) to flip on every frame. Omit to flip the whole canvas: every layer, every frame, the slices and the selection. Returns nothing. #### `app.getCanvasHeight()` — reads The sprite's canvas height in pixels. Returns (number): Height in pixels. #### `app.getCanvasWidth()` — reads The sprite's canvas width in pixels. Returns (number): Width in pixels. #### `app.getColorMode()` — reads The sprite's color mode: "rgba", "grayscale", or "indexed". Returns (string): "rgba", "grayscale" or "indexed". #### `app.getSprite()` — reads The sprite: { id, width, height, colorMode ("rgba" | "grayscale" | "indexed"), palette (hex strings), transparentIndex (indexed sprites: the palette index whose pixels are transparent), pixelRatio ({ w, h }; 1:1 = square pixels), colorDepth (levels per channel: { kind = "rgb", red, green, blue } or { kind = "gray", gray }, with profile; nil for full color), layerCount, frameCount }. Returns (table): Sprite table. #### `app.newSprite(width, height, background?)` — changes, undoable Replaces the whole document with a brand new sprite (one layer, one frame; a Background layer when a background colour is given). Discards the current document's content. - `width` (number) — Canvas width in pixels. - `height` (number) — Canvas height in pixels. - `background` (color, optional) — Fill colour of a Background layer. Omitted = a transparent "Layer 1" instead. Returns (table): { layerId, frameId } of the new sprite's one layer and frame. #### `app.rotateCanvas(degrees)` — changes, undoable Rotates the whole canvas (every layer, every frame) clockwise (90), counter-clockwise (-90), or 180 degrees. - `degrees` (number) — 90, -90, or 180. Returns nothing. #### `app.setCanvasSize(width, height, anchor)` — changes, undoable Resizes the canvas, repositioning (not resampling) existing content relative to `anchor`. Content left past the new edge is kept there (only the Background layer is cut), so growing back restores it. - `width` (number) - `height` (number) - `anchor` (CanvasAnchor: "top-left" | "top" | "top-right" | "left" | "center" | "right" | "bottom-left" | "bottom" | "bottom-right") Returns nothing. #### `app.setPixelRatio(w, h)` — changes, undoable Sets the sprite's pixel aspect ratio (Aseprite's Sprite Properties > Pixel Ratio), e.g. 2, 1 for pixels twice as wide as tall. Only display and ratio-aware exports change, not the pixels. - `w` (number) — Pixel width, a positive integer (1:1 = square). - `h` (number) — Pixel height, a positive integer. Returns nothing. #### `app.setSpriteSize(width, height)` — changes, undoable Resamples every cel (nearest-neighbor) to fit a new sprite size. - `width` (number) - `height` (number) Returns nothing. #### `app.setUserData(target, userData?)` — changes, undoable Sets the user data (colour tag, text and custom properties, as in Aseprite) of a sprite, layer, cel, tag, slice or tileset. A cel target may be a tilemap frame. Linked cels share one user data, like their image. Changes only what you give, so setting the text keeps the colour, and writing your extension's properties namespace keeps everyone else's (as Aseprite's layer.data, layer.color and layer.properties(ns) are separate). Read it back from app.getSprite, getLayers, getCels, getTags, getSlices or getTileset (the userData field). Allowed on locked layers. - `target` (table) — What to annotate: { kind = "sprite" }, { kind = "layer", layerId }, { kind = "cel", layerId, frameId }, { kind = "tag", tagId }, { kind = "slice", sliceId } or { kind = "tileset", layerId }. - `kind` (UserDataKind: "sprite" | "layer" | "cel" | "tag" | "slice" | "tileset") — Which kind of thing. - `layerId` (string) — For layer, cel and tileset targets. - `frameId` (string) — For cel targets. - `tagId` (string) — For tag targets. - `sliceId` (string) — For slice targets. - `userData` (table, optional) — What to change: only the parts you give. text = "" or color = "" clears that part; in properties, each namespace you name is replaced and one set to {} is removed, while the others stay. nil removes all the user data. - `color` (string) — The colour tag as "#rrggbb" or "#rrggbbaa". Not accepted for tags (their colour is Tag.color). - `text` (string) — Free text (Aseprite's user data text). - `properties` (namespaces to properties: { [""] = { score = 3 }, ["my-extension"] = { ... } }. Values are plain JSON: nil-free, finite numbers, strings, booleans, lists and tables. properties[""] is your own namespace (Aseprite's user properties).) — Custom properties by namespace. Aseprite's typed values (uuid, fixed, point, ...) appear as tables with a $type key, e.g. { ["$type"] = "uuid", value = "..." }, and are written back as the Aseprite type. Returns nothing. #### `app.trim()` — changes, undoable Crops the canvas to what's drawn across every frame, as Aseprite's Trim: the visible layers as shown, shrunk against transparency or a solid border colour (such as an opaque Background). No-op when nothing is left to cut. Returns nothing. ### 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. #### `app.blur(layerId, frameId, points, opts?)` — changes, undoable, refused on a locked layer Blurs the pixels under the brush along the points — exactly the GUI blur tool. - `layerId` (string) - `frameId` (string) - `points` (table) — List of { x = n, y = n } (or { n, n }) points; gaps between consecutive points are interpolated like a real drag. - `opts` (table, optional) Also takes the **Painting options** and **Brush options** (see "Option groups"), and: - `strength` (integer 0–8; default context.blurStrength) — Blur radius in pixels. Returns nothing. #### `app.dodgeBurn(layerId, frameId, points, opts?)` — changes, undoable, refused on a locked layer Lightens (dodge) or darkens (burn) the pixels under the brush along the points, in OKLCH lightness — exactly the GUI dodge/burn tool. Hue and chroma are kept unless `hueShift` is set. Each pixel changes once per call; transparent pixels are untouched. - `layerId` (string) - `frameId` (string) - `points` (table) — List of { x = n, y = n } (or { n, n }) points; gaps between consecutive points are interpolated like a real drag. - `opts` (table, optional) Also takes the **Painting options** and **Brush options** (see "Option groups"), and: - `strength` (integer 1–100; default context.dodgeBurnStrength) — How far each touched pixel's OKLCH lightness moves: 100 = half of black-to-white, 25 = 0.125. - `hueShift` (integer 0–60; default context.dodgeBurnHueShift) — Degrees each touched pixel's OKLCH hue turns toward warm yellow (dodge) or cool blue (burn), never past it, with chroma lessened (dodge) or deepened (burn) by 0.5% per degree. 0 keeps hue and chroma. - `burn` (boolean; default false) — Darken (burn) instead of lighten (dodge). Returns nothing. #### `app.drawContour(layerId, frameId, points, opts?)` — changes, undoable, refused on a locked layer Fills the closed path through the points, the path pixels included (Aseprite's contour) — exactly the GUI contour tool. - `layerId` (string) - `frameId` (string) - `points` (table) — The freehand path as a list of { x = n, y = n } points; it is auto-closed and filled. - `opts` (table, optional) Also takes the **Painting options** and **Ink options** (see "Option groups"), and: - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The paint color. Returns nothing. #### `app.drawCurve(layerId, frameId, anchors, opts?)` — changes, undoable, refused on a locked layer Draws a path of cubic Bezier curves through the anchors — exactly the GUI curve tool. Pass closed = true for a closed shape (and fill = "filled" to fill it). - `layerId` (string) - `frameId` (string) - `anchors` (table) — The path's anchors in order: { x = n, y = n } (or { n, n }), each optionally with Bezier handles inX/inY (the control point of the segment arriving) and outX/outY (of the segment leaving). An anchor without handles is a sharp corner; two in a row are joined by a straight line. - `opts` (table, optional) Also takes the **Painting options** and **Ink options** (see "Option groups"), and: - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The paint color. - `closed` (boolean; default false) — Join the last anchor back to the first. - `fill` (FillMode: "outline" | "filled"; default context.shapeFillMode) — Closed paths: draw the outline, or fill the inside too. - `width` (integer 1–64; default context.lineWidth) — Stroke thickness in pixels (1 = a hard 1px curve); wider strokes get round ends. - `pixelPerfect` (boolean; default context.curvePixelPerfect) — Remove the L-shaped corners a 1px curve picks up, for clean pixel-art lines (ignored for wider strokes). Returns nothing. #### `app.drawEllipse(layerId, frameId, x0, y0, x1, y1, opts?)` — changes, undoable, refused on a locked layer Draws an ellipse inscribed in the box spanning the two corners — exactly the GUI ellipse tool. - `layerId` (string) - `frameId` (string) - `x0` (number) — First corner. - `y0` (number) - `x1` (number) — Opposite corner (inclusive). - `y1` (number) - `opts` (table, optional) Also takes the **Painting options** and **Ink options** (see "Option groups"), and: - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The paint color. - `fill` (FillMode: "outline" | "filled"; default context.shapeFillMode) — Outline only, or filled. Returns nothing. #### `app.drawGradient(layerId, frameId, x0, y0, x1, y1, opts?)` — changes, undoable, refused on a locked layer Fills the region the bucket would fill from (x0, y0) — the connected area of its colour (or every matching pixel when not contiguous), inside the selection — with a gradient from startColor at (x0, y0) to endColor at (x1, y1), or along `stops` — exactly the GUI gradient tool. Under symmetry the mirrored regions get the same gradient. A zero-length drag, or a start off the canvas or outside the selection, is a no-op. - `layerId` (string) - `frameId` (string) - `x0` (number) — Gradient start (where startColor sits). - `y0` (number) - `x1` (number) — Gradient end (where endColor sits). - `y1` (number) - `opts` (table, optional) Also takes the **Painting options** and **Ink options** (see "Option groups"), and: - `startColor` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The color at the first point. - `endColor` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.backgroundColor) — The color at the second point. - `stops` ({ color, color, ... } or { { color = c, position = 0-1 }, ... }; default When gradientColors is "stops" and neither startColor nor endColor is given, the gradientStops list.) — A multi-stop ramp of 2-32 colors from the first point to the second, instead of startColor and endColor. Plain colors are spread evenly; positions run 0-1 in order, and two stops at one position make a hard edge. - `gradientType` (GradientType: "linear" | "radial"; default context.gradientType) — A straight ramp between the points, or radiating from the first. - `dithering` (GradientDithering: "none" | "bayer2" | "bayer4" | "bayer8" | "dots" | "lines-h" | "lines-v" | "diagonal" | "noise"; default context.gradientDithering) — Ordered dithering applied to the ramp (none = smooth). - `snap` (GradientSnap: "free" | "palette"; default context.gradientSnap) — Free blends, or only palette colors (dithering then steps between neighbouring palette colors). - `contiguous` (boolean; default context.bucketFillMode (true for the context's contiguous mode, false for global)) — Only the connected region (true), or every matching pixel on the canvas (false). - `tolerance` (integer 0–255; default context.bucketTolerance) — The largest per-channel color difference that still counts as a match. - `diagonal` (boolean; default context.bucketDiagonal) — Contiguous only: also spread to diagonal neighbours (8-connected), so the region leaks through 1px diagonal lines. - `stopAtGrid` (boolean; default context.bucketStopAtGrid) — Confine the region to the grid cell the click lands in (an isometric grid's cell is the diamond it draws, with the line pixels of its two upper edges). - `closeGap` (integer 0–5; default context.bucketCloseGap) — Contiguous fills: treat gaps in the outline up to this many pixels wide as closed, so the fill stops at them instead of leaking out. 0 = off. Returns nothing. #### `app.drawHexagon(layerId, frameId, x0, y0, x1, y1, opts?)` — changes, undoable, refused on a locked layer Draws a flat-top hexagon filling the box spanning the two corners — exactly the GUI hexagon tool. - `layerId` (string) - `frameId` (string) - `x0` (number) — First corner. - `y0` (number) - `x1` (number) — Opposite corner (inclusive). - `y1` (number) - `opts` (table, optional) Also takes the **Painting options** and **Ink options** (see "Option groups"), and: - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The paint color. - `fill` (FillMode: "outline" | "filled"; default context.shapeFillMode) — Outline only, or filled. Returns nothing. #### `app.drawIsoShape(layerId, frameId, x, y, width?, depth?, height?, opts?)` — changes, undoable, refused on a locked layer Draws an isometric solid (box, ramp, stairs, pyramid, prism, cylinder, cone, sphere or dome) in pixel-art projection — exactly the GUI Iso Shape tool. Edges are clean staircases (2 px across per 1 px down at 2:1), faces are filled with the top / left / right colors, inner edges separate them and the silhouette is outlined. The footprint's back corner sits at (x, y); x runs down-right and y down-left on screen, and height raises the top. The round solids (cylinder, cone, sphere, dome) take width as their diameter and ignore depth; the sphere also ignores height. - `layerId` (string) - `frameId` (string) - `x` (number) — Anchor: the leftmost pixel of the cap at the footprint's back (top) corner, on the ground. - `y` (number) - `width` (number, optional) — Footprint along x (down-right), in iso units: each unit is one row of the 2:1 edge staircase. nil = the context's isoWidth. - `depth` (number, optional) — Footprint along y (down-left), in iso units. nil = the context's isoDepth. - `height` (number, optional) — Height in pixels (0 = a flat diamond). nil = the context's isoHeight. - `opts` (table, optional) Also takes the **Painting options** and **Ink options** (see "Option groups"), and: - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The base color: the top face when auto-shading. - `shape` (IsoShape: "box" | "ramp" | "stairs" | "pyramid" | "prism" | "cylinder" | "cone" | "sphere" | "dome"; default context.isoShape) — Which solid to draw. - `cap` (integer 2–4; default context.isoCap) — The flat run of pixels at the top and bottom vertices: 2 (even widths), 3 (odd) or 4 (multiples of four). Ratios above 2 have a wider natural cap. - `bevel` (integer 0–1024; default context.isoBevel) — Box only: chamfers the four top edges by this many pixels, a 45-degree slope between the top and the sides (0 = square edges). Clamped to the height and half the footprint. - `style` (IsoShapeStyle: "filled" | "outline" | "filled-outline"; default context.isoStyle) — Faces, outline, or both. - `hiddenEdges` (boolean; default context.isoHiddenEdges) — Also draw the edges behind the solid, in the inner-edge color (a see-through wireframe with style outline). - `direction` (IsoDirection: "+x" | "-x" | "+y" | "-y"; default context.isoDirection) — Ramp and stairs: the way they descend. Prism: the way one slope faces (+x and -x give the same roof). Ignored by the other shapes. - `ratio` (integer 1–8; default the sprite's isometric grid ratio (run / rise, rounded), else 2) — Pixels across per pixel down along the iso axes. - `colors` ({ outline =, innerEdge =, top =, left =, right = } colors; default auto-shaded from color, or the context's isoColors when isoAutoShade is off) — All five colors at once (left is the face toward +y, down-left; right the face toward +x). Overrides auto-shading. - `autoShade` (boolean; default context.isoAutoShade) — Derive the colors from `color`: top = color, the sides one and two steps darker along `ramp` (or computed), inner edges darker still, the outline darkest. false uses the context's isoColors. Ignored when colors is given. - `ramp` (a list of colors ("#rrggbb[aa]" or { r, g, b, a }); default context.shades) — The ramp auto-shading steps along when it contains `color` (the darker end is found by brightness). Otherwise the shades are computed, and snapped to the palette on an indexed sprite. Returns nothing. #### `app.drawLine(layerId, frameId, x0, y0, x1, y1, opts?)` — changes, undoable, refused on a locked layer Draws a straight line between the two points — exactly the GUI line tool (width thickens it with round ends). - `layerId` (string) - `frameId` (string) - `x0` (number) - `y0` (number) - `x1` (number) - `y1` (number) - `opts` (table, optional) Also takes the **Painting options** and **Ink options** (see "Option groups"), and: - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The paint color. - `width` (integer 1–64; default context.lineWidth) — Line thickness in pixels (1 = a hard 1px line); wider lines get round ends. Returns nothing. #### `app.drawPolygon(layerId, frameId, vertices, opts?)` — changes, undoable, refused on a locked layer Draws a closed polygon through the vertices — exactly the GUI polygon tool. - `layerId` (string) - `frameId` (string) - `vertices` (table) — List of { x = n, y = n } (or { n, n }) vertices, at least 2; the polygon closes back to the first automatically. - `opts` (table, optional) Also takes the **Painting options** and **Ink options** (see "Option groups"), and: - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The paint color. - `fill` (FillMode: "outline" | "filled"; default context.shapeFillMode) — Outline only, or filled. Returns nothing. #### `app.drawRectangle(layerId, frameId, x0, y0, x1, y1, opts?)` — changes, undoable, refused on a locked layer Draws an axis-aligned rectangle spanning the two corners — exactly the GUI rectangle tool (radius rounds the corners, together or one by one). Ignores the selection unless respectSelection = true. - `layerId` (string) - `frameId` (string) - `x0` (number) — First corner. - `y0` (number) - `x1` (number) — Opposite corner (inclusive). - `y1` (number) - `opts` (table, optional) Also takes the **Painting options** and **Ink options** (see "Option groups"), and: - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The paint color. - `fill` (FillMode: "outline" | "filled"; default context.shapeFillMode) — Outline only, or filled. - `radius` (an integer 0–1024 for every corner, or a list of four { topLeft, topRight, bottomRight, bottomLeft }; default context.cornerRadius) — Rounds the corners, in pixels (0 = square): one radius for all four, or one per corner going clockwise from the top-left. Each is clamped to half the smaller side. Returns nothing. #### `app.drawStar(layerId, frameId, x0, y0, x1, y1, opts?)` — changes, undoable, refused on a locked layer Draws a 5-point star (point up) whose outer points touch every side of the box spanning the two corners — exactly the GUI star tool. - `layerId` (string) - `frameId` (string) - `x0` (number) — First corner. - `y0` (number) - `x1` (number) — Opposite corner (inclusive). - `y1` (number) - `opts` (table, optional) Also takes the **Painting options** and **Ink options** (see "Option groups"), and: - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The paint color. - `fill` (FillMode: "outline" | "filled"; default context.shapeFillMode) — Outline only, or filled. Returns nothing. #### `app.drawStroke(layerId, frameId, points, opts?)` — changes, undoable, refused on a locked layer Freehand brush stroke through the points — exactly the GUI pencil (or, with erase = true, the eraser; with dither, the dither brush). - `layerId` (string) - `frameId` (string) - `points` (table) — List of { x = n, y = n } (or { n, n }) points; gaps between consecutive points are interpolated like a real drag. - `opts` (table, optional) Also takes the **Painting options**, **Brush options** and **Ink options** (see "Option groups"), and: - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The paint color. - `erase` (boolean; default false) — Erase to transparent instead of painting (the eraser); `color` is ignored. - `pixelPerfect` (boolean; default context.pixelPerfect) — Remove doubled corner steps from the stroke path (any brush size). - `matchColor` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default nil (every pixel)) — Only paint (or erase) pixels that are exactly this color; others are untouched. With color = the background color and matchColor = the foreground color this is Aseprite's right-click eraser. - `dither` ({ pattern = "bayer2" | "bayer4" | "bayer8" | "dots" | "lines-h" | "lines-v" | "diagonal" | "noise", density = 0-100 }; default nil (every pixel)) — Only paint (or erase) the pixels of an ordered pattern at a density in percent, leaving the rest untouched: the dither brush (50 with bayer2 is a checkerboard). The pattern tiles from the sprite's top-left corner, so overlapping strokes stay aligned. - `dynamics` ({ stabilizer = 0-64, pressure = { size, angle, gradient (booleans), min, max (0-100) }, velocity = { same }, minSize, minAngle, maxAngle, dither, curve = 25-400 }; default built from the context's stabilizer, pressure*/velocity* and dynamics* fields; pass {} for a plain stroke) — Brush dynamics: a stabilizer (moving average of the points) and pressure / velocity sensors that drive the stamp size (from minSize up to the brush size), angle (minAngle to maxAngle) and a color gradient from color to endColor (dither = true picks one or the other per pixel; indexed sprites snap to the nearest palette color). min/max are the sensor range in percent (min > max inverts); curve is the sensitivity in percent (100 = linear). With pixelPerfect and a varying size, elbows are still removed along the center path. Custom brushes only get the stabilizer and gradient. - `pressures` (list of numbers 0-1, one per point; default 1 (full pressure) for every point) — Per-point pen pressure read by the pressure sensor. A mouse has none, so it counts as full. - `velocities` (list of numbers >= 0, one per point; default 0 for every point) — Per-point pointer speed in sprite pixels per millisecond (0.5 = the sensor's maximum) read by the velocity sensor. - `endColor` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.backgroundColor) — The gradient's end color when a sensor drives the gradient; `color` is the start. Returns nothing. #### `app.drawTriangle(layerId, frameId, x0, y0, x1, y1, opts?)` — changes, undoable, refused on a locked layer Draws an isosceles triangle (apex top-centre, base along the bottom) filling the box spanning the two corners — exactly the GUI triangle tool. - `layerId` (string) - `frameId` (string) - `x0` (number) — First corner. - `y0` (number) - `x1` (number) — Opposite corner (inclusive). - `y1` (number) - `opts` (table, optional) Also takes the **Painting options** and **Ink options** (see "Option groups"), and: - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The paint color. - `fill` (FillMode: "outline" | "filled"; default context.shapeFillMode) — Outline only, or filled. Returns nothing. #### `app.floodFill(layerId, frameId, x, y, opts?)` — changes, undoable, refused on a locked layer Paint-bucket fill starting at (x, y) — exactly the GUI bucket tool. - `layerId` (string) - `frameId` (string) - `x` (number) - `y` (number) - `opts` (table, optional) Also takes the **Painting options** and **Ink options** (see "Option groups"), and: - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The paint color. - `contiguous` (boolean; default context.bucketFillMode (true for the context's contiguous mode, false for global)) — Only the connected region (true), or every matching pixel on the canvas (false). - `tolerance` (integer 0–255; default context.bucketTolerance) — The largest per-channel color difference that still counts as a match. - `diagonal` (boolean; default context.bucketDiagonal) — Contiguous only: also spread to diagonal neighbours (8-connected), so the region leaks through 1px diagonal lines. - `stopAtGrid` (boolean; default context.bucketStopAtGrid) — Confine the region to the grid cell the click lands in (an isometric grid's cell is the diamond it draws, with the line pixels of its two upper edges). - `closeGap` (integer 0–5; default context.bucketCloseGap) — Contiguous fills: treat gaps in the outline up to this many pixels wide as closed, so the fill stops at them instead of leaking out. 0 = off. - `pattern` (FillPattern: "solid" | "checker" | "dither25" | "dither75" | "hstripes" | "vstripes" | "diagonal" | "grid" | "dots" | "bricks"; default context.bucketPattern) — Fill with a repeating pattern of `color` and `patternColor`, tiled from the canvas's top-left corner. "solid" = just `color`. - `patternColor` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.backgroundColor) — The pattern's second color (ignored when `pattern` is "solid"). Returns nothing. #### `app.isoProject(layerId, frameId, projection, opts?)` — changes, undoable, refused on a locked layer Projects the selected pixels (the marquee follows) — or the whole cel when nothing is selected — onto a face of an isometric cube: "left" and "right" shear the columns along the slope, "floor" turns the region into the top face (a square becomes a diamond). Pixel-exact: every edge comes out as a clean staircase (2 across per 1 down at 2:1) and no colors are blended. Content landing outside the canvas and the cel bounds is cut (the Background layer is cut to the canvas). - `layerId` (string) - `frameId` (string) - `projection` (IsoProjection: "left" | "right" | "floor") - `opts` (table, optional) - `run` (integer 1–16; default The sprite's isometric grid ratio, or 2 (2:1) when the grid is rectangular.) — Pixels across per `rise` pixels down (the slope, in lowest terms). Give rise too. - `rise` (integer 1–16; default The sprite's isometric grid ratio, or 1 (2:1) when the grid is rectangular.) — Pixels down per `run` pixels across. Give run too. Returns nothing. #### `app.jumble(layerId, frameId, points, opts?)` — changes, undoable, refused on a locked layer Randomized smudge: scrambles pixels under the brush with random neighbors along the points — exactly the GUI jumble tool. - `layerId` (string) - `frameId` (string) - `points` (table) — List of { x = n, y = n } (or { n, n }) points; gaps between consecutive points are interpolated like a real drag. - `opts` (table, optional) Also takes the **Painting options** and **Brush options** (see "Option groups"), and: - `strength` (integer 0–100; default context.jumbleStrength) — Percent chance each touched pixel is scrambled. A 1px brush has no neighbors to scramble with, so use brushSize 2 or more. - `seed` (integer 0–4294967295; default random) — Random seed: the same seed and points give the same result. Returns nothing. #### `app.movePixels(layerId, frameId, dx, dy)` — changes, undoable, refused on a locked layer Moves the selected pixels (per the current selection; the marquee follows) — or the whole cel when nothing is selected — by (dx, dy). Exactly the GUI move tool. Without a selection the whole cel moves and keeps everything, past the canvas edge too; moved selection pixels are kept within the canvas and the cel's bounds, and cut beyond. The Background layer is cut to the canvas. - `layerId` (string) - `frameId` (string) - `dx` (number) — Pixels to move right (negative = left). - `dy` (number) — Pixels to move down (negative = up). Returns nothing. #### `app.outline(layerId, frameIds, opts?)` — changes, undoable, refused on a locked layer Filter > Outline, as in Aseprite: draws an outline around (or just inside) the shapes on a layer's cels, as a single undo step. A pixel is part of a shape unless it is transparent (or matches bgColor). Outside outlines grow the cel, clipped to the canvas. Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer are outlined (one frame for the active cel, every frame for all of them). - `opts` (table, optional) - `cels` (Array of { layerId = ..., frameId = ... }) — Outline these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The outline color. On an indexed sprite it snaps to the nearest palette entry unless colorIndex is given. - `colorIndex` (integer 0–255; default Taken from color.) — Indexed sprites: the palette index to paint, used as-is. Not the transparent index. - `place` (OutlinePlace: "outside" | "inside"; default "outside") — Outline outside the shape (it grows) or inside it (its edge pixels are repainted). - `shape` (OutlineShape: "circle" | "square"; default "circle") — Preset for the neighbor matrix. Ignored when matrix is given. - `matrix` (9 booleans, row-major 3x3 (top-left first): which neighbors count as touching. The center entry is ignored.) — A custom neighbor matrix, like Aseprite's 3x3 toggle grid. - `bgColor` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default Only transparent pixels are background.) — An opaque color that also counts as background, besides fully transparent pixels. - `bgIndex` (integer 0–255) — Indexed sprites: a palette index that also counts as background. - `respectSelection` (boolean; default false) — Pass true to outline only inside the current selection. By default the selection is ignored. Returns nothing. #### `app.pastePixels(layerId, frameId, image, opts?)` — changes, undoable, refused on a locked layer Pastes an image onto a layer/frame's cel like the GUI's floating paste: transparent pixels leave the cel alone, partly transparent ones blend over it, and the pasted pixels become the selection. The optional transform distorts it (corners), then scales, rotates and/or skews the image about its center (unless pivotX/pivotY are given), then shifts it by dx/dy. Needs an editable (unlocked) image layer; a movement lock does not apply. What lands outside the canvas and the cel's bounds is cut (the Background layer is cut to the canvas). - `layerId` (string) - `frameId` (string) - `image` (table) — { x, y, width, height, pixels } — pixels is a list of width*height colors ("#rrggbb[aa]" or { r, g, b, a }), row by row; x, y is where the top-left lands. - `opts` (table, optional) - `scaleX` (number -256–256; default 1) — Horizontal scale about the pivot; negative flips. Not 0. - `scaleY` (number -256–256; default 1) — Vertical scale about the pivot; negative flips. Not 0. - `rotation` (number; default 0) — Degrees clockwise about the pivot. - `skewX` (number -80–80; default 0) — Degrees of horizontal shear (the top leans one way, the bottom the other, about the pivot). - `skewY` (number -80–80; default 0) — Degrees of vertical shear. - `dx` (number; default 0) — Pixels to shift right after transforming. - `dy` (number; default 0) — Pixels to shift down after transforming. - `pivotX` (number; default The center of the selection (or of the cel when nothing is selected).) — X of the fixed point for rotate/scale/skew, in canvas pixels. - `pivotY` (number; default The center of the selection (or of the cel when nothing is selected).) — Y of the fixed point for rotate/scale/skew, in canvas pixels. - `interpolation` (TransformInterpolation: "nearest" | "rotsprite" | "bilinear"; default "nearest") — How pixels are resampled. Use rotsprite for clean rotations of pixel art, bilinear for smooth (blended) results. - `corners` ({ {x=,y=}, {x=,y=}, {x=,y=}, {x=,y=} }; default The region's own corners (no distortion).) — Free distort / perspective: where the region's top-left, top-right, bottom-right and bottom-left corners land, in canvas pixels (fractions allowed), before the scale/skew/rotation/shift above. Must form a convex shape. Pinning the two top corners closer together than the bottom ones gives a floor-like perspective. Returns nothing. #### `app.scatter(layerId, frameId, points, opts?)` — changes, undoable, refused on a locked layer Scatter brush: stamps the brush along the points, one step per brush width, each stamp nudged by a random offset — exactly the GUI scatter tool. - `layerId` (string) - `frameId` (string) - `points` (table) — List of { x = n, y = n } (or { n, n }) points; gaps between consecutive points are interpolated like a real drag. - `opts` (table, optional) Also takes the **Painting options**, **Brush options** and **Ink options** (see "Option groups"), and: - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The paint color. - `offsetX` (integer 0–64; default context.scatterX) — How far each stamp can land left or right of the path, in pixels. - `offsetY` (integer 0–64; default context.scatterY) — How far each stamp can land above or below the path, in pixels. - `count` (integer 1–16; default context.scatterCount) — Stamps placed at each step along the path. - `seed` (integer 0–4294967295; default random) — Random seed: the same seed and points give the same result. Returns nothing. #### `app.spray(layerId, frameId, points, opts?)` — changes, undoable, refused on a locked layer Airbrush: scatters random dots around each point — exactly the GUI spray tool. Each point is one tick, so repeat a point to keep spraying in place. - `layerId` (string) - `frameId` (string) - `points` (table) — List of { x = n, y = n } (or { n, n }) points; gaps between consecutive points are interpolated like a real drag. - `opts` (table, optional) Also takes the **Painting options** and **Ink options** (see "Option groups"), and: - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The paint color. - `radius` (integer 1–128; default twice the context's brushSize, at least 2) — How far from each point the dots scatter, in pixels. - `rate` (integer 1–64; default context.sprayRate) — Dots scattered per point. - `seed` (integer 0–4294967295; default random) — Random seed: the same seed and points give the same result. Returns nothing. #### `app.strokeSelection(layerId, frameId, width, opts?)` — changes, undoable, refused on a locked layer Edit > Stroke Selection: paints a line of the given width along the current selection's edge (inside, centered or outside it) on one cel, as a single undo step. Does nothing when nothing is selected. Image layers only. - `layerId` (string) - `frameId` (string) - `width` (number) — Pixels, 1 to 256. - `opts` (table, optional) - `position` (StrokePosition: "inside" | "center" | "outside"; default "center") — Where the stroke sits relative to the selection's edge. - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default context.foregroundColor) — The stroke color. On an indexed sprite it snaps to the nearest palette entry unless colorIndex is given. - `colorIndex` (integer 0–255; default Taken from color.) — Indexed sprites: the palette index to paint, used as-is. Not the transparent index. - `shape` (SelectionModifyShape: "circle" | "square"; default "circle") — How distance from the edge is measured (round or square corners). Returns nothing. #### `app.transformPixels(layerId, frameId, opts?)` — changes, undoable, refused on a locked layer Scales, rotates, skews and/or distorts (perspective, via corners) the selected pixels (the marquee follows) — or the whole cel when nothing is selected — about a pivot, exactly like the GUI move tool's transform handles. Order: scale, skew, rotate, then shift by dx/dy. On a sprite with non-square pixels (Sprite.pixelRatio) the angles and scales are in what you see on screen, so a 90 degree rotation looks like one. Without a selection the whole cel keeps what lands within the box of the canvas and the cel's bounds, both where it is and shifted by dx/dy (so a plain shift loses nothing, past the canvas edge too); transformed selection pixels are kept within the canvas and the cel's bounds and cut beyond. The Background layer is cut to the canvas. A no-op transform changes nothing. - `layerId` (string) - `frameId` (string) - `opts` (table, optional) - `scaleX` (number -256–256; default 1) — Horizontal scale about the pivot; negative flips. Not 0. - `scaleY` (number -256–256; default 1) — Vertical scale about the pivot; negative flips. Not 0. - `rotation` (number; default 0) — Degrees clockwise about the pivot. - `skewX` (number -80–80; default 0) — Degrees of horizontal shear (the top leans one way, the bottom the other, about the pivot). - `skewY` (number -80–80; default 0) — Degrees of vertical shear. - `dx` (number; default 0) — Pixels to shift right after transforming. - `dy` (number; default 0) — Pixels to shift down after transforming. - `pivotX` (number; default The center of the selection (or of the cel when nothing is selected).) — X of the fixed point for rotate/scale/skew, in canvas pixels. - `pivotY` (number; default The center of the selection (or of the cel when nothing is selected).) — Y of the fixed point for rotate/scale/skew, in canvas pixels. - `interpolation` (TransformInterpolation: "nearest" | "rotsprite" | "bilinear"; default "nearest") — How pixels are resampled. Use rotsprite for clean rotations of pixel art, bilinear for smooth (blended) results. - `corners` ({ {x=,y=}, {x=,y=}, {x=,y=}, {x=,y=} }; default The region's own corners (no distortion).) — Free distort / perspective: where the region's top-left, top-right, bottom-right and bottom-left corners land, in canvas pixels (fractions allowed), before the scale/skew/rotation/shift above. Must form a convex shape. Pinning the two top corners closer together than the bottom ones gives a floor-like perspective. Returns nothing. ### 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"). #### `app.antiAlias(layerId, frameIds, opts?)` — changes, undoable, refused on a locked layer Anti-aliases the stair steps where color A meets color B on a layer's cels, pixel-art style, as a single undo step: only the pixels next to a one-pixel step change, the longer the straight run the more of them, while straight edges, corners and 45-degree diagonals stay crisp (app.setLayerEffects keeps it live instead, kind "antiAlias"). Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer are smoothed (one frame for the active cel, every frame for all of them). - `opts` (table, optional) - `cels` (Array of { layerId = ..., frameId = ... }) — Smooth these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `a` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default "#000000") — Color A: the colour whose stair steps are smoothed where it meets color B. - `anyA` (boolean; default false) — Pass true to treat every visible colour other than B as A (to smooth a whole outline). - `b` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default "#ffffff") — Color B: the colour A meets. A fully transparent colour (`"#00000000"`) means any transparent pixel; the in-betweens then fade A's alpha. - `steps` (integer 1–4; default 2) — How many in-between colours are generated from A to B (in OKLCH), and the most pixels one step takes. - `into` (string; default "b") — Which side of the edge is repainted: "a", "b" or "both". - `strength` (integer 0–100; default 100) — How far along each straight run the smoothing reaches: 100 is up to half the run. - `colors` (string; default "any") — Pass "palette" to snap the in-between colours to the nearest palette entries. Indexed sprites always do. - `respectSelection` (boolean; default false) — Pass true to change only pixels inside the current selection. By default the selection is ignored. Returns nothing. #### `app.autoShade(layerId, frameIds, opts?)` — changes, undoable, refused on a locked layer Shades flat colours from a light direction, as a single undo step (app.setLayerEffects with kind "autoShade" keeps it live instead): pixels near the lit edge of each region get lighter steps, near the far edge darker ones. The steps follow the ramp when it holds the colour, else they are computed in OKLCH. Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer are shaded (one frame for the active cel, every frame for all of them). - `opts` (table, optional) - `cels` (Array of { layerId = ..., frameId = ... }) — Shade these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `target` (string; default "base") — What to shade: "base" shades the pixels of the base colour only, "all" shades every flat colour with a ramp of its own. - `base` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default "#808080") — The colour to shade when target is "base". - `angle` (integer 0–359; default 135) — Where the light comes from, in degrees counter-clockwise from the right: 90 is above, 135 the top left, 270 below. - `highlights` (integer 0–5; default 2) — How many lighter steps the lit side gets; the edge is the lightest. - `shadows` (integer 0–5; default 2) — How many darker steps the far side gets; the edge is the darkest. - `width` (integer 1–4; default 1) — How many pixels deep each step is. - `hueShift` (integer 0–100; default 30) — How far computed highlights turn toward warm yellow and shadows toward cool blue. - `chroma` (integer -100–100; default 0) — How much more (or, negative, less) colourful each computed step gets. - `rim` (boolean; default false) — Pass true for a 1 px highlight along the outer edge of the shadow side. - `outline` (boolean; default false) — Pass true to paint the shape's outer edge one step darker than the deepest shadow. - `colors` (string; default "any") — "palette" snaps the computed steps to the frame's palette; indexed sprites always do. - `ramp` (Array of colors) — A ramp of colours, in either order, to walk instead of computing the steps when it holds the colour being shaded (for example app.getContext().shades). Empty: computed. - `respectSelection` (boolean; default false) — Pass true to shade only inside the current selection. By default the selection is ignored. Returns nothing. #### `app.brightnessContrast(layerId, frameIds, opts?)` — changes, undoable, refused on a locked layer Filter > Brightness/Contrast on a layer's cels, as a single undo step: each channel becomes 128 + factor * (value + brightness * 2.55 - 128), rounded and clamped. Not Aseprite's curve (it applies contrast first and scales brightness). Fully transparent pixels are left alone. Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer are adjusted (one frame for the active cel, every frame for all of them). - `opts` (table, optional) - `brightness` (number -100–100; default 0) — Adds brightness * 2.55 to every colour channel, before the contrast scale. - `contrast` (number -100–100; default 0) — Scales each channel's distance from 128 by 259(C + 255) / (255(259 - C)), with C = contrast * 2.55: -100 flattens all colour to 128, +100 is close to a threshold. - `cels` (Array of { layerId = ..., frameId = ... }) — Adjust these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `respectSelection` (boolean; default false) — Pass true to adjust only inside the current selection. By default the selection is ignored. - `indexedMode` (AdjustIndexedMode: "remap" | "palette"; default "remap") — Indexed sprites only: recolour the pixels and snap them to the nearest palette entry, or edit the palette entries the cels use. Returns nothing. #### `app.colorBalance(layerId, frameIds, opts?)` — changes, undoable, refused on a locked layer Filter > Color Balance on a layer's cels, as a single undo step: shifts the colour of shadows, midtones and highlights (weighted by luma, 100 = 127.5 levels at full weight). Fully transparent pixels are left alone. On grayscale sprites only shifts with equal red, green and blue are accepted. Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer are adjusted (one frame for the active cel, every frame for all of them). - `opts` (table, optional) - `shadows` ({ red = -100..100, green = -100..100, blue = -100..100 }) — The shift for dark pixels. Positive red moves toward red, negative toward cyan; likewise green/magenta and blue/yellow. - `midtones` ({ red = -100..100, green = -100..100, blue = -100..100 }) — The shift for mid-luma pixels. - `highlights` ({ red = -100..100, green = -100..100, blue = -100..100 }) — The shift for light pixels. - `preserveLuminosity` (boolean; default true) — Re-add the lightness the shift changed, so only the colour moves. - `cels` (Array of { layerId = ..., frameId = ... }) — Adjust these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `respectSelection` (boolean; default false) — Pass true to adjust only inside the current selection. By default the selection is ignored. - `indexedMode` (AdjustIndexedMode: "remap" | "palette"; default "remap") — Indexed sprites only: recolour the pixels and snap them to the nearest palette entry, or edit the palette entries the cels use. Returns nothing. #### `app.colorCurve(layerId, frameIds, curves, opts?)` — changes, undoable, refused on a locked layer Filter > Color Curve on a layer's cels, as a single undo step: a monotone cubic curve per channel (Aseprite's is linear, with one curve). Fully transparent pixels are left alone. Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer are adjusted (one frame for the active cel, every frame for all of them). - `curves` (AdjustCurveChannel: "value" | "red" | "green" | "blue" | "alpha") — Channel name to control points, e.g. { red = { {0, 0}, {128, 180}, {255, 255} } }. Channels: "value" (red, green and blue alike), "red", "green", "blue", "alpha"; grayscale sprites take only "value" and "alpha". Points are {x, y} pairs (0-255, x strictly increasing, 2 or more); the curve is a smooth monotone spline through them. - `opts` (table, optional) - `cels` (Array of { layerId = ..., frameId = ... }) — Adjust these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `respectSelection` (boolean; default false) — Pass true to adjust only inside the current selection. By default the selection is ignored. - `indexedMode` (AdjustIndexedMode: "remap" | "palette"; default "remap") — Indexed sprites only: recolour the pixels and snap them to the nearest palette entry, or edit the palette entries the cels use. Returns nothing. #### `app.convolutionMatrix(layerId, frameIds, opts?)` — changes, undoable, refused on a locked layer Filter > Convolution Matrix on a layer's cels, as a single undo step: blur, sharpen, edge detect, emboss or any kernel you give. Transparent neighbours do not darken colours. Indexed sprites need indexedMode "remap" (results snap to the nearest palette entry). Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer are adjusted (one frame for the active cel, every frame for all of them). - `opts` (table, optional) - `preset` (string) — A ready-made kernel instead of kernel/width/height: identity, gaussian3, box3, gaussian5, box5, sharpen, edge, emboss, sobelH, sobelV. - `width` (integer 1–9) — Kernel width (odd). Required with kernel. - `height` (integer 1–9) — Kernel height (odd). Required with kernel. - `kernel` (list of width * height numbers, row by row) — The weights applied to each pixel's neighbourhood, e.g. { 0, -1, 0, -1, 5, -1, 0, -1, 0 } sharpens. - `divisor` (number; default the sum of the kernel (1 if it sums to 0)) — Each weighted sum is divided by this (not 0). - `bias` (number -255–255; default 0) — Added to each channel after dividing (128 centres an edge/emboss result on gray). - `red` (boolean; default true) — Convolve the red channel. On grayscale sprites red, green and blue are the one gray channel: set all three alike. - `green` (boolean; default true) — Convolve the green channel. - `blue` (boolean; default true) — Convolve the blue channel. - `alpha` (boolean; default false) — Convolve the alpha channel too (a blur then grows into transparent pixels). Off, fully transparent pixels stay untouched. - `edges` (ConvolveEdges: "clamp" | "tiled"; default "clamp") — What is read beyond the cel's edge. - `cels` (Array of { layerId = ..., frameId = ... }) — Adjust these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `respectSelection` (boolean; default false) — Pass true to adjust only inside the current selection. By default the selection is ignored. - `indexedMode` (AdjustIndexedMode: "remap" | "palette"; default "remap") — Indexed sprites only: recolour the pixels and snap them to the nearest palette entry, or edit the palette entries the cels use. Returns nothing. #### `app.despeckle(layerId, frameIds, opts?)` — changes, undoable, refused on a locked layer Filter > Despeckle (a median filter) on a layer's cels, as a single undo step: removes isolated pixels and smooths noise. Indexed sprites need indexedMode "remap". Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer are adjusted (one frame for the active cel, every frame for all of them). - `opts` (table, optional) - `width` (integer 1–9; default 3) — Median window width (odd). - `height` (integer 1–9; default 3) — Median window height (odd). - `cels` (Array of { layerId = ..., frameId = ... }) — Adjust these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `respectSelection` (boolean; default false) — Pass true to adjust only inside the current selection. By default the selection is ignored. Returns nothing. #### `app.dropShadow(layerId, frameIds, opts?)` — changes, undoable, refused on a locked layer Draws a drop shadow, glow or inner shadow for the shapes on a layer's cels, as a single undo step (app.setLayerEffects keeps it live instead). Outside shadows grow the cel, clipped to the canvas, and sit beneath the art. Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer get the shadow (one frame for the active cel, every frame for all of them). - `opts` (table, optional) - `cels` (Array of { layerId = ..., frameId = ... }) — Shadow these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `place` (OutlinePlace: "outside" | "inside"; default "outside") — A drop shadow or glow beneath the art ("outside"), or an inner shadow over it ("inside"). - `dx` (integer -10–10; default 1) — How far the shadow falls to the right (negative: left). 0 and 0 makes a glow. - `dy` (integer -10–10; default 1) — How far the shadow falls downward (negative: up). - `spread` (integer 0–10; default 0) — How many pixels the shadow grows past the art's silhouette. - `shape` (FxShape: "circle" | "square" | "diamond"; default "circle") — The footprint of the spread. - `softness` (integer 0–8; default 0) — How many pixels the shadow's edge fades over (a blur radius); 0 is a hard edge. - `edge` (ShadowEdge: "dithered" | "hard" | "smooth"; default "dithered") — How a soft edge (softness above 0) is drawn: "dithered" keeps one colour as an ordered dither that stays put as the art moves, "hard" cuts it at half strength, "smooth" makes real half-transparent pixels. - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default "#00000080") — The shadow colour; its alpha is the shadow's opacity. On an indexed sprite the result snaps to the palette. - `respectSelection` (boolean; default false) — Pass true to paint only inside the current selection. By default the selection is ignored. Returns nothing. #### `app.gradientMap(layerId, frameIds, stops, opts?)` — changes, undoable, refused on a locked layer Filter > Gradient Map on a layer's cels, as a single undo step: each pixel's luma picks a colour from the ramp. A pixel keeps its alpha. Fully transparent pixels are left alone. Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer are adjusted (one frame for the active cel, every frame for all of them). - `stops` (table) — 2-32 gradient stops, `{ { color = ..., position = 0 }, { color = ..., position = 1 } }`: the colour a pixel takes at that luma (0 = darkest, 1 = lightest). - `opts` (table, optional) - `space` (GradientMapSpace: "rgb" | "oklab" | "oklch"; default "oklab") — The colour space blended in between stops. - `reverse` (boolean; default false) — Run the ramp from light to dark. - `cels` (Array of { layerId = ..., frameId = ... }) — Adjust these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `respectSelection` (boolean; default false) — Pass true to adjust only inside the current selection. By default the selection is ignored. - `indexedMode` (AdjustIndexedMode: "remap" | "palette"; default "remap") — Indexed sprites only: recolour the pixels and snap them to the nearest palette entry, or edit the palette entries the cels use. Returns nothing. #### `app.hueSaturation(layerId, frameIds, opts?)` — changes, undoable, refused on a locked layer Filter > Hue/Saturation in hsv, hsl or a perceptual oklch mode on a layer's cels, as a single undo step. Positive lightness and alpha lift toward full rather than multiplying as Aseprite does, so +50 lightness turns black into mid-gray. Fully transparent pixels are left alone. On grayscale sprites hue and saturation have no effect. Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer are adjusted (one frame for the active cel, every frame for all of them). - `opts` (table, optional) - `mode` (AdjustHueMode: "hsv" | "hsl" | "oklch"; default "hsv") — The colour model to work in. - `hue` (number -180–180; default 0) — Degrees added to every hue. - `saturation` (number -100–100; default 0) — Scales saturation (chroma in oklch mode): -100 is gray, +100 doubles it (capped at full, or at the sRGB gamut in oklch mode). Grays stay gray. - `lightness` (number -100–100; default 0) — Lightness in hsl and oklch modes, value in hsv mode. Positive moves that share of the remaining way to white/full (l + (1 - l) * k, k = lightness / 100), so +100 reaches it; negative scales toward black (l * (1 + k)), so -100 reaches it. - `alpha` (number -100–100; default 0) — The same lift on alpha (rounded): +100 makes every non-transparent pixel opaque, -100 makes them transparent. - `cels` (Array of { layerId = ..., frameId = ... }) — Adjust these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `respectSelection` (boolean; default false) — Pass true to adjust only inside the current selection. By default the selection is ignored. - `indexedMode` (AdjustIndexedMode: "remap" | "palette"; default "remap") — Indexed sprites only: recolour the pixels and snap them to the nearest palette entry, or edit the palette entries the cels use. Returns nothing. #### `app.invertColor(layerId, frameIds, opts?)` — changes, undoable, refused on a locked layer Filter > Invert Color, as in Aseprite, on a layer's cels, as a single undo step. Fully transparent pixels are left alone. Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer are adjusted (one frame for the active cel, every frame for all of them). - `opts` (table, optional) - `red` (boolean; default true) — Invert the red channel. On grayscale sprites red, green and blue are the one gray channel: set all three alike. - `green` (boolean; default true) — Invert the green channel. - `blue` (boolean; default true) — Invert the blue channel. - `alpha` (boolean; default false) — Invert the alpha channel (fully transparent pixels still stay transparent). - `cels` (Array of { layerId = ..., frameId = ... }) — Adjust these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `respectSelection` (boolean; default false) — Pass true to adjust only inside the current selection. By default the selection is ignored. - `indexedMode` (AdjustIndexedMode: "remap" | "palette"; default "remap") — Indexed sprites only: recolour the pixels and snap them to the nearest palette entry, or edit the palette entries the cels use. Returns nothing. #### `app.levels(layerId, frameIds, opts?)` — changes, undoable, refused on a locked layer Filter > Levels on a layer's cels, as a single undo step: each channel is rescaled from the input range, bent by gamma and mapped to the output range. Fully transparent pixels are left alone. Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer are adjusted (one frame for the active cel, every frame for all of them). - `opts` (table, optional) - `inBlack` (integer 0–254; default 0) — Input values at or below this become outBlack. - `inWhite` (integer 1–255; default 255) — Input values at or above this become outWhite. Must be above inBlack. - `gamma` (number 0.1–10; default 1) — Midtone curve: above 1 brightens the midtones, below 1 darkens them. - `outBlack` (integer 0–255; default 0) — The output value for black input. - `outWhite` (integer 0–255; default 255) — The output value for white input. Below outBlack inverts. - `red` ({ inBlack, inWhite, gamma, outBlack, outWhite }) — Levels for the red channel alone, applied after the master ones. Grayscale sprites refuse single channels. - `green` ({ inBlack, inWhite, gamma, outBlack, outWhite }) — Levels for the green channel alone. - `blue` ({ inBlack, inWhite, gamma, outBlack, outWhite }) — Levels for the blue channel alone. - `cels` (Array of { layerId = ..., frameId = ... }) — Adjust these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `respectSelection` (boolean; default false) — Pass true to adjust only inside the current selection. By default the selection is ignored. - `indexedMode` (AdjustIndexedMode: "remap" | "palette"; default "remap") — Indexed sprites only: recolour the pixels and snap them to the nearest palette entry, or edit the palette entries the cels use. Returns nothing. #### `app.posterize(layerId, frameIds, opts?)` — changes, undoable, refused on a locked layer Filter > Posterize: reduces each colour channel of a layer's cels to a few levels, as a single undo step. Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer are adjusted (one frame for the active cel, every frame for all of them). - `opts` (table, optional) - `levels` (integer 2–256; default 4) — Distinct levels kept per colour channel (alpha is untouched). - `cels` (Array of { layerId = ..., frameId = ... }) — Adjust these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `respectSelection` (boolean; default false) — Pass true to adjust only inside the current selection. By default the selection is ignored. - `indexedMode` (AdjustIndexedMode: "remap" | "palette"; default "remap") — Indexed sprites only: recolour the pixels and snap them to the nearest palette entry, or edit the palette entries the cels use. Returns nothing. #### `app.reduceColors(layerId, frameIds, opts?)` — changes, undoable, refused on a locked layer Filter > Reduce Colors: maps a layer's cels onto a few colours (generated from those cels, or the palette), optionally dithered, as a single undo step. Alpha is kept; the sprite stays in its colour mode. Indexed sprites need indexedMode "remap". Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer are adjusted (one frame for the active cel, every frame for all of them). - `opts` (table, optional) - `colors` (integer 1–256; default 16) — How many colours to keep (paletteSource "generate"). - `paletteSource` (ReduceColorsPalette: "generate" | "palette"; default "generate") — Generate the colours from the cels, or use the sprite's palette. - `dithering` (DitherMethod: "none" | "bayer2" | "bayer4" | "bayer8" | "floydSteinberg" | "atkinson"; default "none") — How in-between colours are approximated. - `ditherAmount` (integer 0–100; default 100) — Dithering strength (0 = nearest colour only). - `cels` (Array of { layerId = ..., frameId = ... }) — Adjust these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `respectSelection` (boolean; default false) — Pass true to adjust only inside the current selection. By default the selection is ignored. Returns nothing. #### `app.replaceColor(layerId, frameIds, from, to, opts?)` — changes, undoable, refused on a locked layer Filter > Replace Color, as in Aseprite, on a layer's cels, as a single undo step. Fully transparent pixels are never matched. Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer are adjusted (one frame for the active cel, every frame for all of them). - `from` (color) — The colour to find. - `to` (color) — The colour to put in its place (alpha included). - `opts` (table, optional) - `tolerance` (integer 0–255; default 0) — A pixel matches when none of its r, g, b, a channels is more than this far from `from`. - `cels` (Array of { layerId = ..., frameId = ... }) — Adjust these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `respectSelection` (boolean; default false) — Pass true to adjust only inside the current selection. By default the selection is ignored. - `indexedMode` (AdjustIndexedMode: "remap" | "palette"; default "remap") — Indexed sprites only: recolour the pixels and snap them to the nearest palette entry, or edit the palette entries the cels use. Returns nothing. #### `app.threshold(layerId, frameIds, opts?)` — changes, undoable, refused on a locked layer Filter > Threshold on a layer's cels, as a single undo step: every pixel becomes one of two colours by its luma (Aseprite's rgb_luma). Fully transparent pixels are left alone. Image layers only. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose cels on this layer are adjusted (one frame for the active cel, every frame for all of them). - `opts` (table, optional) - `level` (integer 0–255; default 128) — Pixels whose luma is at least this become `above`, the rest `below`. - `below` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default "#000000") — The colour of pixels under the level. Alpha is ignored: a pixel keeps its own. - `above` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default "#ffffff") — The colour of pixels at or over the level. Alpha is ignored. - `cels` (Array of { layerId = ..., frameId = ... }) — Adjust these cels, across layers and frames, instead of layerId/frameIds (pass nil for both). - `respectSelection` (boolean; default false) — Pass true to adjust only inside the current selection. By default the selection is ignored. - `indexedMode` (AdjustIndexedMode: "remap" | "palette"; default "remap") — Indexed sprites only: recolour the pixels and snap them to the nearest palette entry, or edit the palette entries the cels use. Returns nothing. ### 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). #### `app.getCompositeImage(frameId, rect?)` — reads What a frame looks like, as an RGBA Image: every visible layer (image and tilemap) blended with its opacity and blend mode, exactly as exported, over rect (default the whole canvas). Reference layers are excluded (they aren't artwork); outside the canvas reads transparent. Use it to check your result, or as a source for img:drawImage. - `frameId` (string) - `rect` (table, optional) — The area to read, { x, y, width, height } on the canvas (integers; width and height 1-4096). Omitted = the whole canvas. - `x` (integer) — Left edge. - `y` (integer) — Top edge. - `width` (integer 1–4096) — Width in pixels. - `height` (integer 1–4096) — Height in pixels. Returns (image): An Image (see img:getPixel and the other img: methods). #### `app.getImage(layerId, frameId, rect?)` — reads Reads a layer/frame's cel into an Image: a pixel buffer inside the script that you read and change with img:getPixel / img:setPixel (and img:getIndex / img:setIndex on indexed sprites) at no cost per call, then write back with app.putImage as one change. The Image covers rect (default the whole canvas), and its coordinates start at 0, 0 at the rect's top-left; img.x, img.y remember where on the canvas that is, which is where app.putImage writes it by default. Pixels with no cel read as transparent. A linked cel reads its shared image. Image layers only (tile images: app.getTilePixels). - `layerId` (string) - `frameId` (string) - `rect` (table, optional) — The area to read, { x, y, width, height } on the canvas (integers; width and height 1-4096). Omitted = the whole canvas. - `x` (integer) — Left edge. - `y` (integer) — Top edge. - `width` (integer 1–4096) — Width in pixels. - `height` (integer 1–4096) — Height in pixels. Returns (image): An Image (see img:getPixel and the other img: methods). #### `app.newImage(width, height, colorMode?)` — reads A blank (transparent) Image, at x = 0, y = 0: a scratch buffer to build pixels in before app.putImage, or to stamp onto other images with img:drawImage. An indexed one uses the sprite's palette. - `width` (number) — 1-4096. - `height` (number) — 1-4096. - `colorMode` (ColorMode: "rgba" | "grayscale" | "indexed", optional) — Default the sprite's own. "rgba" on an indexed sprite gives a full-colour scratch image (app.putImage then maps its colours to the palette). Returns (image): An Image (see img:getPixel and the other img: methods). #### `app.pixelColor.rgba(r, g, b, a?)` — pure Packs a colour into one pixel integer, the form img:getPixel returns and img:setPixel takes fastest (as in Aseprite). - `r` (number) — 0-255. - `g` (number) — 0-255. - `b` (number) — 0-255. - `a` (number, optional) — 0-255, default 255. Returns (number): r | g << 8 | b << 16 | a << 24. #### `app.pixelColor.rgbaA(pixel)` — pure A packed pixel's alpha. - `pixel` (number) — A packed pixel. Returns (number): 0-255. #### `app.pixelColor.rgbaB(pixel)` — pure A packed pixel's blue. - `pixel` (number) — A packed pixel. Returns (number): 0-255. #### `app.pixelColor.rgbaG(pixel)` — pure A packed pixel's green. - `pixel` (number) — A packed pixel. Returns (number): 0-255. #### `app.pixelColor.rgbaR(pixel)` — pure A packed pixel's red. - `pixel` (number) — A packed pixel. Returns (number): 0-255. #### `app.putImage(layerId, frameId, image, x?, y?)` — changes, undoable, refused on a locked layer Writes an Image into a layer/frame's cel: every pixel of the rectangle it covers is replaced (no blending), as one change however many pixels you set. The cel grows to hold it and is trimmed to its visible pixels; what falls outside the canvas is dropped. Raw write: never clipped to the selection or mirrored. On an indexed sprite each pixel keeps its palette index (img:setIndex), unless the palette changed since and that entry no longer shows the pixel's colour; on a grayscale sprite colours become gray. Writing to a linked cel changes every frame it's linked to. - `layerId` (string) - `frameId` (string) - `image` (image) — The Image to write. - `x` (number, optional) — Where its top-left pixel lands; default image.x (where app.getImage read it, 0 for app.newImage). - `y` (number, optional) — Default image.y. Returns nothing. #### `img:clear(x?, y?, width?, height?)` — pure Makes a rectangle of the image (default all of it) transparent (the transparent index on indexed images). - `x` (number, optional) — Default 0. - `y` (number, optional) — Default 0. - `width` (number, optional) — Default to the right edge. - `height` (number, optional) — Default to the bottom edge. Returns nothing. #### `img:clone()` — pure A copy of the image (same size, position and colour mode). Returns (image): A new Image. #### `img:drawImage(image, x?, y?, opacity?, blendMode?)` — pure Draws another Image onto this one, blended like a layer (its transparent pixels leave this one as it was). Indexed onto indexed at full opacity copies the indices. - `image` (image) — The Image to draw. - `x` (number, optional) — Where its top-left lands; default 0. - `y` (number, optional) — Default 0. - `opacity` (number, optional) — 0-255, default 255. - `blendMode` (string, optional) — A BlendMode, default "normal". Returns nothing. #### `img:fill(color, x?, y?, width?, height?)` — pure Fills a rectangle of the image (default all of it) with one colour. - `color` (color) — A packed pixel (app.pixelColor.rgba(r, g, b, a), what img:getPixel returns), "#rrggbb[aa]" or { r, g, b, a } (a defaults to 255). - `x` (number, optional) — Default 0. - `y` (number, optional) — Default 0. - `width` (number, optional) — Default to the right edge. - `height` (number, optional) — Default to the bottom edge. Returns nothing. #### `img:getIndex(x, y)` — pure Indexed images only: the pixel's palette index (0-based, as in app.getPalette's order and app.setPaletteEntry), so duplicate palette colours can be told apart. Outside the image: the transparent index. - `x` (number) — Column, 0 at the image's left edge. - `y` (number) — Row, 0 at the top. Returns (number): 0-255. #### `img:getPixel(x, y)` — pure A pixel's colour, packed into one integer (the fast form: no table is made). On an indexed image it is the palette colour of the pixel's index (transparent for the transparent index). Outside the image: 0 (transparent). x and y must be integers. - `x` (number) — Column, 0 at the image's left edge. - `y` (number) — Row, 0 at the top. Returns (number): The packed pixel r | g << 8 | b << 16 | a << 24: take it apart with app.pixelColor (or & 255, >> 8 ...). #### `img:setIndex(x, y, index)` — pure Indexed images only: sets the pixel's palette index exactly, even to one of two entries with the same colour (palette cycling, index swaps). - `x` (number) — Column, 0 at the image's left edge. - `y` (number) — Row, 0 at the top. - `index` (number) — A palette index, 0-based. Returns nothing. #### `img:setPixel(x, y, color)` — pure Sets a pixel's colour. Takes the same colours as the draw commands, plus packed pixels, and refuses anything else ({ red = ... }, a list, a bad hex string) with an error. On an indexed image the colour becomes the nearest palette entry, keeping the pixel's index when that entry already shows the colour (img:setIndex picks an exact index); on a grayscale one it becomes gray. Outside the image nothing happens. - `x` (number) — Column, 0 at the image's left edge. - `y` (number) — Row, 0 at the top. - `color` (color) — A packed pixel (app.pixelColor.rgba(r, g, b, a), what img:getPixel returns), "#rrggbb[aa]" or { r, g, b, a } (a defaults to 255). Returns nothing. ### 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. #### `app.clearSelection(layerId, frameId)` — changes, undoable, refused on a locked layer Clears the selection to fully transparent, trimming the cel to what's left (an emptied cel is removed, with every frame linked to it); with nothing selected, removes this frame's cel (a linked frame only drops its link). On the Background layer everything clears to the background colour. - `layerId` (string) - `frameId` (string) Returns nothing. #### `app.fillSelection(layerId, frameId, color, opts?)` — changes, undoable, refused on a locked layer Fills the selection (or the whole cel, if nothing's selected) with a solid color. A plain fill (copy) unless `ink` is given: unlike the drawing commands it never takes the editing context's ink, matching Edit > Fill. - `layerId` (string) - `frameId` (string) - `color` (table) — { r, g, b, a } — each 0-255. - `opts` (table, optional) - `ink` (Ink: "copy" | "simple" | "lock-alpha" | "shading"; default "copy") — copy replaces pixels, simple alpha-composites, lock-alpha recolors only visible pixels, shading steps colors along `shades`. Ignored by erase. - `shades` (a list of colors ("#rrggbb[aa]" or { r, g, b, a }), in ramp order) — The ramp shading steps along. A pixel whose color is not in the list is untouched; a pixel at the end of the list stays there. - `shadeReverse` (boolean; default false) — Shading steps backward in `shades` (the GUI's right mouse button) instead of forward. Returns nothing. #### `app.flipCelRegion(layerId, frameId, axis)` — changes, undoable, refused on a locked layer Flips the selected pixels (or the whole cel, if nothing's selected) within the selection's bounds: every selected pixel moves to its mirror, the ones left behind clear (to the background colour on the Background layer) and the marquee flips too. - `layerId` (string) - `frameId` (string) - `axis` (FlipAxis: "horizontal" | "vertical") Returns nothing. #### `app.getCompositePixels(frameId, x, y, width, height)` — pure app.getCompositeImage's pixels as a flat RGBA list in the app.getPixels shape. At most 262144 pixels per call. - `frameId` (string) - `x` (number) - `y` (number) - `width` (number) - `height` (number) Returns (table): Flat RGBA list. #### `app.getPixels(layerId, frameId, x, y, width, height)` — pure Reads a width x height rectangle at (x, y) of a layer/frame's cel as a flat RGBA list (length width*height*4, row-major: pixel (px, py) starts at index (py*width + px)*4 + 1), in the shape app.setPixels takes. Pixels with no cel read as transparent (0, 0, 0, 0). Built on app.getImage, which is faster when you don't need a list. At most 262144 pixels per call. - `layerId` (string) - `frameId` (string) - `x` (number) - `y` (number) - `width` (number) - `height` (number) Returns (table): Flat RGBA list. #### `app.sampleColor(frameId, x, y, layerId?)` — reads The eyedropper: the color at (x, y) as { r, g, b, a } — by default what the canvas shows there (visible layers blended with their opacity and blend modes), or one layer's color when layerId is given. - `frameId` (string) - `x` (number) - `y` (number) - `layerId` (string, optional) — Sample only this layer. Omitted = every visible layer composited, as shown on screen. Returns (table): { r, g, b, a }, each 0-255. #### `app.setPixels(layerId, frameId, x, y, width, height, pixels)` — pure Overwrites a width x height rectangle at (x, y) of a layer/frame's cel with a flat RGBA list (app.getPixels' shape), as one app.putImage. Raw write: never clipped to the selection or mirrored. On indexed and grayscale sprites colours are mapped as img:setPixel maps them. At most 262144 pixels per call. - `layerId` (string) - `frameId` (string) - `x` (number) - `y` (number) - `width` (number) - `height` (number) - `pixels` (table) — Flat RGBA list, length width*height*4, row-major: pixel (px, py) is at index (py*width + px)*4 + 1 (r, then g, b, a; each 0-255). Returns nothing. ### Selection Selection shapes combine with the current selection (replace, add, subtract, intersect). The selection mask is readable and writable. #### `app.canReselect()` — reads Whether app.reselect() has a previous selection to restore. Returns (boolean): true if there is one. #### `app.deselect()` — changes Clears the selection (Select ▸ Deselect) — draw commands then paint anywhere. app.reselect() brings it back. Returns nothing. #### `app.enterQuickMask()` — changes, undoable Turns Quick Mask on: the selection becomes a mask you paint, and nothing is selected. While it is on, the draw functions paint the mask when you pass `"quickMask"` as their layerId and frameId (any colour adds, transparent paint removes); everything else that would change the sprite is refused until app.exitQuickMask(). Returns nothing. #### `app.exitQuickMask()` — changes, undoable Turns Quick Mask off: every mask pixel at or above half opacity becomes the selection. Returns nothing. #### `app.getSelection()` — reads The current selection as { x, y, width, height, mask }, or nil if nothing is selected. mask is a flat 0/1 list over the bounds (row-major: (px, py) at index py*width + px + 1) — included when the bounds hold at most 262144 pixels; for larger selections read it in pieces with app.getSelectionMask. Returns (table): Selection table or nil. #### `app.getSelectionBounds()` — reads The current selection's { x, y, width, height }, or nil if nothing is selected. Returns (table): { x, y, width, height } or nil. #### `app.getSelectionMask(x, y, width, height)` — reads Which pixels of a sprite-space rectangle are selected, as a flat 0/1 list (row-major: (px, py) at index py*width + px + 1). All 0 when nothing is selected. At most 262144 pixels per call. - `x` (number) - `y` (number) - `width` (number) - `height` (number) Returns (table): Flat 0/1 list. #### `app.isSelected(x, y)` — reads Whether sprite pixel (x, y) is inside the current selection (false when nothing is selected). - `x` (number) - `y` (number) Returns (boolean): true if selected. #### `app.magicWand(layerId, frameId, points, opts?)` — changes, undoable Selects the pixels matching the color under each point on a layer, all the regions as one selection — exactly the GUI magic wand (a click or a drag). - `layerId` (string) - `frameId` (string) - `points` (table) — List of { x = n, y = n } (or { n, n }) points, 1 to 1024: { { x = 3, y = 4 } } wands one region, like a click; with several, every point's region goes into one selection, like a drag. - `opts` (table, optional) - `mode` (SelectionCombineMode: "replace" | "add" | "subtract" | "intersect"; default "replace") — How the new shape combines with the current selection. - `tolerance` (integer 0–255; default context.wandTolerance) — The largest per-channel color difference that still counts as a match. - `contiguous` (boolean; default context.wandFillMode (true for the context's contiguous mode, false for global)) — Only the connected region (true), or every matching pixel on the canvas (false). - `diagonal` (boolean; default context.wandDiagonal) — Contiguous only: also spread to diagonal neighbours (8-connected), so the region leaks through 1px diagonal lines. - `stopAtGrid` (boolean; default context.wandStopAtGrid) — Confine the region to the grid cell the click lands in (an isometric grid's cell is the diamond it draws, with the line pixels of its two upper edges). Returns nothing. #### `app.modifySelection(op, amount, opts?)` — changes, undoable Grows, shrinks or rings the selection, clipped to the canvas — exactly Select > Modify. The canvas edge counts as the selection's edge. - `op` (SelectionModifyOp: "expand" | "contract" | "border") - `amount` (number) — Pixels, 1 to 256. - `opts` (table, optional) - `shape` (SelectionModifyShape: "circle" | "square"; default "circle") — How distance from the edge is measured. Returns nothing. #### `app.reselect()` — changes Restores the most recent non-empty selection (Select ▸ Reselect). Throws if there is none — check app.canReselect(). Returns nothing. #### `app.selectAll()` — changes, undoable Selects the entire canvas. Returns nothing. #### `app.selectEllipse(x0, y0, x1, y1, opts?)` — changes, undoable Selects the ellipse inscribed in the box spanning the two corners — exactly the GUI elliptical selection tool. - `x0` (number) — First corner. - `y0` (number) - `x1` (number) — Opposite corner (inclusive). - `y1` (number) - `opts` (table, optional) - `mode` (SelectionCombineMode: "replace" | "add" | "subtract" | "intersect"; default "replace") — How the new shape combines with the current selection. Returns nothing. #### `app.selectGridCells(x0, y0, x1, y1, opts?)` — changes, undoable Selects whole cells of the sprite's grid (rectangular, isometric or hexagonal): every cell holding a pixel of the box between the two points — exactly a double-click (and drag) with a GUI selection tool. - `x0` (number) — A pixel in the first cell. - `y0` (number) - `x1` (number) — A pixel in the last cell (the same point again selects one cell). - `y1` (number) - `opts` (table, optional) - `mode` (SelectionCombineMode: "replace" | "add" | "subtract" | "intersect"; default "replace") — How the new shape combines with the current selection. Returns nothing. #### `app.selectInverse()` — changes, undoable Inverts the current selection within canvas bounds. Returns nothing. #### `app.selectLayerPixels(layerId, frameId, opts?)` — changes, undoable Selects a layer's painted (or, with transparent = true, unpainted) pixels at a frame — exactly Select > Select Opaque / Select Transparent and Alt+click on a timeline layer. - `layerId` (string) — A group selects the pixels of every layer in it. - `frameId` (string) - `opts` (table, optional) - `mode` (SelectionCombineMode: "replace" | "add" | "subtract" | "intersect"; default "replace") — How the new shape combines with the current selection. - `transparent` (boolean; default false) — Select the pixels with no paint instead of the painted ones. Returns nothing. #### `app.selectPolygon(vertices, opts?)` — changes, undoable Selects the interior (and outline) of the closed polygon through the vertices — exactly the GUI lasso / polygonal lasso. - `vertices` (table) — List of { x = n, y = n } (or { n, n }) vertices; the polygon closes back to the first automatically. Covers both the lasso and the polygonal lasso. - `opts` (table, optional) - `mode` (SelectionCombineMode: "replace" | "add" | "subtract" | "intersect"; default "replace") — How the new shape combines with the current selection. Returns nothing. #### `app.selectRectangle(x0, y0, x1, y1, opts?)` — changes, undoable Selects the rectangle spanning the two corners — exactly the GUI rectangular selection tool (radius rounds its corners). - `x0` (number) — First corner. - `y0` (number) - `x1` (number) — Opposite corner (inclusive). - `y1` (number) - `opts` (table, optional) - `mode` (SelectionCombineMode: "replace" | "add" | "subtract" | "intersect"; default "replace") — How the new shape combines with the current selection. - `radius` (an integer 0–1024 for every corner, or a list of four { topLeft, topRight, bottomRight, bottomLeft }; default context.selectionCornerRadius) — Rounds the corners, in pixels (0 = square): one radius for all four, or one per corner going clockwise from the top-left. Each is clamped to half the smaller side. Returns nothing. #### `app.setSelection(x, y, width, height, mask?)` — changes, undoable Replaces the selection with a rectangle, or with any shape via `mask`. - `x` (number) - `y` (number) - `width` (number) - `height` (number) - `mask` (table, optional) — Flat 0/1 list (width*height, row-major — the shape app.getSelection returns) selecting only those pixels of the rectangle; omitted = the whole rectangle. Returns nothing. ### 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. #### `app.addAdjustmentLayer(name?, effects?, parentId?)` — changes, undoable Adds an adjustment layer on top of every other layer (or inside the group parentId): it holds no pixels, and its effects change everything composited below it in its group (or the whole picture), on every frame, at the layer's opacity. Clip it to change only the layer below. Makes it active. - `name` (string, optional) — Defaults to "Adjustment N". - `effects` (table, optional) — Its effects, as for app.setLayerEffects (colour and per-pixel kinds only). Omitted = none yet. - `parentId` (string, optional) — A group to add it to (on top of its layers); omitted = the top level. Returns (string): The new layer's id. #### `app.addGroup(name?, parentId?)` — changes, undoable Adds a new empty layer group on top of every other layer (or inside the group parentId) and makes it active. Put layers in it with app.moveLayer or app.groupLayers. - `name` (string, optional) — Defaults to "Group N". - `parentId` (string, optional) — A group to nest it in (on top of its layers); omitted = the top level. Returns (string): The new group's id. #### `app.addLayer(name?, parentId?)` — changes, undoable Adds a new blank layer on top of every other layer (or on top of the group parentId's layers) and makes it active. - `name` (string, optional) — Defaults to "Layer N". - `parentId` (string, optional) — A group to add it to (on top of its layers); omitted = the top level. Returns (string): The new layer's id. #### `app.backgroundFromLayer(layerId?, backgroundColor?)` — changes, undoable, refused on a locked layer Layer > "Background from Layer": converts an image layer into the sprite's Background layer: moved to the bottom, named "Background", and made fully opaque (transparent pixels take the background colour). Throws if the sprite already has a Background layer. - `layerId` (string, optional) — The image layer to convert; omitted = the bottom-most image layer. - `backgroundColor` (color, optional) — What fills the transparent pixels. Defaults to the current background colour. Returns (string): The converted layer's id. #### `app.bakeLayerEffects(layerId)` — changes, undoable, refused on a locked layer Renders an image layer's enabled effects into all of its cels and removes them, as a single undo step. Cels grow to hold shadows and outlines (clipped to the canvas); indexed sprites snap the result to each frame's palette. Throws on a locked layer or one without effects. - `layerId` (string) Returns nothing. #### `app.duplicateLayer(layerId)` — changes, undoable Clones a layer (a group with everything inside it) and every cel painted on it, inserting the copy directly above the original and making it active. - `layerId` (string) Returns (string): The copy's layer id. #### `app.flattenGroup(layerId)` — changes, undoable, refused on a locked layer Replaces a group and everything in it with one image layer (same id, name, opacity, blend mode and place) holding its layers composited together on every frame. Hidden, draft and reference layers inside are deleted, like Flatten. - `layerId` (string) — The group to flatten. Returns nothing. #### `app.flattenSprite(visibleOnly?, backgroundColor?)` — changes, undoable Layer > Flatten: collapses every top-level layer (hidden ones included) into one layer, per frame, exactly as it renders with them shown; with visibleOnly, only the visible ones, keeping the hidden ones. A visible Background layer stays the Background (fully opaque); otherwise the result is a new layer named Flattened. Throws if there's nothing to flatten. - `visibleOnly` (boolean, optional) — Flatten Visible: flatten only the visible top-level layers and keep the hidden ones. Default false: everything, hidden layers included. - `backgroundColor` (color, optional) — Only when the result is the Background layer (a visible Background is flattened): what fills the pixels nothing covers. Defaults to the current background colour. Returns nothing. #### `app.getActiveLayerId()` — reads The currently active layer's id, or nil if none. Returns (string): Layer id or nil. #### `app.getLayer(layerId)` — reads One layer, in the same shape as an app.getLayers entry, or nil if there's no such layer. - `layerId` (string) Returns (table): Layer table or nil. #### `app.getLayers()` — reads Every layer, bottom to top (a group sits just above its layers), as { id, name, type ("image" | "tilemap" | "reference" | "group"), parentId (the group it's in; nil at the top level), depth (0 = top level), index (0 = bottom of the whole stack), position (slot among its parent's layers, 0 = bottom; what app.moveLayer takes), opacity (0-1), visible, locked, movementLocked, background (true on the Background layer), clipped (a clipping mask: shows only where the nearest unclipped layer below it has pixels), continuous (app.duplicateFrame links its cels instead of copying them), blendMode, collapsed (groups), tileset = { tileWidth, tileHeight, tileCount, matchFlipH, matchFlipV, matchRotate } (tilemap layers), reference = { width, height } (reference layers) }. visible/locked/movementLocked are the layer's own flags; a hidden, locked or movement-locked group also hides, locks or pins everything inside it. Returns (table): Array of layer tables. #### `app.getReferenceTransform(layerId, frameId)` — reads Where a reference layer's image sits on one frame, as { x, y, width, height } in sprite pixels, or nil if it's blank there. - `layerId` (string) — A reference layer. - `frameId` (string) Returns (table): Transform table or nil. #### `app.groupLayers(layerIds, name?)` — changes, undoable Puts layers (groups with everything inside them) into a new group at the topmost one's position, keeping their order, and makes the group active. - `layerIds` (table) — Array of layer ids, all in the same group (or all at the top level). - `name` (string, optional) — Defaults to "Group N". Returns (string): The new group's id. #### `app.layerFromBackground()` — changes, undoable Layer > "Layer from Background": turns the Background layer into an ordinary layer (transparency and movement allowed again). Throws if the sprite has no Background layer. Returns (string): The converted layer's id. #### `app.mergeLayerDown(layerId)` — changes, undoable, refused on a locked layer Composites a layer (or a whole group, as it renders) onto the layer directly below it in the same group, then deletes it; a clipping base takes its clipped layers with it. The result looks the same, and the layer below ends up at opacity 100% and the normal blend mode. Throws if there's nothing below it in its group, what's below isn't an image layer, or an unclipped layer would merge onto a clipped one. - `layerId` (string) Returns nothing. #### `app.mergeLayers(layerIds)` — changes, undoable, refused on a locked layer Merges layers (groups with everything inside them) into one image layer exactly as they render together, at the topmost one's position and with its name, and makes it active. With the Background among them the result is the Background. - `layerIds` (table) — Array of two or more layer ids, all in the same group (or all at the top level). Hidden ones are merged too. Returns (string): The merged layer's id. #### `app.mergeLayerUp(layerId)` — changes, undoable, refused on a locked layer Merges an image layer into the layer directly above it in the same group: the result looks exactly like Merge Down of that layer, but takes its name and colour tag. Throws if there's nothing above it in its group or it isn't an image layer. - `layerId` (string) Returns nothing. #### `app.moveLayer(layerId, position, parentId?)` — changes, undoable Moves a layer (a group with everything inside it) to a new position, optionally into another group. Throws when moving a group into itself. - `layerId` (string) - `position` (number) — Its slot among the parent's other layers afterwards (0 = bottom), clamped to the valid range. - `parentId` (string, optional) — The group to move it into; "" = the top level; omitted = stay in its current group. Returns nothing. #### `app.removeLayer(layerId)` — changes, undoable Removes a layer and any cels painted on it; removing a group removes everything inside it. Throws if it would leave no (non-group) layer. - `layerId` (string) Returns nothing. #### `app.setActiveLayer(layerId)` — changes Makes a layer the active one (what clicking it in the layer panel does). Not an undoable edit. - `layerId` (string) Returns nothing. #### `app.setLayerEffects(layerId, effects)` — changes, undoable Sets a layer's non-destructive effects: ops run on the layer's pixels (a group's composited children) every time the sprite is composited, in order, and are never written into the cels (app.bakeLayerEffects does that). Allowed on a locked layer. Not for reference layers; the Background layer takes colour effects only. - `layerId` (string) - `effects` (table) — The layer's whole effect list, bottom first (`{}` clears it). Each entry is `{ kind = "levels", ...that effect's fields, enabled = true }`; read the current list from `app.getLayers`, change it and pass it back (entries keep their `id`). Kinds: brightnessContrast, hueSaturation, colorCurve, levels, threshold, gradientMap, colorBalance, invert, replaceColor, posterize take the fields of the matching app.* adjustment; outline takes `place`, `width`, `shape`, `color`; shadow takes `place`, `dx`, `dy`, `spread`, `shape`, `softness` (0-8 px of soft edge), `edge` ("dithered", "hard" or "smooth": how a soft edge is drawn), `color`; noise (`amount`, `size`, `mono`, `seed`), dither (`pattern`, `levels`, `strength`), scanlines (`period`, `thickness`, `offset`, `strength`, `mask`), vignette (`strength`, `radius`, `softness`, `color`), paletteSwap (`from`, `to`, `tolerance`), blur (`radius`, `shape`, `edge`, `colors`), bloom (`threshold`, `radius`, `intensity`, `edge`, `colors`), edgeDetect (`strength`, `mode`, `invert`, `color`), smoother (`size`), pixelate (`size`), chromatic (`dx`, `dy`), wave (`axis`, `amplitude`, `wavelength`, `phase`), bend (`axis`, `amount`), distortion (`axis`, `band`, `amount`, `density`, `seed`), parallax (`shift`), echo (`count`, `dx`, `dy`, `fade`), antiAlias (`a`, `anyA`, `b`, `steps`, `into`, `strength`, `colors`: as app.antiAlias), autoShade (`target`, `base`, `angle`, `highlights`, `shadows`, `width`, `hueShift`, `chroma`, `rim`, `outline`, `colors`, `ramp`: as app.autoShade). Blur and bloom can snap for pixel art: `edge` is "smooth", "hard" or "dithered", `colors` is "any" or "palette" (the frame's palette, the default on indexed sprites). Animate a number or colour parameter with `tracks = { dx = { { frame = frameId, value = 0 }, { frame = otherFrameId, value = 4, easing = ... } } }` (the path is the field, or `value.gamma`, `shadows.red` and so on; a missing easing is linear) and `timing = "frames"` or `"time"`. Every colour (`color`, `below`, `above`, `from`, `to`, a gradient stop's `color`, `a`, `b`, `base`, a colour key's `value`) is `"#rgb"`, `"#rrggbb"`, `"#rrggbbaa"` or `{ r, g, b, a }` (`a` defaults to 255); the colour lists (paletteSwap's `from` and `to`, autoShade's `ramp`) take those too, alpha ignored. Reads (`app.getLayers`) return colours as `{ r, g, b, a }` tables and colour lists as `"#rrggbb"` strings. Returns nothing. #### `app.setLayerProps(layerId, props)` — changes, undoable Updates one or more properties of a layer or group. - `layerId` (string) - `props` (table) — Omitted keys are left unchanged. - `name` (string) — The layer's name. - `opacity` (number 0–1) — Layer opacity. A group below 1 composites its layers together first (it is isolated). - `visible` (boolean) — Whether the layer is shown (and, for scripts, exported). A hidden group hides everything inside it. - `locked` (boolean) — A locked layer's content can't be changed by anything, scripts included. Locking a group locks everything inside it. - `movementLocked` (boolean) — A movement-locked layer's cels can't be moved (movePixels, moveCels, a transformPixels shift, reference placement) — painting still works. Locking a group's movement locks everything inside it. - `clipped` (boolean) — Clipping mask: the layer only shows where the nearest unclipped layer below it in the same group (its base) has pixels, and the base's blend mode applies to the whole stack. Not for reference layers or the Background layer. - `draft` (boolean) — Draft layer: shown while you work but never exported, flattened or stamped (a draft group takes everything inside it along). Not for reference layers or the Background layer. - `continuous` (boolean) — Continuous layer (Aseprite's): New Frame (app.duplicateFrame without `linked`) links this layer's cel to the original's instead of copying it. Not for groups. - `isolated` (boolean) — Groups only: composite the group's layers on their own first, then blend the result as one image (Photoshop's Normal group). Off, a group at normal blend and opacity 1 is pass-through: its layers blend straight into what's below. Any other blend mode or opacity isolates the group anyway. - `blendMode` (BlendMode: "normal" | "multiply" | "screen" | "overlay" | "darken" | "lighten" | "color-dodge" | "color-burn" | "hard-light" | "soft-light" | "difference" | "exclusion" | "hue" | "saturation" | "color" | "luminosity" | "addition" | "subtract" | "divide") — How the layer blends with those below. A group with a mode other than "normal" composites its layers together first (it is isolated). Returns nothing. #### `app.setReferenceTransform(layerId, frameId, x, y, width, height)` — changes, undoable, refused on a locked layer Repositions/resizes a reference layer's placement on one frame (sprite-space). A width or height <= 0 clears it (blank on that frame) instead. Throws if layerId isn't a reference layer. - `layerId` (string) - `frameId` (string) - `x` (number) - `y` (number) - `width` (number) - `height` (number) Returns nothing. #### `app.stampVisible(name?)` — changes, undoable Adds a new layer holding everything visible, per frame, exactly as Flatten would make it (reference and draft layers left out) but keeping every layer, above the active layer's top-level block, and makes it active. - `name` (string, optional) — Defaults to "Visible". Returns (string): The new layer's id. #### `app.ungroupLayer(layerId)` — changes, undoable Removes a group, moving its layers up into its place. Their own properties are kept; the group's opacity, blend mode and visibility are dropped, not baked in. - `layerId` (string) — A group. Returns nothing. ### Frames & tags The timeline: frames, their durations, and animation tags. #### `app.addFrame(frameId?, durationMs?, before?)` — changes, undoable Adds a new blank frame ("New Empty Frame" in the Frame menu), after `frameId` or before it with `before`. The new frame becomes the active one. - `frameId` (string, optional) — The frame to insert next to. Omit (or pass nil) to append at the end. - `durationMs` (number, optional) — Defaults to the sprite's standard frame duration. - `before` (boolean, optional) — True inserts the new frame before `frameId` instead of after it, which is the only way to add a frame ahead of the first one. Needs a `frameId`. Returns (string): The new frame's id. #### `app.addTag(fromFrameId, toFrameId, name?, color?, direction?)` — changes, undoable Creates a new tag spanning [fromFrameId, toFrameId]. - `fromFrameId` (string) - `toFrameId` (string) - `name` (string, optional) — Defaults to "Tag N". - `color` (string, optional) — Hex color. - `direction` (TagDirection: "forward" | "reverse" | "pingpong" | "pingpongReverse", optional) — Playback direction; defaults to forward. Returns (string): The new tag's id. #### `app.bakeTween(layerId, frameId)` — changes, undoable, refused on a locked layer Removes the tween that starts at frameId but keeps every in-between as a plain cel, so they no longer redraw. One undo step. - `layerId` (string) — An image layer (not the Background). - `frameId` (string) — The start keyframe's frame: the layer's own cel there (not a link) is what gets tweened. Returns nothing. #### `app.duplicateFrame(frameId, linked?)` — changes, undoable Duplicates a frame (and its cels) immediately after it, making the copy active ("New Frame" in the Frame menu). - `frameId` (string) - `linked` (boolean, optional) — True links every copied cel to the original's (shared images), false copies every one; omitted, continuous layers link and the others copy. Returns (string): The copy's frame id. #### `app.getActiveFrameId()` — reads The currently active frame's id, or nil if none. Returns (string): Frame id or nil. #### `app.getFrame(frameId)` — reads One frame as { id, index, durationMs }, or nil if there's no such frame. - `frameId` (string) Returns (table): Frame table or nil. #### `app.getFrames()` — reads Every frame in timeline order as { id, index (0 = first), durationMs }. Returns (table): Array of frame tables. #### `app.getTags()` — reads Every animation tag as { id, name, fromFrameId, toFrameId, fromIndex, toIndex (inclusive frame indices), color (hex), direction ("forward" | "reverse" | "pingpong" | "pingpongReverse"), repeat (times played before continuing; absent = loops forever) }. Returns (table): Array of tag tables. #### `app.moveFrame(frameId, toIndex)` — changes, undoable Moves a frame to a new position in the timeline. - `frameId` (string) - `toIndex` (number) — Destination position in the final ordering, clamped to the valid range. Returns nothing. #### `app.regenerateTween(layerId, frameId, revertFrameIds?)` — changes, undoable, refused on a locked layer Redraws the in-betweens of the tween that starts at frameId from its start cel. Frames you edited by hand are skipped unless listed in revertFrameIds. A tween whose end frame is gone can't be redrawn. One undo step; nothing to change adds none. - `layerId` (string) — An image layer (not the Background). - `frameId` (string) — The start keyframe's frame: the layer's own cel there (not a link) is what gets tweened. - `revertFrameIds` (table, optional) — A list of in-between frame ids whose hand-edited cels are redrawn too. Returns nothing. #### `app.removeFrame(frameId)` — changes, undoable Removes a frame and any cels, tile placements and reference placements on it. Throws if it is the last remaining frame. - `frameId` (string) Returns nothing. #### `app.removeTag(tagId)` — changes, undoable Removes a tag (the frames it spanned are untouched). - `tagId` (string) Returns nothing. #### `app.removeTween(layerId, frameId)` — changes, undoable, refused on a locked layer Removes the tween that starts at frameId, deleting its generated in-betweens (frames you edited by hand stay as plain cels). One undo step. - `layerId` (string) — An image layer (not the Background). - `frameId` (string) — The start keyframe's frame: the layer's own cel there (not a link) is what gets tweened. Returns nothing. #### `app.reverseFrames(fromFrameId?, toFrameId?)` — changes, undoable Reverses the order of the frames in a range (all frames when omitted) as one undo step. Cels, links, tilemap placements and reference transforms move with their frames; tags keep covering the same positions. - `fromFrameId` (string, optional) — First frame of the range; omit (with toFrameId) to reverse every frame. - `toFrameId` (string, optional) — Last frame of the range (inclusive). Returns nothing. #### `app.setActiveFrame(frameId)` — changes Makes a frame the active one (what clicking it in the timeline does). Not an undoable edit. - `frameId` (string) Returns nothing. #### `app.setAllFrameDurations(durationMs)` — changes, undoable Sets every frame in the sprite to the same playback duration. - `durationMs` (number) Returns nothing. #### `app.setFrameDuration(frameId, durationMs)` — changes, undoable Sets one frame's playback duration. - `frameId` (string) - `durationMs` (number) — Whole milliseconds, 1-65535. Returns nothing. #### `app.setFrameProps(frameId, props)` — changes, undoable Sets a frame's colour label and/or lock. - `frameId` (string) - `props` (table) — Omitted keys are left unchanged. - `color` (string) — The frame's timeline label as "#rrggbb"; an empty string clears it. - `locked` (boolean) — A locked frame's cels can't be edited by anything, scripts included, and the frame can't be removed, moved or re-timed. Returns nothing. #### `app.setTagProps(tagId, props)` — changes, undoable Updates one or more properties of a tag. - `tagId` (string) - `props` (table) — Omitted keys are left unchanged. - `name` (string) — The tag's name. - `color` (string) — The tag's color as "#rrggbb". - `direction` (TagDirection: "forward" | "reverse" | "pingpong" | "pingpongReverse") — Playback direction. - `fromFrameId` (string) — The first frame of the span. - `toFrameId` (string) — The last frame of the span. - `repeat` (integer ≥ 0) — How many times playback plays the tag before continuing (Aseprite's tag Repeat); 0 loops forever. Returns nothing. #### `app.setTween(layerId, frameId, opts?)` — changes, undoable, refused on a locked layer Tweens a layer's cel at frameId to a later frame: the frames in between get real cels (the start cel's picture moved, scaled, rotated, skewed and faded by the options, eased as chosen) that stay linked to the recipe, so they redraw when the start cel changes. Replaces a tween already starting there. One undo step. Frames you have drawn on stay as they are. Returns the tween's id. - `layerId` (string) — An image layer (not the Background). - `frameId` (string) — The start keyframe's frame: the layer's own cel there (not a link) is what gets tweened. - `opts` (table, optional) - `to` (string) — Required. The id of the end keyframe's frame: a later frame on the same layer. The in-betweens are the frames strictly between; the end frame keeps your own drawing. - `from` ({ dx, dy, scaleX, scaleY, rotation, skewX, skewY, opacity }, each optional (missing = no change: 0, 1, 0, 0, 1); default no change) — The pose at the start keyframe (usually left alone). - `end` ({ dx, dy, scaleX, scaleY, rotation, skewX, skewY, opacity }, each optional (missing = no change: 0, 1, 0, 0, 1); default no change) — The pose reached at the end keyframe: translation in sprite pixels, scale (1 = same), rotation and skew in degrees, opacity multiplier 0..1. - `ease` (TweenEaseFamily: "linear" | "hold" | "sine" | "quad" | "cubic" | "quart" | "quint" | "expo" | "circ" | "back" | "elastic" | "bounce"; default "quad") — The easing curve for every track. Ignored when `steps` is given. - `mode` (TweenEaseMode: "in" | "out" | "inOut"; default "inOut") — Which end of the tween `ease` shapes (ignored by linear and hold). - `overshoot` (number 0–10) — back easing: how far it winds up or overshoots (default 1.70158). Only with ease = "back". - `amplitude` (number 1–10) — elastic easing: peak size (default 1). Only with ease = "elastic". - `period` (number 0.01–1) — elastic easing: wobble length as a fraction of the span (default 0.3). Only with ease = "elastic". - `bounces` (integer 1–8) — bounce easing: number of landings (default 4). Only with ease = "bounce". - `steps` (integer 1–64) — Quantize progress into this many equal jumps (a stepped, held look) instead of a smooth curve. - `curve` (a list of 2-32 anchors { x, y, inX?, inY?, outX?, outY?, corner? }; the first is x = 0, y = 0 and the last x = 1, y = 1, and x rises along the list) — A drawn easing curve through those anchors (cubic beziers; inX/inY and outX/outY are the handles as offsets from the anchor, 0 by default). Replaces `ease` and `steps`. - `trackEasing` ({ position?, scale?, rotation?, skew?, opacity?, color? }, each an easing table with the same keys as the easing options (ease, mode, overshoot, amplitude, period, bounces, steps, curve)) — Easing for single tracks, overriding the default one for just those tracks. - `color` ({ hue?, saturation?, lightness?, tint? }: hue in degrees (-360..360), saturation and lightness -100..100, tint 0..1) — Colour adjustments reached at the end pose, applied to the start picture (HSL Hue/Saturation, then the tint). Omitted fields stay neutral (0). The `color` track's easing shapes it. - `colorFrom` (the same fields as color; default all neutral) — The colour adjustments at the start keyframe (usually left alone). - `tintColor` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default "#ffffff") — The colour the tint mixes toward (its alpha is ignored). - `ramp` ({ first, last, shift, wrap? }: palette indices first..last (0-255), shift in whole places (-255..255), wrap true by default) — Indexed sprites only: pixels whose palette index is in first..last (the transparent index excluded) move along that run of the palette by `shift` places over the span, cycling past the ends with wrap and stopping at them otherwise. Ignored on other colour modes. The `color` track's easing shapes it. - `path` (TweenPath: "straight" | "arc" | "wave" | "zigzag" | "shake" | "custom"; default "straight") — The route position takes between the poses, as an offset across the straight line. - `points` (a list of 2-64 anchors { x, y, inX?, inY?, outX?, outY?, corner? } in sprite pixels relative to the start cel's position; inX/inY and outX/outY are the handles as offsets from the anchor (0 by default)) — custom path: the drawn route, a cubic Bézier through the anchors, travelled at constant speed. Required with path = "custom". The first anchor is the `from` position and the last the `end` position: when `from` or `end` leave out dx/dy they are taken from it, and given ones must match. - `orient` (boolean; default false) — Turn the picture to follow the direction of travel along the path (added to the rotation; no turn at the start). Uses `resample` (RotSprite is a good choice). - `height` (number) — arc path: the hump height in pixels. Default 8. - `pathAmplitude` (number) — wave, zigzag and shake paths: the offset in pixels. Default 4. - `cycles` (number 0–64) — wave and zigzag paths: how many full waves long. Default 1. - `phase` (number -1–1) — wave path: where in the wave it starts, in turns. Default 0. - `seed` (integer ≥ 0) — shake path: the random seed; one seed always jitters the same way. Default 1. - `fade` (TweenFade: "alpha" | "dither"; default "alpha") — How the opacity track shows: partial opacity, or a dither dissolve of whole pixels. - `ditherPattern` (TweenDither: "bayer2" | "bayer4" | "bayer8" | "cluster"; default "bayer4") — The pattern a dither fade dissolves through, fixed to the canvas so it doesn't crawl as the picture moves. Giving it implies fade = "dither". - `crossfade` (boolean; default false) — Fade from the start picture into the end keyframe's own drawing (by the opacity track's easing), dithered or blended per `fade`. The end keyframe's drawing is then an input: editing it redraws the tween. - `delay` (integer 0–4096; default 0) — Frames to hold the start pose before moving; the motion then fills the rest of the span. - `trackDelay` ({ position?, scale?, rotation?, skew?, opacity?, color? }, each a whole number of frames (0-4096)) — Delays for single tracks, overriding `delay` for just those tracks. - `preset` (a preset recipe, as app.tweenPreset returns: { from, end, easing, path, squash, pivotX, pivotY, exposure, shortestRotation, pingPong, trackEasing?, color?, orient?, fade?, delay?, trackDelay? } with distances relative to the start cel's size) — Builds the tween from a preset, scaled to the start cel (distances are fractions of its size, delays fractions of the span). Only `to`, `resample`, `subpixel`, `timing`, `autoRegenerate`, `fade`, `ditherPattern`, `crossfade`, `delay` and `orient` may be given beside it; they override the preset's. - `squash` (number 0–1; default 0) — Velocity-based squash and stretch, volume preserving. - `pivotX` (number; default 0.5) — Fixed point of scale, rotation and skew, as a fraction of the start cel's width (0.5 = centre). - `pivotY` (number; default 0.5) — The same, as a fraction of the start cel's height. - `pivotSpace` (TweenPivotSpace: "cel" | "sprite"; default "cel") — What pivotX and pivotY are in. "sprite" makes them a point in sprite pixels, the same for every layer tweened with it. - `resample` (TransformInterpolation: "nearest" | "rotsprite" | "bilinear"; default "nearest") — How the pixels are resampled when scale, rotation or skew change. Pure translation always copies pixels exactly. - `subpixel` (TweenSubpixel: "round" | "floor" | "monotonic"; default "round") — How fractional positions land on whole pixels. - `timing` (TweenTiming: "frames" | "time"; default "frames") — What progress counts. - `exposure` (integer 1–8; default 1) — Draw every Nth pose and hold it ("on twos" = 2). - `shortestRotation` (boolean; default true) — Rotate the short way round (the change wrapped into -180..180) rather than the exact number of degrees. - `pingPong` (boolean; default false) — Out and back: the motion reaches the end pose halfway through the span and returns to the start pose at the end keyframe, so a loop that ends where it began needs no second drawing. The easing shapes each half. - `autoRegenerate` (boolean; default true) — Redraw the in-betweens by themselves when the start keyframe or the frames change. Returns (string): The tween's id. #### `app.setTweenChain(layerId, frameIds, opts?)` — changes, undoable, refused on a locked layer Tweens through several keyframes of one layer in one step: one tween from each keyframe to the next, all with the same options (the same as app.setTween without `to`). Tweens already starting at those frames are replaced; others on the layer must not overlap. One undo step. Returns the tweens' ids, in order. - `layerId` (string) — An image layer (not the Background). - `frameIds` (table) — The chain's keyframes as a list of frame ids, earliest first (2 or more). A tween runs from each to the next, so neighbours share a keyframe; each but the last needs its own cel. - `opts` (table, optional) - `from` ({ dx, dy, scaleX, scaleY, rotation, skewX, skewY, opacity }, each optional (missing = no change: 0, 1, 0, 0, 1); default no change) — The pose at the start keyframe (usually left alone). - `end` ({ dx, dy, scaleX, scaleY, rotation, skewX, skewY, opacity }, each optional (missing = no change: 0, 1, 0, 0, 1); default no change) — The pose reached at the end keyframe: translation in sprite pixels, scale (1 = same), rotation and skew in degrees, opacity multiplier 0..1. - `ease` (TweenEaseFamily: "linear" | "hold" | "sine" | "quad" | "cubic" | "quart" | "quint" | "expo" | "circ" | "back" | "elastic" | "bounce"; default "quad") — The easing curve for every track. Ignored when `steps` is given. - `mode` (TweenEaseMode: "in" | "out" | "inOut"; default "inOut") — Which end of the tween `ease` shapes (ignored by linear and hold). - `overshoot` (number 0–10) — back easing: how far it winds up or overshoots (default 1.70158). Only with ease = "back". - `amplitude` (number 1–10) — elastic easing: peak size (default 1). Only with ease = "elastic". - `period` (number 0.01–1) — elastic easing: wobble length as a fraction of the span (default 0.3). Only with ease = "elastic". - `bounces` (integer 1–8) — bounce easing: number of landings (default 4). Only with ease = "bounce". - `steps` (integer 1–64) — Quantize progress into this many equal jumps (a stepped, held look) instead of a smooth curve. - `curve` (a list of 2-32 anchors { x, y, inX?, inY?, outX?, outY?, corner? }; the first is x = 0, y = 0 and the last x = 1, y = 1, and x rises along the list) — A drawn easing curve through those anchors (cubic beziers; inX/inY and outX/outY are the handles as offsets from the anchor, 0 by default). Replaces `ease` and `steps`. - `trackEasing` ({ position?, scale?, rotation?, skew?, opacity?, color? }, each an easing table with the same keys as the easing options (ease, mode, overshoot, amplitude, period, bounces, steps, curve)) — Easing for single tracks, overriding the default one for just those tracks. - `ends` (a list of end pose tables, one per segment (same fields as end)) — Only for app.setTweenChain: a different end pose for each segment, in order; each replaces `end` for its segment. - `color` ({ hue?, saturation?, lightness?, tint? }: hue in degrees (-360..360), saturation and lightness -100..100, tint 0..1) — Colour adjustments reached at the end pose, applied to the start picture (HSL Hue/Saturation, then the tint). Omitted fields stay neutral (0). The `color` track's easing shapes it. - `colorFrom` (the same fields as color; default all neutral) — The colour adjustments at the start keyframe (usually left alone). - `tintColor` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default "#ffffff") — The colour the tint mixes toward (its alpha is ignored). - `ramp` ({ first, last, shift, wrap? }: palette indices first..last (0-255), shift in whole places (-255..255), wrap true by default) — Indexed sprites only: pixels whose palette index is in first..last (the transparent index excluded) move along that run of the palette by `shift` places over the span, cycling past the ends with wrap and stopping at them otherwise. Ignored on other colour modes. The `color` track's easing shapes it. - `path` (TweenPath: "straight" | "arc" | "wave" | "zigzag" | "shake" | "custom"; default "straight") — The route position takes between the poses, as an offset across the straight line. - `points` (a list of 2-64 anchors { x, y, inX?, inY?, outX?, outY?, corner? } in sprite pixels relative to the start cel's position; inX/inY and outX/outY are the handles as offsets from the anchor (0 by default)) — custom path: the drawn route, a cubic Bézier through the anchors, travelled at constant speed. Required with path = "custom". The first anchor is the `from` position and the last the `end` position: when `from` or `end` leave out dx/dy they are taken from it, and given ones must match. - `orient` (boolean; default false) — Turn the picture to follow the direction of travel along the path (added to the rotation; no turn at the start). Uses `resample` (RotSprite is a good choice). - `height` (number) — arc path: the hump height in pixels. Default 8. - `pathAmplitude` (number) — wave, zigzag and shake paths: the offset in pixels. Default 4. - `cycles` (number 0–64) — wave and zigzag paths: how many full waves long. Default 1. - `phase` (number -1–1) — wave path: where in the wave it starts, in turns. Default 0. - `seed` (integer ≥ 0) — shake path: the random seed; one seed always jitters the same way. Default 1. - `fade` (TweenFade: "alpha" | "dither"; default "alpha") — How the opacity track shows: partial opacity, or a dither dissolve of whole pixels. - `ditherPattern` (TweenDither: "bayer2" | "bayer4" | "bayer8" | "cluster"; default "bayer4") — The pattern a dither fade dissolves through, fixed to the canvas so it doesn't crawl as the picture moves. Giving it implies fade = "dither". - `crossfade` (boolean; default false) — Fade from the start picture into the end keyframe's own drawing (by the opacity track's easing), dithered or blended per `fade`. The end keyframe's drawing is then an input: editing it redraws the tween. - `delay` (integer 0–4096; default 0) — Frames to hold the start pose before moving; the motion then fills the rest of the span. - `trackDelay` ({ position?, scale?, rotation?, skew?, opacity?, color? }, each a whole number of frames (0-4096)) — Delays for single tracks, overriding `delay` for just those tracks. - `squash` (number 0–1; default 0) — Velocity-based squash and stretch, volume preserving. - `pivotX` (number; default 0.5) — Fixed point of scale, rotation and skew, as a fraction of the start cel's width (0.5 = centre). - `pivotY` (number; default 0.5) — The same, as a fraction of the start cel's height. - `pivotSpace` (TweenPivotSpace: "cel" | "sprite"; default "cel") — What pivotX and pivotY are in. "sprite" makes them a point in sprite pixels, the same for every layer tweened with it. - `resample` (TransformInterpolation: "nearest" | "rotsprite" | "bilinear"; default "nearest") — How the pixels are resampled when scale, rotation or skew change. Pure translation always copies pixels exactly. - `subpixel` (TweenSubpixel: "round" | "floor" | "monotonic"; default "round") — How fractional positions land on whole pixels. - `timing` (TweenTiming: "frames" | "time"; default "frames") — What progress counts. - `exposure` (integer 1–8; default 1) — Draw every Nth pose and hold it ("on twos" = 2). - `shortestRotation` (boolean; default true) — Rotate the short way round (the change wrapped into -180..180) rather than the exact number of degrees. - `pingPong` (boolean; default false) — Out and back: the motion reaches the end pose halfway through the span and returns to the start pose at the end keyframe, so a loop that ends where it began needs no second drawing. The easing shapes each half. - `autoRegenerate` (boolean; default true) — Redraw the in-betweens by themselves when the start keyframe or the frames change. Returns (table): The tweens' ids, one per segment, in order. #### `app.setTweenEnd(layerId, frameId, toFrameId)` — changes, undoable, refused on a locked layer Moves the end of the tween that starts at frameId to another frame and redraws it. In-betweens that fall outside the new range are deleted (or kept as plain cels if you edited them); the end frame itself is never touched. Fails if the new range would overlap another tween on the layer. One undo step. - `layerId` (string) — An image layer (not the Background). - `frameId` (string) — The start keyframe's frame: the layer's own cel there (not a link) is what gets tweened. - `toFrameId` (string) — The new end keyframe: a later frame on the same layer. Returns nothing. #### `app.setTweenStagger(layerIds, frameId, opts?)` — changes, undoable, refused on a locked layer Tweens several layers with one recipe (the same options as app.setTween, or a preset scaled to each layer's start cel), each layer starting `step` frames after the previous one. With groupPivot the layers scale and rotate about one shared point, so they move as one rigid group. Tweens already starting there are replaced. One undo step. Returns the tweens' ids, in the order of layerIds. - `layerIds` (table) — The layers to tween, as a list of ids: image layers (not the Background), each with its own cel on frameId. With order = "given" the list order is the stagger order. - `frameId` (string) — The start keyframe's frame, shared by every layer. - `opts` (table, optional) - `to` (string) — Required. The id of the end keyframe's frame: a later frame on the same layer. The in-betweens are the frames strictly between; the end frame keeps your own drawing. - `from` ({ dx, dy, scaleX, scaleY, rotation, skewX, skewY, opacity }, each optional (missing = no change: 0, 1, 0, 0, 1); default no change) — The pose at the start keyframe (usually left alone). - `end` ({ dx, dy, scaleX, scaleY, rotation, skewX, skewY, opacity }, each optional (missing = no change: 0, 1, 0, 0, 1); default no change) — The pose reached at the end keyframe: translation in sprite pixels, scale (1 = same), rotation and skew in degrees, opacity multiplier 0..1. - `ease` (TweenEaseFamily: "linear" | "hold" | "sine" | "quad" | "cubic" | "quart" | "quint" | "expo" | "circ" | "back" | "elastic" | "bounce"; default "quad") — The easing curve for every track. Ignored when `steps` is given. - `mode` (TweenEaseMode: "in" | "out" | "inOut"; default "inOut") — Which end of the tween `ease` shapes (ignored by linear and hold). - `overshoot` (number 0–10) — back easing: how far it winds up or overshoots (default 1.70158). Only with ease = "back". - `amplitude` (number 1–10) — elastic easing: peak size (default 1). Only with ease = "elastic". - `period` (number 0.01–1) — elastic easing: wobble length as a fraction of the span (default 0.3). Only with ease = "elastic". - `bounces` (integer 1–8) — bounce easing: number of landings (default 4). Only with ease = "bounce". - `steps` (integer 1–64) — Quantize progress into this many equal jumps (a stepped, held look) instead of a smooth curve. - `curve` (a list of 2-32 anchors { x, y, inX?, inY?, outX?, outY?, corner? }; the first is x = 0, y = 0 and the last x = 1, y = 1, and x rises along the list) — A drawn easing curve through those anchors (cubic beziers; inX/inY and outX/outY are the handles as offsets from the anchor, 0 by default). Replaces `ease` and `steps`. - `trackEasing` ({ position?, scale?, rotation?, skew?, opacity?, color? }, each an easing table with the same keys as the easing options (ease, mode, overshoot, amplitude, period, bounces, steps, curve)) — Easing for single tracks, overriding the default one for just those tracks. - `color` ({ hue?, saturation?, lightness?, tint? }: hue in degrees (-360..360), saturation and lightness -100..100, tint 0..1) — Colour adjustments reached at the end pose, applied to the start picture (HSL Hue/Saturation, then the tint). Omitted fields stay neutral (0). The `color` track's easing shapes it. - `colorFrom` (the same fields as color; default all neutral) — The colour adjustments at the start keyframe (usually left alone). - `tintColor` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default "#ffffff") — The colour the tint mixes toward (its alpha is ignored). - `ramp` ({ first, last, shift, wrap? }: palette indices first..last (0-255), shift in whole places (-255..255), wrap true by default) — Indexed sprites only: pixels whose palette index is in first..last (the transparent index excluded) move along that run of the palette by `shift` places over the span, cycling past the ends with wrap and stopping at them otherwise. Ignored on other colour modes. The `color` track's easing shapes it. - `path` (TweenPath: "straight" | "arc" | "wave" | "zigzag" | "shake" | "custom"; default "straight") — The route position takes between the poses, as an offset across the straight line. - `points` (a list of 2-64 anchors { x, y, inX?, inY?, outX?, outY?, corner? } in sprite pixels relative to the start cel's position; inX/inY and outX/outY are the handles as offsets from the anchor (0 by default)) — custom path: the drawn route, a cubic Bézier through the anchors, travelled at constant speed. Required with path = "custom". The first anchor is the `from` position and the last the `end` position: when `from` or `end` leave out dx/dy they are taken from it, and given ones must match. - `orient` (boolean; default false) — Turn the picture to follow the direction of travel along the path (added to the rotation; no turn at the start). Uses `resample` (RotSprite is a good choice). - `height` (number) — arc path: the hump height in pixels. Default 8. - `pathAmplitude` (number) — wave, zigzag and shake paths: the offset in pixels. Default 4. - `cycles` (number 0–64) — wave and zigzag paths: how many full waves long. Default 1. - `phase` (number -1–1) — wave path: where in the wave it starts, in turns. Default 0. - `seed` (integer ≥ 0) — shake path: the random seed; one seed always jitters the same way. Default 1. - `fade` (TweenFade: "alpha" | "dither"; default "alpha") — How the opacity track shows: partial opacity, or a dither dissolve of whole pixels. - `ditherPattern` (TweenDither: "bayer2" | "bayer4" | "bayer8" | "cluster"; default "bayer4") — The pattern a dither fade dissolves through, fixed to the canvas so it doesn't crawl as the picture moves. Giving it implies fade = "dither". - `crossfade` (boolean; default false) — Fade from the start picture into the end keyframe's own drawing (by the opacity track's easing), dithered or blended per `fade`. The end keyframe's drawing is then an input: editing it redraws the tween. - `delay` (integer 0–4096; default 0) — Frames to hold the start pose before moving; the motion then fills the rest of the span. - `trackDelay` ({ position?, scale?, rotation?, skew?, opacity?, color? }, each a whole number of frames (0-4096)) — Delays for single tracks, overriding `delay` for just those tracks. - `preset` (a preset recipe, as app.tweenPreset returns: { from, end, easing, path, squash, pivotX, pivotY, exposure, shortestRotation, pingPong, trackEasing?, color?, orient?, fade?, delay?, trackDelay? } with distances relative to the start cel's size) — Builds the tween from a preset, scaled to the start cel (distances are fractions of its size, delays fractions of the span). Only `to`, `resample`, `subpixel`, `timing`, `autoRegenerate`, `fade`, `ditherPattern`, `crossfade`, `delay` and `orient` may be given beside it; they override the preset's. - `squash` (number 0–1; default 0) — Velocity-based squash and stretch, volume preserving. - `pivotX` (number; default 0.5) — Fixed point of scale, rotation and skew, as a fraction of the start cel's width (0.5 = centre). - `pivotY` (number; default 0.5) — The same, as a fraction of the start cel's height. - `pivotSpace` (TweenPivotSpace: "cel" | "sprite"; default "cel") — What pivotX and pivotY are in. "sprite" makes them a point in sprite pixels, the same for every layer tweened with it. - `resample` (TransformInterpolation: "nearest" | "rotsprite" | "bilinear"; default "nearest") — How the pixels are resampled when scale, rotation or skew change. Pure translation always copies pixels exactly. - `subpixel` (TweenSubpixel: "round" | "floor" | "monotonic"; default "round") — How fractional positions land on whole pixels. - `timing` (TweenTiming: "frames" | "time"; default "frames") — What progress counts. - `exposure` (integer 1–8; default 1) — Draw every Nth pose and hold it ("on twos" = 2). - `shortestRotation` (boolean; default true) — Rotate the short way round (the change wrapped into -180..180) rather than the exact number of degrees. - `pingPong` (boolean; default false) — Out and back: the motion reaches the end pose halfway through the span and returns to the start pose at the end keyframe, so a loop that ends where it began needs no second drawing. The easing shapes each half. - `autoRegenerate` (boolean; default true) — Redraw the in-betweens by themselves when the start keyframe or the frames change. - `step` (integer 0–4096; default 0) — Frames between one layer's start and the next's (0 = all together). - `order` (TweenStaggerOrder: "topDown" | "bottomUp" | "given"; default "topDown") — Which layer starts first. - `length` (TweenStaggerLength: "keep" | "extend"; default "keep") — Whether later layers finish together with the first (keep) or keep the same motion length and end later (extend; the frames must exist). - `groupPivot` (boolean; default false) — Scale, rotate and skew every layer about one point: pivotX/pivotY taken as fractions of the box around all the start cels, so the layers move rigidly as a group. Returns (table): The tweens' ids, one per layer, in the order of layerIds. #### `app.tweenPreset(id)` — pure One built-in tween preset by id, or nil. Pass its recipe as app.setTween's preset option: app.setTween(layerId, frameId, { to = endFrameId, preset = app.tweenPreset("popIn").recipe }). - `id` (string) — A built-in preset's id, e.g. "popIn" or "slideInLeft". Returns (table): The preset, { id, name, description, frames, recipe }: recipe is the relative tween recipe app.setTween's preset option takes; nil for an unknown id. #### `app.tweenPresets()` — pure The built-in tween presets (fade in and out, dither in and out, pop, drop, slides, swoop, shake, hop, squash and stretch, float, swing, spin, blink...), in the order the Tween inspector lists them. Your own presets live in the editor on your device and aren't listed. Returns (table): A list of presets, each { id, name, description, frames, recipe }: recipe is the relative tween recipe app.setTween's preset option takes. ### Cels A cel is one layer's image on one frame. Move, clear and link them in blocks. #### `app.clearCels(cels)` — changes, undoable, refused on a locked layer Deletes several cels at once as a single undo step: images on image layers, tile placements on tilemap layers, placements on reference layers. Cels that don't exist are ignored. - `cels` (table) — Array of { layerId = ..., frameId = ... } tables naming the cels to delete. Returns nothing. #### `app.getCel(layerId, frameId)` — reads The cel at layerId/frameId in the app.getCels shape, or nil if that layer has no image (or tile placements) on that frame. - `layerId` (string) - `frameId` (string) Returns (table): Cel table or nil. #### `app.getCelLink(layerId, frameId)` — reads If the cel at layerId/frameId is linked, the frame id of the source cel whose image it shares; otherwise nil. - `layerId` (string) - `frameId` (string) Returns (string): Source frame id or nil. #### `app.getCels()` — reads Every cel (a layer's image on one frame) as { layerId, frameId, x, y, width, height, opacity, zIndex (draw-order shift, only when not 0), linkedTo (frame id of the shared image, linked cels only) }, layer by layer bottom-first then frame order. A tilemap frame with placements (or a link) is a cel too: x/y/width/height are its placements' pixel bounds. Pixel data is read with app.getImage, placements with app.getTilemap. Returns (table): Array of cel tables. #### `app.getLinkedFrames()` — reads Every linked frame on tilemap and reference layers as { layerId, frameId, linkedTo (the frame whose tile placements or placement it shares) }, layer by layer bottom-first then frame order. Image layers report their links through app.getCels' linkedTo. Returns (table): Array of link tables. #### `app.linkCels(layerId, frameIds)` — changes, undoable, refused on a locked layer Links the cels of a layer on the given frames so they share one image (editing one edits all); on a tilemap layer they share tile placements, on a reference layer the placement. Throws if the first frame has no cel. - `layerId` (string) - `frameIds` (table) — Array of frame ids. The first frame's cel image is shared by all of them; their own cels are replaced by links. Returns nothing. #### `app.moveCels(cels, dLayer, dFrame)` — changes, undoable, refused on a locked layer Moves a block of cels by (dLayer, dFrame), overwriting the destination cells, as a single undo step. Image cels move between image layers, tile placements between tilemap layers sharing a tileset, reference placements only along their own layer. Throws if the move leaves the grid or breaks that rule. - `cels` (table) — Array of { layerId = ..., frameId = ... } tables naming the block of cels to move. - `dLayer` (number) — Layer-index delta; positive moves up the stack. - `dFrame` (number) — Frame-order delta; positive moves to later frames. Returns nothing. #### `app.setCelProps(layerId, frameId, props)` — changes, undoable, refused on a locked layer Updates properties of the cel at a layer and frame (an image cel or a tilemap frame): its opacity and z-index. - `layerId` (string) - `frameId` (string) - `props` (table) — Omitted keys are left unchanged. - `opacity` (number 0–1) — Cel opacity, combined with the layer's. A linked cel shares its source's (setting it on any cel of the link sets it for all). The Background layer's cels stay at 1. - `zIndex` (integer -32767–32767) — Aseprite's cel z-index: shifts this cel's drawing order among the layers of its group on this frame (draw order = layer position + zIndex). 0 = the layer's own position. Returns nothing. #### `app.unlinkCels(layerId, frameIds)` — changes, undoable, refused on a locked layer Turns linked cels back into independent copies of the shared image (tile placements, reference placement). Listing the frame the others link to unlinks it too; the rest stay linked together. Unlinked cels are left alone. - `layerId` (string) - `frameIds` (table) — Array of frame ids whose linked cels become independent copies. Returns nothing. ### Tilemaps Tilemap layers place tiles from their own tileset on a grid. Tiles are numbered from 1 (0 is the empty tile). #### `app.addTilemapLayer(name?, tileWidth, tileHeight, parentId?, tilesetLayerId?)` — changes, undoable Creates a new tilemap layer with an empty tileset of tileWidth x tileHeight tiles on top of every other layer (or of the group parentId's layers) and makes it active. Use app.addTilemapTile to add tiles to it. With tilesetLayerId the layer shares that layer's tileset: editing, adding or removing tiles affects both. - `name` (string, optional) — Layer name; defaults to 'Tilemap N'. - `tileWidth` (number) - `tileHeight` (number) - `parentId` (string, optional) — A group to add it to (on top of its layers); omitted = the top level. - `tilesetLayerId` (string, optional) — Share the tileset of this existing tilemap layer instead of making a new one (tileWidth and tileHeight are then ignored). Returns (string): The new tilemap layer's id. #### `app.addTilemapTile(layerId, pixels?)` — changes, undoable Appends a tile to a tilemap layer's tileset and returns its tile index (1-based; 0 is the implicit empty tile). Throws if layerId isn't a tilemap layer. - `layerId` (string) - `pixels` (table, optional) — Flat pixel buffer (RGBA = 4 numbers per pixel, indexed = 1) sized to the tile; omit for a transparent tile. Returns (number): The new tile's 1-based index. #### `app.clearTilemapTile(layerId, tileIndex)` — changes, undoable, refused on a locked layer Makes a tile fully transparent. Every placement of it goes blank. - `layerId` (string) - `tileIndex` (number) — 1-based tile index. Returns nothing. #### `app.convertToNormalLayer(layerId)` — changes, undoable, refused on a locked layer Converts a tilemap layer into an image layer, rasterizing its tile placements into a cel on every frame (Layer > Convert to Normal Layer). The layer keeps its id. One undo step. - `layerId` (string) — A tilemap layer. Returns nothing. #### `app.convertToTilemap(layerId, tileWidth, tileHeight, opts?)` — changes, undoable, refused on a locked layer Converts an image layer into a tilemap layer with a new tileset of tileWidth x tileHeight tiles cut from its cels on every frame (Layer > Convert to Tilemap). The layer keeps its id; its cels become tile placements. One undo step. - `layerId` (string) — An image layer (not the Background). - `tileWidth` (number) - `tileHeight` (number) - `opts` (table, optional) - `dedupe` (boolean; default true) — Share identical tiles. - `matchFlips` (boolean; default false) — Also share tiles that match flipped or rotated 90 degrees (and turn the tileset's match options on). Returns nothing. #### `app.duplicateTilemapTile(layerId, tileIndex)` — changes, undoable Appends a copy of a tile to the end of a tilemap layer's tileset (its index is the old tile count + 1). - `layerId` (string) - `tileIndex` (number) — 1-based index of the tile to copy. Returns (number): The copy's 1-based tile index. #### `app.fillTiles(layerId, frameId, col, row, opts?)` — changes, undoable, refused on a locked layer Tile bucket: fills the cells holding the same tile as cell (col, row) with one tile (an empty cell is tile 0) — exactly the GUI bucket in Place tiles mode. The fill stays inside the canvas's tile grid. - `layerId` (string) — A tilemap layer. - `frameId` (string) - `col` (number) — The clicked tile-grid column (not a sprite pixel). - `row` (number) — The clicked tile-grid row. - `opts` (table, optional) Also takes the **Placement options** (see "Option groups"), and: - `contiguous` (boolean; default context.bucketFillMode (true for the context's contiguous mode, false for global)) — Only the connected cells (true), or every cell holding the clicked cell's tile (false). - `tileIndex` (integer ≥ 1; default context.tileIndex) — The 1-based tile to fill with. - `flipH` (boolean; default context.tileFlipH) — Flip placed tiles horizontally. - `flipV` (boolean; default context.tileFlipV) — Flip placed tiles vertically. - `rotate90` (boolean; default context.tileRotate90) — Rotate placed tiles 90° clockwise (square tiles only). Returns nothing. #### `app.getTilemap(layerId, frameId)` — reads A tilemap layer's tile placements on one frame as { x, y (tile-grid cell), tileIndex, flipH, flipV, rotate90 } — the shape app.setTilemapInstances takes. Linked frames report their shared placements. - `layerId` (string) — A tilemap layer. - `frameId` (string) Returns (table): Array of placement tables. #### `app.getTilePixels(layerId, tileIndex)` — reads A tile's image as a flat RGBA list (tileWidth*tileHeight*4, row-major; palette colors resolved on indexed sprites). - `layerId` (string) — A tilemap layer. - `tileIndex` (number) — 1-based tile. Returns (table): Flat RGBA list. #### `app.getTileset(layerId)` — reads A tilemap layer's tileset as { tileWidth, tileHeight, tileCount, matchFlipH, matchFlipV, matchRotate, baseIndex, name?, id? } (the match flags are the Auto-paint options, see app.setTilesetMatch; baseIndex is the number the first tile is displayed with, see app.setTilesetProperties; id is present when layers share the tileset, and equal ids mean the same tileset). Tiles are numbered 1..tileCount (0 = the empty tile). - `layerId` (string) — A tilemap layer. Returns (table): Tileset table. #### `app.importTilesFromImage(layerId, width, height, pixels, opts?)` — changes, undoable, refused on a locked layer Slices an image into the layer's tileset in tileWidth x tileHeight cells (edge cells are padded with transparency), appending the tiles it doesn't already have. With frameId it also places the result on that frame. One undo step. - `layerId` (string) — A tilemap layer. - `width` (number) — Image width in pixels. - `height` (number) — Image height in pixels. - `pixels` (table) — Flat RGBA numbers (4 per pixel, width*height pixels). - `opts` (table, optional) - `dedupe` (boolean; default true) — Reuse identical tiles instead of adding duplicates. - `matchFlips` (boolean; default false) — With dedupe: also reuse a tile that matches flipped horizontally/vertically or rotated 90 degrees (square tiles), placing it with the matching flags. - `skipEmpty` (boolean; default true) — Cells with no visible pixel make no tile. - `frameId` (string) — Also place the sliced cells on this frame of the layer, replacing the cells the image covers. - `col` (integer; default 0) — Tile column of the image's left edge when placing. - `row` (integer; default 0) — Tile row of the image's top edge when placing. Returns (number): How many tiles were added. #### `app.moveTilemapTile(layerId, from, to)` — changes, undoable Moves a tile to a new position in the tileset, remapping every placement so nothing changes visually. - `layerId` (string) - `from` (number) — 1-based current tile index. - `to` (number) — 1-based tile index after the move. Returns nothing. #### `app.paintTilemapPixels(layerId, frameId, x, y, width, height, pixels, mode)` — changes, undoable, refused on a locked layer Paints canvas-space pixels onto a tilemap layer, writing through each tile placement's flip/rotation. One undo step. - `layerId` (string) - `frameId` (string) - `x` (number) — Canvas-space left edge of the written region. - `y` (number) — Canvas-space top edge of the written region. - `width` (number) - `height` (number) - `pixels` (table) — Flat RGBA numbers (4 per pixel, width*height pixels); alpha 0 erases. - `mode` (TilePaintMode: "edit" | "auto") — "edit" changes the tiles the cells already use (all placements update); "auto" works per painted cell like Aseprite Auto: reuses an identical tile (or a flipped/rotated one, per app.setTilesetMatch), else a copy-on-write / new tile, then removes tiles the edit left unused. Returns nothing. #### `app.removeTilemapTile(layerId, tileIndex)` — changes, undoable, refused on a locked layer Deletes a tile from a tilemap layer's tileset. Placements of it are cleared; placements of higher-numbered tiles shift down by one so they keep showing the same image (exactly the tileset panel's Delete). - `layerId` (string) — A tilemap layer. - `tileIndex` (number) — 1-based tile to delete. Returns nothing. #### `app.setTilemapInstances(layerId, frameId, instances)` — changes, undoable, refused on a locked layer Replaces a tilemap layer's tile placements for one frame. x/y are tile-grid coordinates. Throws if layerId isn't a tilemap layer. - `layerId` (string) - `frameId` (string) - `instances` (table) — Array of {x, y, tileIndex, flipH, flipV, rotate90} tables replacing this frame's placements; empty clears them. Returns nothing. #### `app.setTilemapTilePixels(layerId, tileIndex, pixels)` — changes, undoable, refused on a locked layer Replaces a tilemap layer tile's pixels. Every placement of that tile (all frames, any flip/rotation) updates instantly. Throws on bad layerId/index/pixels length. - `layerId` (string) - `tileIndex` (number) - `pixels` (table) — Flat pixel buffer (RGBA = 4 numbers per pixel, indexed = 1) sized to the tile. Returns nothing. #### `app.setTilesetMatch(layerId, flipH, flipV, rotate)` — changes, undoable Sets the tileset's "match flips" options: in "auto" tilemap painting, new pixels equal to an existing tile flipped/rotated reuse that tile with the placement flags set, instead of creating a tile. - `layerId` (string) — A tilemap layer. - `flipH` (boolean) — Match tiles flipped horizontally. - `flipV` (boolean) — Match tiles flipped vertically. - `rotate` (boolean) — Match tiles rotated 90 degrees (square tiles only). Returns nothing. #### `app.setTilesetProperties(layerId, name?, baseIndex?)` — changes, undoable Sets a tileset's name and Base Index. The base index only changes how tiles are numbered in the Tileset panel and in Aseprite; tile indices in scripts stay 1-based. Applies to every layer sharing the tileset. - `layerId` (string) — A tilemap layer. - `name` (string, optional) — The tileset's name; "" clears it; nil leaves it. - `baseIndex` (number, optional) — Aseprite's Base Index: the number the first tile is displayed with (default 1), -32768 to 32767; nil leaves it. Returns nothing. #### `app.stampTiles(layerId, frameId, cells, opts?)` — changes, undoable, refused on a locked layer Stamps a tile into (or, with erase, clears) the given tile-grid cells of a tilemap layer — exactly the GUI tile stamp. Untouched cells keep their placements. With symmetry, the mirrored cells get the mirrored tile (flips mirror with them); with respectSelection, cells outside the selection are skipped. - `layerId` (string) — A tilemap layer. - `frameId` (string) - `cells` (table) — List of TILE-GRID cells { x = col, y = row } (or { col, row }) — not sprite pixels. - `opts` (table, optional) Also takes the **Placement options** (see "Option groups"), and: - `tileIndex` (integer ≥ 0; default context.tileIndex) — The 1-based tile to place. - `flipH` (boolean; default context.tileFlipH) — Flip placed tiles horizontally. - `flipV` (boolean; default context.tileFlipV) — Flip placed tiles vertically. - `rotate90` (boolean; default context.tileRotate90) — Rotate placed tiles 90° clockwise (square tiles only). - `erase` (boolean; default false) — Remove the cells' placements instead of placing a tile. Returns nothing. ### Palette The sprite's palette. #### `app.addColorCycle(fromIndex, toIndex, opts?)` — changes, undoable Adds a colour-cycling range (indexed sprites): while playing, the colours of that palette range rotate. Pixels aren't changed; use app.bakeColorCycles to turn the cycle into per-frame palettes. - `fromIndex` (number) — First palette index of the cycled range. - `toIndex` (number) — Last palette index of the range (inclusive). - `opts` (table, optional) - `direction` (string; default "forward") — "forward" moves each colour to the next higher index, "reverse" to the lower. - `framesPerStep` (integer 1–1000; default 1) — Playback frames per one-step rotation. Returns (string): The new cycle's id. #### `app.bakeColorCycles(frames?)` — changes, undoable Bakes the colour cycles into per-frame palettes (adding frames if needed) so exports such as GIF reproduce the cycling, then removes the cycles. Indexed sprites only. - `frames` (number, optional) — Total frames of the baked animation (at least the current count); default: one full loop of the cycles. Frames are appended after the last one. Returns nothing. #### `app.cleanPalette(opts?)` — changes, undoable Cleans the palette: merges duplicate and similar colours into their first occurrence and removes unused entries. On indexed sprites pixels follow their colours. - `opts` (table, optional) — { removeDuplicates = bool, removeUnused = bool, mergeDistance = 0-100 (delta E; ~2 is barely visible, 10 clearly different), frameId = palette in effect at this frame }. Every option defaults to off. Returns nothing. #### `app.createPaletteChange(frameId, colors?)` — changes, undoable Starts a new palette at a frame: that frame and the ones after it (until the next change) resolve indexed pixels through it. Pixel indices don't change. - `frameId` (string) — Frame the new palette starts at (not the first frame). - `colors` (table, optional) — The palette from that frame on, an array of hex colors; default: a copy of the palette in effect there. Returns nothing. #### `app.createPaletteRamp(from, to, count?, space?, mode?, frameId?)` — changes, undoable Generates a color ramp between two palette entries (Aseprite's palette Gradient): by default it overwrites the entries between them, or with mode "insert" inserts new colors before the later entry. On indexed sprites an insert shifts pixel indices so the image is unchanged. One undoable step. - `from` (number) — Palette index of one end of the ramp (0-based). - `to` (number) — Palette index of the other end (0-based). - `count` (number, optional) — How many colors to insert between them (1-256). Required for mode "insert"; "replace" fills every entry between the ends and ignores it. - `space` (string, optional) — Interpolation space: "oklab" (default, perceptually even), "oklch", "rgb" or "hsl". - `mode` (string, optional) — "replace" overwrites the entries between the two ends with the ramp (Aseprite's palette Gradient; pixels are untouched); "insert" adds `count` new colors before the later end. Default: "replace" when entries lie between the ends, otherwise "insert". - `frameId` (string, optional) — Edit the palette in effect at this frame (palettes can change per frame); default: the first frame's palette. On indexed sprites "insert" only shifts the pixels of the frames sharing that palette. Returns nothing. #### `app.getColorCycles()` — reads The colour-cycling ranges of an indexed sprite: { id, fromIndex, toIndex, direction, framesPerStep } each. Returns (table): Array of colour cycle tables. #### `app.getPalette(frameId?)` — reads The palette as an array of hex color strings, "#rrggbb" or, for entries with alpha below 255, "#rrggbbaa", e.g. { "#ff0000", "#00ff0080", ... }. Pass a frame id for that frame's palette. - `frameId` (string, optional) — Frame whose palette to read (palettes can change per frame); omit for the first frame's palette. Returns (table): Array of hex color strings. #### `app.insertPaletteEntries(index, colors, frameId?)` — changes, undoable Inserts palette entries at an index. On indexed sprites every pixel (and the transparent index) follows its entry, so the image is unchanged. - `index` (number) — Where the first new entry goes (0-based; the palette's length appends). - `colors` (table) — Array of hex color strings, "#rrggbb" or "#rrggbbaa", e.g. { "#ff0000", "#00ff0080" }. - `frameId` (string, optional) — Edit the palette in effect at this frame (palettes can change per frame); default: the first frame's palette. Returns nothing. #### `app.movePaletteEntry(fromIndex, toIndex, frameId?)` — changes, undoable Moves a palette entry to a new position. On indexed sprites every pixel follows its color, so the image is unchanged. - `fromIndex` (number) — Current palette index (0-based). - `toIndex` (number) — Index the entry should end up at (0-based). - `frameId` (string, optional) — Edit the palette in effect at this frame (palettes can change per frame); default: the first frame's palette. Returns nothing. #### `app.remapColors(mappings, cels)` — changes, undoable, refused on a locked layer Replaces exact colours with other colours across a set of cels: the Palette Audit's remap. RGBA and grayscale sprites. - `mappings` (table) — Array of { from = "#rrggbb", to = "#rrggbb" } (either may carry alpha, "#rrggbbaa"); every mapping applies at once. - `cels` (table) — Array of { layerId = ..., frameId = ... }: the cels to recolour (image layers). Returns nothing. #### `app.removeColorCycle(cycleId)` — changes, undoable Removes a colour cycle. - `cycleId` (string) Returns nothing. #### `app.removePaletteChange(frameId)` — changes, undoable Removes the palette change at a frame, so it uses the palette before it again. Pixel indices don't change. - `frameId` (string) — Frame whose palette change to remove. Returns nothing. #### `app.removePaletteEntries(indices, frameId?)` — changes, undoable Removes palette entries by index. On indexed sprites the removed entries' pixels become transparent and every other pixel (and the transparent index) follows its entry. - `indices` (table) — Array of the palette indices to remove (0-based), e.g. { 3 } or { 4, 5, 6 }. - `frameId` (string, optional) — Edit the palette in effect at this frame (palettes can change per frame); default: the first frame's palette. Returns nothing. #### `app.setColorDepth(depth?)` — changes, undoable Pins the sprite to a colour depth, like a console that can only show certain values: pixels, tiles and palettes are folded into it now (one undo step), and everything drawn later stays on it. nil removes it (the lost precision doesn't come back). Read it back with app.getSprite().colorDepth. - `depth` (table, optional) — The colour depth as levels per channel, or nil to remove it. n bits are 2^n levels (3 bits = 8). - `red` (integer 2–256) — Levels of red (with green and blue). - `green` (integer 2–256) — Levels of green. - `blue` (integer 2–256) — Levels of blue. - `gray` (integer 2–256) — Levels of gray, instead of red, green and blue: every colour becomes a gray. - `profile` (string) — The hardware profile id it stands for (e.g. "mega-drive"), shown in the editor. Returns nothing. #### `app.setPalette(colors, frameId?)` — changes, undoable Replaces the sprite's whole palette slot by slot. On indexed sprites every pixel keeps its index and shows the new color at it (a shorter palette is padded with the old colors of slots still in use). To add or remove colors with pixels following them, use app.insertPaletteEntries / app.removePaletteEntries. - `colors` (table) — Array of hex color strings, "#rrggbb" or "#rrggbbaa" (alpha), e.g. { "#ff0000", "#00ff0080" }. - `frameId` (string, optional) — Edit the palette in effect at this frame (palettes can change per frame); default: the first frame's palette. Returns nothing. #### `app.setPaletteEntry(index, color, frameId?)` — changes, undoable Recolors one palette entry in place. On indexed sprites, every pixel using that entry shows the new color. - `index` (number) — Palette entry to recolor (0-based). - `color` (string) — Hex color, "#rrggbb" (opaque) or "#rrggbbaa" (with alpha), e.g. "#ff0000" or "#ff000080". - `frameId` (string, optional) — Edit the palette in effect at this frame (palettes can change per frame); default: the first frame's palette. Returns nothing. #### `app.setPaletteGroups(groups)` — changes, undoable Replaces the palette groups: named labels over palette index ranges. They label slots, so moving or sorting colours doesn't move a group. - `groups` (table) — Array of { name = "Skin", from = 0, to = 3 } (0-based inclusive palette indices), sorted by from and not overlapping. {} removes every group. Returns nothing. #### `app.setPaletteLock(locked)` — changes, undoable Turns Palette Lock on or off. RGBA sprites only (indexed sprites always paint palette colours); saved with the file. - `locked` (boolean) — true: every draw command on an RGBA sprite snaps the pixels it paints to the nearest palette colour. Returns nothing. #### `app.setTransparentIndex(index)` — changes, undoable Sets the indexed sprite's transparent color index (Aseprite's Sprite Properties > Transparent Color). Only the property changes, not the pixel bytes. - `index` (number) — Palette index (0-255) whose pixels are transparent on indexed sprites (default 0). Returns nothing. #### `app.sortPalette(by, descending?, from?, to?, frameId?)` — changes, undoable Sorts the palette (or the from..to range) by a color property, ordered as Aseprite's palette sort: fully transparent entries first in either direction; hue puts grays first (by value) when ascending, last when descending; hue, saturation and brightness compare whole degrees and percent (saturation ties by brightness and the other way round); luminance weighs the squared channels. Remaining ties keep their order. On indexed sprites every pixel follows its color, so the image is unchanged. - `by` (string) — Sort key: "hue", "saturation", "brightness", "luminance", "red", "green", "blue" or "alpha". - `descending` (boolean, optional) — Sort high-to-low (default false). - `from` (number, optional) — First palette index to sort (0-based, default 0). - `to` (number, optional) — Last palette index to sort (inclusive, default: last). - `frameId` (string, optional) — Edit the palette in effect at this frame (palettes can change per frame); default: the first frame's palette. Returns nothing. #### `app.updateColorCycle(cycleId, props?)` — changes, undoable Changes a colour cycle's range, direction or speed. - `cycleId` (string) - `props` (table, optional) - `fromIndex` (integer 0–255) — First palette index of the range. - `toIndex` (integer 0–255) — Last palette index of the range (inclusive). - `direction` (string) — "forward" or "reverse". - `framesPerStep` (integer 1–1000) — Playback frames per one-step rotation. Returns nothing. ### 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. #### `app.addSlice(name, x, y, width, height, opts?)` — changes, undoable Adds a new named rectangular slice, optionally a 9-slice (center) and/or with a pivot. - `name` (string) - `x` (number) - `y` (number) - `width` (number) - `height` (number) - `opts` (table, optional) - `center` ({ x, y, width, height }) — The 9-slice centre { x, y, width, height }, relative to the slice's origin: inside the slice, positive size. - `pivot` ({ x, y }) — The pivot point { x, y }, relative to the slice's origin (may lie outside the slice). Returns (string): The new slice's id. #### `app.getSlices(frameId?)` — reads Every slice as { id, name, x, y, width, height, center = { x, y, width, height } (9-slice slices), pivot = { x, y } (slices with a pivot), keyFrameIds (frames where the slice has its own key, slices with per-frame keys only) }. center and pivot are relative to the slice's origin. Pass a frame id for the geometry in effect on that frame. - `frameId` (string, optional) — Frame to read the slices at (a slice can change bounds, centre and pivot per frame); omit for the first frame's. Returns (table): Array of slice tables. #### `app.removeSlice(sliceId)` — changes, undoable Removes a slice. - `sliceId` (string) Returns nothing. #### `app.removeSliceKey(sliceId, frameId)` — changes, undoable Removes a slice's key at a frame (not the first frame, which holds the slice's own geometry): from that frame on, the previous key's geometry applies again. Errors when the slice has no key on that frame. - `sliceId` (string) - `frameId` (string) Returns nothing. #### `app.updateSlice(sliceId, props, frameId?)` — changes, undoable Updates one or more properties of a slice. Resizing a 9-slice keeps its center only if it still fits; otherwise pass a new center (or false) too. With a frameId past the first frame, x, y, width, height, center and pivot edit the slice's key on that frame (creating one: the new geometry holds from that frame on, until the next key); name is always slice-wide. - `sliceId` (string) - `props` (table) — Omitted keys are left unchanged. - `name` (string) — The slice's name. - `x` (integer) — Left edge, in sprite pixels. - `y` (integer) — Top edge, in sprite pixels. - `width` (integer ≥ 1) — Width in pixels. - `height` (integer ≥ 1) — Height in pixels. - `center` ({ x, y, width, height } or false) — The 9-slice centre { x, y, width, height }, relative to the slice's origin: inside the slice, positive size. false clears it. - `pivot` ({ x, y } or false) — The pivot point { x, y }, relative to the slice's origin (may lie outside the slice). false clears it. - `frameId` (string, optional) — The frame the geometry applies at; omitted or the first frame edits the slice's own geometry. Returns nothing. ### 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. #### `app.brushFromSelection(name)` — changes Defines a custom brush from the shape of the current selection (selected pixels paint). Throws if nothing is selected or the selection is larger than 64 x 64. - `name` (string) — The name to give the brush. Returns (table): { width, height } of the brush. #### `app.defineBrush(name, brush)` — changes Defines a custom brush by name, for use as `brush = "name"` in the drawing commands or with app.useBrush. Brushes live only in the engine for the session: they aren't part of the document, aren't undoable, and aren't saved or added to the editor's brush library. - `name` (string) — 1-64 characters. Defining an existing name replaces it. - `brush` (table) — Give `rows` (readable) or `width`, `height` and `mask`. Example: { rows = { ".#.", "###", ".#." } } is a plus sign. - `name` (string) — Ignored — present so a brush from a config form field (app.config.) can be passed as is. - `rows` (list of strings, one per row; ".", " " and "0" are empty, anything else paints) — The brush as text, up to 64 x 64. Short rows are padded with empty pixels. - `width` (integer 1–64) — Width in pixels (with `mask`). - `height` (integer 1–64) — Height in pixels (with `mask`). - `mask` (flat row-major list of width*height 0/1 values) — The brush as numbers (with `width` and `height`). Returns (table): { width, height } of the brush. #### `app.getBrush(name)` — reads A custom brush by name as { name, width, height, mask (flat 0/1 list), rows (the same as strings of # and .) }, or nil if there's no such brush. - `name` (string) Returns (table): Brush table or nil. #### `app.getBrushes()` — reads The custom brushes defined with app.defineBrush, in definition order, as { name, width, height }. Built-in brushes (circle, square, horizontal) aren't listed. Returns (table): Array of { name, width, height }. #### `app.removeBrush(name)` — changes Deletes a custom brush defined with app.defineBrush. Draw commands already run keep the brush they used. - `name` (string) Returns (boolean): true if there was such a brush. #### `app.useBrush(name)` — changes Makes a custom brush the current brush (the editing context's brushShape = "custom" and customBrush), so draw commands without a `brush` option use it — the editor's toolbar shows it too. Throws if the name is unknown. - `name` (string) — A brush defined with app.defineBrush. Returns nothing. ### 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. #### `app.getContext()` — reads The editing context — the settings draw commands use for colors, brush and tool options you don't pass, shared with the GUI's toolbar. symmetry is the toolbar's symmetry toggle, readable here but NOT applied to commands implicitly — pass symmetry = app.getContext().symmetry to a draw command to follow it. Fields: { foregroundColor, backgroundColor, brushSize, brushShape, brushAngle, customBrush, pixelPerfect, eraserRightClick, ink, shades, stabilizer, pressureSize, pressureAngle, pressureGradient, pressureMin, pressureMax, velocitySize, velocityAngle, velocityGradient, velocityMin, velocityMax, dynamicsMinSize, dynamicsMinAngle, dynamicsMaxAngle, dynamicsDither, dynamicsCurve, symmetry, symmetryX, symmetryY, tilemapMode, shapeFillMode, cornerRadius, selectionCornerRadius, cornerRadiusLinked, lineWidth, curvePixelPerfect, bucketFillMode, bucketTolerance, bucketCloseGap, bucketDiagonal, bucketStopAtGrid, bucketPattern, wandFillMode, wandTolerance, wandDiagonal, wandStopAtGrid, sprayRate, scatterX, scatterY, scatterCount, blurStrength, jumbleStrength, dodgeBurnStrength, dodgeBurnHueShift, ditherPattern, ditherDensity, gradientType, gradientDithering, gradientSnap, gradientColors, gradientStops, eyedropperSource, tileIndex, tileFlipH, tileFlipV, tileRotate90, tilePlace, isoShape, isoWidth, isoDepth, isoHeight, isoBevel, isoCap, isoStyle, isoHiddenEdges, isoDirection, isoAutoShade, isoColors }. Colors are { r, g, b, a } tables; symmetry is an array of axes; customBrush is { width, height, mask (flat 0/1 list) } or nil. Change it with app.setContext. Returns (table): Context table. #### `app.setContext(props)` — changes Changes the editing context — the current colors, brush, symmetry, tilemap mode and tool options. Draw commands use them for options they aren't given, except symmetry, which is only stored for the UI (and readable via app.getContext) and is never applied to a command unless you pass it. Shared with the GUI: the toolbar shows the change, and it persists after the script ends. Not an undoable edit. - `props` (table) — Omitted keys are unchanged. - `foregroundColor` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a })) — The paint color: every draw command's `color`, and the gradient's start color. - `backgroundColor` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a })) — The secondary color: the gradient's end color, and what swapColors exchanges. - `brushSize` (integer 1–64) — Brush size in pixels for the built-in shapes (ignored by a custom brush). Also sets the spray radius (2x the size). - `brushShape` (BrushShape: "circle" | "square" | "line" | "diamond" | "cross" | "x" | "star" | "horizontal" | "custom") — "line" is a 1px line tilted by `brushAngle`; "square" is rotated by it; "circle", "diamond", "cross", "x" and "star" ignore it; "horizontal" is a line at angle 0 that ignores it; "custom" paints `customBrush`. - `brushAngle` (integer 0–180) — Brush angle in degrees (0-180): tilts the "line" brush and rotates the "square" brush. Ignored by other shapes. - `customBrush` ({ width, height, mask } with mask a flat list of 0/1, or false to clear) — The mask a custom brush paints: { rows = { ".#.", "###", ".#." } } or { width, height, mask } with mask a flat 0/1 list. Normally set with app.useBrush(name) from a brush defined with app.defineBrush. Without one, a custom brush paints a 1px dot. - `pixelPerfect` (boolean) — Removes doubled corner steps from freehand strokes (any brush size); the brush is stamped along the corrected path. - `eraserRightClick` (EraserRightClick: "background" | "replace") — Eraser right button: "background" paints the background color everywhere it touches, "replace" turns only foreground-colored pixels into the background color (Aseprite). - `ink` (Ink: "copy" | "simple" | "lock-alpha" | "shading") — How paint (strokes, spray, shapes, lines, polygon, contour, bucket, gradient, fillSelection) combines with existing pixels: "copy" replaces them (the default), "simple" alpha-composites, "lock-alpha" recolors only visible pixels, "shading" steps pixels along `shades`. - `shades` (a list of colors ("#rrggbb[aa]" or { r, g, b, a }), in order) — The ramp the shading ink steps along, in order: the editor's Shades box (or the selected palette range). Draw commands use it when ink is shading. - `stabilizer` (integer 0–64) — Stabilizer strength for freehand strokes: each point is averaged with the previous N points, smoothing wobble (the stroke lags the cursor). 0 = off. - `pressureSize` (boolean) — Let pen pressure (a mouse counts as full pressure) drive the brush size, from dynamicsMinSize up to brushSize. - `pressureAngle` (boolean) — Let pen pressure (a mouse counts as full pressure) drive the brush angle, from dynamicsMinAngle to dynamicsMaxAngle. - `pressureGradient` (boolean) — Let pen pressure (a mouse counts as full pressure) blend the color from foregroundColor to backgroundColor along the stroke. - `pressureMin` (integer 0–100) — Reading (percent) of the pressure sensor that maps to the low end. Greater than pressureMax inverts the mapping. - `pressureMax` (integer 0–100) — Reading (percent) of the pressure sensor that maps to the high end. - `velocitySize` (boolean) — Let pointer speed drive the brush size, from dynamicsMinSize up to brushSize. - `velocityAngle` (boolean) — Let pointer speed drive the brush angle, from dynamicsMinAngle to dynamicsMaxAngle. - `velocityGradient` (boolean) — Let pointer speed blend the color from foregroundColor to backgroundColor along the stroke. - `velocityMin` (integer 0–100) — Reading (percent) of the velocity sensor that maps to the low end. Greater than velocityMax inverts the mapping. - `velocityMax` (integer 0–100) — Reading (percent) of the velocity sensor that maps to the high end. - `dynamicsMinSize` (integer 1–64) — Brush size at the low end of a sensor mapped to size (the high end is brushSize). - `dynamicsMinAngle` (integer 0–180) — Brush angle at the low end of a sensor mapped to angle. - `dynamicsMaxAngle` (integer 0–180) — Brush angle at the high end of a sensor mapped to angle. - `dynamicsDither` (boolean) — Gradient dynamics pick foregroundColor or backgroundColor per pixel (ordered dither) instead of blending. Indexed sprites snap blends to the nearest palette color. - `dynamicsCurve` (integer 25–400) — Sensitivity curve in percent: the sensor value is raised to curve/100 (100 = linear, above 100 needs more pressure/speed to take effect). - `symmetry` (list of SymmetryAxis: "horizontal" | "vertical" | "diagonal" | "antidiagonal") — The active sprite's symmetry setting (each sprite keeps its own, like its axis position): mirror axes through the symmetry center. Readable information only — draw commands do not apply it unless you pass symmetry = app.getContext().symmetry. Empty = off; several axes combine (horizontal + vertical = 4-way). - `symmetryX` (a number (multiple of 0.5), or false to re-center) — Where the vertical symmetry axis (and the point both diagonals cross) sits: pixels from the canvas's left edge, a multiple of 0.5 (whole = between pixels, x.5 = through a pixel's middle). nil = centered. Like symmetry, readable information only: pass symmetryX = app.getContext().symmetryX to a draw command to follow it. Set false to re-center. - `symmetryY` (a number (multiple of 0.5), or false to re-center) — Where the horizontal symmetry axis (and the diagonals' crossing) sits: pixels from the canvas's top edge, a multiple of 0.5. nil = centered. Set false to re-center. - `tilemapMode` (TilemapEditMode: "tiles" | "pixels-edit" | "pixels-auto") — How pixel paint treats a tilemap layer: "pixels-auto" gives each painted cell its own tile, "pixels-edit" edits the shared tile in place (every placement updates), "tiles" allows only whole-tile stamping. - `shapeFillMode` (FillMode: "outline" | "filled") — Rectangles, ellipses, polygons and closed curves: outline or filled. - `cornerRadius` (an integer 0–1024 for every corner, or a list of four { topLeft, topRight, bottomRight, bottomLeft }) — Rectangle: corner radii in pixels, clockwise from the top-left (0 = a square corner), each clamped to half the smaller side. - `selectionCornerRadius` (an integer 0–1024 for every corner, or a list of four { topLeft, topRight, bottomRight, bottomLeft }) — Rectangular marquee: corner radii in pixels, clockwise from the top-left (0 = a square corner). - `cornerRadiusLinked` (boolean) — The tool options bar edits all four corner radii together (one Radius field) instead of one field per corner. - `lineWidth` (integer 1–64) — Line and Curve tools: thickness in pixels (1 = a hard 1px line); wider lines get round ends. - `curvePixelPerfect` (boolean) — Curve tool: remove the L-shaped corners a 1px curve picks up, for clean pixel-art lines. - `bucketFillMode` (BucketFillMode: "contiguous" | "global") — Flood fill: only the connected region, or every matching pixel. - `bucketTolerance` (integer 0–255) — Flood fill: the largest per-channel difference that still counts as the same color. - `bucketCloseGap` (integer 0–5) — Flood fill (contiguous): treat gaps in the outline up to this many pixels wide as closed, so the fill doesn't leak through them. 0 = off. - `bucketDiagonal` (boolean) — Flood fill (contiguous): also spread to diagonal neighbours (8-connected), so the region leaks through 1px diagonal lines. - `bucketStopAtGrid` (boolean) — Flood fill: confine the region to the grid cell the click lands in. - `bucketPattern` (FillPattern: "solid" | "checker" | "dither25" | "dither75" | "hstripes" | "vstripes" | "diagonal" | "grid" | "dots" | "bricks") — Flood fill: paint a repeating two-color pattern (the fill color and the other of foreground/background) instead of a solid color. "solid" = no pattern. - `wandFillMode` (BucketFillMode: "contiguous" | "global") — Magic wand: only the connected region, or every matching pixel. Separate from the flood fill's bucketFillMode. - `wandTolerance` (integer 0–255) — Magic wand: the largest per-channel difference that still counts as the same color. Separate from the flood fill's bucketTolerance. - `wandDiagonal` (boolean) — Magic wand (contiguous): also spread to diagonal neighbours (8-connected), so the region leaks through 1px diagonal lines. - `wandStopAtGrid` (boolean) — Magic wand: confine the region to the grid cell the click lands in. - `sprayRate` (integer 1–64) — Spray: dots per point. - `scatterX` (integer 0–64) — Scatter: how far each stamp can land left or right of the path, in pixels. - `scatterY` (integer 0–64) — Scatter: how far each stamp can land above or below the path, in pixels. - `scatterCount` (integer 1–16) — Scatter: stamps placed at each step along the path. - `blurStrength` (integer 1–4) — Blur: neighborhood radius in pixels. - `jumbleStrength` (integer 0–100) — Jumble: percent chance each touched pixel is scrambled. - `dodgeBurnStrength` (integer 1–100) — Dodge/burn: how far each touched pixel's OKLCH lightness moves (100 = half of black-to-white). - `dodgeBurnHueShift` (integer 0–60) — Dodge/burn: degrees each touched pixel's OKLCH hue turns toward warm yellow (dodge) or cool blue (burn), never past it; 0 keeps the hue. - `ditherPattern` (DitherPattern: "bayer2" | "bayer4" | "bayer8" | "dots" | "lines-h" | "lines-v" | "diagonal" | "noise") — Dither brush: the ordered pattern it paints through. Patterns tile from the sprite's top-left corner, so strokes line up. - `ditherDensity` (integer 0–100) — Dither brush: the share of the pattern's pixels it paints, in percent (50 with bayer2 is a checkerboard). - `gradientType` (GradientType: "linear" | "radial") — Gradient: a straight ramp between the points, or radiating from the start point. - `gradientDithering` (GradientDithering: "none" | "bayer2" | "bayer4" | "bayer8" | "dots" | "lines-h" | "lines-v" | "diagonal" | "noise") — Gradient: the ordered-dithering pattern (none = a smooth ramp). - `gradientSnap` (GradientSnap: "free" | "palette") — Gradient: free blends, or only palette colors (each pixel the nearest palette color; dithering steps between neighbouring palette colors). - `gradientColors` (GradientColors: "fg-bg" | "stops") — Gradient: run from the foreground to the background color, or along gradientStops ("stops"; fewer than two stops falls back to foreground to background). - `gradientStops` (a list of { color, position } (position 0-1, in order), or a list of colors spread evenly) — Gradient: the multi-stop ramp from the start point, as { color, position } entries (position 0-1, in order; two at one position make a hard edge). A plain list of colors is spread evenly. Used when gradientColors is "stops"; the editor's Stops bar edits it. - `eyedropperSource` (EyedropperSource: "composite" | "layer") — Eyedropper: sample what the canvas shows, or only the active layer. - `tileIndex` (integer ≥ 0) — Tile stamp: the 1-based tile to place (0 = the empty tile). - `tileFlipH` (boolean) — Tile stamp: flip placed tiles horizontally. - `tileFlipV` (boolean) — Tile stamp: flip placed tiles vertically. - `tileRotate90` (boolean) — Tile stamp: rotate placed tiles 90° clockwise. - `tilePlace` (boolean) — Place tiles: on a tilemap layer the pencil, eraser, line, shape tools and bucket place the tile stamp's tile (erase it, for the eraser) on the cells they cover instead of painting pixels. - `isoShape` (IsoShape: "box" | "ramp" | "stairs" | "pyramid" | "prism" | "cylinder" | "cone" | "sphere" | "dome") — Iso Shape tool: the solid it draws. - `isoWidth` (integer 1–1024) — Iso Shape tool: footprint along x (down-right), in iso units (one row of the edge staircase each). - `isoDepth` (integer 1–1024) — Iso Shape tool: footprint along y (down-left), in iso units. - `isoHeight` (integer 0–1024) — Iso Shape tool: height in pixels (0 = flat). - `isoBevel` (integer 0–1024) — Iso Shape tool: boxes only, how many pixels the top edges are chamfered (0 = square). - `isoCap` (integer 2–4) — Iso Shape tool: the flat run at the top and bottom vertices, 2 (even), 3 (odd) or 4 (multiples of four). Also the W / D drag step (2 units at cap 4). - `isoStyle` (IsoShapeStyle: "filled" | "outline" | "filled-outline") — Iso Shape tool: faces, outline, or both. - `isoHiddenEdges` (boolean) — Iso Shape tool: also draw the edges behind the solid. - `isoDirection` (IsoDirection: "+x" | "-x" | "+y" | "-y") — Iso Shape tool: which way a ramp or stairs descend, or a prism's slopes face. - `isoAutoShade` (boolean) — Iso Shape tool: derive the five colors from the foreground color along `shades` (true), or use isoColors (false). - `isoColors` (a list of colors ("#rrggbb[aa]" or { r, g, b, a }), in order) — Iso Shape tool: the colors used when isoAutoShade is off, in order outline, inner edge, top, left, right. Returns nothing. #### `app.swapColors()` — changes Swaps the foreground and background colors (the GUI's X). Returns nothing. ### 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)). #### `app.random.hash(x, y, seed?)` — pure A stateless float in [0, 1) from integer inputs (an integer avalanche hash): the same (x, y, seed) always gives the same value, in any order. For per-pixel noise or dithering without a generator. - `x` (number) — Integer (floored). - `y` (number) — Integer (floored). - `seed` (number, optional) — Default 0. Returns (number): A float, 0 <= x < 1. #### `app.random.new(seed)` — pure Creates a seeded random generator (mulberry32, the same algorithm the editor's spray and jumble use). The same seed always gives the same sequence. Prefer it to math.random, which is different on every run. Use the methods with a colon: local rng = app.random.new(app.config.seed); rng:int(1, 6). - `seed` (number) — An integer 0-4294967295. Floats are floored; anything else is reduced to 32 bits. Returns (table): A generator object; call its methods with a colon. #### `app.random.seed()` — pure A fresh random integer seed, 0-4294967295. NOT deterministic: it differs on every run, so print it or store it if you want to reproduce the result. Use it when the user leaves the seed field empty: app.random.new(app.config.seed or app.random.seed()). Returns (number): An integer 0-4294967295. #### `rng:chance(p)` — pure true with probability p. - `p` (number) — Probability 0-1. Returns (boolean): true or false. #### `rng:fork(label)` — pure An independent child generator derived deterministically from this generator's current state and the label, WITHOUT advancing this one. Give each feature its own fork (rng:fork("leaves")) so adding a feature doesn't change every later roll. Forking twice at the same point with the same label gives identical children. - `label` (string) — A string or a number naming the child. Returns (table): A generator object; call its methods with a colon. #### `rng:int(lo, hi)` — pure A random integer from lo to hi, both included. - `lo` (number) — Smallest result (floored). - `hi` (number) — Largest result, inclusive (floored); at least lo. Returns (number): An integer, lo <= x <= hi. #### `rng:next()` — pure The next float in [0, 1). Returns (number): A float, 0 <= x < 1. #### `rng:normal(mean?, sd?)` — pure A normally distributed float (Box-Muller). Uses two rolls per call. - `mean` (number, optional) — Default 0. - `sd` (number, optional) — Standard deviation; default 1. Returns (number): A float around mean. #### `rng:pick(list)` — pure A random element of the list; nil for an empty list (and the generator isn't advanced). - `list` (table) — A list (1..#list). Returns (table): One element of the list, or nil. #### `rng:range(a, b)` — pure A random float from a up to (not including) b. - `a` (number) - `b` (number) Returns (number): A float in [a, b). #### `rng:shuffle(list)` — pure A new list with the same elements in random order (Fisher-Yates). The input list is not changed. - `list` (table) — A list (1..#list). Returns (table): A new shuffled list. ### 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. #### `app.color.adjust(c, opts?)` — pure Changes a color's lightness, chroma, hue, saturation or alpha, absolutely or relatively. The result is gamut-clamped to sRGB by reducing chroma (lightness and hue are kept), not by clipping channels. - `c` (color) — A color: "#rrggbb", "#rrggbbaa" or { r, g, b, a }. - `opts` (table, optional) — Absolute keys set a value; the d-prefixed keys add to it (the set is applied first, then the delta). Omitted keys keep the color's own value. - `lightness` (number) — Set the lightness: OKLab/OKLCH L (0 black - 1 white), or HSL lightness with space = "hsl". - `chroma` (number ≥ 0) — Set the OKLCH chroma (colorfulness, 0 = gray; sRGB tops out near 0.32). Not available with space = "hsl". - `hue` (number) — Set the hue angle in degrees (wraps around 360). - `saturation` (number 0–1) — Set the saturation 0-1: HSL saturation with space = "hsl"; otherwise chroma as a fraction of the most chroma sRGB allows at that lightness and hue. - `alpha` (number 0–255) — Set the alpha, 0-255. - `dLightness` (number) — Add to the lightness (negative darkens). - `dChroma` (number) — Add to the chroma. - `dHue` (number) — Rotate the hue by this many degrees. - `dSaturation` (number) — Add to the saturation (0-1 scale). - `dAlpha` (number) — Add to the alpha (0-255 scale). - `space` (string; default "oklab") — "oklab" | "oklch" (the same here) | "hsl". "rgb" has no lightness or hue axis and is rejected. Returns (table): { r, g, b, a }, each 0-255. #### `app.color.contrast(a, b)` — pure WCAG contrast ratio between two colors (alpha ignored), from 1 (identical) to 21 (black on white). 4.5 is the usual minimum for readable text. - `a` (color) — A color: "#rrggbb", "#rrggbbaa" or { r, g, b, a }. - `b` (color) — A color: "#rrggbb", "#rrggbbaa" or { r, g, b, a }. Returns (number): The ratio, 1-21. #### `app.color.darken(c, amount, space?)` — pure Shortcut for app.color.adjust(c, { dLightness = -amount }). - `c` (color) — A color: "#rrggbb", "#rrggbbaa" or { r, g, b, a }. - `amount` (number) — Subtracted from the lightness (OKLab L units, 0-1). - `space` (string, optional) — Where lightness is measured: "oklab" (default), "oklch" or "hsl". Returns (table): { r, g, b, a }, each 0-255. #### `app.color.distance(a, b)` — pure Euclidean distance between two colors in OKLab (alpha ignored). 0 = identical; black to white is 1; about 0.02 is barely noticeable. - `a` (color) — A color: "#rrggbb", "#rrggbbaa" or { r, g, b, a }. - `b` (color) — A color: "#rrggbb", "#rrggbbaa" or { r, g, b, a }. Returns (number): The distance, 0 or more. #### `app.color.hex(c, withAlpha?)` — pure A color as a lowercase hex string. - `c` (color) — A color: "#rrggbb", "#rrggbbaa" or { r, g, b, a }. - `withAlpha` (boolean, optional) — true for "#rrggbbaa"; default "#rrggbb". Returns (string): "#rrggbb", or "#rrggbbaa" with withAlpha. #### `app.color.lighten(c, amount, space?)` — pure Shortcut for app.color.adjust(c, { dLightness = amount }). - `c` (color) — A color: "#rrggbb", "#rrggbbaa" or { r, g, b, a }. - `amount` (number) — Added to the lightness (OKLab L units, 0-1). - `space` (string, optional) — Where lightness is measured: "oklab" (default), "oklch" or "hsl". Returns (table): { r, g, b, a }, each 0-255. #### `app.color.mix(a, b, t, space?)` — pure Blends two colors (alpha too) in a perceptual space; OKLab by default, which avoids the muddy midpoints of RGB mixing. - `a` (color) — A color: "#rrggbb", "#rrggbbaa" or { r, g, b, a }. - `b` (color) — A color: "#rrggbb", "#rrggbbaa" or { r, g, b, a }. - `t` (number) — 0 = a, 1 = b; clamped to 0-1. - `space` (string, optional) — Interpolation space: "oklab" (default), "oklch" (hue travels the short way round), "rgb" or "hsl". Returns (table): { r, g, b, a }, each 0-255. #### `app.color.nearest(c, palette)` — pure The palette entry closest to c by OKLab distance (alpha ignored). Ties go to the earlier entry. - `c` (color) — A color: "#rrggbb", "#rrggbbaa" or { r, g, b, a }. - `palette` (table) — A non-empty list of colors. Returns (number): The 1-based index into palette. #### `app.color.parse(c)` — pure Normalizes any accepted color to { r, g, b, a } (a = 255 when the input has none). - `c` (color) — A color: "#rrggbb", "#rrggbbaa" or { r, g, b, a }. Returns (table): { r, g, b, a }, each 0-255. #### `app.color.ramp(stops, n, space?)` — pure A gradient sampled into a list of n colors, first to last stop, both endpoints included. One call regardless of n. - `stops` (table) — A list of colors, or of { color =, pos = } with pos 0-1 (stops without pos are spaced evenly). Needs at least one. - `n` (number) — How many colors to return, 1-4096. 1 returns the first stop. - `space` (string, optional) — Interpolation space: "oklab" (default), "oklch" (hue travels the short way round), "rgb" or "hsl". Returns (table): A list of n { r, g, b, a } colors. #### `app.color.shades(base, n, opts?)` — pure A shadow-to-highlight list of n colors around base, for shading. The base sits at index floor(n / 2) + 1 (the middle for odd n) and is returned exactly. With f running from -1 (darkest) through 0 (base) to +1 (lightest), shade i has OKLab lightness L + f * spread / 2 (clamped to 0-1), hue H + f * hueShift, and chroma C * (1 - 0.4 * f^2) so the extremes stay inside sRGB (anything still outside loses chroma, never lightness). Alpha is kept. Lightness is monotonic along the list. - `base` (color) — A color: "#rrggbb", "#rrggbbaa" or { r, g, b, a }. - `n` (number) — How many colors to return, 1-4096. - `opts` (table, optional) - `spread` (number; default 0.5) — Total OKLab lightness range, shadow to highlight (the base is the middle: half the spread down, half up). Clamped to 0-1. - `hueShift` (number; default 8) — Degrees the hue turns at the extremes: shadows by -hueShift, highlights by +hueShift, scaling linearly to 0 at the base. Returns (table): A list of n { r, g, b, a } colors, darkest first. ### 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. #### `Dialog(opts)` — pure Creates a dialog: a window the script draws in the editor, built with the dlg: methods and opened with dlg:show. The window always names the script and is marked "script" so it cannot pass for part of the editor, and everything a script puts in it is plain text. Add widgets with the dlg: methods (they chain), then dlg:show{ wait = false } for a window that stays open while the user keeps drawing, or dlg:show{ wait = true } (the default) to stop the script until it is closed. A script with a dialog open stays alive, idle; stopping the script closes it. Callbacks (onclick, onchange, onclose) run in the script; each one is a single undo step. At most 4 dialogs can be open and 10 opened per 10 seconds; at most 100 widgets each. - `opts` (table) — A title string, or a table. Dialog("Title") is Dialog{ title = "Title" }. - `title` (string) — Shown at the top of the dialog (max 80 characters). Text only. - `onclose` (function(ev)) — Called, in the script, when the user closes the dialog. Returns (table): The dialog. dlg.data is a table of the current values of the widgets that have an id. #### `dlg:button(opts?)` — pure Adds a button. Without an onclick, pressing it closes the dialog and sets dlg.data. to true (so after dlg:show{ wait = true } you can tell which button closed it). - `opts` (table, optional) - `id` (string) — Names the widget: its value is read as dlg.data. and it is the target of dlg:modify. Letters, digits and _ , starting with a letter; unique in the dialog. - `label` (string) — Text shown to the left of the widget; a widget with a label starts its own row. - `visible` (boolean; default true) — Show the widget. - `enabled` (boolean; default true) — Let the user change it. - `text` (string) — The button's text. - `focus` (boolean; default false) — Start with the keyboard focus here. - `onclick` (function(ev)) — Called, in the script, when the user presses it, with ev = { id }. Everything it changes in the document is one undo step. Returns (table): The dialog, for chaining. #### `dlg:check(opts?)` — pure Adds a checkbox. Its value is dlg.data. (a boolean). - `opts` (table, optional) - `id` (string) — Names the widget: its value is read as dlg.data. and it is the target of dlg:modify. Letters, digits and _ , starting with a letter; unique in the dialog. - `label` (string) — Text shown to the left of the widget; a widget with a label starts its own row. - `visible` (boolean; default true) — Show the widget. - `enabled` (boolean; default true) — Let the user change it. - `text` (string) — The text next to the box. - `selected` (boolean; default false) — Start checked. - `onchange` (function(ev)) — Called, in the script, each time the user changes the value, with ev = { id, value }. A burst of changes (dragging a slider) may arrive as just the last one. Everything it changes in the document is one undo step. Returns (table): The dialog, for chaining. #### `dlg:close()` — pure Closes the dialog (from the script, e.g. in a button's onclick). A script ends when its main chunk is done and no dialog is open. Returns (table): The dialog, for chaining. #### `dlg:color(opts?)` — pure Adds a color field that opens the full color picker, with the sprite's palette and recent colors. Its value is dlg.data. (a "#rrggbb" or "#rrggbbaa" string). - `opts` (table, optional) - `id` (string) — Names the widget: its value is read as dlg.data. and it is the target of dlg:modify. Letters, digits and _ , starting with a letter; unique in the dialog. - `label` (string) — Text shown to the left of the widget; a widget with a label starts its own row. - `visible` (boolean; default true) — Show the widget. - `enabled` (boolean; default true) — Let the user change it. - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a }); default "#000000") — The starting color. - `onchange` (function(ev)) — Called, in the script, each time the user changes the value, with ev = { id, value }. A burst of changes (dragging a slider) may arrive as just the last one. Everything it changes in the document is one undo step. Returns (table): The dialog, for chaining. #### `dlg:combobox(opts?)` — pure Adds a drop-down list. Its value is dlg.data. (the chosen option's text). - `opts` (table, optional) - `id` (string) — Names the widget: its value is read as dlg.data. and it is the target of dlg:modify. Letters, digits and _ , starting with a letter; unique in the dialog. - `label` (string) — Text shown to the left of the widget; a widget with a label starts its own row. - `visible` (boolean; default true) — Show the widget. - `enabled` (boolean; default true) — Let the user change it. - `options` (a list of strings (max 200)) — The choices. - `option` (string; default the first option) — The starting choice. - `onchange` (function(ev)) — Called, in the script, each time the user changes the value, with ev = { id, value }. A burst of changes (dragging a slider) may arrive as just the last one. Everything it changes in the document is one undo step. Returns (table): The dialog, for chaining. #### `dlg:entry(opts?)` — pure Adds a text box. Its value is dlg.data. (a string). - `opts` (table, optional) - `id` (string) — Names the widget: its value is read as dlg.data. and it is the target of dlg:modify. Letters, digits and _ , starting with a letter; unique in the dialog. - `label` (string) — Text shown to the left of the widget; a widget with a label starts its own row. - `visible` (boolean; default true) — Show the widget. - `enabled` (boolean; default true) — Let the user change it. - `text` (string; default "") — The starting text. - `focus` (boolean; default false) — Start with the keyboard focus here. - `onchange` (function(ev)) — Called, in the script, each time the user changes the value, with ev = { id, value }. A burst of changes (dragging a slider) may arrive as just the last one. Everything it changes in the document is one undo step. Returns (table): The dialog, for chaining. #### `dlg:file(opts?)` — pure Adds a file picker (open only: the browser's file chooser). dlg.data. is the chosen file's name; the file's content is reached through ev.file in onchange (and dlg.files.): { name, size, type, text }, where text is only set for small text files (up to 256 KiB). Images and other binary files are not readable. - `opts` (table, optional) - `id` (string) — Names the widget: its value is read as dlg.data. and it is the target of dlg:modify. Letters, digits and _ , starting with a letter; unique in the dialog. - `label` (string) — Text shown to the left of the widget; a widget with a label starts its own row. - `visible` (boolean; default true) — Show the widget. - `enabled` (boolean; default true) — Let the user change it. - `filename` (string) — A name to show before a file is chosen. - `title` (string; default "Choose file...") — The picker button's text. - `filetypes` (a list of extensions without the dot, e.g. { "png", "gpl" }) — Files the chooser offers. - `onchange` (function(ev)) — Called, in the script, each time the user changes the value, with ev = { id, value }. A burst of changes (dragging a slider) may arrive as just the last one. Everything it changes in the document is one undo step. Returns (table): The dialog, for chaining. #### `dlg:label(opts?)` — pure Adds a line of text (several with \n, up to 20 lines). - `opts` (table, optional) - `id` (string) — Names the widget: its value is read as dlg.data. and it is the target of dlg:modify. Letters, digits and _ , starting with a letter; unique in the dialog. - `label` (string) — Text shown to the left of the widget; a widget with a label starts its own row. - `visible` (boolean; default true) — Show the widget. - `enabled` (boolean; default true) — Let the user change it. - `text` (string) — The text. Returns (table): The dialog, for chaining. #### `dlg:modify(opts?)` — pure Changes a widget (or the dialog's title) while the dialog is open or before it is shown. Values are checked and clamped, and dlg.data follows. Assigning dlg.data = { id = value, ... } does the same for several values. - `opts` (table, optional) - `id` (string) — The widget to change. Without it, the dialog itself (only title). - `title` (string) — The dialog's title (with no id), or a file widget's button text. - `text` (string) — A new text (an entry's or number's value; a label's, check's, radio's or button's text). - `value` (a number, string or boolean to match the widget) — A new value of any value widget (a slider's number, ...). - `selected` (boolean) — A new state for a check or radio. - `option` (string) — A new choice for a combobox. - `options` (a list of strings) — New choices for a combobox. - `color` (color ("#rgb", "#rrggbb[aa]" or { r, g, b, a })) — A new color for a color widget. - `colors` (a list of colors) — New swatches for shades. - `min` (number) — A new smallest value (number, slider). - `max` (number) — A new largest value (number, slider). - `label` (string) — A new label. - `visible` (boolean) — Show or hide the widget. - `enabled` (boolean) — Enable or disable the widget. Returns (table): The dialog, for chaining. #### `dlg:newrow()` — pure Starts a new row: widgets added next go below instead of beside the previous one. Returns (table): The dialog, for chaining. #### `dlg:number(opts?)` — pure Adds a number box. Its value is dlg.data. (a number). - `opts` (table, optional) - `id` (string) — Names the widget: its value is read as dlg.data. and it is the target of dlg:modify. Letters, digits and _ , starting with a letter; unique in the dialog. - `label` (string) — Text shown to the left of the widget; a widget with a label starts its own row. - `visible` (boolean; default true) — Show the widget. - `enabled` (boolean; default true) — Let the user change it. - `text` (number; default 0) — The starting value. - `min` (number) — Smallest value. - `max` (number) — Largest value. - `decimals` (integer 0–6; default 0) — Decimal places kept. - `onchange` (function(ev)) — Called, in the script, each time the user changes the value, with ev = { id, value }. A burst of changes (dragging a slider) may arrive as just the last one. Everything it changes in the document is one undo step. Returns (table): The dialog, for chaining. #### `dlg:radio(opts?)` — pure Adds a radio button. Radio buttons next to each other in a row form a group where one is selected; each one's value is dlg.data. (a boolean). - `opts` (table, optional) - `id` (string) — Names the widget: its value is read as dlg.data. and it is the target of dlg:modify. Letters, digits and _ , starting with a letter; unique in the dialog. - `label` (string) — Text shown to the left of the widget; a widget with a label starts its own row. - `visible` (boolean; default true) — Show the widget. - `enabled` (boolean; default true) — Let the user change it. - `text` (string) — The text next to the button. - `selected` (boolean; default false) — Start selected. - `onchange` (function(ev)) — Called, in the script, each time the user changes the value, with ev = { id, value }. A burst of changes (dragging a slider) may arrive as just the last one. Everything it changes in the document is one undo step. Returns (table): The dialog, for chaining. #### `dlg:separator(opts?)` — pure Adds a horizontal line, with optional text, on its own row. - `opts` (table, optional) - `id` (string) — Names the widget: its value is read as dlg.data. and it is the target of dlg:modify. Letters, digits and _ , starting with a letter; unique in the dialog. - `label` (string) — Text shown to the left of the widget; a widget with a label starts its own row. - `visible` (boolean; default true) — Show the widget. - `enabled` (boolean; default true) — Let the user change it. - `text` (string) — Text shown beside the line. Returns (table): The dialog, for chaining. #### `dlg:shades(opts?)` — pure Adds a row of color swatches to pick from. Pressing one calls onclick with ev = { id, color }. - `opts` (table, optional) - `id` (string) — Names the widget: its value is read as dlg.data. and it is the target of dlg:modify. Letters, digits and _ , starting with a letter; unique in the dialog. - `label` (string) — Text shown to the left of the widget; a widget with a label starts its own row. - `visible` (boolean; default true) — Show the widget. - `enabled` (boolean; default true) — Let the user change it. - `colors` (a list of colors (max 512)) — The swatches. - `onclick` (function(ev)) — Called, in the script, when the user presses it, with ev = { id }. Everything it changes in the document is one undo step. Returns (table): The dialog, for chaining. #### `dlg:show(opts?)` — pure Opens the dialog. Widgets cannot be added afterwards; change them with dlg:modify. - `opts` (table, optional) - `wait` (boolean; default true) — true: the dialog is modal (the studio is dimmed and locked) and the script stops here until it is closed; the changes made before are their own undo step, the rest of the script a new one. false: the dialog is a floating window; show returns at once and the script stays alive, idle, until the dialog is closed. Returns (table): The dialog, for chaining. #### `dlg:slider(opts?)` — pure Adds a slider. Its value is dlg.data. (a number). - `opts` (table, optional) - `id` (string) — Names the widget: its value is read as dlg.data. and it is the target of dlg:modify. Letters, digits and _ , starting with a letter; unique in the dialog. - `label` (string) — Text shown to the left of the widget; a widget with a label starts its own row. - `visible` (boolean; default true) — Show the widget. - `enabled` (boolean; default true) — Let the user change it. - `min` (number; default 0) — Smallest value. - `max` (number; default 100) — Largest value. - `value` (number; default min) — The starting value. - `onchange` (function(ev)) — Called, in the script, each time the user changes the value, with ev = { id, value }. A burst of changes (dragging a slider) may arrive as just the last one. Everything it changes in the document is one undo step. Returns (table): The dialog, for chaining. #### `plugin:newCommand(opts)` — pure Registers a command, the way an Aseprite plugin does. Define a global function init(plugin) and call this inside it: function init(plugin) plugin:newCommand{ id = "outline", title = "Thick Outline", group = "edit_fx", onclick = function() ... end } end. Install the script from Scripts > Script Commands...: its init runs once, with no access to the document, to see what it registers, and the commands then appear in the menus, the command palette and the Keyboard Shortcuts editor (where they can be given a shortcut). Running one starts the script (its top level runs again, then init, then the command's onclick), so keep the top level to definitions. Up to 32 commands per script. - `opts` (table) - `id` (string) — The command's id within the script: a letter, then letters, digits, _ or - (max 48). Its action id, used for keyboard shortcuts, is script... - `title` (string) — The menu and command palette text (max 60 characters). - `group` (string) — Where the command goes in the menus: file, file_import, edit, edit_transform, edit_fx, edit_adjustments, sprite, layer, frame, select or view. Without one, it goes under Scripts >