Scene

Scenes, the timeline cursor and the blocks that shape time (s.play, s.start, s.during).

Contents:

  • k.scene: Turns a function def name(s: k.Scene) into a scene.
  • k.SceneDef: A scene function plus its configuration.
  • k.Scene: Timeline of one scene.
  • k.TimeSpan: Where a play/start landed.
  • k.time: Global scene time in seconds, as a read-only signal.

Methods in this area:

  • s.play: Schedules animations at the cursor and advances the cursor to their end.
  • s.start: Schedules animations at the cursor without moving it: this is how to run something in the background (clocks, simulations, continuous motion) while the script continues.
  • s.wait: Advances the cursor by d seconds (default 1).
  • s.wait_for: Moves the main cursor to the time of an event (the count-th firing after the cursor) and returns the EventInfo.
  • s.add: Puts objects in the scene instantly, at the cursor.
  • s.remove: Takes objects out of the scene instantly, at the cursor.
  • s.mark: Creates a time anchor at the cursor without moving it and returns the time.
  • s.during: with block that applies state changes on entry and reverts them, animated, on exit.
  • s.tempo: with block that multiplies the speed of everything inside: s.tempo(4) divides durations and waits by 4.
  • s.voice: Narrated with block: takes text (TTS from the provider in kinemo.toml) or an audio file, and lasts at least as long as the audio.

Back to the reference index.

k.scene (function)

k.scene(fn: SceneFn) -> SceneDef
k.scene(
    *,
    size: str | tuple[int, int] = "1080p",
    fps: float = 60,
    background: ColorLike | None = None,
    seed: int = 0,
    tail: float = 0.5,
    theme: Theme | str | None = None,
    camera: str = "2d",
    params: dict[str, Param] | None = None,
    name: str | None = None,
) -> Callable[[SceneFn], SceneDef]

Written as: @k.scene(size="1080p", fps=60, background=None, seed=0, tail=0.5, theme=None, camera="2d", params=None, name=None)

Turns a function def name(s: k.Scene) into a scene. The body runs exactly once, in the build phase, and produces a timeline; the frame at time t is a pure function of t. The decorator arguments (size, fps, background, seed, tail, theme, parameters) take precedence over kinemo.toml.

Parameters:

Name Type Default Description
size str | tuple[int, int] "1080p" Output size: a preset name ("1080p", "4k", "vertical", ...) or (width, height) in pixels.
fps float 60 Frames per second of the final render.
background ColorLike | None None Background color; None uses the theme's bg.
seed int 0 Seed for random and numpy.random during the build, so scenes are reproducible.
tail float 0.5 Seconds added after the last animation ends.
theme Theme | str | None None A k.Theme or the name of one in k.themes ("dark", "light", "blueprint").
camera str "2d" "2d" or "3d".
params dict[str, Param] | None None Scene parameters (k.Int, k.Float, k.Bool, k.Choice, k.Str), passed to the function by name.
name str | None None Scene name used by --scene; defaults to the function name.

Example:

@k.scene(size="1080p", fps=60, tail=0.5)
def hello(s: k.Scene):
    title = k.Text("Hello, kinemo").place(at="center")
    s.play(k.write(title))
    s.play(title.to(color=k.BLUE, scale=1.5))
    s.wait(1)

See also: s.play, k.Text, k.Int.

k.SceneDef (class)

k.SceneDef(fn: SceneFn, config: SceneConfig)

A scene function plus its configuration. Building runs the function exactly once.

Documented together with k.scene.

Parameters:

Name Type Default Description
fn SceneFn required
config SceneConfig required

Attributes:

Attribute Type Description
scenedef.config
scenedef.fn
scenedef.name

Members:

  • build: Run the build phase and return the scene with its finished timeline.

k.SceneDef.build (method)

scenedef.build(params: dict[str, object] | None = None, **overrides: object) -> Scene

Run the build phase and return the scene with its finished timeline.

Parameters:

Name Type Default Description
params dict[str, object] | None None
**overrides object variadic

k.Scene (class)

k.Scene(config: SceneConfig)

Timeline of one scene. Code runs once; every call records something at the cursor.

Documented together with s.play.

Parameters:

Name Type Default Description
config SceneConfig required

Attributes:

Attribute Type Description
s.config
s.cursor Build cursor in seconds.
s.frame Frame
s.lints
s.theme Theme

Members:

  • builder: The native IR builder of this scene (outputs: frames, video, inspect).
  • duration: Total length in seconds (known after the build).
  • play: Schedules animations at the cursor and advances the cursor to their end.
  • start: Schedules animations at the cursor without moving it: this is how to run something in the background (clocks, simulations, continuous motion) while the script continues.
  • wait: Advances the cursor by d seconds (default 1).
  • mark: Creates a time anchor at the cursor without moving it and returns the time.
  • marks: Named marks: name → time in seconds.
  • during: with block that applies state changes on entry and reverts them, animated, on exit.
  • tempo: with block that multiplies the speed of everything inside: s.tempo(4) divides durations and waits by 4.
  • add: Puts objects in the scene instantly, at the cursor.
  • remove: Takes objects out of the scene instantly, at the cursor.
  • wait_for: Moves the main cursor to the time of an event (the count-th firing after the cursor) and returns the EventInfo.
  • voice: Narrated with block: takes text (TTS from the provider in kinemo.toml) or an audio file, and lasts at least as long as the audio.

