TinyGames · how it works

Divot

Six holes of putting. Pull back from the ball and let go.

Open index.html. No build step, no dependencies, no images.

divot/
  index.html      markup
  style.css       the frame around the canvas
  js/engine.js    copied unchanged from Overdrive - the eighteenth game on it
  js/putt.js      the course, the bounces and the roll
                  - no pixels, no engine, no DOM
  js/divot.js     the playable layer
  test-putt.js    node test-putt.js

Two properties decide this game, and neither is the putting

The ball never escapes

A hard shot into a corner is the classic way out of a walled course. Test collision at a point once a frame and a fast ball steps clean over a wall; test only against the flat of each wall and it slips between two of them at the join. Both faults are intermittent, both look like the ball glitching through, and neither is ever found by playing.

So the wall is grown by the ball's radius — turning the ball into a point and the wall into a capsule — and both the flat and the rounded ends are tested. The ends are what make a corner a corner rather than a gap.

$ node test-putt.js

  firing at the walls:
    Straight    700 shots at full power   none escaped
    Dogleg      700 shots at full power   none escaped
    Pillar      700 shots at full power   none escaped
    Bottleneck  700 shots at full power   none escaped
    Chicane     700 shots at full power   none escaped
    Island      700 shots at full power   none escaped

31 passed, 0 failed

4,200 shots at maximum power from random positions in every direction, plus 500 more aimed directly at a corner. None got out.

The resolver also loops within a frame: after a bounce there is still time left, so it carries on with the remainder. Resolving one collision per frame is what lets a ball hit into a corner sink through the join, because the second wall never gets a turn.

The ball always stops

Multiplying the speed by 0.98 every frame never reaches zero. It reaches 1e-8 and keeps creeping, and the player waits for a turn that will not end. The test demonstrates it: fifty simulated seconds of exponential damping still leaves measurable speed.

Friction here subtracts a fixed amount per second and snaps to a dead stop below a threshold. Over 900 full-power shots across all six holes, every one came to rest, and the slowest took under fifteen seconds.


The nudge that stops a ball sticking to a wall

After a bounce the ball is moved a hundredth of a unit clear along the normal. Without it the ball can finish a frame a hair inside the wall, and the next frame's sweep reports a hit at t = 0 — forever. The ball glues itself to the wall and never leaves.


Pull back to shoot

The shot vector runs from your finger towards where the drag began, so dragging left sends the ball right, the way a putter is drawn back.

The first version had it the other way round and the ball simply followed the finger. That is the commonest way to make an aiming game feel inverted, and it is worth stating that the code and its own comment disagreed — the comment described a slingshot and the arithmetic did the opposite.

A power bar is drawn on the ball, not in the corner, so it is read without looking away from the shot. It turns red near maximum.


The hole

A ball only drops if it is over the hole and slow enough. One crossing at speed rims out, which is what makes a long putt a gamble rather than a formality. The tests pin the boundary, including that the limit itself still counts as holed.

Every hole is also proved reachable: the suite searches aim and power from the tee and confirms all six can be holed in one. A hole nobody can finish is the putting equivalent of an unsolvable level.


Controls

Drag back from the ball, release play a shot
N skip to the next hole
R new round

Things deliberately left undone