
Makepad 2.0 Animation
- 43 installs
- 745 repo stars
- Updated April 7, 2026
- zhanghandong/makepad-skills
Helps with ai & agent building tasks during AI-assisted development.
About
makepad-2.0-animation is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- makepad-2.0-animation
- AI & Agent Building
- AI-coding skill
Makepad 2.0 Animation by the numbers
- 43 all-time installs (skills.sh)
- +1 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #7,972 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/zhanghandong/makepad-skills --skill makepad-2.0-animationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 43 |
|---|---|
| repo stars | ★ 745 |
| Last updated | April 7, 2026 |
| Repository | zhanghandong/makepad-skills ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Makepad 2.0 Animation Skill
Version: makepad-widgets (dev branch) | Last Updated: 2026-03-03
Overview
The Animator system drives instance() shader variables over time, enabling hover effects, transitions, and looping animations. It uses independent animation tracks called "groups" that run simultaneously.
Documentation
Refer to the local files for detailed documentation:
./references/animator-reference.md- Complete Animator API, play types, ease functions, examples
IMPORTANT: Documentation Completeness Check
Before answering questions, Claude MUST:
1. Read the relevant reference file(s) listed above 2. If file read fails or file is empty:
- Inform user: "Reference docs incomplete. Still answering based on SKILL.md patterns."
- Still answer based on SKILL.md patterns + built-in knowledge
3. If reference file exists, incorporate its content into the answer
---
Critical: Widget Animator Support
NOT all widgets support `animator`! Adding animator: Animator{...} to an unsupported widget is silently ignored - no error, no animation, nothing happens.
Widgets that SUPPORT Animator
View, SolidView, RoundedView, ScrollXView, ScrollYView, ScrollXYView, Button, ButtonFlat, ButtonFlatter, CheckBox, Toggle, RadioButton, LinkLabel, TextInput
Widgets that DO NOT Support Animator
Label, H1-H4, P, TextBox, Image, Icon, Markdown, Html, Slider, DropDown, Splitter, Hr, Filler
To animate a Label: Wrap it in a View with the animator:
View{
width: Fit height: Fit
animator: Animator{
hover: {
default: @off
off: AnimatorState{ from: {all: Forward {duration: 0.15}} apply: {draw_bg: {hover: 0.0}} }
on: AnimatorState{ from: {all: Forward {duration: 0.15}} apply: {draw_bg: {hover: 1.0}} }
}
}
label := Label{text: "Animated via parent"}
}---
Animator Structure
animator: Animator{
<group_name>: {
default: @<state_name>
<state_name>: AnimatorState{
from: { ... }
ease: <EaseFunction>
redraw: true
apply: { ... }
}
<state_name>: AnimatorState{ ... }
}
<group_name>: { ... }
}---
Groups
Each group is an independent animation track. Common groups:
| Group | Purpose | Typical States |
|---|---|---|
hover | Mouse hover in/out | off, on |
focus | Keyboard focus | off, on |
active | Toggled/checked state | off, on |
disabled | Disabled state | off, on |
time | Continuous looping | off, on |
Multiple groups animate simultaneously without interfering.
---
The from Block
Controls transition timing. Keys are states being transitioned FROM, or all as catch-all.
// From any state, animate over 0.2 seconds
from: {all: Forward {duration: 0.2}}
// Instant from any state
from: {all: Snap}
// Different timing depending on origin
from: {
all: Forward {duration: 0.1}
down: Forward {duration: 0.01}
}---
Play Types
| Type | Description | Example |
|---|---|---|
Forward {duration: 0.2} | One-shot forward | Hover transitions |
Snap | Instant jump, no animation | Default state initialization |
Loop {duration: 1.0} | Looping animation | Loading spinners |
ReverseLoop {duration: 1.0} | Ping-pong loop | Pulsing effects |
BounceLoop {duration: 1.0} | Bounce back and forth | Bouncing animations |
---
Ease Functions
| Function | Description |
|---|---|
Linear | Constant speed |
InQuad / OutQuad / InOutQuad | Quadratic easing |
InCubic / OutCubic / InOutCubic | Cubic easing |
InQuart / OutQuart / InOutQuart | Quartic easing |
InQuint / OutQuint / InOutQuint | Quintic easing |
InSine / OutSine / InOutSine | Sine easing |
InExp / OutExp / InOutExp | Exponential easing |
InCirc / OutCirc / InOutCirc | Circular easing |
InElastic / OutElastic / InOutElastic | Elastic spring |
InBack / OutBack / InOutBack | Overshoot |
InBounce / OutBounce / InOutBounce | Bounce |
Usage: ease: OutCubic in the AnimatorState (optional, defaults to Linear).
---
The apply Block
Target values to animate TO. Structure mirrors the widget's shader properties.
// Animate single properties
apply: {
draw_bg: {hover: 1.0}
}
// Animate multiple properties
apply: {
draw_bg: {hover: 1.0 color: #f00}
draw_text: {color: #fff}
}CRITICAL: Only instance() shader variables can be animated. uniform() variables cannot.
---
Complete Hover Button Example
use mod.prelude.widgets.*
let HoverCard = RoundedView{
width: Fill height: Fit
padding: 16
new_batch: true
cursor: Hand
draw_bg +: {
instance hover: 0.0
color: mix(#2a2a3d, #3a3a5d, self.hover)
border_radius: 8.0
}
animator: Animator{
hover: {
default: @off
off: AnimatorState{
from: {all: Forward {duration: 0.15}}
apply: {draw_bg: {hover: 0.0}}
}
on: AnimatorState{
from: {all: Forward {duration: 0.15}}
apply: {draw_bg: {hover: 1.0}}
}
}
}
title := Label{text: "Hover me" draw_text.color: #fff}
}Key points:
new_batch: trueis REQUIRED for hoverable items with backgroundscursor: Handshows pointer cursor on hoverinstance hover: 0.0declares the animatable variablemix(color1, color2, self.hover)interpolates between colors
---
Timeline Animation (Keyframes)
For multi-step animations use timeline():
animator: Animator{
time: {
default: @off
on: AnimatorState{
from: {all: Loop {duration: 2.0}}
apply: {
draw_bg: {
rotation: timeline(){
snap(0.0)
snap(6.28)
}
}
}
}
}
}---
Loading Spinner Pattern
let Spinner = View{
width: 40 height: 40
draw_bg +: {
instance rotation: 0.0
pixel: fn() {
let sdf = Sdf2d.viewport(self.pos * self.rect_size)
let cx = self.rect_size.x * 0.5
let cy = self.rect_size.y * 0.5
let r = min(cx, cy) * 0.8
sdf.arc(cx, cy, r, self.rotation, self.rotation + 4.5, 3.0)
sdf.stroke(#4488ff, 2.5)
return sdf.result
}
}
animator: Animator{
time: {
default: @on
on: AnimatorState{
from: {all: Loop {duration: 1.0}}
apply: {
draw_bg: {
rotation: timeline(){
snap(0.0)
snap(6.28318)
}
}
}
}
}
}
}---
Animator vs Vector Tween
Makepad has two separate animation systems:
| Feature | Animator (this skill) | Tween (see makepad-2.0-vector) |
|---|---|---|
| Target | Widget shader instance vars | SVG shape properties (fill, cx, opacity...) |
| Syntax | animator: Animator{...} block | Property value: opacity:Tween{...} |
| Triggers | State transitions (hover, focus) | Automatic on render |
| Loop | Loop {duration: 1.0} in from | loop_:true (bool, NOT string!) |
| Use for | UI interactions (hover, click) | SVG/Vector graphic animations |
Use Animator for: Button hover effects, toggle states, loading spinners (shader-based) Use Vector Tween for: SVG path animations, pulsing indicators, moving dots, color transitions
See the makepad-2.0-vector skill for Tween details.
---
Best Practices
1. Always add `new_batch: true` to Views with backgrounds that have hover animations 2. Use `instance` variables for anything you want to animate 3. Match group names to semantic purposes (hover, focus, active) 4. Use Forward for transitions, Snap for instant state changes 5. Set `default: @off` for states that start inactive 6. Label cannot animate - wrap in View if you need animation on text 7. Keep durations short (0.1-0.3s) for hover, longer (0.5-2.0s) for time-based
Makepad 2.0 Animator Reference
Overview
The Animator system drives instance() variables on draw shaders over time, enabling hover effects, transitions, and looping animations. It operates on independent animation tracks called "groups" that run simultaneously without interfering.
---
Animator Structure
animator: Animator{
<group_name>: {
default: @<state_name> // initial state (@ prefix required)
<state_name>: AnimatorState{
from: { ... } // transition timing
ease: <EaseFunction> // optional ease override
redraw: true // optional: force redraw each frame
apply: { ... } // target values
}
<state_name>: AnimatorState{ ... }
}
<group_name>: { ... } // multiple groups allowed
}---
CRITICAL: Widget Animator Support
NOT all widgets have an animator field. If you add animator: Animator{...} to a widget that doesn't support it, the definition is silently ignored -- no error, no hover, nothing happens.
Widgets that SUPPORT Animator
View, SolidView, RoundedView, ScrollXView, ScrollYView, ScrollXYView, Button, ButtonFlat, ButtonFlatter, CheckBox, Toggle, RadioButton, LinkLabel, TextInput
Widgets that DO NOT Support Animator
Label, H1-H4, P, TextBox, Image, Icon, Markdown, Html, Slider, DropDown, Splitter, Hr, Filler
---
Groups
Each group is an independent animation track. Common groups:
hover-- mouse hover in/outfocus-- keyboard focusactive-- toggled/checked state (CheckBox, Toggle, RadioButton)disabled-- disabled statetime-- continuous/looping time-based animation
Multiple groups animate simultaneously without interfering with each other.
---
The from Block
Controls when and how the transition plays. Keys are state names being transitioned FROM, or all as a catch-all.
// From any state, animate over 0.2 seconds
from: {all: Forward {duration: 0.2}}
// Instant from any state
from: {all: Snap}
// Different timing depending on origin state
from: {
all: Forward {duration: 0.1} // default
down: Forward {duration: 0.01} // faster when coming from "down"
}---
The apply Block
Target values to animate TO. The structure mirrors the widget's property tree. Keys are the widget's sub-objects (like draw_bg, draw_text), values are the shader instance variables to animate.
// Animate single properties
apply: {
draw_bg: {hover: 1.0}
draw_text: {hover: 1.0}
}
// Multiple properties in one block
apply: {
draw_bg: {down: 1.0, hover: 0.5}
draw_text: {down: 1.0, hover: 0.5}
}
// Non-draw properties (widget's own fields)
apply: {
opened: 1.0
active: 0.0
}---
snap() -- Instant Jump
Wrapping a value in snap() makes it jump instantly instead of interpolating:
apply: {
draw_bg: {down: snap(1.0), hover: 1.0} // down jumps, hover interpolates
}---
timeline() -- Keyframes
Animate through multiple values over the duration using time/value pairs (times 0.0-1.0):
apply: {
draw_bg: {anim_time: timeline(0.0 0.0 1.0 1.0)} // linear 0 to 1
}---
Play Types (Transition Modes)
| Type | Syntax | Description |
|---|---|---|
| Forward | Forward {duration: 0.2} | Play once forward |
| Snap | Snap | Instant, no interpolation |
| Reverse | Reverse {duration: 0.2, end: 1.0} | Play in reverse |
| Loop | Loop {duration: 1.0, end: 1000000000.0} | Repeat forward |
| ReverseLoop | ReverseLoop {duration: 1.0, end: 1.0} | Repeat in reverse |
| BounceLoop | BounceLoop {duration: 1.0, end: 1.0} | Bounce back and forth |
---
Ease Functions
Standard Easing
Linear // default
InQuad OutQuad InOutQuad
InCubic OutCubic InOutCubic
InQuart OutQuart InOutQuart
InQuint OutQuint InOutQuint
InSine OutSine InOutSine
InExp OutExp InOutExp
InCirc OutCirc InOutCirc
InElastic OutElastic InOutElastic
InBack OutBack InOutBack
InBounce OutBounce InOutBounceParametric Easing
ExpDecay {d1: 0.82, d2: 0.97, max: 100}
Pow {begin: 0.0, end: 1.0}
Bezier {cp0: 0.0, cp1: 0.0, cp2: 1.0, cp3: 1.0}Usage:
off: AnimatorState{
from: {all: Forward {duration: 0.3}}
ease: OutCubic
apply: {draw_bg: {hover: 0.0}}
}---
Hoverable Label Pattern
Label does NOT support Animator. To make hoverable/clickable text, wrap a Label inside a View with animator + cursor.
CRITICAL: Always set new_batch: true on any View that has show_bg: true AND contains text children. Without it, when the hover activates and the background becomes opaque, it covers the text -- making text disappear on hover.
View{
width: Fill height: Fit
cursor: MouseCursor.Hand
new_batch: true
show_bg: true
draw_bg +: {
color: uniform(#0000)
color_hover: uniform(#fff2)
hover: instance(0.0)
pixel: fn(){
return Pal.premul(self.color.mix(self.color_hover, self.hover))
}
}
animator: Animator{
hover: {
default: @off
off: AnimatorState{
from: {all: Forward {duration: 0.15}}
apply: {draw_bg: {hover: 0.0}}
}
on: AnimatorState{
from: {all: Forward {duration: 0.15}}
apply: {draw_bg: {hover: 1.0}}
}
}
}
label := Label{text: "hoverable item" draw_text.color: #fff}
}---
Hoverable List Item Pattern
For list items, use label := to declare the inner Label so each instance can override its text:
let HoverItem = View{
width: Fill height: Fit
padding: theme.mspace_2
cursor: MouseCursor.Hand
new_batch: true
show_bg: true
draw_bg +: {
color: uniform(#0000)
color_hover: uniform(#fff2)
hover: instance(0.0)
pixel: fn(){
return self.color.mix(self.color_hover, self.hover)
}
}
animator: Animator{
hover: {
default: @off
off: AnimatorState{
from: {all: Forward {duration: 0.15}}
apply: {draw_bg: {hover: 0.0}}
}
on: AnimatorState{
from: {all: Forward {duration: 0.15}}
apply: {draw_bg: {hover: 1.0}}
}
}
}
label := Label{text: "item" draw_text.color: #fff}
}---
Complete Button Animator Example
This example shows all four standard animation groups (disabled, hover, focus, time) plus a three-state hover group (off/on/down):
animator: Animator{
disabled: {
default: @off
off: AnimatorState{
from: {all: Forward {duration: 0.}}
apply: {
draw_bg: {disabled: 0.0}
draw_text: {disabled: 0.0}
}
}
on: AnimatorState{
from: {all: Forward {duration: 0.2}}
apply: {
draw_bg: {disabled: 1.0}
draw_text: {disabled: 1.0}
}
}
}
hover: {
default: @off
off: AnimatorState{
from: {all: Forward {duration: 0.1}}
apply: {
draw_bg: {down: 0.0, hover: 0.0}
draw_text: {down: 0.0, hover: 0.0}
}
}
on: AnimatorState{
from: {
all: Forward {duration: 0.1}
down: Forward {duration: 0.01}
}
apply: {
draw_bg: {down: 0.0, hover: snap(1.0)}
draw_text: {down: 0.0, hover: snap(1.0)}
}
}
down: AnimatorState{
from: {all: Forward {duration: 0.2}}
apply: {
draw_bg: {down: snap(1.0), hover: 1.0}
draw_text: {down: snap(1.0), hover: 1.0}
}
}
}
focus: {
default: @off
off: AnimatorState{
from: {all: Snap}
apply: {
draw_bg: {focus: 0.0}
draw_text: {focus: 0.0}
}
}
on: AnimatorState{
from: {all: Snap}
apply: {
draw_bg: {focus: 1.0}
draw_text: {focus: 1.0}
}
}
}
time: {
default: @off
off: AnimatorState{
from: {all: Forward {duration: 0.}}
apply: {}
}
on: AnimatorState{
from: {all: Loop {duration: 1.0, end: 1000000000.0}}
apply: {
draw_bg: {anim_time: timeline(0.0 0.0 1.0 1.0)}
}
}
}
}---
Button draw_bg Instance Variables
These are per-instance floats driven by the Animator:
hover,down,focus,disabled
Color uniforms (each with _hover, _down, _focus, _disabled variants):
color,border_color,icon_color
---
CheckBox/Toggle draw_bg Instance Variables
Animator-driven: hover, down, focus, active, disabled
Uniforms: size, border_size, border_radius
Color uniforms (each with _hover, _down, _active, _focus, _disabled variants):
color,border_color,mark_color- Also:
mark_size
---
Rust Struct Setup for Animated Widgets
// The widget struct needs Animator derive
#[derive(Script, ScriptHook, Widget, Animator)]
pub struct MyAnimatedWidget {
#[source] source: ScriptObjectRef,
#[apply_default] animator: Animator,
#[walk] walk: Walk,
#[layout] layout: Layout,
#[redraw] #[live] draw_bg: DrawQuad,
#[live] draw_text: DrawText,
}---
Continuous Time Animation
Use the time group with Loop play type for continuous shader animations. Access the animated value in shaders via self.draw_pass.time for elapsed time, or use the anim_time instance variable driven by timeline().
time: {
default: @off
off: AnimatorState{
from: {all: Forward {duration: 0.}}
apply: {}
}
on: AnimatorState{
from: {all: Loop {duration: 1.0, end: 1000000000.0}}
apply: {
draw_bg: {anim_time: timeline(0.0 0.0 1.0 1.0)}
}
}
}---
Instance vs Uniform for Animation
instance()-- per-draw-call value, varies per widget instance, animatable by Animator. Use for: hover, down, focus, active, disabled, per-widget colors, scale/pan.uniform()-- shared across all instances of the same shader variant. Cannot be animated. Use for: theme constants, border_size, border_radius, base theme colors.
draw_bg +: {
hover: instance(0.0) // each button has its own hover state
color: uniform(theme.color_x) // shared base color for all instances
color_hover: instance(theme.color_y) // per-instance if color varies
}---
FoldHeader Animation
FoldHeader has a built-in active animation group that drives an opened float:
FoldHeader{
body_walk: Walk{...}
animator: Animator{
active: {
default: @on
off: AnimatorState{
from: {all: Forward {duration: 0.2}}
apply: {opened: 0.0}
}
on: AnimatorState{
from: {all: Forward {duration: 0.2}}
apply: {opened: 1.0}
}
}
}
header: View{ height: Fit ... }
body: View{ ... }
}