DEV Community

Cover image for 🏃 The Ultimate Guide to Limn Engine's `move` Utility
Kehinde Owolabi
Kehinde Owolabi

Posted on

🏃 The Ultimate Guide to Limn Engine's `move` Utility

🏃 The Ultimate Guide to Limn Engine's move Utility

Everything You Need to Know About Movement, Physics, and Control


📖 Introduction

Movement is the foundation of every game. Whether you're building a platformer, a top-down shooter, or an open-world RPG, how objects move determines how your game feels. Get it right, and your game feels polished and professional. Get it wrong, and your game feels clunky and frustrating.

The Limn Engine's move utility is a collection of 34 functions that handle movement, physics, and control. It's designed to save you from writing the same boilerplate code over and over, and to give you powerful, flexible control over how objects move in your game.

The philosophy: Common outcomes should be functions. Every function in move exists because I was tired of writing the same code repeatedly — tired of edge detection, tired of trigonometry, tired of lerp functions, tired of configuring particles.

In this guide, we'll cover every function in the move utility — what it does, when to use it, and why it's powerful.


📊 Complete Function List

Category Functions
Movement & Positioning teleport(), setX(), setY(), position(), stamp(), clearStamp()
Angular & Directional forward(), backward(), turnLeft(), turnRight(), circle(), pointTo()
Boundaries bound(), boundTo()
Smooth Movement glideX(), glideY(), glideTo()
Physics accelerate(), decelerate(), project(), hitObject()
Particles explosion(), smoke(), sparkle(), rain(), blood(), magic()
Sound play(), playMusic(), stopMusic(), setMasterVolume(), mute(), unmute()
Debugging dot()

Total: 34 functions


🎯 Movement & Positioning Functions

These functions handle basic movement and positioning of components.


move.teleport()

move.teleport(player, 400, 300);
Enter fullscreen mode Exit fullscreen mode

What It Does: Instantly moves a component to a new position.

Why It's Useful: It's the foundation of respawns, checkpoints, and teleportation mechanics. Simple but essential.

When to Use:

  • Respawn players at checkpoints
  • Teleport enemies to random positions
  • Move objects instantly without animation

Example:

// Respawn player at checkpoint
if (player.health <= 0) {
    move.teleport(player, checkpoint.x, checkpoint.y);
}

// Teleport enemy to random position
move.teleport(enemy, Math.random() * 700 + 50, Math.random() * 500 + 50);
Enter fullscreen mode Exit fullscreen mode

move.setX() / move.setY()

move.setX(player, 100);
move.setY(player, 200);
Enter fullscreen mode Exit fullscreen mode

What It Does: Sets the X or Y position of a component individually.

Why It's Useful: Sometimes you only need to change one axis.

When to Use:

  • Adjusting position on one axis only
  • Keeping one axis fixed while changing the other

The Hard Truth: This is just obj.x = value or obj.y = value. It's a convenience wrapper.


move.position()

move.position(healthBar, "top", 20);
Enter fullscreen mode Exit fullscreen mode

What It Does: Snaps UI to screen edges or center with one line.

Why It's Useful: It saves you from doing manual centering calculations. Makes UI layout trivial.

When to Use:

  • Positioning UI elements (health bars, score displays)
  • Centering logos or titles
  • Placing buttons at screen edges

Supported Directions:

  • "top" — top edge with offset
  • "bottom" — bottom edge with offset
  • "left" — left edge with offset
  • "right" — right edge with offset
  • "center" — center of screen

Example:

move.position(healthBar, "top", 20);
move.position(scoreText, "top", 50);
move.position(logo, "center");
move.position(menuButton, "bottom", 30);
Enter fullscreen mode Exit fullscreen mode

move.stamp() / move.clearStamp()

const clone = move.stamp(enemy);
move.clearStamp(clone);
Enter fullscreen mode Exit fullscreen mode

What It Does: stamp() creates a clone of a component at its current position. clearStamp() removes it.

Why It's Useful: Useful for creating duplicates of objects or creating effects.

When to Use:

  • Particle-like duplication
  • Creating multiple objects from a template
  • Spawning effects

🧭 Angular & Directional Functions

These functions handle movement and rotation based on a component's angle.


move.forward() / move.backward()

move.forward(player, 5);   // Move in facing direction
move.backward(player, 5);  // Move opposite facing direction
Enter fullscreen mode Exit fullscreen mode

What It Does: Moves a component along its current angle direction. Uses Math.cos(angle) and Math.sin(angle) internally.

Why It's Useful: It simplifies movement in a specific direction. Perfect for driving games, top-down shooters, and any game with directional movement.

When to Use:

  • Driving games
  • Top-down shooters
  • Any time you need to move in the direction the component is facing

How It Works Internally:

