# Native Amiga browser preview

## Current player preview and release status

Open `?play` for the native game. The packaged local browser preview selects this mode automatically. Portrait touch controls, visible Pause/Sound/Fullscreen, background pause, remembered sound preference and native browser score persistence are available. All rules, menus and guidance execute inside `Drains`; the wrapper only transports input, audio and native services. See `PLAYER.txt` for controls and `../../amiga/RELEASE.md` for candidate packages and remaining checks.

The emulator/ROM source packaging is resolved; this is a **local release candidate**, not a published release. Physical A500 and phone compatibility still require device testing. Later sections record earlier development stages; references to a partial or silent build describe those stages.

Serve this folder over HTTP and open `index.html`. The browser downloads local WebAssembly and AROS ROM files, then mounts `DrainsPerf.exe`. vAmiga converts this executable into a temporary bootable AmigaDOS disk and runs its startup sequence. No Spectrum renderer or JavaScript version of the game is loaded here.

The screen is rendered by the emulated Amiga. The surrounding page only starts, pauses, resets and sends a left-mouse-button event to the emulator. The native benchmark is silent; the playable preview has native Paula sound and mobile controls.

## Dependencies and provenance

The emulator is now built locally from vAmigaWeb commit `42bdfa2f70c0f1a34d6bad74a378c021cf04e42a`, with Emscripten 4.0.19 and one documented CMake shell-path patch. The unmodified AROS ROM pair identifies AROS commit `3ffc074471670bdf47d10f12c9e4244037680f05` in its embedded revision and matches the pinned upstream pair. ROM source provenance is established; the upstream nightly has not been independently rebuilt.

The native-browser candidate includes complete source archives, notices, integration source, build instructions and SHA-256 provenance under `sources/`. See `sources/README.md` (repository: `amiga/third-party/README.md`). Keep that directory with all public distributions. The emulator and browser transport are GPL v3; AROS uses APL 1.1 plus the component terms in its LEGAL file. Emscripten, zlib and upstream library notices are preserved. Native game/artwork licensing is unchanged. No Commodore ROM or Workbench files are included.

The startup snapshot was recaptured using the rebuilt emulator. Packaging rejects changed binaries, missing source archives and stale snapshots. This is a local candidate; hardware, mobile and portal acceptance still require release testing.

## Tested result (2026-10-05)

- Successful native boot and scrolling character workload in the desktop browser; 50.0 emulated frames/sec observed.
- The optimised native counter displayed 99–100 ticks per 50 completed updates (25 updates/sec), up from the original 240–241 ticks (about 10.4 updates/sec). Emulator refresh and native drawing throughput are different measurements.
- AROS failed to launch the program with only 512 KB chip + 512 KB slow RAM. This prototype explicitly adds 2 MB fast RAM; it does not validate the planned 1 MB A500 target.
- Pause/resume and native exit restoring the AROS Workbench display verified. Phone-sized viewport controls verified; physical mobile performance remains untested.
- The current native renderer uses direct masked blits, hardware scrolling and two buffers switched by the Copper. The playable edition now ports the core game and sound; the benchmark remains a separate graphics test. Hardware scrolling matched the 25 fps direct-blitter full-copy result; it did not reach 50 fps.

Host tests: `node test/native-browser-host.cjs` from the repository root. These check the native binary and dependency hashes, loading, pause/resume, restart without duplicate animation loops, mouse release, and background pause. Run `python3 amiga/build-native-browser.py` after rebuilding native code. It verifies pinned dependency hashes, copies the exact native executable, updates its checksum, and refreshes the host script cache key.


## Playable native preview

Open `index.html?play` to boot `Drains.exe`, a byte-for-byte copy of `amiga/native/Drains`. This runs the native induction and an endless survey with twelve visible rows. Instructions, hat-loss feedback, lamp effects, clue recounting, scoring, scrolling, levels and retry are drawn/executed in 68000 code. Arrow keys or Q/A/O/P and Space, or the touch panel, send port-2 joystick input. Quick taps are held briefly so the native poll can see them; pause/background loss releases controls. The page fits a 320×568 portrait viewport with build controls collapsed. Physical phone testing remains outstanding.

