Skip to main content

Animation reference

Tetra's animation primitives change a widget's position, scale, or rotation from frame to frame. Load animation.star as animation and render.star as render before building an animated widget tree.

Evolving API

The transformation API may change as Tetra's animation system evolves. Test animations after upgrading Tetra.

Easing curves

An easing curve controls the rate of change between two keyframes. Use one of the built-in names:

  • "linear"
  • "ease_in"
  • "ease_out"
  • "ease_in_out"

You can also provide a CSS-style cubic Bézier string such as "cubic-bezier(0.42, 0, 0.58, 1)", or a function that accepts and returns a value between 0.0 and 1.0.

app.star
load("animation.star", "animation")
load("render.star", "render")

def smooth_step(t):
return t * t * (3 - 2 * t)

def main():
movement = animation.Transformation(
child = render.Circle(diameter = 6, color = "#22d3ee"),
duration = 48,
width = 64,
height = 32,
direction = "alternate",
keyframes = [
animation.Keyframe(
percentage = 0.0,
transforms = [animation.Translate(2, 13)],
),
animation.Keyframe(
percentage = 1.0,
transforms = [animation.Translate(56, 13)],
curve = smooth_step,
),
],
)

return render.Root(
delay = 50,
child = movement,
)
Circle moving across the display with a custom smooth-step easing curve

Keyframe

Keyframe defines the transforms at a specific point in an animation. Its percentage is a floating-point value from 0.0 at the start to 1.0 at the end.

ParameterTypeRequiredPurpose
percentagefloatYesPosition within the animation from 0.0 to 1.0.
transforms[Transform]YesTransforms to interpolate to or from.
curvestr or functionNoEasing curve; defaults to "linear".
app.star
load("animation.star", "animation")
load("render.star", "render")

def main():
movement = animation.Transformation(
child = render.Box(width = 8, height = 8, color = "#a855f7"),
duration = 48,
width = 64,
height = 32,
direction = "alternate",
keyframes = [
animation.Keyframe(
percentage = 0.0,
transforms = [animation.Translate(3, 12)],
),
animation.Keyframe(
percentage = 0.5,
transforms = [
animation.Translate(28, 12),
animation.Scale(1.5, 1.5),
],
curve = "ease_in_out",
),
animation.Keyframe(
percentage = 1.0,
transforms = [animation.Translate(53, 12)],
),
],
)

return render.Root(
delay = 50,
child = movement,
)
Square moving and growing through three animation keyframes

When the keyframe list omits 0.0 or 1.0, Tetra inserts a default keyframe with no transforms and linear easing at the missing endpoint.

Origin

Origin is the relative anchor used by scale and rotation transforms. The default Origin(0.5, 0.5) places the anchor at the center of the child.

ParameterTypeRequiredPurpose
xfloatYesHorizontal anchor position.
yfloatYesVertical anchor position.

This example rotates an L shape around its top-left corner:

app.star
load("animation.star", "animation")
load("render.star", "render")

def main():
spinner = animation.Transformation(
child = render.Stack(
children = [
render.Box(width = 14, height = 3, color = "#fbbf24"),
render.Box(width = 3, height = 14, color = "#fbbf24"),
],
),
duration = 60,
width = 64,
height = 32,
origin = animation.Origin(0.0, 0.0),
keyframes = [
animation.Keyframe(
percentage = 0.0,
transforms = [
animation.Translate(32, 16),
animation.Rotate(0),
],
),
animation.Keyframe(
percentage = 1.0,
transforms = [
animation.Translate(32, 16),
animation.Rotate(360),
],
curve = "ease_in_out",
),
],
)

return render.Root(
delay = 50,
child = spinner,
)
L-shaped widget rotating around its top-left origin

Translate

Translate moves the child by a horizontal and vertical pixel offset.

ParameterTypeRequiredPurpose
xfloat or intYesHorizontal offset.
yfloat or intYesVertical offset.
app.star
load("animation.star", "animation")
load("render.star", "render")

def main():
movement = animation.Transformation(
child = render.Box(width = 6, height = 6, color = "#34d399"),
duration = 48,
width = 64,
height = 32,
direction = "alternate",
keyframes = [
animation.Keyframe(
percentage = 0.0,
transforms = [animation.Translate(2, 13)],
),
animation.Keyframe(
percentage = 1.0,
transforms = [animation.Translate(56, 13)],
curve = "ease_in_out",
),
],
)