// Simplified version
function forward(id, steps) {
    id.speedX = steps * Math.cos(id.angle);
    id.speedY = steps * Math.sin(id.angle);
}
Enter fullscreen mode Exit fullscreen mode

Example:

const car = new Component(40, 20, "red", 400, 300, "rect");
car.changeAngle = true;
display.add(car);

function update(dt) {
    if (display.keys[38]) move.forward(car, 3);
    if (display.keys[40]) move.backward(car, 2);
    if (display.keys[37]) move.turnLeft(car, 0.05);
    if (display.keys[39]) move.turnRight(car, 0.05);
}
Enter fullscreen mode Exit fullscreen mode

move.turnLeft() / move.turnRight()

move.turnLeft(car, 0.05);   // Rotate left
move.turnRight(car, 0.05);  // Rotate right
Enter fullscreen mode Exit fullscreen mode

What It Does: Rotates a component by a given angle. Works with changeAngle = true (the default).

Why It's Useful: It simplifies rotation. Instead of obj.angle += 0.05, you get a readable function.

When to Use:

  • Driving games
  • Top-down shooters
  • Any time you need to rotate a component

move.circle()

move.circle(orbiter, 2);  // 2 degrees per frame
Enter fullscreen mode Exit fullscreen mode

What It Does: Creates orbiting motion without trigonometry. It adds to the component's angle each frame, and the component's moveAngle() method converts that angle into movement.

Why It's Useful: It's a unique feature. Most engines require manual trigonometry for orbiting. Limn handles it with one line.

When to Use:

  • Orbiting enemies
  • Planetary systems
  • Circular patterns

How It Works Internally:

// Simplified version
function circle(id, speed) {
    id.physics = true;
    id.changeAngle = true;
    id.angle += speed * Math.PI / 180;
}

// And in the component's moveAngle():
this.x += this.speedX * Math.cos(this.angle);
this.y += this.speedY * Math.sin(this.angle) + this.gravitySpeed;
Enter fullscreen mode Exit fullscreen mode

Example:

const orbiter = new Component(16, 16, "cyan", 400, 300, "rect");
orbiter.angularMovement = true;
orbiter.speedX = 80;
orbiter.speedY = 80;
display.add(orbiter);

function update(dt) {
    move.circle(orbiter, 2);  // Orbits automatically!
}
Enter fullscreen mode Exit fullscreen mode

Note: Requires angularMovement = true and speedX / speedY to be set.


move.pointTo()

move.pointTo(turret, mouse.x, mouse.y);
Enter fullscreen mode Exit fullscreen mode

What It Does: Rotates any component to face a target coordinate. Uses Math.atan2() internally and sets the component's angle property.

Why It's Useful: It removes a common barrier — trigonometry. Beginners can now build aiming mechanics without math anxiety.

When to Use:

  • Turrets
  • Enemies that track the player
  • Aiming mechanics
  • Any time you need an object to face a specific point

How It Works Internally:

// Simplified version
function pointTo(id, targetX, targetY) {
    const deltaX = targetX - id.x;
    const deltaY = targetY - id.y;
    id.angle = Math.atan2(deltaY, deltaX);
}
Enter fullscreen mode Exit fullscreen mode

Example:

function update() {
    // Turret faces the mouse
    move.pointTo(turret, display.x, display.y);

    // Enemy faces the player
    move.pointTo(enemy, player.x, player.y);
}
Enter fullscreen mode Exit fullscreen mode

📐 Boundaries

These functions keep components within defined boundaries.


move.bound()

move.bound(player);
Enter fullscreen mode Exit fullscreen mode

What It Does: Keeps any component on screen with one line. It clamps the component's position so it can never go past any of the four canvas edges.

Why It's Useful: It's the most used function in almost every game. Every game needs boundaries. This replaces four if statements in every object, forever.

When to Use: Every game. Every component that moves. Every frame.

Example:

function update(dt) {
    // Move the player
    if (display.keys[39]) player.speedX =  4;
    if (display.keys[37]) player.speedX = -4;
    else if (!display.keys[39]) player.speedX = 0;

    // Keep the player on screen
    move.bound(player);
}
Enter fullscreen mode Exit fullscreen mode

move.boundTo()

move.boundTo(player, 50, 750, 100, 500);
Enter fullscreen mode Exit fullscreen mode

What It Does: Custom boundaries for any component. It clamps the component to specific pixel values on each side — or leaves a side open by passing false.

Why It's Useful: It enables arena games, room-based levels, and custom playfields. Sometimes you need boundaries that aren't the canvas edges.

When to Use:

  • Arena games
  • Room-based levels
  • Custom playfields
  • Any situation where you need different boundaries for different objects

Example:

// Keep player inside an arena
move.boundTo(player, 50, 750, 50, 550);