This is still a partial port, not yet the complete game port, and remains silent. The benchmark is preserved at the default URL. This paragraph describes an earlier partial-port stage; the current candidate includes the playable native game and its dependency sources.


## Fast opening and larger field (2026-10-05)

The playable URL restores a clean native title-screen snapshot. It contains the running 68000 program and AROS state, including ROMs; no browser implementation of the game is substituted. The compressed snapshot is 1,157,111 bytes (about 1130 KiB), in addition to the emulator runtime. Local desktop verification reached the visible, playable opening in about 0.1 seconds after Play, once the emulator was loaded. This is not an internet cold-download or physical-phone timing. Restart restores the same opening without rebooting AROS.

The field is now 240×240 pixels, with the same ten columns and twelve rows. Tiles are 24×20, the sidebar uses a compact six-pixel text advance, and the browser display grows to fit available space up to 720 pixels wide. Verified at 320×568 and 390×844 portrait viewports and the default desktop viewport. The induction remains entirely native.

`build.json` fingerprints the executable and pinned runtime dependencies. The host checks this against `boot.json`, verifies the downloaded snapshot's SHA-256, and falls back to normal boot when the snapshot is missing, stale or damaged. `?play&cold` explicitly tests normal boot. Run host tests with `PLAY=1 SNAPSHOT=valid`, `stale`, or `corrupt`; the main native build runs them all.

After changing the executable or emulator, refresh the opening:

1. Run `python3 amiga/build-native.py --assembler /path/to/vasmm68k_mot`.
2. Run `python3 amiga/capture-boot.py` and open `http://127.0.0.1:8766/amiga-native/?play&capture`.
3. Press Play; wait for the native title screen. Do not move or advance it.
4. Press **Capture untouched opening**. This releases controls, disables warp, pauses, and saves the snapshot and manifest through a loopback-only endpoint.
5. Test a fresh `?play` page and restart. Stop the capture server when finished; normal serving requires only static files.

A changed native build invalidates the previous snapshot automatically. The current native-browser candidate includes the snapshot and emulator alongside their dependency source archives. Keep the sources directory when redistributing. Physical Amiga startup is unaffected by this browser optimization.


## Endless native survey (2026-10-05)

The short authored survey is replaced by procedural scrolling. Ten columns and twelve visible rows are unchanged. Four offscreen rows keep incoming clues final, and a retained southern row preserves counts at the bottom edge. The native generator guarantees a connected safe route; the camera keeps the retreat row when dropping it would strand the player. Distance, five-digit score, ten difficulty levels, level awards, hats and lamps are implemented in 68000 code.

The CPU regression runs 700 scrolling rows through actual assembled routines. Full browser-generator parity and persistent scores remain future port work. No new gameplay modes or browser-driven rules were introduced.


## Scrolling performance correction (2026-10-06)

The native scrolling path now uses five descending blits to shift the existing 240×220-pixel field down by one row. It shifts the tile cache with it, so only the incoming row and genuinely changed squares repaint. Recounting is limited to the new top two logical rows, and flood continuation checks only the ten cells bordering the newly finalised row. The sidebar updates individual changed characters; movement cooldown expiry alone no longer requests a redraw.

On the same seeded twenty-scroll CPU profile, mean executed 68000 instructions per movement+render fell from 209,097 to 37,569 (82.0% less), and mean tile repaints fell from 120 to 10.1. These are CPU instruction counts, not a wall-clock speedup or physical-A500 frame-rate claim: DMA transfer time and display waits are excluded. Reproduce the current profile with `python3 test/native-game-logic.py --assembler /path/to/vasmm68k_mot --profile` (requires Unicorn).

Regression coverage includes 700 scrolling rows, preserved neighbour counts/routes, the actual blitter register setup against an independent overlapping-copy model, untouched sidebar/border pixels, and incremental text matching a complete repaint.

Final browser check: successive touch-button steps reached eleven yards through repeated scrolls, with clean field/sidebar rendering and pickup updates. Refreshed native snapshot retained approximately 0.1-second local startup after the runtime loaded.


## Native enemies and coins (2026-10-06)

