Pilot Matter Docs Play

Cheatsheet

Everything on one page, for a reader who has already read the rest. No teaching, no explanation: the keys, the options, the elements, and the calls.

Keys

KeyAction
W ↑Pitch up
S ↓Pitch down
A ←Roll left
D →Roll right
Q / EYaw left / right
Shift / CtrlThrottle up / down
SpaceLevel off: vertical speed to zero, nose eased to level over 0.6s
CCycle camera: chase, cockpit, orbit
PPause, and the pause menu
OSettings panel, and close it
LElement editor, and close it
MMute the engine and wind
HCollapse the control list
TabShow or hide the instruments
RReset to the start condition
F2Photo: download the view as a PNG
Esc BackspaceBack out of a panel
Enter SpaceChoose the menu entry under the cursor

Without a keyboard

Drawn on a machine that takes touches and has no pointer that can hover: a phone or a tablet, never a laptop with a touchscreen. Menus were always worked by pointer, so only the flight controls are added.

PadWorksCluster
PITCH + / PITCH -Nose up / downLeft
ROLL L / ROLL RWing down either wayLeft
THR + / THR -The throttle leverRight
YAW L / YAW RNose left / rightRight

Tilt is what those machines open in, and it takes the four attitude pads off the glass. Whatever angle the device is held at when a flight starts is level for it, and a reset levels it again. Seven degrees either side of level is nothing. Refused, or on a device with no gyroscope, the pads stay and the flight is flown from them: a sensor that reports no angles at all is no reading rather than a device held level. The pads take the bottom corners, so the attitude indicator moves to the top left of the screen, the readouts drop below it and the chart it now shares that top with, and the control list comes off.

A pad is 44 pixels square, which is both the smallest a control under a thumb should be and what lets two clusters and the padding either side of them fit the 320 pixel screen a phone is narrowest at.

The pads take a fixed band off the bottom whatever the screen, so a short one has less left between them and the instruments above. Below 541 pixels of height - which is every phone held sideways - the readouts are drawn compact and drop THROTTLE and CAMERA, the two you are not flying on - and on a screen 640 pixels or narrower that starts at 657 instead, because a narrow one has the objective card under the readouts as well as the pads under both. Shorter again and they move into the band between the two clusters of pads, with the AUDIO MUTED line going up beside the attitude indicator rather than staying under it. The objective card, which is what carries the breakdown of a landing, is lifted clear of the pads on any screen narrow enough for them to cross it; on a screen short and wide enough for the readouts to be in that middle band it is hung from the top of the same lane instead, because the band is where the readouts now are. The two are placed against each other rather than each against the pads, so neither is drawn over the other at any size a phone comes in.

Bounded to the room the readouts leave, the card holds what it says in flight and not a landing read off it, which is five lines more. So the readouts stand down for as long as a breakdown is up and the card takes the column - on the screens the two are in one column on. An aircraft stopped on a strip reads zero knots, zero feet a minute and the strip's own elevation, which is everything the breakdown says and nothing it does not, so of the two wanting that column it is the stack that gives way. From 680 pixels of width, on a screen tall enough to keep the readouts in their own corner, the card's left edge is past their right edge and the two never meet, so there the stack stays up and the card is bounded to the screen rather than to anything above it. A screen short enough to have put the readouts in the middle lane is the exception at any width, because the card is hung in that same lane and the two are back in one column.

On the shortest screen a browser leaves, 320 by 460, there are 108 pixels between the chart and the pads for both of them, so the stack keeps the airspeed and the altitude and the card keeps its stage and its objective. What the card has no room for comes off rather than being cut through: the bound is written in the card's own rows, so every line on it is either read whole or not drawn at all.

Flight numbers

QuantityValue
HUD scale2 knots per world unit/s
Stall speed40 units/s (80 knots)
Cruise speed120 units/s
Full throttle200 units/s
Throttle sweep2 seconds, 0 to 100%
Terrain crashFaster than 30 units/s down
Runway crashFaster than 48 units/s down
Runway landingUnder 18 units/s, wings 11 deg, nose 15 deg, heading 25 deg
Landing aimA fifth of the strip's length past the threshold
Landing reachA third of its length either side of that is worth nothing
Landing rolloutEnds at 1 unit/s, or after 10 seconds
Ground clearance5 units
Crash countdown2.5 seconds, then reset
Low altitude warningWithin 200 ft of the ground below
Terrain tile16000 x 16000 units, 200 x 200 segments
Ground drawn to12000 units past the aircraft
fBm octaves7

The start state

FieldRangeStepDefault
START AIRSPEED0 to 200 knots580 knots
START ALTITUDE0 to 8000 ft101390 ft
START CLIMB-2000 to +3000 ft/min20+1260 ft/min
START HEADING000 to 355 deg5000
START THROTTLE0 to 100%520%
START CAMERAchase, cockpit, orbit-CHASE

Radio group: START OFF FLYING (default) or RUNWAY TAKEOFF. Checkbox: RUNWAY, forced on and disabled under RUNWAY TAKEOFF. Pitch is derived, never set.

Options

OptionSteps through
CONTROL SENSITIVITY50% to 200%
ORBIT SWEEP6°/S to 36°/S, default 12°/S
FOG DENSITYCLEAR to THICK
AIRSPEED INKNOTS, MPH
ALTITUDE INFEET, METERS

Everything above is stored in localStorage and reopens with the next session.

Worlds

