9.0 KiB
Product
Scope: this record covers terrain studio, the world-authoring tool in Tools/Terrain/. The Unreal Engine
project it feeds (Salty) is operating context, not the subject; CLAUDE.md at the repository root remains that
project's authority. Game UI is out of scope here and will need its own record.
Platform
web
Users
The project's developers, the author of the generator among them. They can read the Go and the docs but are not in the generator daily, so the tool has to be self-explaining without being a tutorial.
The situation is a desktop browser at 127.0.0.1:8099 with a terminal open beside it running the binary, mouse
and keyboard, one person per process. The job is to decide what a planet is — where the continents sit, what rate
each region is rising at, which stretches of coast were drawn deliberately — and to find out whether it made the
world they meant before committing hours of solve to it.
Confirmed as durable: terrain studio is the permanent authoring tool for this project's worlds, not a scaffold waiting to be replaced by in-editor tooling.
Product Purpose
terrain studio is the painting front end to the terrain generator. An author paints a flat cylindrical world
map whose colours are geology; a JSON legend beside it says what each colour means in uplift mm/yr and
erodibility; a Braun-Willett stream-power simulation makes the terrain from it. The studio exists so that the
painting, the legend and the planet manifest can be edited in one place, against the map they affect, with the
consequence of each number visible as it is typed.
Success is that an author can tell, in four seconds rather than two hours, whether the planet they have described is the planet they wanted — and that when they commit to the bake, the result is the thing the four-second preview promised.
Positioning
Paint the uplift, never the height. A generator handed a painted surface erodes it into something else and throws the drainage network away; this one is handed a rate field, and the simulation produces the terrain. Every valley, divide, scarp and river mouth is a consequence of physics rather than a brush stroke. That is the reason the generator exists, and it is the constraint the interface is built around.
Two consequences a neighbouring heightmap painter could not truthfully copy:
- The brushes are the legend's classes. Painting and parameterising are the same act; there is no export step between a colour and what it means.
- The panel answers in ground, not in input. A rate typed in mm/yr is reported back as the hillslope angle it buys, and specifically as the typical angle — the median over the class, a third of the divide angle in tangent — because almost none of a map is divide and the divide figure is how a legend gets set two or three times too hot.
Operating Context
- Launched from a terminal:
terrain studio [path],--addrdefaulting to127.0.0.1:8099. Local only; no deployment, no accounts, no network beyond the loopback. - The binary holds the painting in memory and answers
/api/*. Openingpage.htmlfrom disk is the one mistake worth detecting: every fetch fails as a bareNetworkError, and the page says so instead of letting it. - Two registered sheets over one world. The class painting is the geology — every colour is an uplift rate
the solve answers for. The overlay is annotation the simulation has no opinion about: forests, settlements,
roads, and the stretches of coast the author wants left exactly where they drew them. Tabs, or
o, switch between them; the overlay always rides on top, dimmed while the brush is on the classes. - The loop is plan-then-bake. Plan is about four seconds: it cuts the planet into regions and returns a
report plus map layers (class, uplift, erodibility, overlay, regions), eroding nothing. Bake is about two
hours on the 100 km-round template; it runs in the server, survives a page reload, fills the preview in one
landmass at a time because the solve is decomposed per landmass, and can be cancelled.
terrain tilesthen writes 5 km detail tiles at 2 m, about twelve seconds each. - Saving patches text. Legend and manifest edits are written into the existing
*.legend.jsonandPlanet.jsonas patches, so the author's commentary in those files survives a save. - The same binary carries
generate,plan,bake,tilesandpalette. The studio is the only graphical surface in the toolchain; everything else is a terminal report.
Capabilities and Constraints
- One binary, no build step, no dependencies.
go.moddeclares zero third-party modules (Go 1.25, stdlib only) andpage.htmlis a single file embedded withgo:embed. No npm, bundler, framework, CDN or web font. The tool ships as one executable and must keep working offline. - Desktop, pointer and keyboard. Wheel to zoom, middle/right/shift-drag to pan,
[and]for brush size,oto switch sheets,Escapeto leave a map view. Not a touch or small-screen surface, and there is no reason for it to become one. - The canvas wraps in X, because the world is a cylinder. The seam is never an edge and a stroke across it is continuous, both on screen and in the pixels.
- Strokes are hard-edged discs written straight into
ImageData. Antialiasing is forbidden: a blended pixel is not a colour between two classes, it is a pixel that classifies as whichever third class sits near the midpoint. - Blank on the overlay is alpha, never a reserved colour, and the eraser writes transparent black. An opaque overlay pixel matching no mark is dropped and counted, not snapped to the nearest — the class legend's rule deliberately inverted, because most of an overlay is nothing.
- Every map view carries a key, and the key is generated by the code that drew the map, so the two cannot drift apart.
- The seed is a composition control, not a debug field. One painting is many worlds: the seed re-rolls the massifs, the rock, the faults, the initial relief and the coastline detail while the painting stays exactly as drawn. It sits next to a Re-roll button for that reason.
- Not present today, and not to be assumed: undo or edit history, concurrent editing (the server holds a single painting in memory), any persistence beyond the two PNGs and the two JSON files, and any authentication.
Evidence on Hand
Tools/Terrain/internal/studio/page.html— the shipped interface, andserver.gofor the API it speaks to.RawContent/World/Templates/— the first painting and the README explaining how to paint another:Map3_001.png,Map3.legend.json,Map3.overlay.json,Map3_cliff.legend.json,default.palette.json.RawContent/World/Planet.json— the planet manifest the studio patches.Docs/Terrain-Next.md— the working brief: how to run it, what each output map is for, what still looks wrong, and what has already been measured and rejected.Docs/Terrain.mdandDocs/Decisions.md(D-46 … D-60) hold the decision record.- Measured numbers exist and must not be re-guessed or rounded into slogans: plan ≈ 4 s; a 100 km-round bake ≈ 2 h; a 5 km detail tile ≈ 12 s; the coastal pass 7.9 s over 76 M cells at ≈ 8.3 GB peak against the solve's 3.6 GB; the sea-floor seam step 9.1 m → 0.32 m; seed 7 against 9342 moving 13.8 % of the uplift map and 24.9 % of the erodibility map; a real 45.9 × 19.8 km lowland baking to 0–47 m at a 0.61° median slope.
- Observed in the product's own copy but not confirmed as binding: lowercase panel headings, and notes that say why rather than what ("a mark you cannot see is one you will paint over"). Treat as evidence of an incumbent voice, not as an approved commitment.
- There are no committed screenshots or visual-regression goldens of the interface, no usage data, no users outside the team, and no published release. Future work must not fabricate any of them.
Product Principles
- The author steers in consequences, not in inputs. A number the panel accepts is reported back as the ground it makes — degrees, metres, kilometres, minutes — at the moment it is typed. A control that shows only its raw value has failed at the one job this tool has.
- Cheap answers before expensive ones. Four seconds and two hours are the two costs in this product. Every addition declares which side of that line it is on, and the four-second path must keep being able to answer "is this the world I meant".
- The picture and its key come from the same place. A map, report or preview is published with the legend that describes it, generated by the code that drew it. A map with no key is a picture.
- Nothing silently reinterprets what the author drew. Strokes are hard-edged, unmatched marks are dropped and counted rather than snapped, saves patch text and keep commentary. Where the tool cannot honour a mark, it says so in the report.
- One binary, no build. Any capability needing a package manager, a bundler or a network at runtime is out of scope here, however convenient it would be.