The native survey now includes coin rewards and the foreman, goat and resident, introduced at levels two, three and four. Six actor slots support two of each type at level nine. Collisions, knockback, temporary protection, lamp dismissal and group awards run in 68000 code. Masked sprites preserve underlying field clues; no browser-side game rules were added. The 20-scroll CPU profile remains around 37,500 instructions per movement/render and 10.1 repainted tiles with no active enemies; active actors add their own drawing and update cost.

Saved scores, daily surveys, further animation polish and exact procedural-generator parity remain unfinished. Physical A500/phone validation and public emulator dependency packaging are still required before release.


Native animation pass adds alternating character gaits, coin-width frames, pickup sparkles, lamp bursts and hit feedback/blinking. These are native masked sprites, with no browser-side gameplay animation. Effects expire and idle frames stop; ongoing enemies animate at a bounded five updates per second. Native chip allocation is 202,596 bytes. All 18 frames pass the native draw-boundary tests, alongside gameplay and animation-timer regressions. Native sound and character interpolation are now implemented (below).


Current native polish adds six-tick player/enemy interpolation, cone/puddle obstacle variants and a framed information panel with capacity counters and separate progress/instruction areas. Camera scrolling still advances a row at a time. The native executable owns five original PCM sound effects; audio.device reserves two Paula channels and releases them on exit. `audio-worklet.js` transports the emulator's stereo output, with bounded buffering, mute/pause clearing and stale-sample discard on resume. No gameplay or sound synthesis was moved into the browser. Running-preview diagnostics confirmed a nonzero native Paula signal. Tests now cover all pixel shifts, native sample addresses/expiry, sidebar cache restoration, obstacle stability and audio queue lifecycle.


The native generator no longer clears a three-wide safety corridor. It preserves only the minimum connected exit cells, allowing the surrounding random drains to interrupt zero-region flooding. Natural clearings still reveal normally. A 30-seed regression comparison reduced fully revealed forward routes from 69% to 19%, with safe connectivity and existing clue stability preserved. Restart the game to generate a corrected field.


Current presentation: the native illustrated title offers Start Survey and Staff Induction. The right panel now follows the HTML5 Borough Survey styling, with a large score and graphical equipment. Native quiet/obstacle/pickup/level commentary is restored, and induction instructions occupy a field callout. Coins, hats, lamps and puddles animate while the player stands still. The native renderer restores dirty areas per buffer instead of copying the whole screen, and removes an extra frame wait. Its controlled regression copies 85% fewer steady-frame bitmap bytes; actual physical A500/phone FPS is still unverified. Chip allocation is 344,476 bytes. Browser code remains a display/audio/input transport.

Native effects now include footstep dust, pickup bursts and shadowed floating score/equipment/hat-loss labels. They run inside the executable, continue without input and use a bounded 12-particle/two-label pool. Tile clues 1–8 use distinct colours. Native transparent-label pixel tests and effect/full-repaint comparisons pass; final browser verification confirmed +10 coin and +1 HAT labels and updated inventory. Chip allocation is unchanged.

Native flood reveals now spread in breadth-first layers every two PAL ticks, with a brief cream highlight and a light chime for larger fills when audio is free. Logical clues update immediately; movement and lamp visibility remain responsive. Verified the expanding highlight and clean settled field in the emulator. Chip allocation is now 348,076 bytes.

Movement now requires a fresh directional press: holding a key, touch direction or native joystick moves only one square, and neutral/release rearms it. The actual 68000 regression holds input across 20 polls with expired cooldown, verifies one step, then releases/represses and verifies the next step. All gameplay/render tests pass.

### Native front end and records

The title has five joystick-selected entries: Start Survey, Daily Survey, Staff Induction, Staff Handbook and High Scores. Its three characters animate independently while waiting. The handbook has three pages; Fire advances and returns to the title. A finished survey is filed with Fire: qualifying scores offer three-letter native initials entry, then the sorted eight-place table. Up/down changes a letter, Left goes back and Fire confirms.

