Separate the difficulty controls

Orbit Dodge asks the player to steer horizontally while asteroids fall toward the ship. Two settings determine much of its feel: how fast each asteroid moves, and how often a new one appears. Increasing both at once makes it difficult to explain why a version feels unfair. Change one, play several rounds, and keep notes before touching the other.

The opening obstacle speed is 140 pixels per second. The spawn interval begins near 0.85 seconds and gradually shrinks. New rocks inherit the current speed when they are created; existing rocks keep their assigned speed. This matters when evaluating a sudden difficulty jump: the screen can contain rocks from several different moments in the progression.

Read the progression

// Existing values inside update(dt):
spawn = Math.max(.25, .85 - elapsed * .008);
const speed = 140 + Math.min(elapsed * 3, 180);

The first expression is the interval used when creating the next rock. At 30 seconds it is 0.61 seconds; at 60 seconds it is 0.37 seconds. It eventually stops shrinking at 0.25. The speed expression reaches its cap of 320 pixels per second at 60 seconds. Those are separate caps, reached at different times.

The excerpt names the speed expression for clarity. In the original file it appears directly as the speed property inside rocks.push. Change it there when experimenting, rather than adding an unused variable elsewhere.

Build an easier opening

For a gentler version, change the starting interval from .85 to 1.1 and the shrink rate from .008 to .006. Keep the floor at .25 for the first test. The opening becomes less crowded while the movement speed remains familiar. Test several rounds because a random arrangement can make one run unusually easy or hard.

Next, restore the interval and reduce the speed growth from elapsed * 3 to elapsed * 2. This gives players longer to read later obstacles. Decide which experiment better supports your intended round length. Avoid judging a survival game from only the first ten seconds.

Keep motion independent of frame rate

The update function receives elapsed seconds as dt. A rock moves with rock.y += rock.speed * dt. The ship uses the same pattern for keyboard movement, with a speed of 310 pixels per second. Replacing those statements with a fixed number of pixels each frame would make play depend on how often the browser draws.

Rocks are removed after passing below the board. That cleanup prevents the array from keeping every obstacle ever created. When adding particles or pickups, give those objects a similar removal condition. A long survival run should not accumulate invisible work forever.

Make collisions feel understandable

The drawn ship is triangular, but the starter uses a smaller rectangular collision region. The inset gives some forgiveness around its pointed outline. A rock also uses its bounding box rather than its exact polygon. These are deliberate simplifications; changing the artwork does not automatically update those checks.

  • Steer to both edges with a keyboard and a pointer.
  • Try a near miss along each side of the ship.
  • Play long enough to reach the chosen speed and interval caps.
  • Restart after a long run and verify the board and survival time clear.

Random spawning does not guarantee an escape route. A more ambitious extension can reserve a horizontal gap in each wave. Decide the minimum gap from the ship width and movement speed, then generate rocks around it. That changes the generator from independent random rocks into a system you can reason about and test.

Put this lesson into practice: download Orbit Dodge or play the original before making your changes.

Keep building