Two quality-of-life improvements landed today: a completely reworked sprite loading pipeline that eliminates the waterfall of individual image fetches, and a tighter main menu layout that breathes a little more comfortably on smaller screens.

Sprite Bundle Loader

Previously, every game session started with 80 separate HTTP requests — one per sprite PNG. Each image was decoded through the browser's Canvas API, resized on the CPU if needed, and then copied pixel-by-pixel into the WASM sprite storage. The overhead added up: parallel fetches meant the browser was juggling dozens of connections, canvas resize operations introduced extra allocations, and the loading screen had to track each asset individually.

The new approach packs all 80 sprites into a single binary file (sprites.bin) at build time using a new scripts/pack_sprites.mjs script. The bundle uses a simple custom format:

At runtime, a single fetch("assets/sprites.bin") retrieves the entire 5.1 MB bundle. The loader then walks the index and copies each sprite's pixel slice straight into the WASM sprite storage with Uint8Array.set(). No Canvas, no per-image decode, no individual HTTP connections — just one request and a fast memory copy.

The build scripts now run pack_sprites.mjs automatically on every ./build.sh and ./builddist.sh invocation, so the bundle is always fresh. Only sprites.bin and the OG image are shipped to dist/ — the raw PNG source tree stays out of the production bundle entirely.

Main Menu Layout Polish

The mode selection overlay received a pass to reduce visual crowding. Padding, margins, and gaps across the mode cards have been tightened uniformly, and the heading font size has been reduced from 1.6rem to 1.1rem to better match the compact pixel-art aesthetic. The container max-width was also widened slightly from 720px to 800px to make better use of the available canvas width.

The result is a menu that fits cleanly without scrolling on compact viewports, while still feeling open and navigable at full size.

Draggable Minimap

The minimap in Skirmish and AI-vs-AI modes now supports click-and-drag to scroll the camera, not just a single click-to-jump. Previously, clicking the minimap snapped the camera to the clicked world position. Now, holding the mouse button down and dragging pans the view continuously — handy for scanning across the map without repeatedly clicking.

Under the hood, minimap.zig gained three functions: minimapBounds() extracts the hit-test rectangle, repositionCamera() handles the coordinate mapping, and the new handleMouseMove() / handleMouseUp() pair manage drag state. The JS bridge was updated to forward mousemove and mouseup events to the corresponding WASM exports.

JS Correctness Fixes

A batch of correctness issues in main.js were cleaned up:

Skirmish AI Rebalancing

Fighter AI has been tuned to handle simultaneous base assault and miner harassment more effectively. Previously, a red-alert signal pulled every available fighter back to base defence, leaving miners completely unescorted. Now, odd-indexed fighters check whether a miner is actively in distress before joining the base rush — if so, they stay on escort duty while even-indexed fighters respond to the threat. The base still gets roughly half its fighters; miners retain meaningful coverage.

Fighters on miner escort also received a proactive intercept behaviour: if an enemy closes to within 1,200 units of the escorted miner, the fighter breaks off the follow pattern and engages immediately — before the first shot lands rather than after.

RPG Escort AI Improvements

The escort fighter logic in RPG mode received two adjustments. Miner protection used to be gated behind the player's base having no active threat, which meant miners could be picked off freely during a base assault. That gate has been removed for active attacks: if a miner has taken fire in the last 30 seconds, escorts respond regardless of base status. Proactive patrol scanning (no shots fired yet, but enemies are nearby) is still suppressed during base assaults to avoid splitting the squad.

Detection radii were also widened — the proactive enemy scan range grew from 2,000 to 3,000 units, the "area clear" threshold from 2,500 to 3,500 units, and the pursuit envelope when protecting a distressed ship from 2,500 to 4,000 units. Escorts now commit harder and chase attackers further before considering the area safe.

Home Key Base Selection Fix

Pressing the Home key in Skirmish re-centres the camera on your starting base. It now also selects the station and updates the right-hand panel selection state, so the base info panel opens immediately rather than showing nothing until the player manually clicks the station.

All of these changes are live in the current build. See you in the void, Commanders.