The jump is the heart of every platformer. This demo shows a complete 2D platformer physics loop in vanilla JavaScript and Canvas — gravity, variable jump height, coyote time so platform edges feel forgiving, horizontal acceleration, friction, and solid AABB collision. The character moves automatically when idle, but keyboard input instantly takes control.
A Canvas character that runs across platforms, jumps with a variable-height arc (hold longer = jump higher), and gets 80ms of coyote time after leaving a platform. Collision is resolved separately on the X and Y axes for predictable floor, wall, and ceiling detection.
2D Platformer Physics — JavaScript Canvas
How 2D platformer physics works in JavaScript
A simple platformer does not need a full physics engine. Each frame, the game updates horizontal velocity, applies gravity to vertical velocity, moves the character, checks for collisions, and corrects any overlap with the platforms.
The important part is resolving horizontal and vertical movement separately. First the player moves on the X axis and wall collisions are resolved. Then the player moves on the Y axis and floor or ceiling collisions are resolved. This approach keeps collision behavior predictable and avoids many common corner-sticking problems found in beginner platformer code.
// Gravity
```
player.vy += GRAVITY * dt;
// Horizontal movement
player.x += player.vx * dt;
resolveCollisionsX(player, platforms);
// Vertical movement
player.y += player.vy * dt;
resolveCollisionsY(player, platforms);
// Jump
if (jumpPressed && player.coyote > 0) {
player.vy = JUMP_FORCE;
player.coyote = 0;
}
Separating horizontal and vertical collision resolution makes floor, wall, and ceiling detection easier to control and debug.
Variable jump height — tap or hold
Good platformer jumps are usually not fixed. A quick tap should create a short jump, while holding the jump button should allow the character to travel higher.
This demo implements variable jump height by watching for the moment the jump button is released. If the player is still moving upward, the game immediately reduces the upward velocity.
const jumpReleased =
```
!jump && player.jumpHeld;
if (jumpReleased && player.vy < 0) {
player.vy *= JUMP_CUT;
}
The result is simple: tap the button for a low jump or hold it for a higher arc. This small change gives the player much more control over landing precision.
Coyote time — why platformers feel more forgiving
Coyote time gives the player a tiny window to jump after walking off the edge of a platform. Without it, pressing jump one frame too late can feel unresponsive. With coyote time, the controls tolerate a small amount of human timing error.
This demo uses an 80ms coyote-time window. Whenever the player is standing on a platform, the timer resets. After leaving the platform, the timer counts down toward zero. A jump is still allowed while that timer remains active.
if (player.onGround) {
```
player.coyote = COYOTE_TIME;
} else {
player.coyote =
Math.max(0, player.coyote - delta);
}
if (jumpPressed && player.coyote > 0) {
player.vy = JUMP_FORCE;
player.coyote = 0;
}
Automatic demo without fighting the player
Interactive examples embedded inside articles have one useful problem: if the character starts completely still, some readers may never realize the Canvas is playable.
This demo therefore moves automatically while idle. However, automatic input must never compete with real keyboard input. The moment the reader presses a movement or jump key, automatic movement is disabled and manual controls receive full priority.
const manualMode =
```
now < manualUntil;
const left =
manualMode ? manualLeft : auto.left;
const right =
manualMode ? manualRight : auto.right;
const jump =
manualMode ? manualJump : auto.jump;
After a short period without keyboard input, the automatic demo resumes. This keeps the example visually active while still making the controls feel immediate when the reader interacts with it.
Why the original input logic could fail
A common mistake is combining automatic and manual movement in the same condition:
if (rightKey || autoRight) {
```
player.vx += acceleration;
} else if (leftKey) {
player.vx -= acceleration;
}
If autoRight is true, the first condition wins even when
the player is pressing left. The automatic demo effectively fights the keyboard.
The corrected version keeps automatic and manual input separate and explicitly selects which input source controls the character.
Axis-separated AABB collision
The platforms and player are rectangles, so collision detection uses axis-aligned bounding boxes, usually called AABB collision.
An overlap exists when the player's rectangle intersects a platform on both the horizontal and vertical axes. Once an overlap is found, the game compares the penetration depth and pushes the character out along the appropriate axis.
function aabb(a, b) {
```
return (
a.x < b.x + b.w &&
a.x + a.w > b.x &&
a.y < b.y + b.h &&
a.y + a.h > b.y
);
}
Delta time keeps movement more consistent
Browser animation is usually close to 60 frames per second, but it is not guaranteed to run at exactly the same speed on every frame. That is why this version calculates a small delta-time multiplier.
Movement and gravity are multiplied by that value, which reduces visible speed differences when frames are slightly delayed. The delta value is also capped so switching browser tabs does not cause a huge physics update when the tab becomes active again.
This structure can be used as the foundation of a larger Canvas platformer. Replace the rectangle with a sprite, add animation states, move platforms into level data, and introduce a camera for scrolling worlds. The same player state can also be extended with wall detection, dash mechanics, one-way platforms, enemies, checkpoints, and collectibles.
```Next steps: make it a real game
Once the base movement feels good, add features one at a time. One-way platforms can ignore upward collisions and only catch the player while falling. Wall jumps can detect horizontal contact and apply a vertical impulse. Moving platforms can update their own position before player collision is resolved.
For a side-scrolling level, add a camera offset instead of moving the entire world manually. Draw platforms and the player relative to the camera position, while keeping physics coordinates in world space.
From there, the most important improvement is not adding more physics —
it is tuning the values you already have.
Experiment with GRAVITY, JUMP_FORCE,
FRICTION, JUMP_CUT, and
COYOTE_TIME until movement feels responsive.
In platformers, small timing changes can completely change how the game feels.
No hay comentarios:
Publicar un comentario