SOURCE / PINNED RELEASE
Made of little things.
Poly Dogfight
- Release
- b8511ee374e3…
- Author-recorded commit
- 5579434b9d8b…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.napplet.soy%2F/n-44f63e5422b
Archive hash verified: bcadd2430d8ea2bc…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
# Poly Dogfight game design
Gameplay, controls, flight model, and visual direction for contributors.
See the [README](../README.md) for playing and remixing.
## Design brief
- **Single-purpose job:** fly a biplane around a small planet with
momentum-based, stall-able flight. Pure free flight: no score or objectives.
- **Must-have flows:** take off from the title card; fly with keyboard, gamepad
or touch; boost; stall and recover; crash and respawn after 5 s.
- **Optional flows:** invert pitch / mute preferences (saved through the
optional `storage` domain, or set by the host through `config`).
- **Form factor:** any frame from 200×160 to full screen, touch portrait and
landscape. No declared minimum: tiny frames keep the speed/altitude chips,
the pause button and the stall warning.
- **NAP domains:** none required. `storage` and `config` are optional and
feature-detected with `runtimeHasDomain`; without them, preferences last for
the session only. No Nostr reads/publishes, so no OUTBOX or RELAY use.
- **Runtime assumptions:** WebGL (three.js, bundled into the single-file build)
and the browser Gamepad API (optional). A notice is shown when WebGL is absent.
### Visual direction
- **Character:** "toy-box planet": a bright, flat-shaded storybook world in warm
afternoon light. Chunky facets read clearly at speed and keep the polygon
budget (and frame rate) friendly.
- **Main surface:** an 82k-triangle icosphere planet displaced by seeded noise:
sandy coasts, green lowlands, ridged grey mountains with snow caps, and the
Great Rift, a striped red-rock plateau with a canyon cut along a great circle
and natural bridges across it. Obstacles are instanced low-poly kits with
vertex colours. The gradient sky dome and sun follow the player, so it is
always afternoon where you fly. Fog softens the horizon while keeping the
planet's curvature visible.
- **Interface:** cream-on-translucent-ink rounded "instrument chips" (speed,
altitude, throttle and boost bars), a rounded system font, an amber primary
action, and a red STALL label with text and icon (not colour alone). The touch
controls use the same chips: a left stick, rudder buttons, a boost button and
a vertical throttle slider.
- **Theme policy:** app-owned. Host theme data is not applied to the scene or
the HUD; both host themes leave the game unchanged.
- **Proof / cover frame:** chase camera above the Great Rift heading toward the
first natural bridge (the opening spawn). Checked at 200×160, 320×560,
900×600, 1200×665, phone portrait 390×709 and landscape 844×300.
### Beginner controls
Choose **Beginner controls** on the start/pause menu before flying. Gamepad LB
switches your own mode in that menu; A starts/resumes. Each split-screen pilot
can choose independently. The primary pilot's preference uses optional SDK storage;
without storage it lasts for the session.
Left stick up / W climbs, down / S dives, and left/right banks and pulls into a
turn. Full left/right holds a 90-degree bank and adds full elevator pull, like a
normal pilot banking and pitching up together. Vertical stick overrides that pull.
Throttle is automatic at full power; boost, fire, and camera controls remain available.
Steering uses normal pitch/roll authority without added smoothing. Horizontal
steering holds the requested bank rather than continuously rolling through it;
full banks, loops, stalls, and crashes are possible. There is no terrain protection.
Releasing the stick levels the wings using the local spherical horizon and existing flight trim. The cockpit remains fixed
to the plane and follows the full maneuver; no separate camera restrictions apply.
The HUD and menu keep the existing toy-box styling and app-owned colors.
Three ground, water, or obstacle crashes within 120 active simulation seconds offer
Beginner controls to that pilot. Shotdowns and midair collisions do not count.
Accept with A or the on-screen button; switching happens at respawn if still dead.
B declines, and the offer waits five active minutes before appearing again. The
crash window is session-only. No new required NAP domains, network, or relay use.
### Flight model (src/sim/flight.ts)
Rigid-body biplane on a sphere with gravity toward the centre, lift/drag from
angle of attack, an induced-drag term (so turns bleed speed) and propeller thrust
that fades with airspeed. Past ~15° the wing stalls: lift drops, the elevator
loses authority, the nose and a wing drop. Lowering the nose to regain airspeed
clears the stall. Arcade assists keep it friendly on a gamepad: auto-trim with
vertical-speed damping, light auto-rudder, a speed-dependent AoA limiter and a
gentle wings-level tendency. On a planet this small, cruise speed is near
orbital, so a curvature term makes "level" follow the surface like a flat world.
| Action | Keyboard | Gamepad | Touch |
| --- | --- | --- | --- |
| Pitch / roll | W S A D or arrows | left stick / d-pad | left stick |
| Rudder | Q / E | LT / RT (or right stick X) | ◁ ▷ |
| Throttle | T or Shift up, G down | RB / LB | slider |
| Boost (limited meter) | Space | A | Boost |
| Fire (unlimited) | F / Enter | B | Fire |
| Rocket (2, reloading) | R | Y | Rocket |
| Camera (chase / far / cockpit) | C | X | ◉ |
| Pause | Esc / P | Start | ❚❚ |
| Mute | M | | menu toggle |
Stick back / S = nose up (flight convention); invert pitch in the menu.
### Guns and battle damage (src/sim/guns.ts)
Two cowling guns alternate at about 11 rounds/s with unlimited ammunition.
Rounds travel at 300 m/s plus the plane's speed and fall away after 1.4 s (about
420 m). Each round is swept as a segment against hit boxes in the target's body
frame, so fast rounds can't tunnel through a wing.
- **Sight:** in the chase and far cameras, the round gun sight sits where the
gun line (rounds inherit the plane's velocity) first meets ground or an
obstacle, out to 150 m. A winged flight-path marker shows where the plane is
actually heading, cast the same way, and turns amber when that path meets
ground or an obstacle. Both stay inside the view. The cockpit keeps a fixed
centre sight.
- **Hit zones:** the left wing, right wing and tail take damage independently,
0 to 1 per part: 7% per hit on a wing, 8% on the tail. The rear fuselage
counts as tail. The engine and cockpit block is armoured: hits there spark
but do no damage.
- **Wing damage:** the holed wing loses lift, so the plane rolls toward it. At
60% damage it takes steady opposite stick to stay level.
- **Tail damage:** the nose slowly pitches down and the elevator loses
authority.
- **Destroyed part:** the plane is crippled. Control and lift collapse, a lost
wing spirals the plane down and a lost tail tumbles it. It doesn't explode
mid-air; it falls until it hits the ground ("Shot down!") and respawns
repaired.
- **Visible damage, no HUD:** bullet holes appear where rounds struck. Damaged
parts scorch darker and trail smoke, light at first and black once destroyed.
A failing wing sags at the root; a destroyed one loses its outer panel in a
burst of shards. A destroyed tail droops and loses its fin.
### Local multiplayer (src/players.ts)
Up to four pilots share one screen. Each standard-mapped controller joins as
soon as the browser exposes it, including controllers connected during play.
Some browsers require a button press before revealing a device; there is no
additional in-game activation step. Pilot 1 also answers to keyboard and touch.
When a controller disconnects, its pilot leaves. If pilot 1's pad leaves,
pilot 1 stays on the keyboard, and the next connected pad takes that seat.
Focus loss neutralizes input but keeps pilot assignments. Temporary Gamepad API
errors also preserve seats and release controls until access recovers. Click
inside the game to restore focus, then release and press A to resume. Controllers
without a standard mapping need an explicit mapping; their layout is not guessed.
- **Split screen:** two pilots split side by side or stacked, whichever keeps
each view closer to a wide aspect. Three or four pilots use a 2×2 grid. With
three, the spare cell shows a slow overview of the planet with every pilot's
beacon.
- **Views:** each pilot has their own follow camera (X cycles it), HUD, stall
warning and crash overlay. HUDs size themselves with container queries, so
small split views compact on their own. Narrow views widen the field of view
so the horizontal view stays around 80°.
- **Liveries:** red, blue, yellow and purple. Each pilot keeps theirs while they
stay in, and a floating beacon in that colour marks other pilots at range.
- **Mid-air collisions:** any two live planes within 2 × 2.3 m crash together
("Mid-air collision!") and respawn after the usual 5 s. New pilots join in
formation off pilot 1's wing; respawns pick a clear spot at least 80 m from
everyone.
- Any pilot's Start pauses for everyone. Audio mixes the live planes.