// Keep enemy in a specific room
move.boundTo(enemy, 200, 600, 200, 400);

// Leave top and bottom open (only clamp horizontally)
move.boundTo(player, 0, 800, false, false);
Enter fullscreen mode Exit fullscreen mode

🌊 Smooth Movement (Glide)

These functions create smooth, easing-based movement.


move.glideX() / move.glideY() / move.glideTo()

move.glideX(coin, 1000, 500);        // Move X only
move.glideY(coin, 1000, 300);        // Move Y only
move.glideTo(coin, 1000, 500, 300);  // Move both
Enter fullscreen mode Exit fullscreen mode

What It Does: Smooth, easing-based movement to a target point. Uses cubic ease-out for natural deceleration — fast at the start, slow at the end.

Why It's Useful: It saves you from writing lerp functions, animation loops, and timing management every time. It makes movement feel polished.

When to Use:

  • UI animations
  • Cutscenes
  • Enemy patrols
  • Smooth transitions
  • Any time you need polished movement

How It Works Internally:

// Simplified version of glideX()
function glideX(id, duration, x) {
    const startX = id.x;
    const startTime = performance.now();

    function step(currentTime) {
        const elapsed = currentTime - startTime;
        const progress = Math.min(1, elapsed / duration);
        const eased = 1 - Math.pow(1 - progress, 3); // Cubic ease-out

        id.x = startX + (x - startX) * eased;

        if (progress < 1) {
            requestAnimationFrame(step);
        }
    }
    requestAnimationFrame(step);
}
Enter fullscreen mode Exit fullscreen mode

Example:

// Move a coin to a random position over 1 second
move.glideTo(coin, 1000, Math.random() * 700, Math.random() * 500);

// Move the player to a cutscene position
move.glideTo(player, 2000, 400, 300);

// Move UI element smoothly
move.glideTo(menu, 500, 400, 300);
Enter fullscreen mode Exit fullscreen mode

⚡ Physics

These functions handle physics-based movement.


move.accelerate() / move.decelerate()

move.accelerate(player, 0.6, 0, 8, 0);
move.decelerate(player, 0.4, 0);
Enter fullscreen mode Exit fullscreen mode

What It Does: Creates smooth, weighty movement with acceleration curves. accelerate() adds to speed and clamps to a maximum. decelerate() reduces speed toward zero without overshooting.

Why It's Useful: It enables realistic physics and vehicle-like motion. Games with weighty movement feel more polished and professional.

When to Use:

  • Vehicles
  • Platformer movement
  • Realistic motion
  • Any time you want smooth acceleration

How It Works Internally:

// Simplified version
function accelerate(id, accelX, accelY, maxX, maxY) {
    id.speedX += accelX;
    id.speedY += accelY;

    if (Math.abs(id.speedX) > maxX) {
        id.speedX = id.speedX > 0 ? maxX : -maxX;
    }
    if (Math.abs(id.speedY) > maxY) {
        id.speedY = id.speedY > 0 ? maxY : -maxY;
    }
}

function decelerate(id, decelX, decelY) {
    if (id.speedX > 0) {
        id.speedX -= decelX;
        if (id.speedX < 0) id.speedX = 0;
    } else if (id.speedX < 0) {
        id.speedX += decelX;
        if (id.speedX > 0) id.speedX = 0;
    }
    // Same for Y
}
Enter fullscreen mode Exit fullscreen mode

Example:

function update(dt) {
    if (display.keys[68]) {
        move.accelerate(player, 0.6, 0, 8, 0);  // Accelerate right
    } else if (display.keys[65]) {
        move.accelerate(player, -0.6, 0, 8, 0); // Accelerate left
    } else {
        move.decelerate(player, 0.4, 0);        // Slow down
    }
    move.bound(player);
}
Enter fullscreen mode Exit fullscreen mode

move.project()

move.project(bullet, 300, 45, 0.5);
move.project(arrow, 220, 28, 0.4, 420);
Enter fullscreen mode Exit fullscreen mode

What It Does: Launches a component as a projectile with gravity and bounce. Converts angle to velocity components and handles the arc.

Parameters:

  • id — The component to launch
  • initialVelocity — Speed in pixels per second
  • angle — Launch angle in degrees (0° = right, 90° = up)
  • gravity — Added to vertical speed each frame
  • ground (optional) — Custom floor Y position

Why It's Useful: It handles all the physics of projectile motion — angle conversion, gravity, arc, and bouncing.

When to Use:

  • Arrows
  • Fireballs
  • Angry-birds style games
  • Any projectile with gravity

Example:

// Launch a fireball
move.project(fireball, 400, 35, 0.3);

// Launch an arrow with custom ground
move.project(arrow, 250, 30, 0.4, 400);
Enter fullscreen mode Exit fullscreen mode

