Build one bounded side-view level before adding enemies or scrolling worlds. The original layout below gives you concrete landing, head-hit, edge-touch, and goal cases. The local HTML example follows this teaching specification; verify runtime behavior and collision corner cases in the intended host.

Write down the level coordinates
Use a 320 × 240 world with the origin at the top left, positive x to the right, and positive y downward. Store the player as a 20 × 20 rectangle whose position is its top-left corner.
| Object | Bounds or initial state |
|---|---|
| Player | x=20, y=200, width=20, height=20; grounded on floor |
| Floor | x=0..320, y=220..240 |
| Solid platform | x=120..200, y=160..170 |
| Goal region | x=260..280, y=200..220 |
Clamp the player's x within 0..300 and its top within the world ceiling at y=0. This level has a full-width floor and no pits, enemies, coins, or lives. The goal is a nonsolid trigger, not another wall. Display it with an original label and shape distinct from the player and solid terrain.
Marijn Haverbeke's platform-game chapter explains separate horizontal and vertical movement, gravity, rectangular collision, and bounded animation steps. This example uses those general concepts with its own level, constants, fresh-press jump policy, and goal; it does not reuse the book's code, styling, coin/lava rules, or maps.
Resolve x and y separately
Choose illustrative movement values: horizontal speed 120 pixels per second, gravity 600 pixels per second squared, and initial jump velocity −300 pixels per second. These values define this game, not realistic physical measurements.
Use fixed simulation substeps of 1/120 second. Horizontal movement is proposed and resolved first against solids and world boundaries. Then update vertical velocity by gravity times the substep and propose the new y position using that updated velocity. Resolve the earliest crossed solid face and clamp the player to it. A small substep supports debugging but does not by itself prove that corners or tunneling are handled correctly.
A horizontal collision stops horizontal movement while leaving vertical movement available. Pressing Left and Right together produces zero horizontal input. Vertical contacts need their own classification: downward contact with a supporting top surface can ground the player; upward contact with an underside or ceiling cannot.
Recompute support after horizontal movement. Walking off the platform must clear grounded rather than preserve yesterday's landing flag. Grounded is evidence of current support, not a synonym for vertical velocity being zero.
Keep three collision fixtures distinct
For landing on the floor, a downward crossing of y=220 by the player's bottom clamps its top to y=200, sets vertical velocity to zero, and sets grounded true.
For the platform, use these isolated fixtures, with enough proposed vertical displacement to cross the indicated face:
| Fixture | Expected resolution |
|---|---|
| x=140, y=138, height=20, descending; bottom crosses y=160 | Land at y=140; vertical velocity 0; grounded true |
| x=140, y=171, ascending; top crosses platform bottom y=170 | Head stops at y=170; vertical velocity 0; grounded false |
| x=100, width=20; right edge equals platform left x=120 | Edge contact alone is not horizontal overlap or support |
Strict horizontal overlap requires the player's right edge to be greater than the platform's left and its left edge to be less than the platform's right. The edge fixture fails that test. Do not grant a landing merely because the rectangles share a boundary line.
For an upward hit at the world ceiling, clamp y to zero and stop upward velocity, keeping grounded false. Gravity then brings the player down. A head collision never provides permission for another jump.
Jump on a fresh activation
Jump is accepted only in playing state when the player is currently grounded. It sets vertical velocity to −300 and immediately clears grounded. An airborne activation is ignored. Holding the key through a landing must not trigger another jump; require release and a fresh activation.
Use scoped Space input or a native Jump button for that action. Left and Right may remain held for movement. Provide an equivalent native control path for starting and stopping either direction, clear held input on release or cancellation, and ignore commands directed at unrelated page controls.
Start begins the animation from the initial awaiting-start state. Pause and hidden-page or window-blur handling stop the loop and clear held directions. Resume uses a fresh timestamp, without simulating the time spent paused. Reset cancels the old animation session, restores (20,200), zero velocity, grounded true, and the unwon goal, and returns to awaiting Start.
Use one animation loop, cap accepted frame elapsed time at 50 ms, and feed it into the fixed-step accumulator. Clear its timestamp and accumulator on pause/resume or Reset. Session guards prevent an old callback from starting a second loop.
Finish only on overlap with the goal
Require strict overlap of the player and goal on both axes. A player at x=240, y=200 ends exactly at the goal's x=260 boundary and has not won. At x=241, y=200 there is a one-pixel horizontal overlap and full vertical overlap, so the goal condition is satisfied. Winning stops simulation and movement; Reset remains available.
This single goal can be reached on the floor. The platform is a deliberate collision exercise, not a required obstacle or an implied multi-level challenge.
Copyable platformer prompt
Create an original self-contained HTML/CSS/JavaScript side-view teaching game, readable source and acceptance-test report. Use a 320x240 world, player 20x20 top-left(20,200), full floor y220..240, solid platform x120..200/y160..170, nonsolid goal x260..280/y200..220. Start grounded, velocity0, awaiting Start. Use neutral original rectangles/labels; no copied author code, maps, styling or assets.
Declare illustrative horizontal speed 120 px/s, gravity 600 px/s^2 downward, jump velocity -300 px/s. Resolve horizontal motion against solids/bounds first, then vertical using fixed 1/120-second semi-implicit substeps: vy+=g*dt, proposed y+=vy*dt. Detect earliest face crossing and clamp; verify corners/tunneling rather than claiming small steps guarantee correctness. Bounds: player x0..300, ceiling y0, floor player y200. Recompute support after horizontal movement; walking off a platform clears grounded. Both horizontal directions held cancel.
Downward floor contact sets y200/vy0/grounded true. Platform top contact sets y140/vy0/grounded true only with strict horizontal overlap. Upward platform underside contact sets y170/vy0/grounded false. World ceiling also stops upward movement without granting grounded. Fixture x140,y138,height20 descending crosses platform top160; fixture x140,y171 ascending crosses bottom170; x100,width20 only touches platform edge and is not supported.
Fresh Jump activation is accepted only while playing and grounded, sets vy=-300 and grounded false. Held jump cannot auto-rejump on landing; airborne jump ignored. Use native Jump and scoped Space, plus native Left/Right controls and scoped held keyboard movement with release/cancellation handling. Provide Start/Pause/Resume/Reset and text grounded/status. Hidden/blur pauses and clears held input. One animation loop accepts at most 50 ms frame elapsed into fixed-step accumulator; pause/resume resets timestamp/accumulator without catch-up. Reset invalidates old callbacks/listeners, restores all initial state and awaits Start.
Win only on strict x/y player-goal overlap: x240,y200 is boundary touch, not won; x241,y200 overlaps and wins. Stop simulation/movement after win; Reset remains. No pit-loss rule, coins, lives, enemies, saved progress or realistic-physics promise. Test landing/head/edge cases, wall contact with ongoing vertical motion, support departure, fresh jump, pause/reset guards, goal boundary, focus, article width and 360 px. Report unexecuted tests as not_tested; do not promise host compatibility or completed runtime.
Keep the collision cases beside the prepared level
Retain the native source and collision results when changing the platform or movement values. If you prepare an HTML version for NOS, its supplied documentation supports HTML saving and rendering; verify the actual controls, animation, and pause behavior before calling the workspace version playable. Explore NOS with prepared HTML and the landing and underside fixtures nearby so the next change preserves the distinction between support and a head hit.