🏃 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);
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);
move.setX() / move.setY()
move.setX(player, 100);
move.setY(player, 200);
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);
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);
move.stamp() / move.clearStamp()
const clone = move.stamp(enemy);
move.clearStamp(clone);
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
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);
}
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);
}
move.turnLeft() / move.turnRight()
move.turnLeft(car, 0.05); // Rotate left
move.turnRight(car, 0.05); // Rotate right
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
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;
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!
}
Note: Requires angularMovement = true and speedX / speedY to be set.
move.pointTo()
move.pointTo(turret, mouse.x, mouse.y);
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);
}
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);
}
📐 Boundaries
These functions keep components within defined boundaries.
move.bound()
move.bound(player);
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);
}
move.boundTo()
move.boundTo(player, 50, 750, 100, 500);
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);
🌊 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
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);
}
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);
⚡ 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);
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
}
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);
}
move.project()
move.project(bullet, 300, 45, 0.5);
move.project(arrow, 220, 28, 0.4, 420);
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);
move.hitObject()
move.hitObject(player, floor);
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
}
✨ 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);
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);
🔊 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();
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");
}
}
🐛 Debugging
move.dot()
move.dot(id);
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
moveutility turns complex movement mechanics into one-line commands — saving you hundreds of lines of boilerplate and making your game feel polished."
🔗 Resources
| Resource | Link |
|---|---|
| 10x Developer Guide | limn-engine-doc.vercel.app/10x.html |
| API Reference | limn-engine-doc.vercel.app/reference.html |
| GitHub Repository | github.com/terracodes004/limn-engine-doc |
Draw your game into existence — one powerful function at a time. 🚀
Top comments (0)