The Orchestrator creates smooth animations and sequences by applying effects to Transform objects over time.
What is the Orchestrator?
The Orchestrator manages effects — transformations applied to objects over time. Unlike manual animation, effects automatically interpolate values, perfect for:
- Moving objects smoothly from A to B
- Rotating and scaling objects
- Creating cutscenes and sequences
- Screen shake effects
Creating an Orchestrator
Create an orchestrator with a target object:
# Create a transform to animate
transform = kn.Transform(pos=kn.Vec2(100, 300))
# Create orchestrator targeting the transform
orch = kn.Orchestrator(transform)// Create a transform to animate
kn::Transform transform = { .pos = {100, 300} };
// Create orchestrator targeting the transform
kn::Orchestrator orch(transform);The target must be a Transform object.
Sequencing Effects
One at a Time
Chain effects with then() for sequential animation:
from pykraken import fx
# Create a bouncing ball animation
(
orch.then(fx.move_to(kn.Vec2(400, 100), dur=0.5))
.then(fx.move_to(kn.Vec2(400, 500), dur=0.5))
.then(fx.scale_to(kn.Vec2(1.2, 0.8), dur=0.1)) # Squash
.then(fx.scale_to(kn.Vec2(0.8, 1.2), dur=0.1)) # Stretch
.then(fx.scale_to(kn.Vec2(1.0), dur=0.1))
)namespace fx = kn::fx;
// Create a bouncing ball animation
orch.then(fx::moveTo(kn::Vec2(400, 100), 0.5))
.then(fx::moveTo(kn::Vec2(400, 500), 0.5))
.then(fx::scaleTo(kn::Vec2(1.2, 0.8), 0.1)) // Squash
.then(fx::scaleTo(kn::Vec2(0.8, 1.2), 0.1)) // Stretch
.then(fx::scaleTo(kn::Vec2(1.0), 0.1));In Parallel
Run multiple effects simultaneously with parallel().
The duration of the parallel block is determined by the longest effect.
from pykraken import fx
# Move and rotate at the same time
# Entire sequence lasts 1.2 seconds
orch.parallel(
fx.move_to(kn.Vec2(600, 300), dur=1.0),
fx.rotate_to(180 * kn.math.DEG2RAD, dur=1.2),
)namespace fx = kn::fx;
// Effects are owned by move-only unique_ptrs. A vector initializer list
// would copy them from const elements, so parallel({...}) cannot be used.
// push_back moves each effect; std::move transfers the vector to parallel.
std::vector<std::unique_ptr<kn::Effect>> effects;
effects.push_back(fx::moveTo(kn::Vec2(600, 300), 1.0));
effects.push_back(fx::rotateTo(kn::math::toRadians(180), true, 1.2));
orch.parallel(std::move(effects));Effect Behavior
Effects returned by factory functions like fx.move_to, fx.rotate_to, and fx.scale_to are single-use objects.
When you pass an effect to then() or parallel(), that orchestrator consumes it as part of its timeline.
That means this is the right pattern:
orch.then(fx.move_to(kn.Vec2(400, 100), dur=0.5))
orch.then(fx.move_to(kn.Vec2(100, 300), dur=0.5))orch.then(fx::moveTo(kn::Vec2(400, 100), 0.5));
orch.then(fx::moveTo(kn::Vec2(100, 300), 0.5));Avoid reusing the same effect object in multiple timeline steps:
hop = fx.move_to(kn.Vec2(400, 100), dur=0.5)
orch.then(hop)
orch.then(hop) # Do not reuse an already-consumed effect.auto hop = fx::moveTo(kn::Vec2(400, 100), 0.5);
orch.then(std::move(hop));
// hop is now empty. Do not pass it to then() again.If you intentionally want the same effect settings in more than one place, clone it before giving it to another timeline step:
hop = fx.move_to(kn.Vec2(400, 100), dur=0.5)
orch.then(hop.clone())
orch.then(hop.clone())auto hop = fx::moveTo(kn::Vec2(400, 100), 0.5);
orch.then(hop->clone());
orch.then(hop->clone());See Effect.clone for the API reference.
Easing Functions
Add easing for natural motion:
from pykraken import fx
# Smooth start and end
orch.then(fx.move_to(
kn.Vec2(700, 300),
dur=1.0,
ease=kn.ease.in_out_quad
))
# Bouncy ending
orch.then(fx.move_to(
kn.Vec2(100, 300),
dur=1.0,
ease=kn.ease.out_bounce
))
# Elastic overshoot
orch.then(fx.scale_to(
kn.Vec2(1.5),
dur=1.0,
ease=kn.ease.out_elastic
))namespace fx = kn::fx;
// Smooth start and end
orch.then(fx::moveTo(kn::Vec2(700, 300), 1.0, kn::ease::inOutQuad));
// Bouncy ending
orch.then(fx::moveTo(kn::Vec2(100, 300), 1.0, kn::ease::outBounce));
// Elastic overshoot
orch.then(fx::scaleTo(kn::Vec2(1.5), 1.0, kn::ease::outElastic));You can find more easing functions in the kn.ease submodule.
Playing Animations
Before playing, you MUST finalize the orchestrator. This will prevent further changes and prepare the timeline.
# Finalize and play
orch.finalize()
orch.play()orch.finalize();
orch.play();The orchestrator updates itself every frame behind the scenes once played, which means no manual updating is necessary.
while kn.window.is_open():
kn.event.poll()
# Render using the animated transform
kn.renderer.clear()
kn.renderer.draw(texture, transform)
kn.renderer.present()while (kn::window::isOpen()) {
kn::event::poll();
// Render using the animated transform
kn::renderer::clear();
kn::renderer::draw(texture, transform);
kn::renderer::present();
}Playback Control
Check Status
if orch.playing:
print("Animation is running")
if orch.finished:
print("Animation completed")if (orch.isPlaying())
std::cout << "Animation is running\n";
if (orch.isFinished())
std::cout << "Animation completed\n";Pause/Resume/Stop
orch.pause() # Pause at current position
orch.resume() # Continue from paused position
orch.stop() # Stop and reset to beginning
orch.rewind() # Reset to beginning without stoppingorch.pause(); // Pause at the current position.
orch.resume(); // Continue from the paused position.
orch.stop(); // Stop and reset.
orch.rewind(); // Reset without stopping.Looping
Set looping for repeating animations:
orch.looping = True
orch.finalize()
orch.play() # Will loop when finishedorch.setLooping(true);
orch.finalize();
orch.play();