tween_cels
Interpolates one Aseprite cel's scale, rotation, and opacity across selected frames, creating animation inbetweens from a single drawing without pixel blending.
Instructions
Scale, turn and fade one drawn cel across frames, in one Aseprite launch.
`offset_cels` moves a cel along a path, which is one of the four things an inbetween
does. This is the other three. The cel on the **first listed frame is the source**,
and every listed frame becomes that one drawing resampled by its share of the change:
frame nine is sampled from the original and not from frame eight, so the rounding
never compounds.
**It interpolates a transform, not pixels.** Blending two different drawings gives a
double exposure, two silhouettes at half strength, which reads as a mistake rather
than as motion. So this takes one drawing, and when two cels genuinely differ in shape
there is no honest automatic inbetween: draw the second pose and tween each one out
from its own extreme, or express the difference as a scale and a turn, which most of
them are.
Args:
frames: The frames to write, in the order the change passes through them. The
first is the source, and it is rewritten too when `scale_from`, `rotate_from`
or `opacity_from` is not the identity.
scale_from, scale_to: The scale at the first and last frame, on both axes.
scale_y_from, scale_y_to: Override the vertical scale, for squash and stretch,
where the two axes go opposite ways (0.7 tall against 1.25 wide is a squash
that keeps its volume). Unset means the same as the horizontal, which is a
zoom and not a squash.
rotate_from, rotate_to: Clockwise degrees at the first and last frame, snapped to
whole degrees. Quarter turns are exact and lose nothing; sampling within about
15 degrees of one shuffles the edge pixels rather than turning the shape, and
the result says so in `warnings` instead of pretending otherwise.
opacity_from, opacity_to: Cel opacity, 0 to 255. This sets the cel's own opacity
and never bakes alpha into the pixels: a baked fade invents colours that are
not on the palette. It is the per-frame `set_cel_opacity` done in one call.
ease: How the change is *spaced*: `linear`, `ease_in` (starts slow), `ease_out`
(arrives slow), `ease_in_out`, or `gravity` (accelerating, as a falling object
does). Easing belongs in one place at a time, and `apply_timing_curve` carries
the durations.
anchor: The point the transform holds still, taken from the cel's own drawn
bounds: `center`, `top`, `bottom`, `left` or `right`. This matters more than it
looks. A scale about the centre makes a ball grow in every direction; a scale
about `bottom` makes it squash *onto the ground*, which is the one that reads
as weight, and `validate_loop`'s contact-edge check then confirms that the
contact row did not move.
Refuses rather than doing something approximate: fewer than two frames, a frame listed
twice, a from and to that are identical on every channel (each frame would be
rewritten with the drawing the source already holds), a scale of zero or below, a
group or tilemap layer, a source frame with no cel or nothing drawn on it, a frame
whose transform lands entirely off the canvas, and any frame that shares its image
with another frame. That last one is the sharp edge: linked cels share one image, one
position and one opacity, so a tween written into a link group would change every
frame in it. The refusal names the frames and points at `unlink_cels`.
Every listed frame after the first is **overwritten** with the transformed source
rather than blended with what was there. `steps[].bounds` is the written cel's own
content box, so a scale series can be checked instead of trusted: `rendered_monotone`
is that check, and it is omitted while the cel is also rotating, because a turning
shape's bounding box oscillates by design. `steps[].drawn_pixels` is how many opaque
samples that frame took; `pixels_written` is what landed, and the two differ only when
an active selection masked part of it, which `pixels_outside_selection` then reports.
Sampling is nearest-neighbour and copies the source pixel whole, so no colour appears
that the drawing did not already contain and `palette_conformance` is unchanged.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| ease | No | Default: "linear". | linear |
| layer | Yes | ||
| anchor | No | Default: "center". | center |
| frames | Yes | ||
| filename | Yes | ||
| scale_to | No | Default: 1.0. | |
| rotate_to | No | Default: 0.0. | |
| opacity_to | No | Default: 255. | |
| scale_from | No | Default: 1.0. | |
| scale_y_to | No | ||
| rotate_from | No | Default: 0.0. | |
| opacity_from | No | Default: 255. | |
| scale_y_from | No |