Cheatsheet

Every key, flag, script and figure on one page, for someone who has already read the documentation. The full text lives on the site.

Controls

InputAction
W A S D or arrowsSteer
F or Shift (browser)Boost
SpaceFire pulse cannon
Q / EBarrel roll
PPause / resume
MMute toggle
C (browser)CRT scanline overlay
V (browser, title screen)Open a two-pilot split-screen round
EnterLaunch / relaunch
EscMenu / quit
Ctrl+CQuit immediately (terminal)
Thumbstick / FIRE / BOOSTTouch: steer / fire or confirm / boost
Left stick / A / BGamepad: steer / fire or confirm / boost
TriggersGamepad: barrel roll, left and right

On the name entry screen the same keys spell a name instead of steering: up and down walk the character under the cursor through A-Z, 0-9 and a space, left and right move between the three slots, and Enter files the score. Esc files it too, under whatever name is showing, because the score was earned before the screen came up.

Press-not-hold keys are P, M, Esc, Q and E. They fire once per press and take a short deadzone after a hold.

A gamepad has to report the standard layout before it drives anything, and its stick takes the same dead zone and eight sectors the on-screen thumbstick does. Any pad plugged in can fly the run, and a second one alongside does no harm. In a split-screen round the whole pad flies pilot 1, stick and trigger both.

CLI

space-rider                          Start in normal mode
space-rider --debug                  Open the debug scenario menu
space-rider --mode <mode>            Start a specific debug mode directly
space-rider --help                   Show help

Browser URL parameters

index.html?debug                     Open the debug scenario menu
index.html?mode=chaos                Start a specific debug mode directly
index.html?mode=mines                Mine Field
index.html?mode=orbs                 Orb Harvest
index.html?mode=obstacleCollision    Collision Course
index.html?mode=mineCollision        Mine Sweeper
index.html?versus                    Open a split-screen round

Split screen (browser)

Two pilots at one keyboard, each flying their own run in half the grid. V on the title screen opens a round, or index.html?versus goes straight into one.

ControlPilot 1Pilot 2
SteerW A S DI J K L
FireSpaceEnter
BoostF;
Barrel rollQ / EU / O

P pauses the round and M mutes it from either hand, and the mute carries into a round when one opens and into its rematch. Esc leaves, and Enter starts a rematch once both pilots are down, on a window still wide enough to open one. The arrow keys and Shift steer nobody in a round, because two players cannot agree whose they are.

Both pilots launch into the same tunnel, from one seed drawn when the round opens, so the opening field is the same in both halves. The fields drift apart from there, because each pilot reaches a given depth at their own speed. Scores are separate and the higher one takes the round; a round never reaches the leaderboard, since the table has one name to a row.

A round needs 45 columns a half, so 91 columns of grid. The key is only offered on a window wide enough for one. A round dragged narrower than that keeps flying, with each half shortening its score, distance and shield readings to whatever fits, and the rematch waits for the window to come back.

Debug modes

#ModeFlag / paramWhat it isolates
1Mine FieldminesPure evasion
2Orb HarvestorbsCollection
3Collision CourseobstacleCollisionObstacle impacts
4Mine SweepermineCollisionMine collisions
5Chaos ProtocolchaosEverything, permanent boost, auto-fire

Debug runs never record a best score.

Numbers

ThingValue
Obstacle collision25 shield
Mine collision35 shield
Mine hit points5 pulse hits
Orb pickup10 shield, 500 points
Obstacle destroyed200 points
Mine destroyed500 points
Powerup drop chanceabout 1 in 3 mines shot down
Shield Regen +25 shield
Rapid Fire !10 seconds, 3x cadence
Slow Motion ~5 seconds, half world speed
Combo window2 seconds, caps at COMBO x8
Barrel roll0.5 seconds invulnerable, then a cooldown
Warp stepevery 60 seconds, banner for 2 seconds
Asteroid fieldevery 150 seconds of tunnel, open for 15, 70 rocks
Boss encounterevery 180 seconds of flying, 3 second warning
Boss hit points20 pulse hits
Boss approach200 units in, 14 units a second
Boss return firea shot every 1.5 seconds, 12 shield each
Boss collision50 shield, and it pays nothing
Boss destroyed5000 points, 3 pickups, one of each kind
Grid floor60 columns by 20 rows
Split-screen floor45 columns a half, 91 of grid
Leaderboardtop 10, three characters a name
Replay ghost10 path samples a second, 10 minutes of run
Detail ladder40 / 24 / 12 stars, full / 60% / 30% of a burst
Detail window30 frames; down under 24 fps, back up at 27.3 fps

Scripts

npm run build      # Compile TypeScript to out/
npm run start      # Run the compiled game
npm run dev        # Build and run in one step
npm run debug      # Build and run in debug mode
npm run watch      # Watch mode for development
npm test           # Build, then run the test suite
npm run probe      # Build, then run a measurement probe

Probes

npm run probe with no arguments lists what there is to measure, and which flags each probe takes. The probes that fly a shot take a grid and a build:

npm run probe -- seen-versus-kill
npm run probe -- column --grid 205x50 --build browser
npm run probe -- free-flight --passes 2
npm run probe -- versus-half --count 300   # seconds of game time to fly out to
npm run probe -- overlay-anchor    # no grid, no build: it walks viewports
npm run probe -- glyph-tuning      # no flags: it walks the font size range

A flag the named probe does not read is refused rather than discarded, so npm run probe -- overlay-anchor --grid 80x24 exits non-zero instead of reporting a figure from a walk that never used the grid.

The table the rig checks against lives in test/probes/probes.mjs, and test/probes.test.mjs reads it against the probes themselves: every probe takes exactly the flags listed for it, so a probe that stops reading an argument fails the suite rather than going on accepting the flag in silence. Every probe that calls the shared engagement is run there too, on one grid, one build and the smallest --count and --passes the rig takes, because reading a probe's argument list never reaches the calls the probe makes. What --count 1 narrows to depends on the probe: one placement for column, frame-rate and seen-versus-kill, a single frame for free-flight and drop-clip, which read the flag as frames, and a one-second flight for versus-half, which reads it as the seconds of game time to fly out to.

A probe reports; it never asserts. Every walk is seeded, so the same command gives the same numbers every time.

versus-half is the one probe that reports a width. The split screen gives each pilot half the grid and the half carries the whole HUD, so the probe flies a seeded run out to twenty minutes, reads the score and the distance off it at six marks, and sweeps half widths for the narrowest at which SCORE, DIST and SHIELD stop overlapping. 45 columns is what holds every mark, which is the floor a round opens on.

glyph-tuning is the one probe that measures nothing: it rebuilds the figures the browser build's glyph centring quotes about itself, out of Courier New's 1229/2048 em advance and the page's own glyphInset. It also lists, at the foot of its output, the four figures in that tuning that needed a rasterizer and so are not rebuilt - each with the instrument that read it and what it concluded, which is the method published beside the number.

Test grids

The pulse cannon checks run at three sizes, because the hit test is not size-neutral:

GridWhy
80x24What a terminal opens at
60x20The floor a small browser window is clamped to
205x50A full-screen window at the default font

Terminal requirements

Browser-only features

High score and leaderboard persistence, sound, the CRT overlay (C), light mode, touch controls, the two-pilot split screen (V), and a canvas-offset screen shake. The terminal build jolts the play area by a whole character column instead, and keeps a best score and a table for the session only.

The replay ghost and the detail ladder are in both builds and neither is stored: a ghost lasts as long as the session, and the ladder survives the run that measured it and is re-measured a second into every session.