EnvironmentIdIs
HIGHLANDShighlandsPeaks over fBm ground, snow above 300. The default
RIVER BASINriver-basinA river the width of the world, low forested country
CANYON COUNTRYcanyon-countryBranching canyons in a high plateau, no standing water
DUNE SEAdune-seaDunes, outcrops, one desert river, palm groves
LAKESIDElakesideA town on a lake, forested hills, snow peaks

Three more are built for the modes and kept out of the panel: OPEN COUNTRY, LOOP VALLEY and BACK COUNTRY.

Elements

ElementConfigured through
MountainCount, height, radius, girth
CanyonDepth, width, steepness, branches, wander
DesertDune height, crest spacing, coverage, sand gradient
GrassGreen gradient, height band
SandBrown gradient, height band
Water bodyWater line, blue gradient by depth, basin pull
RiverBlue gradient, windiness, width, depth
ForestTree height, density, grove size, grove count, canopy, band
TownBlock size, block count, building height, extent, gradients
SnowSnow line, coverage, slope limit, white gradient
RunwayLength, width, heading, height band, apron, gradients

Pipeline order: landforms, ground cover, water, cuts and built things, snow, runway last.

Element editor

L opens the world being flown as the elements it was assembled from. Enter opens an element onto its ranges; A/D step the range under the cursor.

RowReads
SpanTwo rows, MIN and MAX, neither able to cross the other
ScalarOne row, one number
GradientTwo rows, LIGHT and DARK, as a percentage of the preset's colour

One press moves a fiftieth of what the range allows, rounded to 1, 2, or 5 times a power of ten. RESTORE THE PRESET puts every range back. Edits belong to the world they were made on and are not stored between sessions. Not available while a game mode is being played: a mode brings its own ground.

Game modes

ModeStagesObjective
RUNWAY LANDING4Land on the strip. Harder: further out, off the line, shorter strip, higher country, less help
FLYING THROUGH LOOPS4Fly the gates in order. Harder: more gates, tighter, closer, bending more, laid further over
DEAD STICK4Glide to the strip, engine out. Harder: less height, further out, off the line, less help
CARGO RUN3Land at each strip in turn. Harder: strips further apart, less budget, a third stop
SEARCH AND RESCUE3Find the marker and set down beside it. Harder: further out, rougher ground, a tighter ring

Gate colours: green is the one the course is waiting on, amber is still to come, dim is behind you. A crash restarts the stage.

With no engine - a dead stick, or a cargo run out of budget - the nose sets the airspeed instead of the lever: nose down buys speed with height, nose up spends that speed for a slower descent, and a level glide reaches about twelve times the height it spends. A cargo run writes what is left of its budget where the time to beat goes, and only an open throttle spends it.

Gates are laid over from the second stage on, and closed up one way across as they are - a round hoop is the same hoop at every angle, so a gate that has to be flown at the angle it was laid at is wider along its span than it is tall.

StageGate height against its widthLaid over up to
THREE GATESRoundUpright
FIVE GATES0.800.40 rad
SEVEN GATES0.660.70 rad
NINE GATES0.551.00 rad

Approach guidance is withdrawn the same way: an extended centreline of eight marks running 3200 units back down the approach and a bar across the threshold on stage one, the bar alone on stage two, and nothing from stage three on. Both are laid off the threshold the stage opens you out from.

The whole course is drawn on the minimap the moment the stage is laid out, in the same three colours. A gate past the edge of the square the chart covers is held hollow at that edge.

ReadingWhereSays
TIME / BESTObjective cardThe stage clock, and your best for this stage of this mode
↑ LOOP 3 · 045° · 4200 ftObjective cardThe gate the course is waiting on, while it is more than 35 degrees off the nose
LOOP 3 MISSED · COME ROUNDObjective cardYou crossed the gate's plane outside the hoop, going the way the course runs
LANDING · 85Objective cardWhat a landing came to, shown once the aircraft has stopped
DOWN THE STRIP OFF THE CENTRELINE SINK RATE OFF THE STRIPObjective cardThe four readings behind that score, each marked against its own limit

Best times are kept per stage per mode in localStorage. The clock runs from the moment a stage is laid out to the moment its objective is met, and a stage restarted is timed from nothing.

API

import { createPilot, createEnvironment, createTiledEnvironment } from './js/api/index.js';

createPilot({ scene, camera, aircraft, anchor, terrain, keymap, flight })
    .update(dt)          // returns { airspeed, altitude, verticalSpeed, heading, throttle }

createEnvironment({ environment, runway })
    .group               // one detachable Object3D
    .applyDepth(scene)   // the sky and the fog, without the ground
    .attach(aircraft)    // throws with every gap in the contract
    .register(mesh, { x, z })
    .runways[0]
    .setEnvironment(id)
    .setDaylight(phase)  // 0 to 1
    .updateWater(dt, phase)

createTiledEnvironment({ environment, tiles, size })
    .sampleHeight(x, z)  // answered by whichever square the point is over
import { validateAircraftContract, TELEMETRY_FIELDS, API_VERSION } from './js/api/contract.js';

validateAircraftContract(aircraft)   // every gap at once, or []

Commands

CommandDoes
npm run serveStatic server on http://localhost:8080
npm run serve -- 3000Another port, and PORT works too
npm testThe whole suite, no dependencies
node --testThe same thing without npm
npm run docs:apiBuilds the site's API reference page from docs/api.md

Photo names

pilot-matter-YYYYMMDD-HHMMSS.png, for example pilot-matter-20260824-050709.png.