Standalone Amiga records use the 80-byte versioned `Drains.scores` file in the launch working directory. The executable validates loaded scores/names/order before accepting them. Save failure retains session records and displays a session-only notice; DOS disk requesters are suppressed/restored around I/O so unavailable media does not hide a requester behind the game. Physical A500 storage remains unverified.

In the browser, an emulator service adapter retains the same opaque 80-byte payload in localStorage (`mind-the-drains-native-records-v1`), supplies the current UTC Amiga-epoch day, and acknowledges native requests. It does not rank scores, edit initials, choose seeds, draw pages or run rules. A signature-identified runtime service block is discovered in emulated RAM, validated before use, and disabled on exit/reset; its address hint is recorded in the build-checked startup snapshot. Storage errors preserve native session records. The native executable continues to work without this adapter on AmigaOS.

Daily Survey offers an editable date for machines without a working clock (Left/Right changes day; Up returns; Fire starts). The browser defaults to current UTC, while standalone uses AmigaDOS's date. The date seeds the native rules-version-1 plan; a separate daily enemy random stream prevents timing from changing field generation. Daily maps are repeatable within this native edition, not identical to the separate HTML5 generator. The date stays in the survey sidebar; the score table shows the last filed daily date and best score for it.

Validation: actual 68000 tests cover menu routes, handbook paging, title timing, Gregorian leap dates, daily replay and enemy-RNG isolation, initials, ranking, malformed record rejection, DOS save/load ABI and failed-media fallback. Browser-adapter tests cover opaque save/load, absent/corrupt/unavailable storage and UTC input. Full field/render regression remains passing.

Final local verification (2026-10-08): all three handbook pages, title navigation, score table, current UTC daily date and daily gameplay were checked in vAmiga. The refreshed snapshot matched executable build `81e0748b` and reached the title in 0.2 seconds after Play with the runtime loaded. Cold-boot storage discovery now refreshes RAM ranges after AutoConfig assigns fast memory; regression checks cover that transition. Physical A500 performance and writable-media saving still need hardware validation.

### Sound and council report polish (9 Oct 2026)

Fire after the final hat loss now opens a native Council Survey Report with score, yards surveyed and best chain. A new personal or daily best earns a stamp and fanfare. Up/down selects filing (initials when qualified) or retry without filing. Daily retries preserve the selected date and map; every retry resets run statistics. Best chain is bounded at 65535; report yards are displayed up to 99999.

The Paula bank now contains twelve effects (32,256 chip bytes): distinct hat, lamp and coin pickups, a descending chain-loss cue, a quiet enemy-arrival cue, a record fanfare, and a chain reward whose pitch rises with the multiplier, alongside the original effects. Arrival cues wait for idle audio; chain rewards cannot replace pending pickups or damage. Playback retains channel ownership, stop-before-programming and bounded expiry. Total chip allocation is 366,508 bytes. No title music is included in this pass.

Validation: actual 68000 regression covers all twelve sample ranges and expiries, exact report totals, personal/daily stamps, daily retry map preservation, pickup register preservation, cue priority and rising reward pitch. The complete field/render suite remains passing. The native report bitmap was rendered and inspected with known totals. Browser startup snapshot matches build 09e0bd00; local Play reached the title in 0.3 seconds with the runtime loaded. Physical A500 performance, phone audio and listening balance remain to be checked.

### Last-hat pause and save fallback fix (9 Oct 2026)

Both drain and enemy last-hat loss enter a shared 36-PAL-tick transition (about 0.72 seconds; Fire can skip it). The report then opens automatically. Entering the report clears reveal waves, particles, floating labels, lamp/protection and pose timers so expired gameplay effects cannot force repeated full-page redraws. The report remains idle after settling.

Reproduced an AmigaDOS saving stall in vAmiga when the browser storage adapter was unavailable: score filing attempted disk I/O while the native game held display/blitter ownership. Standalone fallback now queues the latest 80-byte record payload and writes it on normal exit, after audio shutdown, DisownBlitter and restoration of the OS view. The table says RECORDS SAVE WHEN YOU EXIT. Browser-connected saves still use the existing immediate opaque-payload adapter. Closing/resetting an emulator without a normal native exit cannot flush a queued standalone save.

