# Daybay — a firefight in a sunlit concrete bay, in the browser

A body-camera shooter that runs in a tab. Three opponents, a carbine, an Uzi, a
service pistol and a pump shotgun; a room lit by a baked bounce and a live sun that
casts every slat shadow this frame; a layered, positioned shot sound with the room's
own decay. **A Plate Labs showcase.** Not a finished game, not a claim of parity with
any commercial title, and not a tutorial: it is real source you can run, read and
adapt.

## Run it

1. Serve the folder that contains `labs/` over HTTP. ES modules do not load from
   `file://`. With Python 3 installed:

   ```
   python -m http.server 8080
   ```

2. Open `http://localhost:8080/labs/daybay/` in a desktop browser with WebGL 2
   (current Chrome, Edge or Firefox). First load fetches about 95 MB of assets; the
   card shows progress and reports "Ready".

3. Press **Play · Combat** (or Enter). The click that starts the game also starts the
   sound and takes the mouse. **Practice range** (or P) is the same room without
   opponents, with steel plates to shoot.

`labs/shared/` must stay next to `labs/daybay/`: it holds three.js, the interface
stylesheet and the font.

**Keyboard and mouse only.** Walking and firing need a pointer lock, which touch
devices do not grant. On a phone you can look around the bay, and that is all.

## Controls

| Key | Does |
| --- | --- |
| `W A S D` | walk · `Shift` slow · `C` crouch |
| mouse | look. The weapon leads and the body follows: a small flick moves the sights inside a cone without swinging the room |
| left button | fire, one shot per press (the Uzi holds) |
| right button | aim down the sights. **Hip fire is loose on purpose**; aimed fire is where the rounds go |
| `R` | reload · `1` `2` `3` `4` weapon slots |
| `H` | pistol: one hand / two hands |
| `F` | inspect the weapon |
| `Space` | vault a sill, sandbags or low cover |
| `Esc` | pause menu: resume, restart the encounter, controls, settings, main menu |
| `[` `]` | volume · `M` room tail on/off · `N` microphone stage on/off |
| `V` | lens on/off · `L` lighting breakdown · `P` pause · `K` back to the start |

## What happens

You start deep in the bay; two opponents are already in the building and a third
steps up when a slot opens. They hear your shots, take cover, peek, flank and shoot
back. A leg hit costs an opponent his footing without his life; he fights on from the
floor and has to be finished. Two body hits from them put you down. Standing still at
the spawn, you are hit at about six seconds and dead at about seven — move, aim, and
use the cover.

## Query flags

| Flag | |
| --- | --- |
| `?rifle=old`, `?arms=v4` | the previous CC0 carbine / the previous arms, for comparison (the defaults are our own models) |
| `?map=bay` | the original loading bay instead of Block 17 (the default combat map since 2026-09-23: a shelled five-storey apartment block, its courtyard and a shop, eight enemy posts, six men at a time) |
| `?quality=low`, `medium`, `high` | force a quality tier (shadow map, pixel ratio, anisotropy). The default is picked from the device |
| `?lens=0` | clean view: same render, the body-camera lens terms all at zero |
| `?chest=0` | camera locked to the aim, no chest trail |
| `?rig=0` | no stride bob or roll |
| `?fov=70` | vertical field of view after the lens |
| `?hud=0` | no interface over the frame, for captures |
| `?bake=0` | the arena on its blockout lighting (a hemisphere) instead of the baked bounce — see `ADAPT.md` |
| `?shafts=0`, `?shafts=0.05` | sun shafts off, or their scattering per metre (default 0.035; off on the low tier) |
| `?grime=0` | the arena without its graffiti, grime and floor debris, for a before/after |
| `?res=0.8` | render the 3D scene at a fixed scale of the display (the default adapts it to hold the frame rate; `?res=1` turns that off) |
| `?yard=0` | the closed room: no loading bay and no yard outside it |
| `?kit=0` | the arena's cover as the original plastered boxes instead of the machine, barriers, bench, block load, cabinet and racking |
| `?exposure=locked` | the fixed exposure the room was lit against, instead of the slowly adapting auto exposure |
| `?faces=1` | a mosaic over the opponents' faces, as published bodycam footage does (off by default) |
| `?look=lightmap` / `sun` / `flat` | lighting breakdown |
| `?mic=0`, `?acoustics=0` | bypass the microphone stage / the position-driven room balance |
| `?handling=a`, `b`, `c` | three recoil variants for comparison |
| `?auto=1` | skip the start card (screenshot harness) |
| `?vol=50` | starting volume, percent |
| `?stats=1` | frame time readout |
| `?pos=x,y,z&rot=yaw,pitch` | place the camera |

