Every animation starts by defining the canvas. This must be the first line in your .markdy file.
scene width=800 height=400 fps=30 bg=#fafafa
| Property | Default | Description |
width | 800 | Canvas width in pixels |
height | 400 | Canvas height in pixels |
fps | 30 | Frame rate for the rendering engine |
bg | white | Any CSS color value (e.g. #1a1a2e) |
duration | auto | Override total length in seconds |
Actors are the objects in your scene. Place them with at (x, y) and add optional modifiers.
actor a = figure(#c68642, m, 😎) at (100, 200) scale 1.2
actor b = text("Hello World") at (50, 50) size 24
actor c = box() at (300, 200)
| Type | Arguments | Description |
text | "quoted string" | Plain text label. Customise with size. |
box | — | 100x100 solid box. Great for prototyping. |
figure | skinColor, gender, face | Stick figure with articulatable limbs. |
sprite | asset name | Renders an image or icon asset. |
Modifiers: opacity (0–1), scale, rotate, size (text only), z (layer order).
Schedule actions on a timeline with @time: actor.action(). Time is in decimal seconds.
@0.5: a.enter(from=left, dur=0.8)
@1.5: a.move(to=(400, 200), dur=1.0, ease=out)
@3.0: a.fade_out(dur=0.5)
| Action | Parameters | What it does |
enter | from, dur | Slides in from canvas edge |
move | to, dur, ease | Translates to target coordinates |
fade_in / fade_out | dur | Animates opacity |
scale / rotate | to, dur | Transforms the actor |
Easing: linear, in, out, inout
Make actors talk with comic speech bubbles and add dynamic effects like shaking.
@1.0: a.say("Hello! 👋", dur=1.5)
@2.5: a.shake(intensity=8, dur=0.4)
| Action | Parameters | What it does |
say | "text", dur | Shows an anchored speech bubble |
shake | intensity, dur | Oscillates on the X axis |
throw | asset, to, dur | Launches a projectile between actors |
figure actors have articulated limbs you can individually control for expressive animations.
@1.0: a.face("😵")
@1.5: a.rotate_part(part=arm_right, to=130, dur=0.4)
@2.0: a.punch(side=right, dur=0.2)
@2.5: a.kick(side=left, dur=0.3)
| Action | Parameters |
face | "emoji" — instant expression swap |
rotate_part | part, to (degrees), dur |
punch / kick | side (left|right), dur |
Body parts: head, face, body, arm_left, arm_right, leg_left, leg_right
Built-in gestures save you from chaining rotate_part calls. pose sets multiple parts at once.
@1.0: a.wave(side=right, dur=0.8)
@2.0: a.nod(dur=0.4)
@3.0: a.pose(arm_left=70, arm_right=-70, dur=0.4)
@4.0: a.jump(height=25, dur=0.5)
@4.5: a.bounce(intensity=12, count=3, dur=0.5)
| Action | Parameters | What it does |
wave | side, dur | Wave gesture — arm up, oscillate, return |
nod | dur | Head nod — down and up twice |
pose | arm_left, arm_right, leg_left, leg_right, head, body, dur | Set multiple parts at once |
jump | height, dur | Jump with squash/stretch |
bounce | intensity, count, dur | Diminishing vertical bounce |
Load external images and vector icons as assets, then render them as sprite actors.
# Declare assets first
asset cat = image("https://example.com/cat.gif")
asset fire = icon("lucide:flame")
# Render them as actors
actor meme = sprite(cat) at (400, 200) scale 0.5
actor icon = sprite(fire) at (100, 100) scale 2.0
image("url") | Loads images, GIFs, photos — any URL or local path. |
icon("set:name") | Loads SVG icons from Iconify. Never blurs when scaled. |
Avoid repetition with var for values and def for reusable actor templates.
var accent_skin = #fad4c0
def char(skin, face) {
figure($${skin}, f, $${face})
}
actor lily = char($${accent_skin}, 😊) at (100, 200)
var substitutes values (robust for #hex colors). def creates actor factory templates — build character systems without any JavaScript.
Package multi-step choreographies into reusable seq blocks. The ultimate power move.
seq wave(arm, angle) {
@+0.0: $.rotate_part(part=$${arm}, to=$${angle}, dur=0.3)
@+0.3: $.rotate_part(part=$${arm}, to=25, dur=0.3)
}
@2.0: lily.play(wave, arm=arm_left, angle=-80)
$ refers to the calling actor. Times with @+ are relative offsets from when .play() is triggered.