Animations
Animation in kadokit is declarative: you attach an animation configuration to a property, and every change to that property moves through it, whether it comes from state, firmware, or user interaction. There are three levels: tween easings, spring physics, and orchestrated motion groups.
Tween animations
Animate property changes with an easing curve:
arc {
value bind="app.speed" animate="out-cubic" duration=400
}Widgets can also declare per-property animations as standalone nodes. This is useful when several properties of the same widget animate differently:
box {
x bind="app.offset"
animate "x" tween-duration=300 tween-easing="out-cubic"
animate "opa" tween-duration=180 tween-easing="linear"
}Easing functions
linear, plus in- / out- / in-out- variants of quad, cubic,
quart, quint, sine, expo, circ, and back. There are 26 curves total.
out-* easings are generally best for UI: fast start, gentle stop.
Spring animations
Physics-based motion for more natural movement. Named presets:
arc {
value bind="app.speed" spring="snappy"
}| Preset | Feel |
|---|---|
snappy | Quick, minimal overshoot (default) |
gentle | Slow, smooth |
soft | Very slow, no overshoot |
sheet | Like a bottom sheet opening |
overshoot | Strong bounce past target |
critically-damped | Fastest path to target, no bounce |
Or custom parameters:
box {
transform-scale {
bind="app.scale_var"
spring-duration=0.3 // seconds, 0.1 to 2.0
spring-bounce=0.2 // 0.0 (none) to 1.0 (maximum)
}
}Animatable properties
value (arc, slider, bar), opa, bg-opa, x, y, width, height,
transform-scale, transform-rotation, and the color properties
(bg-color, text-color, border-color, animated per channel).
Non-animatable properties (text, booleans) change immediately even if an
animation is specified.
Components with animated props
Animation configuration lives inside the component; callers just drive the value:
component "ArcGauge" {
prop "value" type="int" default=50
arc {
value bind="prop.value" spring="snappy"
}
}
ArcGauge { value bind="app.cpu_load" }Motion groups
Motion groups orchestrate animations across many widgets at once: staggered
entrances, waves, coordinated reveals. A group declares its targets, a
stagger pattern, and the property tracks to run; it is triggered with the
play action from any event, mount hook, or timer:
grid {
columns 16; rows 4; cell-width 25; cell-height 25; gap 7
on-mount { play "charge-wave" }
motion-group "charge-wave" {
target "children"
// Top-to-bottom wave: each row starts 90ms after the previous
stagger grid="16x4" row-offset=90 col-offset=90 from="first" max-active=18
to "transform-scale" 256 from=64 tween-duration=440 tween-easing="out-cubic"
}
repeat count=64 as="cell" {
box { style "cell"; on-click { play "charge-wave" } }
}
// Auto-replay every 4.5 seconds
timer {
interval 4500; repeat #true
play "charge-wave"
}
}This is a complete charge-wave effect over a 16×4 grid, with 64 widgets animating in a staggered pattern and no imperative animation code.
Layout (FLIP) animations
When firmware reorders widgets (shuffle, sort), layout-aware spring transitions can animate each widget from its old position to its new one using the First-Last-Invert-Play technique, driven by the same motion engine.
Screen transitions
Transitions between screens are configured on the screen itself and are separate from property animations. See Navigation → Transitions.
How it behaves
Animated properties settle on their target automatically, and if the target changes mid-animation, motion continues from the current position and velocity. When a change cannot be animated, the property is applied directly — a bound value always reaches its final state.