k.Scene.builder (property)

s.builder: Builder  # read-only

The native IR builder of this scene (outputs: frames, video, inspect).

k.Scene.duration (property)

s.duration: float  # read-only

Total length in seconds (known after the build).

k.Scene.play (method)

s.play(
    *anims: Animation,
    duration: float | None = None,
    ease: EaseLike | None = None,
    at: float | None = None,
) -> TimeSpan

Schedules animations at the cursor and advances the cursor to their end. Multiple arguments run in parallel (s.play(a, b) is by definition s.play(k.par(a, b))). duration= sets the total duration of the group, rescaling its contents.

Parameters:

Name Type Default Description
*anims Animation variadic
duration float | None None duration= sets the total duration of the group, rescaling its contents.
ease EaseLike | None None
at float | None None

Example:

@k.scene
def steps(s: k.Scene):
    a = k.Circle(r=0.8).place(at="center")
    b = k.Square(1.4).place(right_of=a, gap=0.6)
    s.play(k.draw(a), k.draw(b), duration=2)
    s.play(a.to(color=k.RED))

See also: s.start, k.par, s.wait.

k.Scene.start (method)

s.start(
    *anims: Animation,
    duration: float | None = None,
    ease: EaseLike | None = None,
    at: float | None = None,
) -> TimeSpan

Schedules animations at the cursor without moving it: this is how to run something in the background (clocks, simulations, continuous motion) while the script continues. Returns a TimeSpan; h.done is an event at the end of the animation.

Parameters:

Name Type Default Description
*anims Animation variadic
duration float | None None
ease EaseLike | None None
at float | None None

Example:

@k.scene
def background(s: k.Scene):
    dot = k.Dot(r=0.2, x=-4)
    title = k.Text("In parallel").place(at="top", margin=0.8)
    s.add(dot)
    s.start(dot.to(x=4), duration=3)
    s.play(k.write(title))
    s.wait(2)

See also: s.play, s.wait_for, k.simulate.

k.Scene.wait (method)

s.wait(d: float = 1.0) -> TimeSpan

Advances the cursor by d seconds (default 1). It is the script's pause: nothing is scheduled, but whatever was started with s.start keeps running.

Parameters:

Name Type Default Description
d float 1.0 Advances the cursor by d seconds (default 1).

Example:

@k.scene
def pause(s: k.Scene):
    title = k.Text("Think for a moment").place(at="center")
    s.play(k.write(title))
    s.wait(1.5)
    s.play(k.fade_out(title))

See also: s.play, s.mark.

k.Scene.mark (method)

s.mark(name: str | None = None, slide: bool = False) -> float

Creates a time anchor at the cursor without moving it and returns the time. Named marks are stored in s.marks[name] (useful with at=); slide=True defines a slide break for kinemo render --format slides.

Parameters:

Name Type Default Description
name str | None None
slide bool False Named marks are stored in s.marks[name] (useful with at=); slide=True defines a slide break for kinemo render --format slides.

Example:

@k.scene
def presentation(s: k.Scene):
    title = k.Text("Part 1").place(at="center")
    s.play(k.write(title))
    s.mark("part2", slide=True)
    s.play(title.to(text="Part 2"))
    s.wait(1)

See also: s.start, s.voice.

k.Scene.marks (property)

s.marks: dict[str, float]  # read-only

Named marks: name → time in seconds.

k.Scene.during (method)

s.during(
    *anims: Animation,
    duration: float | None = None,
    ease: EaseLike | None = None,
    revert: Literal['instant'] | float | Ease | None = None,
) -> Iterator[None]

with block that applies state changes on entry and reverts them, animated, on exit. The revert uses the same duration and easing; revert="instant", revert=0.3 or revert=k.ease.out change that. Only accepts reversible animations (.to, k.indicate).

Parameters:

Name Type Default Description
*anims Animation variadic
duration float | None None
ease EaseLike | None None
revert Literal['instant'] | float | Ease | None None The revert uses the same duration and easing; revert="instant", revert=0.3 or revert=k.ease.out change that.

Example:

@k.scene
def highlight(s: k.Scene):
    a = k.Circle(r=0.6)
    b = k.Circle(r=0.6)
    row = k.Row(a, b, gap=1).place(at="center")
    s.play(k.draw(row))
    with s.during(a.to(color=k.YELLOW), b.to(color=k.YELLOW), duration=0.3):
        s.play(row.swap(0, 1))
    s.wait(0.5)

See also: obj.to, k.indicate, group.swap.

k.Scene.tempo (method)

s.tempo(factor: float, to: float | None = None) -> Iterator[None]