return render.Root(
delay = 50,
child = movement,
)
Green square translating horizontally across the display

Scale

Scale resizes the child relative to the transformation origin. A value of 1 preserves the original size.

ParameterTypeRequiredPurpose
xfloat or intYesHorizontal scale factor.
yfloat or intYesVertical scale factor.
app.star
load("animation.star", "animation")
load("render.star", "render")

def main():
pulse = animation.Transformation(
child = render.Circle(diameter = 10, color = "#f472b6"),
duration = 40,
width = 64,
height = 32,
origin = animation.Origin(0.5, 0.5),
direction = "alternate",
keyframes = [
animation.Keyframe(
percentage = 0.0,
transforms = [
animation.Translate(27, 11),
animation.Scale(0.5, 0.5),
],
),
animation.Keyframe(
percentage = 1.0,
transforms = [
animation.Translate(27, 11),
animation.Scale(2, 2),
],
curve = "ease_in_out",
),
],
)

return render.Root(
delay = 50,
child = pulse,
)
Pink circle pulsing between two scale values

Rotate

Rotate turns the child clockwise around the transformation origin.

ParameterTypeRequiredPurpose
anglefloat or intYesRotation in degrees.
app.star
load("animation.star", "animation")
load("render.star", "render")

def main():
spinner = animation.Transformation(
child = render.Box(width = 16, height = 4, color = "#fb923c"),
duration = 60,
width = 64,
height = 32,
origin = animation.Origin(0.5, 0.5),
keyframes = [
animation.Keyframe(
percentage = 0.0,
transforms = [
animation.Translate(24, 14),
animation.Rotate(0),
],
),
animation.Keyframe(
percentage = 1.0,
transforms = [
animation.Translate(24, 14),
animation.Rotate(360),
],
curve = "ease_in_out",
),
],
)

return render.Root(
delay = 50,
child = spinner,
)
Orange bar rotating around its center

Transformation

Transformation animates a child by interpolating between keyframes. Each keyframe can combine translation, scale, and rotation. Transform order matters: Tetra applies them in the order they appear in transforms.

Durations and delays are measured in frames, not milliseconds.

ParameterTypeRequiredPurpose
childWidgetYesWidget to animate.
keyframes[Keyframe]YesKeyframes to interpolate between.
durationintYesAnimation duration in frames.
delayintNoFrames to wait before starting.
widthintNoAnimation canvas width.
heightintNoAnimation canvas height.
originOriginNoScale and rotation anchor; defaults to the center.
directionstrNoPlayback direction; defaults to "normal".
fill_modestrNoTransform retained after playback; defaults to "forwards".
roundingstrNoTranslation-coordinate rounding; defaults to "round".
wait_for_childboolNoWait for every animated child frame before restarting.

Direction

direction accepts:

ValueBehavior
"normal"Play forward.
"reverse"Play backward.
"alternate"Play forward, then backward.
"alternate-reverse"Play backward, then forward.

Fill mode

fill_mode controls the transform applied after the animation finishes:

ValueBehavior
"forwards"Retain the final keyframe.
"backwards"Return to the first keyframe.

Translation rounding

Interpolated positions often fall between physical pixels. Set rounding to "round", "floor", "ceil", or "none". This setting affects only translation; scale and rotation keep their interpolated values.

Example

This animation moves a circle across the display while rotating it. The alternate direction brings it back to the starting point:

app.star
load("animation.star", "animation")
load("render.star", "render")

def main():
movement = animation.Transformation(
child = render.Circle(diameter = 6, color = "#22d3ee"),
duration = 60,
width = 64,
height = 32,
origin = animation.Origin(0.5, 0.5),
direction = "alternate",
fill_mode = "forwards",
keyframes = [
animation.Keyframe(
percentage = 0.0,
transforms = [
animation.Translate(2, 13),
animation.Rotate(0),
],
curve = "ease_in_out",
),
animation.Keyframe(
percentage = 1.0,
transforms = [
animation.Translate(56, 13),
animation.Rotate(360),
],
),
],
)

return render.Root(
delay = 50,
child = movement,
)
Circle translating and rotating across the display