move.hitObject()

move.hitObject(player, floor);
Enter fullscreen mode Exit fullscreen mode

What It Does: Handles platform-style collision with another component. Treats the top edge of otherid as a floor and applies bounce.

Why It's Useful: It's essential for platformers. It makes the component land on top of another component.

When to Use:

  • Platformer games
  • Landing on platforms
  • Ground collision

Example:

const player = new Component(36, 48, "cyan", 100, 0, "rect");
const platform = new Component(200, 20, "#555", 200, 300, "rect");
display.add(player);
display.add(platform);

player.physics = true;
player.gravity = 0.5;
player.bounce = 0.05;

function update() {
    move.hitObject(player, platform);  // Land on platform
    player.hitBottom();                // Also stop at canvas floor
}
Enter fullscreen mode Exit fullscreen mode

✨ Particles

These functions create visual effects with particles.


move.particles.*

move.particles.explosion(ps, x, y, 30);
move.particles.sparkle(ps, x, y);
move.particles.blood(ps, x, y, 15);
Enter fullscreen mode Exit fullscreen mode

What It Does: Six built-in particle presets with zero configuration. Each preset is a carefully tuned burst or emission of particles.

Why It's Useful: It saves you hours of particle system configuration. In other engines, setting up an explosion requires tweaking gravity, friction, alpha fade, scale, color, and speed parameters. Limn does it all for you.

The Presets:

Preset What It Does When to Use
explosion(ps, x, y, intensity) Orange-red radial burst Explosions, bombs, death
smoke(ps, x, y) Single upward grey circle Fire, exhaust, atmosphere
sparkle(ps, x, y) Yellow radial burst Coins, power-ups, magic
rain(ps, x, y, intensity) Thin blue vertical rects Weather effects
blood(ps, x, y, amount) Red radial burst with gravity Combat, hits
magic(ps, x, y) Multicolour rotating rects Spells, power-ups

Example:

// One line = instant explosion effect
move.particles.explosion(ps, enemy.x, enemy.y, 30);

// One line = coin collection sparkle
move.particles.sparkle(ps, coin.x, coin.y);

// One line = blood splatter
move.particles.blood(ps, player.x, player.y, 10);
Enter fullscreen mode Exit fullscreen mode

🔊 Sound

These functions handle audio playback.


move.sound.*

move.sound.play("jump");
move.sound.playMusic("theme");
move.sound.stopMusic();
move.sound.setMasterVolume(0.8);
move.sound.mute();
move.sound.unmute();
Enter fullscreen mode Exit fullscreen mode

What It Does: A thin wrapper around SoundManager that provides one-line audio control.

Why It's Useful: It simplifies audio management. No need to check if SoundManager exists — just call the function.

When to Use:

  • Playing sound effects
  • Background music
  • Volume control
  • Mute/unmute

Example:

function update() {
    if (display.keys[32]) {
        display.keys[32] = false;
        move.sound.play("shoot");
    }
    if (playerDied) {
        move.sound.stopMusic();
        move.sound.play("explode");
    }
}
Enter fullscreen mode Exit fullscreen mode

🐛 Debugging

move.dot()

move.dot(id);
Enter fullscreen mode Exit fullscreen mode

What It Does: Draws a dot at a component's position. Useful for debugging.

Why It's Useful: It helps visualize positions during development.


📊 Quick Reference

What You Want Code
Keep on screen move.bound(player)
Custom boundaries move.boundTo(player, 50, 750, 50, 550)
Instant move move.teleport(player, 400, 300)
Snap to edge move.position(healthBar, "top", 20)
Smooth movement move.glideTo(coin, 1000, 500, 300)
Realistic physics move.accelerate(player, 0.6, 0, 8, 0)
Face target move.pointTo(turret, mouse.x, mouse.y)
Orbit move.circle(orbiter, 2)
Move forward move.forward(car, 3)
Turn move.turnLeft(car, 0.05)
Explosion move.particles.explosion(ps, x, y, 30)
Sparkle move.particles.sparkle(ps, x, y)
Play sound move.sound.play("jump")
Play music move.sound.playMusic("theme")
Mute move.sound.mute()

💡 The Philosophy Behind move

Every function in move exists because I was tired of writing the same code over and over:

  • move.bound() — tired of edge detection
  • move.glideTo() — tired of lerp functions
  • move.pointTo() — tired of trigonometry
  • move.particles.* — tired of configuring particles

"Common outcomes should be functions." That's the philosophy that created the move utility.


🎯 The One-Line Summary

"The move utility turns complex movement mechanics into one-line commands — saving you hundreds of lines of boilerplate and making your game feel polished."


🔗 Resources


Draw your game into existence — one powerful function at a time. 🚀

Top comments (0)