Motion and Animation

Rippily's motion system controls how elements move across the canvas. Whether it's a character walking to a waypoint, a progress bar filling up, or a cloud drifting across the screen, the motion system handles the animation.

Motion Modes

Every movement has a timing mode that determines how long the animation takes.

Speed-Based

The element moves at a consistent speed (pixels per second). Greater distances take longer. This feels natural for character movement and game pieces — walking across a room takes longer than walking across a table.

  • Speed: Pixels per second (e.g. 200 means the element moves 200 pixels every second)
  • Max Duration: An optional cap that prevents very long animations for extreme distances

Duration-Based

The animation always takes the same amount of time regardless of distance. This is predictable for UI animations — a panel slides in over 300ms whether it's moving 100 or 500 pixels.

  • Duration: Fixed time in milliseconds (e.g. 300ms)

Easing Functions

Easing controls acceleration and deceleration during the animation. The right easing makes movement feel natural rather than robotic.

Easing Behaviour Best For
Linear Constant speed, no acceleration Mechanical movement, loading bars
Ease Gentle acceleration and deceleration General purpose
Ease In Starts slow, then speeds up Objects leaving (doors closing)
Ease Out Starts fast, then slows down Objects arriving (menus sliding in)
Ease In Out Slow start and end, fast middle Back-and-forth motion
Spring Overshoots slightly, then settles Playful, bouncy UI elements

Wrap Modes

Wrap modes control what happens when a value reaches its boundaries. They're essential for looping and oscillating animations.

None

No wrapping — the value passes through unchanged. This is the default and works for most one-off movements.

Wrap

When the value exceeds the maximum, it loops back to the minimum. Like a clock hand going from 12 back to 1.

Example: A cloud at x: 0 moves right. When it reaches x: 1200 (the right edge), it wraps back to x: 0 and continues moving right. Infinite loop.

Ping-Pong

The value bounces between minimum and maximum. When it hits the max, it reverses direction towards the min, and vice versa.

Example: A floating element oscillates between y: 100 and y: 150, bobbing gently up and down.

Clamp

The value stops at the boundary and cannot exceed it. Like a slider that can't go past its track.

Example: A health bar width is bound to $health. Clamped between 0 and 200 pixels — even if $health somehow goes negative, the bar stays at 0.

Wrap modes require wrap bounds — a minimum and maximum value that define the boundaries.

Teleport

Sometimes you need an element to jump instantly without animation:

Teleport

Skip the animation and place the element at the target position immediately. Useful for:

  • Resetting an element to its start position
  • Scene entry — place a character at their starting spot
  • Respawning in games

Teleport Threshold

Automatically teleport if the movement distance exceeds a threshold. This is the secret to seamless wrap-around animations:

  1. A cloud moves smoothly from x: 0 to x: 1200 (the right edge)
  2. Its variable wraps back to x: 0
  3. Without a threshold, the cloud would animate backwards across the entire screen
  4. With a teleport threshold of 200, the cloud recognises the jump is too large and teleports instead

The cloud appears to loop seamlessly.

Position Bindings

Position bindings are the most powerful motion feature. They tie an element's position (and other properties) to variables. When the variable changes, the element animates to the new value automatically.

Bindable Properties

You can bind:

  • x, y: Absolute position on the canvas
  • offsetX, offsetY: Relative offset from the element's base position
  • width, height: Element dimensions
  • scale: Uniform scale factor
  • rotation: Rotation in degrees
  • opacity: Transparency (0-1)

How It Works

  1. Create a variable (e.g. $characterX)
  2. Open the element's position bindings
  3. Bind the x property to $characterX
  4. Whenever $characterX changes (via a Set Variable action), the element animates to the new x position

Per-Binding Motion Configs

Each bound property can have its own motion configuration. The x binding might use speed-based motion with spring easing, while the opacity binding uses duration-based with linear easing.

This lets you create complex, natural-feeling animations where different properties animate differently.

Common Patterns

Infinite Scrolling Background

  1. Bind a background element's x to a variable $bgScroll
  2. Set wrap mode to Wrap with bounds 0-1200
  3. Use a Timer trigger to increment $bgScroll by 1 every 50ms
  4. Set teleport threshold so the wrap-around is instant

Bobbing Animation

  1. Bind an element's offsetY to $bob
  2. Set wrap mode to Ping-Pong with bounds -10 to 10
  3. Use a timer to increment $bob every 50ms
  4. The element bobs gently up and down indefinitely

Character on a Grid

  1. Bind position to $charX and $charY
  2. Use speed-based motion with ease-out easing
  3. Arrow key triggers update $charX and $charY by the grid cell size
  4. The character slides smoothly from cell to cell

Animated Progress Bar

  1. Create a rectangle shape for the fill
  2. Bind its width to $progress
  3. Set duration-based motion (300ms, ease-out)
  4. As $progress increases, the bar fills smoothly

Tips

  • Speed mode for character movement: It feels right because walking across a room should take longer than walking across a table.
  • Duration mode for UI: Panels, menus, and indicators should animate consistently regardless of distance.
  • Spring easing for delight: Use sparingly — a spring bounce on a button click or notification entry feels playful and polished.
  • Teleport threshold for wrapping: Always set a teleport threshold when using Wrap mode, otherwise the wrap-around will animate backwards.

What's Next?