with block that multiplies the speed of everything inside: s.tempo(4) divides durations and waits by 4. s.tempo(1, to=8) speeds up progressively over the block. Nested tempos multiply; k.time is not affected.

Parameters:

Name Type Default Description
factor float required
to float | None None s.tempo(1, to=8) speeds up progressively over the block.

Example:

@k.scene
def speed_up(s: k.Scene):
    dots = [k.Dot(r=0.2) for _ in range(6)]
    row = k.Row(*dots, gap=0.6).place(at="center")
    s.add(row)
    with s.tempo(1, to=4):
        for d in dots:
            s.play(k.indicate(d), duration=0.5)

See also: s.during, k.stagger.

k.Scene.add (method)

s.add(*objs: Node)

Puts objects in the scene instantly, at the cursor. Creating an object does not put it in the scene: it enters with s.add or with an entrance verb (k.draw, k.write, k.fade_in, k.grow).

Parameters:

Name Type Default Description
*objs Node variadic

Example:

@k.scene
def instant(s: k.Scene):
    box = k.Rect(w=3, h=1.5).place(at="center")
    label = k.Text("Ready").place(inside=box)
    s.add(box, label)
    s.wait(1)
    s.play(box.to(color=k.GREEN))

See also: s.remove, k.draw, k.fade_in.

k.Scene.remove (method)

s.remove(*objs: Node)

Takes objects out of the scene instantly, at the cursor. After that the object does not accept .to() (error K0102) until it enters again with an entrance verb.

Parameters:

Name Type Default Description
*objs Node variadic

Example:

@k.scene
def cut(s: k.Scene):
    a = k.Text("Before").place(at="center")
    b = k.Text("After").place(at="center")
    s.add(a)
    s.wait(1)
    s.remove(a)
    s.add(b)
    s.wait(1)

See also: s.add, k.fade_out, k.shrink.

k.Scene.wait_for (method)

s.wait_for(
    event: EventSource[P],
    count: int = 1,
    timeout: float | None = None,
) -> EventInfo[P]

Moves the main cursor to the time of an event (the count-th firing after the cursor) and returns the EventInfo. It only sees what has already been scheduled, so the usual pattern is s.start(...) followed by s.wait_for(...). timeout= is required when the source has no guaranteed end.

Parameters:

Name Type Default Description
event EventSource[P] required Move the cursor to the count-th firing of event after it (what is scheduled so far).
count int 1 Moves the main cursor to the time of an event (the count-th firing after the cursor) and returns the EventInfo.
timeout float | None None timeout= is required when the source has no guaranteed end.

Example:

@k.scene
def wait_for_it(s: k.Scene):
    dot = k.Dot(r=0.2, x=-5)
    title = k.Text("Moving...").place(at="top", margin=0.8)
    s.add(dot)
    h = s.start(dot.to(x=5), duration=3)
    s.play(k.write(title))
    s.wait_for(h.done)
    s.play(k.fade_out(dot, title))

See also: k.when, event.on, s.start.

k.Scene.voice (method)

s.voice(
    narration: str,
    *,
    voice: str | None = None,
    gain: float = 1.0,
) -> Iterator[None]

Narrated with block: takes text (TTS from the provider in kinemo.toml) or an audio file, and lasts at least as long as the audio. Words marked [word]{name} become s.marks[name] at the moment they are spoken. Without a TTS provider, the voice becomes silence with an estimated duration and lint W1401 warns.

Parameters:

Name Type Default Description
narration str required
voice str | None None
gain float 1.0

Example:

@k.scene
def narrated(s: k.Scene):
    tri = k.Triangle.right(3, 4, scale=0.6).place(at="center")
    with s.voice("Every right [triangle]{tri} hides a relation."):  # kinemo: allow W1401
        s.play(k.draw(tri))
    s.start(k.indicate(tri), at=s.marks["tri"])

See also: s.mark, k.sound.

k.TimeSpan (class)

k.TimeSpan(start: float, end: float)

Where a play/start landed. h.done is an event at its end.

Documented together with s.start.

Parameters:

Name Type Default Description
start float required Where a play/start landed.
end float required

Members:

  • duration: Length of the span in seconds.
  • done: Event fired at the end of the span (s.wait_for(h.done)).

k.TimeSpan.duration (property)

timespan.duration: float  # read-only

Length of the span in seconds.

k.TimeSpan.done (property)

timespan.done: EventSource[None]  # read-only

Event fired at the end of the span (s.wait_for(h.done)).

k.time (constant)

k.time: Expr[float]  # read-only signal

Global scene time in seconds, as a read-only signal. It is the right way to express clocks and continuous motion, because it advances linearly and is never eased. Works at module level, in contexts and in components.

Example:

@k.scene
def clock(s: k.Scene):
    hand = k.Line(start=(0, 0), end=(0, 2), rotate=-k.time * 90)
    label = k.Text(lambda: f"{k.time():.1f} s").place(at="top", margin=0.8)
    s.add(hand, label)
    s.wait(4)

See also: k.signal, x.map, k.integrate.