Validation: actual 68000 checks cover both death causes, the bounded transition, a clean idle report and deferred DOS calls. Full regressions pass. A real vAmiga/AROS integration fixture reproduced the old fallback stall; the fixed executable reached the report, filed to the table, returned to the title and successfully wrote the deferred file on intentional exit (recordstatus=1). No spontaneous Workbench exit was reproduced; physical A500 and phone verification remain outstanding. Startup snapshot refreshed for build 56e9a700.

### Audio cut-off and buffering fix (9 Oct 2026)

Native effects use a centred pair on Paula channels 0 and 1, with identical sample pointers, periods and volumes. Real vAmiga stereo output measured nonzero balanced left/right energy (within 0.03%) without digital clipping; this is one centred effect voice, not two independent voices.

Every PCM sample now includes a 1024-byte zero guard so DMA cannot replay its attack while waiting for frame-based expiry. Expiry uses rounded-up audible duration with a guard interval; rising chain playback scales expiry with its faster period. Lower/equal-priority queued effects wait for the playing effect's tail; higher-priority interruptions reduce both channel volumes before stopping/reprogramming DMA. Native samples total 44,544 chip bytes; total chip allocation is 378,796 bytes.

Browser Paula transport now prefills two stereo blocks, bounds its queue at four, and applies matched 64-sample fade-out/fade-in on underflow and recovery. Mute/pause still clear the queue. The worklet URL is versioned to avoid reusing the earlier transport from cache. No sound generation or gameplay moved into the browser.

Validation: complete actual 68000 regression passes with all twelve revised DMA ranges/expiry values and interruption priority/fade checks. Asset tests check every silent guard and PCM headroom. Worklet tests verify channel separation, prefill, latency bounds, queue clearing and dropout/recovery ramps. Updated emulator snapshot and preview use build 8ffd42ac. Listening balance still needs a human/phone playtest.

## Movement and scrolling optimization — 9 October 2026

Northward scrolling retains the same field, clues and one-step-per-press controls.
The camera safety BFS now works directly on cell indices, with identical neighbour
order and an independent 300-field BFS oracle. Bulk aligned copies shift the four
logical arrays and tile cache. Coordinate lookup tables remove repeated 68000
division from actor footprint restoration and tile placement; normal tile selection
skips lamp coordinate work when no lamp is active.

Camera motion invalidates the two field buffers separately from the sidebar.
Each scroll restore can retain an unchanged sidebar, avoiding 12,800 bytes of DMA
copying per buffer restoration (38,400 instead of 51,200). Changed HUD text and
items still mark both sidebars dirty. Actor footprints and busy scenes with all six
enemy slots, particles, floating scores, reveal rings and both buffers match a
forced full repaint pixel-for-pixel. The existing 700-row gameplay and fresh-press
movement checks pass.

The same seeded 20-scroll CPU profile fell from 43,604 to 39,743 instructions per
scroll (8.9%); tile repaints remain 11.0. Instruction counts exclude DMA time and
OS waits and are not a claim of 8.9% higher frame rate. This does not yet establish
performance on physical Amiga 500 hardware. Chip allocation stays 378,796 bytes.
Browser startup snapshot and standalone preview archive refreshed for build
`cb880875`. Browser smoke check covers survey entry, north movement and scrolling.

## Native field rules closer to HTML5 — 9 October 2026

The native generator now selects randomly among reachable frontier columns,
retains at least three open cells in each added row and an opening near the
player's column, and creates a small zero-number pocket in each finalised row
where unseen drains can safely be removed. Drain removal respects all already
revealed numbered neighbours. Four empty starting rows, the initial flood reveal
and zero starting lamps now match HTML5; the induction retains its practice lamp.

Pickups are placed only on unrevealed zero squares or open drains, with HTML5's
coin/lamp/hat probability tables at every level. Hidden-drain bait is drawn, the
lamp reveals the underlying hole, and falling clears the bait without awarding
it. The handbook warns about this. Existing eight-neighbour clues, fresh-press
movement and stable daily replay remain intact. Maps change with this rules build.

