SAIL2D LITE · PROMPT + SPEC (ONE PAGE)

Sail2D LiteTop-down 2D browser sailing toy · auto-trimmed sail · table-driven speed · single self-contained page. This document is the complete contract: prompt and requirements together.

v1.0 · 2026-08-15 · raw prompt: prompt.md
Deliberately out of scope — no 3D, no WebGL, no libraries. No race, start line, timing, laps, scores or penalties: no time-capture concept at all. No manual sail trimming — the sail trims itself. No physics engine and no force calculation — boat speed comes from a static lookup table given below; the agent MUST NOT invent its own aerodynamic model. No waves, no leeway, no heel, no collision. Buoys are scenery only.

§0Kickoff — paste into Claude Code

Fetch https://says.hermione.online/sail2d-sim-lite/prompt.md and implement it
fully and autonomously in one session. Do not stop to ask questions; where
something is unspecified, choose the simplest option that keeps the
acceptance checklist passing. Deliverable is a single index.html.

§1Mission

Build a top-down 2D sailing toy on an HTML5 <canvas>: one small yacht seen from above on an endless checkerboard sea, a wind that drifts gently over time, and a scattering of buoys to sail around. The player only steers. Sail trim is automatic; boat speed is looked up from a fixed table of speed-vs-wind-angle and approached with lag so the boat feels like it has mass. HUD shows the wind and the boat's speed. Ships as one index.html that runs from any static host with no build step.

§2Conventions

§3Speed table — static input, do not derive

Boat target speed is a fixed fraction of true wind speed depending on |TWA|. Interpolate linearly between rows; below 30° the boat is in the no-go zone (target 0). Values are the design intent — copy them into the code verbatim as a constant array, do not fit curves or compute lift.

|TWA| °speed / TWSspeed at TWS 6 m/s (m/s)≈ knotspoint of sail
00.000.00.0head to wind (no-go)
250.000.00.0edge of no-go
300.301.83.5pinching
450.482.95.6close-hauled
600.583.56.8close reach
750.633.87.3close reach
900.664.07.7beam reach (fastest)
1050.643.87.5broad reach
1200.593.56.9broad reach
1350.533.26.2broad reach
1500.462.85.4run
1650.412.54.8run
1800.382.34.4dead run
targetSpeed = ratio(|TWA|) × TWS where ratio() is piecewise-linear interpolation over the first two columns. Between 25° and 30° the interpolation naturally ramps from 0 to 0.30.

§4Motion model — all rules

Integrate with dt clamped to ≤ 50 ms. State: position p, heading ψ, speed v ≥ 0. The boat only moves along its heading.

IDRule
2M-01Speed lag (the "hysteresis"): v approaches targetSpeed exponentially with two different time constants — τ_acc = 4 s when target > v, τ_dec = 8 s when target < v. Formula: v += (target − v) · (1 − exp(−dt/τ)). Consequence: tacking through the wind costs speed and the boat coasts rather than stopping dead; a boat pointed into the wind glides for a while.
2M-02Turning: rudder input r ∈ [−1, 1]; yaw rate ψ̇ = r · (15°/s + 5°/s · v), capped at 50°/s. Non-zero base rate so the boat can always turn out of irons.
2M-03Position: p += v · dt · (sin ψ, −cos ψ). No leeway, no drift, no current.
2M-04Auto trim (visual only): boom angle from centreline b = clamp(|TWA| · 0.5, 10°, 85°), drawn on the leeward side (opposite the sign of TWA). Swaps side when TWA changes sign; smooth over ~0.5 s. In the no-go zone (|TWA| < 25°) the sail is drawn centred and MAY flutter (±5° wobble).
2M-05Wind drift: TWD and TWS are smooth random walks. Every 3–6 s pick a new target: TWD target = base_TWD + uniform(−20°, +20°), TWS target = base_TWS + uniform(−1.5, +1.5) m/s (clamped ≥ 2 m/s). Ease the live TWD/TWS toward the targets with τ = 5 s. Wind never gusts abruptly and never leaves the ±20° / ±1.5 m/s band around the base. Defaults: base TWD 0°, base TWS 6 m/s; MUST be overridable via ?wind=deg&tws=mps.

§5World, drawing & buoys

IDRequirement
2W-01Checkerboard sea: two blue shades (e.g. #1a5c9e / #17538f, subtle), squares of 10 m, aligned to world coordinates (square edges at multiples of 10 m). Draw only the squares that intersect the viewport each frame using fillRect; the pattern MUST stay fixed to the world so scrolling is visible as the boat moves.
2W-02Buoys: exactly six, at fixed world positions: (0,−200), (170,−100), (170,100), (0,200), (−170,100), (−170,−100) in (x, y) metres (±20% fine). Each buoy is an orange filled circle r 1.5 m with a thin dark outline. Pure scenery — MUST NOT have collision, proximity, rounding or sequencing logic. They never move.
2W-03Yacht sprite (drawn in boat-local space, bow toward local −y, then rotated by heading): hull = a pointed polygon ~6 m long × 2 m wide, white fill, dark outline; small grey deck rectangle; mast = dark dot at ~1/3 from bow; boom = dark line 3 m long from the mast aft, rotated by 2M-04; sail = a light-grey filled triangle (or curved wedge) between mast and boom end. Nothing else.
2W-04Camera fixed on the boat (boat at screen centre). Whole world scrolls; north always up (no rotating camera).
2W-05Optional short fading wake (a few dots behind the stern) is a MAY — helps show motion; skip if in doubt.

§6Controls & HUD

IDRequirement
2C-01←/→ (and A/D): rudder, spring-returns to centre on release. R: reset boat to origin, heading 90°. H: toggle help overlay. Nothing else — there is no sheet control.
2H-01Wind indicator: a compass rose (circle with N/E/S/W ticks) in the top-right corner, with an arrow pointing in the direction the wind is blowing toward (i.e. from TWD across the rose), labelled with TWD in ° and TWS in kn. Live-updates as the wind drifts.
2H-02Speed readout: boat speed in kn, large monospace text, top-left. Also heading ° and |TWA| ° in smaller text underneath. Text lives in DOM overlays or is drawn on the canvas — either is fine.
2H-03Help overlay listing the controls, visible on first load, toggled with H.

§7Technical constraints

IDRequirement
2T-01Exactly one file index.html, inline CSS + JS, 2D canvas only. No libraries, no CDN, no build step, no frameworks, no WebGL.
2T-02Plain JavaScript. Keep the sim step a pure function of (state, input, dt), separate from drawing code, and put the speed table, time constants and wind band as named constants at the top of the script.
2T-03Canvas fills the window and resizes with it (handle devicePixelRatio). Smooth ≈60 fps on an ordinary laptop.
2T-04No console errors; sailing unattended for 5+ minutes produces no NaNs, wind stays inside its band, checkerboard stays aligned.

§8Acceptance checklist — walk it, then done

Definition of done — every box above ticked by manual play. There is no automated test suite; the checklist is the gate.
Sail2D Lite v1.0 · raw prompt.md