Skip to main content
Glama

cycle_palette

Rotates chosen palette indices across generated frames to animate color cycling in indexed Aseprite sprites, making pixels appear to flow without moving them.

Instructions

Animate a sprite by cycling a run of palette colours, the oldest trick in the medium: the pixels do not move, the colours do, and water flows.

**What this ships, and why.** Aseprite's file format carries a palette per frame, but
its Lua API does not expose one, so the real thing cannot be authored from here.
Measured on 1.3.18.6: `#spr.palettes` is 1 and stays 1, the collection is read-only
(`spr.palettes[2] = ...` and `table.insert` both raise "attempt to index a nil value
(field '__setters')"), `Palette` has no `frame` property, `Sprite:newPalette` does not
exist, and `Sprite:setPalette` replaces the single sprite-wide palette no matter which
frame `app.frame` is on, as does `app.command.LoadPalette`. So this is the fallback:
the frames are generated, it costs one frame of storage each, and it works everywhere
a frame does, including in a GIF or a PNG sequence.

What rotates is the pixels' own indices, not the palette. On an indexed sprite those
are the same picture, and the index move is exact: no colour matching, nothing routed
through `nearest_index`, and the palette comes back byte for byte as it went in, so
`get_palette` still shows the ramp that was authored.

Args:
    indices: The palette indices to rotate, **in the order the colours travel**. At
        least two, distinct, each one in the palette, and none of them an entry that
        cannot draw (the sprite's transparent index, or an entry whose alpha is 0):
        rotating one of those through the cycle would make drawn pixels vanish.
        `list_palette_usage` reports the contiguous runs worth passing here.
    frame_count: How many frames the cycle occupies. Defaults to `len(indices)`, which
        is one frame per colour, and is capped there: past that the rotation repeats a
        frame already written, so the extra frames cost storage and show nothing new.
    step: How far the colours travel per frame. Positive moves them forward along
        `indices`; negative moves them back. A multiple of `len(indices)` is refused
        rather than silently producing identical frames.
    layer: Cycle only this layer's pixels. Default: every pixel layer.

The sprite must have exactly one frame. A cycle generates the whole timeline from the
one drawn frame, so a sprite that already animates would have its timeline redefined,
and that is refused rather than guessed at: `duplicate_frame` the pose into a sprite
of its own first.

Refuses, before anything is written, an index outside the palette (it names the size),
an index that cannot draw, a repeated index, a step that is a whole number of laps, and
a cycle none of whose indices appear in the art, which would write identical frames and
animate nothing. That last refusal names the indices the art *is* drawn with.

Returns the frames it wrote and how many pixels moved on each, `closes` (whether the
rotation returns to where it started at the wrap), and `warnings` when the cycle does
not close, or when an index in it has no pixels to travel through. `warnings` is
absent rather than empty when there is nothing to say.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
stepNoDefault: 1.
layerNo
indicesYes
filenameYes
frame_countNo

Schema Changelog

Changes observed during successful MCP inspections.

  1. Addedv0.10.0

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint=false and openWorldHint=false; the description goes far beyond them, disclosing that it rewrites the entire timeline, costs one frame of storage per colour, rotates index data rather than palette entries, and enumerates every refusal (out-of-palette index, non-drawing index, repeats, whole-lap step, no-op cycles) before listing what the return payload contains.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded and the Args section is well structured, but the middle paragraph justifying the fallback against Aseprite 1.3.18.6 internals is dense and only partly load-bearing for an agent. Most sentences still earn their place given the tool's non-obvious semantics.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, and the description fully covers what comes back (frames written, per-frame pixel movement, closes, and warnings that are absent rather than empty). Combined with the refusal list and preconditions, an agent has everything needed to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is only 20% (just step's default), and the description compensates thoroughly for indices (order, distinctness, palette membership, alpha-0 exclusion), frame_count (default and cap logic), step (direction and whole-lap refusal) and layer (default scope). The required filename parameter is left undocumented in both schema and description, a small remaining gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("Animate a sprite by cycling a run of palette colours") and immediately disambiguates from related tools by insisting the pixels don't move, the colours do. An agent can distinguish this from set_palette, get_palette, generate_ramp and shift_along_ramp without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit prerequisites and when-not conditions: the sprite must have exactly one frame, and a sprite that already animates is refused with a named workaround (duplicate_frame into its own sprite). It also routes the agent to list_palette_usage for finding the runs worth passing, and explains the fallback context versus per-frame palettes.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Deploy Server

Other Tools