River Raid

Activision · Atari 2600 · 1982

Carol Shaw's endless river, the same every time you fly it. The cartridge rebuilt from the original ROM, one instruction at a time: every bank, bridge, fuel depot and missile behaves as the real machine plays them.

River Raid's opening screen: the river between green banks, a helicopter, a fuel depot and a ship ahead, a house on the bank, the jet on the road and the status bar with score, fuel gauge and lives.
Press Play, then push up or fire to take off
←→ steer · ↑↓ speed · Space fire · Enter Game Reset

How to play

Fly up the river and shoot what gets in your way. Fuel drains the whole time: fly over a depot to fill the tank before the gauge reaches E. Touch a bank, a bridge or anything on the water and the jet goes down.

At the controls

  • Steer←→ or AD
  • Speed up↑ or W
  • Slow down↓ or S
  • FireSpace / Z
  • Game ResetEnter
  • Pause · MuteP · M

On a gamepad

  • The joystickstick or D-pad
  • FireA · B · X · Y
  • Game ResetStart
  • PauseBack

The Atari joystick has one button and eight directions, so every face button fires. Sound starts after your first click or key press; that is the browser's rule, not the cartridge's.

Fuel

The gauge under the river runs from E to F. It drains every frame you fly, faster than it looks, and a warning tone starts once it drops below a quarter.

Fly over a depot and the tank refills while you stay on it. Shoot a depot instead and it is worth 80 points, but the fuel is gone.

Lives

You start with three jets and earn another every 10,000 points, up to nine.

After a crash the river rewinds to the start of the section and replays it exactly, because the cartridge saved its random numbers there.

The river is never stored. The whole game fits in a 4,096-byte cartridge, so each 32-line block of river is built just before it scrolls into view, from a 16-bit random number generator that starts from the same seed, $A814, every game. That is why the river never ends, and why it is the same river every time you play.

The river

Thirty-two blocks make a section, and every section ends at a road and a bridge. Odd sections run straight; even ones wind and split around islands. Further up the river there are more enemies and fewer depots.

Tanker

Tanker 30

Starts moving at random and patrols, turning back when it touches a bank. Where the river is too narrow for a ship, the cartridge puts a helicopter there instead.

Helicopter

Helicopter 60

Its rotor is two shapes swapped every other frame. Patrols like the tanker.

Jet fighter

Jet 100

Appears from section three. Crosses the whole screen without stopping and wraps around at the edge.

Fuel depot

Fuel depot 80

Refuels you while you fly over it, or scores if you shoot it.

The road and bridge at the end of a section

Bridge 500

Shoot it to open the next section. Flying into it costs a life.

House and tree

House 0

Scenery on the banks and islands, and the only thing on the river worth nothing.

Built from the ROM

This is not an emulator. Every routine of the cartridge was rewritten in JavaScript one 6502 instruction at a time, working on the same 128 bytes of RAM at the same addresses. To prove it, a small 6502 core runs the real ROM beside the port and every frame is compared, byte for byte.

0differences from the cartridge in 45,000 compared frames of play
300,000river-generator states checked, sections 1 to 48
31,840pixels of the opening frame, identical to a Stella capture
4,096bytes in the whole cartridge, code and graphics
Read in the ROM. The code holds a few things the manual never mentions:
  • There is no score in memory. The six digits are pointers to the digit graphics, and points are added to the pointers.
  • Collisions come only from the TIA's hardware latches, read once per river block. There is no collision arithmetic at all.
  • Flying into a ship, helicopter or jet still scores it as you go down.
  • Touching a house runs the same code as touching a fuel depot.
  • When a new block has only just scrolled in, a collision at the very top is filed under a seventh block that does not exist, and the flag lands in the first object's position byte.
The port keeps all of it, because that is the game.
Developer pages