`?review=experience` and `?review=combat` refer to review harnesses that are not in
this download.

## What is in the folder

| File | Owns |
| --- | --- |
| `main.js` | boot, renderer, input wiring, HUD, mode switching, the `window.daybay` surface used by tests |
| `arena-layout.js` | **the floor plan as data**: solids, walls, openings, opponent posts, spawn, roof slots, and the pure checks that say the plan is walkable |
| `arena.js`, `arena-finish.js`, `arena-lightmap.js`, `arena-ambient.js` | builds the arena from the plan, its materials, the baked bounce and the ambient handed to moving things |
| `sunlight.js` | the live sun and its fixed shadow camera; the gradient sky |
| `bay.js`, `range.js` | the practice corridor and its steel targets |
| `player.js`, `view.js`, `freeaim.js`, `player-state.js` | movement and collision, the chest-trail camera, free aim, the player's health and downed rules |
| `lens.js`, `postfx-aa.js` | the body-camera pass: overscan, barrel distortion, chromatic separation, the camera's sharpening, near-field defocus, slow auto exposure metered on the GPU, face mosaic, sun shafts marched against the shadow map, vignette, ACES |
| `yard.js` | the loading bay in the far wall and the walled yard behind it: containers, a burnt car, barriers, scaffold, weeds, trees, distant blocks |
| `arena-kit.js` | the cover made into things, in the same footprints; collision still reads the plan |
| `merge-static.js` | bakes static dressing into one mesh per material; also the room's wall section, decals and finish once the bake is on |
| `attachments.js` | the carbine's red-dot optic (the reticle is angle-based, so it sits at infinity) and weapon light |
| `grime.js` | graffiti, leak stains, base dirt, floor dirt and debris for the arena, all drawn on canvases at load and placed by rays |
| `blood.js` | spatter behind an exit wound and a pool under a body at rest |
| `weapons.js` | **the weapon table**: one data entry per slot — file, rig anchors, poses, fire mode, recoil, sound profile, reload |
| `weapon.js`, `arms.js`, `hero-viewmodel.js`, `gunfinish.js`, `shot-fx.js`, `decals.js` | the shared rig, the hands, finishes, muzzle flash, casings, marks |
| `enemy-config.js` | **every number the opponent runs on** |
| `enemy.js`, `enemy-ai.js`, `enemy-state.mjs`, `enemy-animation.js`, `enemy-ragdoll.js`, `enemy-limb-physics.js`, `enemy-body-response.js`, `enemy-hitboxes.js`, `enemy-navigation.mjs`, `squad.js` | one opponent, three of them, and what happens to a body. The ragdoll has hinged knees and elbows, a three-part spine, a short loss of muscle tone after death, and a twist limit on a swing-twist split |
| `audio.js`, `player-voice.js`, `voice-bag.js` | the shot layers, impacts, room acoustics, microphone stage, voices |
| `damage.js`, `damage-math.js`, `composure.js` | what being hit does to the camera |
| `ui.js`, `style.css`, `index.html` | menus, start card, HUD |
| `assets/` | meshes, textures, lightmaps, sound — each folder with its own `LICENSE.md` |
| `vendor/` | cannon-es, GLTFLoader, BufferGeometryUtils |
| `check-layout.mjs` | run with Node after editing the plan: proves the posts are still reachable |

## Adapting it

`ADAPT.md` walks the four changes worth making — the arena, the opponents, the
weapons and the sound — and says plainly which are data edits and which need a line of
code. `LICENSES.md` is the summary of what you may redistribute and the one credit you
must keep.

## Limits, stated

- Ragdolls are cannon-es bodies driven from the skeleton; they are convincing, not
  simulated to the joint. Bodies do not collide with themselves.
- Rounds go through bodies and thin materials (board, paper) by measured thickness and
  glance off hard surfaces at grazing angles. Nothing penetrates concrete, a drum or a
  solid crate, and the opponents' own rounds are still single traces.
- Graffiti names are generated syllables. Every texture in `grime.js` and `blood.js` is
  drawn in code at load; no image files were added.
- The arms are an adapted CC0 base, not motion-captured hands.
- Opponents walk straight lines between their anchors; there is no path-finding, which
  is why the plan's checks matter.
- Frame times were measured on one machine (RTX 3070, 1600x900, median 6.9 ms). Nothing
  is claimed for laptops or integrated graphics.
- The sound has been measured and played; it was not auditioned by a sound engineer.