Actual 68000 clearing/item routines match 240 fixtures exported from the HTML5
implementation. Tests cover reachable connection selection/open/ahead guarantees
at all ten levels, 700 scrolling rows, unchanged revealed clues, pickup traps,
starting reveal and busy-scene compositor equivalence. Over 30 seeded surveys
and 2,580 scrolling rows, 379 frontiers (14.7%) had a fully revealed onward route;
1,035 (40.1%) required a fresh forward reveal. This avoids restoring the former
always-revealed route through a cleared ribbon.

This is closer behaviour, not seed-for-seed parity: native keeps a bounded
16-row window, its existing stable obstacle variants and one zero-pocket pass.
HTML5's additional five-row zero distribution passes and emergency weighted
carver remain distinct; native's connected row creation supplies its safe route.
Chip allocation remains 378,796 bytes. Snapshot and preview archive refreshed
for build `04d749f9`; physical Amiga 500 performance remains unverified.

Browser smoke check: refreshed title started in 0.2 seconds, opening flood settled,
five northward steps scrolled the field, and a zero-square lamp was collected
with the native equipment panel updating correctly.

## Native cast personality — 9 October 2026

The cast now has distinct short idle gestures: surveyor hat adjustment and
clipboard checking, foreman watch checking, goat grazing/tail flicks, and resident
pointing with a tea mug. Mirrored walking/resting/gesture frames face travel;
goat/resident facing tracks horizontal steps while foreman reversal keeps its
patrol direction. Walking poses play while movement interpolation is active,
instead of actors marching in place continuously. Idle gestures are staggered
without consuming random numbers. The native title uses the same cast poses.

Hat loss gives the surveyor a startled, slipped-hat pose. The lamp removes enemies
and awards points immediately, followed by a bounded 18-PAL-tick offended gesture
before their picture disappears. These render-only records follow scrolling,
expire independently and clear on restart/replacement; they are never collision
objects. Resident/foreman/goat reactions differ. Quiet commentary occasionally
mentions an actually present cast member without interrupting live event copy.

50 pre-exported, transparent 24x20 frames avoid runtime flipping or pixel work.
Additional chip artwork is 23,040 bytes; total chip allocation is now 401,836
bytes (248,236 art + 153,600 display). Physical A500 memory/performance still needs
hardware validation. Asset tests verify exact mirroring, distinct gestures and
padding; actual 68000 checks cover every frame/shift/edge, cosmetic state/RNG
isolation, facing, presence-aware text, departure expiry/reset and both-buffer
trail-free rendering alongside all existing field/control/audio regressions.
Build `feeea276`; browser snapshot and standalone preview archive refreshed.


## Local-network quick start

Snapshot checks use Web Crypto on HTTPS/localhost and the same portable SHA-256 verification when testing over an ordinary HTTP LAN address. Compressed-snapshot/decompression failures retry the raw snapshot with its own size/hash checks. Only an unavailable, stale or invalid snapshot falls back to a full boot, visibly labelled Quick start unavailable; bootMode/snapshotError/startupMs diagnostics are retained on the status element. A normal snapshot startup shows Loading quick-start snapshot, Starting the game, then Ready. AudioWorklet playback requires a secure context. HTTP LAN testing now falls back to legacy Web Audio with the same bounded stereo sample queue, dropout fades and pause clearing. HTTPS still uses AudioWorklet and is preferred for release. The legacy fallback runs on the main thread and may be less resistant to scheduling stalls; physical phone listening tests remain required.

Regression checks: `PLAY=1 SNAPSHOT=valid NO_CRYPTO=1 PACKED=broken node test/native-browser-host.cjs` verifies no-Web-Crypto startup, raw retry, no reset after restore, cached-byte restart and actual snapshot SHA-256. `PACKED=valid` covers gzip startup; stale/corrupt fixtures still take the full boot path.


## iPhone audio diagnosis

Play/Resume/Sound-on unlock the audio context during the tap before asynchronous loading. Where supported, the Audio Session playback category is requested. HTTP fallback includes a silent input clock for WebKit scheduling; this adds no game sounds. Controls and downloads shows whether output is waiting, paused, running or receiving native samples. Start a survey and move to trigger a native effect; the title has no soundtrack. Desktop sample-signal checks are not proof of physical iPhone audibility.
