Animations¶
Timeline and time functions¶
FairyFlow animations are built by advancing a global current frame and setting attribute values at specific frames. Five functions control the timeline:
| Function | Description |
|---|---|
adv_time(seconds) |
Advance time by the given number of seconds |
set_time(seconds) |
Jump the timeline to an absolute time |
adv_frames(n) |
Advance by an integer number of frames |
set_frame(n) |
Jump to a specific frame number |
The default frame rate is 24 fps. This can be changed in fairyflow.toml.
step vs linear¶
Two transition modes control how attribute values change between keyframes:
step()(default) — values change instantaneously at the target framelinear()— values interpolate linearly between the previous and next keyframe
Call these functions at any point to switch mode for subsequent attribute changes.
with Scene(width=300, height=140):
# left rect: step (instant color change at 1.5 s)
a = Rect().size(120, 80).xy(15, 30).color("steelblue")
step()
adv_time(1.5)
a.color("tomato")
# right rect: linear (smooth color transition over 1.5 s)
set_time(0.0)
b = Rect().size(120, 80).xy(165, 30).color("steelblue")
linear()
adv_time(1.5)
b.color("tomato")
Output video
The left rect keeps its color and then changes instantly; the right rect transitions smoothly.
Moving and transforming¶
Combine linear() and adv_time() to animate position, size, color, or any other attribute:
with Scene():
r = Rect().size(60, 60).color("tomato").xy(20, 70)
linear()
adv_time(1.5)
r.xy(220, 70).color("steelblue").size(80, 80)
Output video
Use .hold() to freeze all current attribute values before starting a new segment:
with Scene():
r = Rect().size(60, 60).color("gold").align_x(0.5).align_y(0.5)
adv_time(1)
r.hold()
linear()
adv_time(1)
r.color("tomato").size(120, 120)
adv_time(0.5)
r.hold()
linear()
adv_time(1)
r.color("steelblue").size(60, 60)
Output video
Rotation and scale¶
Group nodes support .rotate() and .scale() animations. The pivot defaults to the
group's center (pivot_x=0.5, pivot_y=0.5).
with Scene():
with Group().size(80, 80).align_x(0.5).align_y(0.5) as g:
Rect().size(80, 80).color("steelblue")
Rect().size(20, 20).color("white").xy(30, 30)
g.hold()
linear()
adv_time(2)
g.rotate(360)
Output video
with Scene():
with Group().size(80, 80).align_x(0.5).align_y(0.5) as g:
Ellipse().size(80, 80).color("coral")
linear()
adv_time(1)
g.scale(0.2)
linear()
adv_time(1)
g.scale(1)
Output video
Fade in and fade out¶
.fade_in(time) animates alpha from 0 to 1. .fade_out(time) animates alpha from its current
value down to 0. Both advance the timeline automatically.
with Scene():
r = Rect().size(120, 80).color("orchid").align_x(0.5).align_y(0.5)
r.fade_in(0.6)
adv_time(0.6) # hold at full opacity
r.fade_out(0.6)
Output video
You can also set alpha directly:
with Scene():
r = Rect().size(120, 80).color("steelblue").align_x(0.5).align_y(0.5)
r.alpha(0)
linear()
adv_time(1)
r.alpha(1)
adv_time(0.5)
r.hold()
linear()
adv_time(1)
r.alpha(0)
Output video
Clipping animations¶
Group nodes have an animatable clipping window. clip_x/clip_w control the horizontal
extent (as a fraction of the group width), and clip_y/clip_h control the vertical extent.
with Scene():
with Group().size(200, 60) as g:
Rect().size(200, 60).color("cornflowerblue")
t = Text()
t.span("Revealed!").font_size(22).bold().color("white")
t.xy(40, 18)
g.clip_w(0) # start fully hidden
linear()
adv_time(1.2)
g.clip_w(1) # reveal left-to-right
Output video
.hide_right(time), .hide_left(time), .reveal_right(time), and .reveal_left(time) are
convenience helpers that animate the clip to conceal or reveal the group content. The direction
refers to the sweep direction: reveal_right expands the clip window rightward, reveal_left
sweeps it leftward. Each call also advances the timeline automatically.
with Scene():
with Group().size(200, 60).align_x(0.5).align_y(0.5) as g:
Rect().size(200, 60).color("cornflowerblue")
t = Text()
t.span("reveal_right / hide_left").font_size(14).bold().color("white")
t.xy(18, 22)
g.reveal_right(1) # expand clip from left to right
adv_time(0.4)
g.hide_left(1) # shrink clip from right to left
Output video
Build State¶
bstate() captures a snapshot of the current time and transition style. Using it as a context
manager temporarily applies that snapshot — changes inside the block do not affect the outer
timeline, so the clock is restored when the block exits. This is useful for animating multiple
elements in parallel from the same starting point.
with Scene():
a = Rect().size(80, 80).color("steelblue").xy(20, 60)
b = Rect().size(80, 80).color("tomato").xy(200, 60)
with bstate(): # snapshot time = 0
linear()
adv_time(1.5)
a.xy(110, 60) # a slides right over 1.5 s
# time is restored to 0 — b animates in parallel
linear()
adv_time(1)
b.color("gold") # b changes color over 1 s
Output video