Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Python scenes

The same API is available from Python, as the fastanim package. A Python scene is a function marked @scene that takes the scene as its argument:

# hello.py
from fastanim import *

@scene
def hello(s: Scene) -> None:
    title = s.add(text("Hello, fastanim").to_edge(UP))
    s.play(write(title))
    ring = s.add(circle(1.5, stroke=BLUE, stroke_width=0.06))
    s.play(create(ring), run_time=1.5)

A plain def construct(scene) works too.

Installing

fastanim records and bakes scenes and exports SVG. Video, PNG and the preview window come with the preview extra, a second native module built with the renderer, which fastanim uses when it is installed:

pip install fastanim            # SVG stills and frames
pip install fastanim[preview]   # + video, PNG and the preview window

From a checkout, build both into a virtualenv with maturin:

maturin develop -m fastanim-py/pyproject.toml
maturin develop -m fastanim-py/preview/pyproject.toml

From the command line

python -m fastanim takes the commands and options of the fastanim CLI:

python -m fastanim preview hello.py
python -m fastanim render hello.py -q 720p30 -o hello.mp4
python -m fastanim render hello.py --section intro -o intro.webm
python -m fastanim still hello.py --at 2.5s -o frame.png

When a file has more than one @scene, name the one to run: hello.py:hello.

Preview runs the file again whenever it is saved and keeps the current time; if the new version fails, the error is printed and the last good scene stays up. Modules the scene imports are not reloaded. The file runs with __name__ set to something other than "__main__", so code under if __name__ == "__main__": is skipped.

From code

hello.preview()                                   # blocks until the window closes
hello.render("hello.mp4", quality="1080p60")      # also section="intro"
hello.still("frame.svg", at=3.5)                  # or frame=210
tl = hello.bake()                                 # the timeline: tl.duration, tl.svg(t), …
tl.render("frames.svg", quality="480p15")         # a directory of SVG frames

Only svg output works without the preview extra; other formats raise a RuntimeError saying to install it. The preview window can be opened once per Python process.

Styles

Shape functions take stroke=, stroke_width=, fill= and fill_opacity= keywords, and the style methods are as in Rhai, with optional arguments as keywords: fill(color, opacity=None), fill_gradient(colors, direction=RIGHT), stroke_gradient(colors, direction=RIGHT), dashed(dashes=15, ratio=0.5), line_cap(cap), line_join(join) and dashed_line(a, b, dash_length=0.05, ratio=0.5). Scene.background(color) sets the background, and Timeline.svg(t) draws over it unless given another background=.

@scene
def styled(s: Scene) -> None:
    s.background("#1c1c28")
    s.add(square(2, fill=BLUE, fill_opacity=0.5).fill_gradient([BLUE, PINK], UP))
    s.add(circle(1.5).dashed(24).line_cap("butt"))

Images, Lottie animations and fonts

image takes a path, or rows of pixels as nested lists or a NumPy array (anything with tolist()), plus height=, width= and pixelated= keywords. lottie(path) takes height= and width= too, and play_lottie(m, start=, end=, loops=) plays it. Text functions take a font= keyword, and load_font(path) loads a font file (relative to the current directory):

import numpy as np

load_font("fonts/Inter-Regular.ttf")

@scene
def gallery(s: Scene) -> None:
    ramp = np.tile(np.arange(256, dtype=np.uint8), (64, 1))
    s.add(image(ramp, height=2, pixelated=True).to_edge(DOWN))
    s.add(text("Hello", font="Inter").to_edge(UP))
    spinner = s.add(lottie("spinner.json", height=3))
    s.play(play_lottie(spinner, loops=3))